diff --git a/.gitignore b/.gitignore index 330ceb8..2b4aea1 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,7 @@ phpunit.xml # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 -# User-specific stuff: +# Admin-specific stuff: .idea ## File-based project format: diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e51c86..38c95bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,572 +1,15 @@ -# Changelog +## 0.1.0 - 2017-03-12 -All notable changes to this project will be documented in this file, in reverse chronological order by release. - -## 1.0.3 - 2016-09-01 - -### Added - -- [#93](https://github.com/zendframework/zend-expressive-skeleton/pull/93) adds - support for Pimple "extensions" (`$pimple->extend()`) via the `dependencies` - sub-key `extensions`, as follows: - - ```php - return [ - 'dependencies' => [ - SomeClass::class => ExtendingFactory::class, - ], - ]; - ``` - -- [#93](https://github.com/zendframework/zend-expressive-skeleton/pull/93) adds - support to the Pimple container script to allow wrapping `delegators` - (delegator factories from zend-servicemanager) as anonymous Pimple extensions. - -### Deprecated - -- Nothing. - -### Removed - -- [#102](https://github.com/zendframework/zend-expressive-skeleton/pull/102) - removes the development dependendy on ocramius/proxy-manager, as it is not - required. - -### Fixed - -- [#91](https://github.com/zendframework/zend-expressive-skeleton/pull/91) fixes - the Pimple factory caching to work correctly with invokable classes used as - factories. -- [#95](https://github.com/zendframework/zend-expressive-skeleton/pull/95) fixes - the prompt for a minimal install to ensure that only `n` and `y` (or uppercase - versions of each) are valid answers, looping until a valid answer is provided. -- [#101](https://github.com/zendframework/zend-expressive-skeleton/pull/101) - removes filp/whoops from the `composer.json` prior to prompting the user for - packages to install, ensuring it does not remain if a user selects a minimal - install or to not use whoops for development. -- [#109](https://github.com/zendframework/zend-expressive-skeleton/pull/109) - adds comprehensive, granular tests covering all functionality of the - installer, raising coverage from 40% to 100%. - -## 1.0.2 - 2016-04-21 - -### Added - -- Nothing. - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- [#85](https://github.com/zendframework/zend-expressive-skeleton/pull/85) - updates the Aura.Di dependency to stable 3.X versions. -- [#88](https://github.com/zendframework/zend-expressive-skeleton/pull/88) - modifies the installer to remove `composer.lock` from the `.gitignore` file - during initial installation. -- [#89](https://github.com/zendframework/zend-expressive-skeleton/pull/89) - updates the zend-stdlib dependency to allow usage of its v3 series. - -## 1.0.1 - 2016-03-17 - -### Added - -- Nothing. - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- [#53](https://github.com/zendframework/zend-expressive-skeleton/pull/53) - updates the default Pimple container script such that it now caches factory - instances for re-use. -- [#72](https://github.com/zendframework/zend-expressive-skeleton/pull/72) - updates the `composer.json` to remove the possibility of installing an - Expressive RC version, updates zend-servicemanager to allow using 3.0 - versions, and updates whoops to allow either 1.1 or 2.0 versions. -- [#80](https://github.com/zendframework/zend-expressive-skeleton/pull/80) - updates the default ProxyManager constraints to also allow v2 versions. -- [#81](https://github.com/zendframework/zend-expressive-skeleton/pull/81) - fixes an issue in the installer whereby specified constraints were not being - passed to Composer prior to dependency resolution/installation, resulting in - stale dependencies. -- [#78](https://github.com/zendframework/zend-expressive-skeleton/pull/78) - updates the shipped default error templates to remove error/exception display. - Users who really need this functionality can write their own templates; the - project aims to deliver a "safe by default" setting. - -## 1.0.0 - 2016-01-28 - -First stable release. - -### Added - -- Nothing. - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- [#69](https://github.com/zendframework/zend-expressive-skeleton/pull/69) - updates the links in templates to point to the new documentation site on - https://zendframework.github.io/zend-expressive/ instead of rtfd.org. - -## 1.0.0rc8 - 2016-01-21 - -Eighth release candidate. - -### Added - -- Nothing. - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- [#66](https://github.com/zendframework/zend-expressive-skeleton/pull/66) - adds the `'error' => true,` declaration to the `'error'` pipeline middleware - specification. -- [#67](https://github.com/zendframework/zend-expressive-skeleton/pull/67) - updates the `filp/whoops` dependency for installer development to `^1.1 || ^2.0`; - the two are compatible for our use cases, but we should prefer the latest - that can be installed. As 2.0 requires PHP 5.5.9, but our minimum PHP version - is 5.5.0, we must specify both. - -## 1.0.0rc7 - 2016-01-19 - -Seventh release candidate. - -### Added - -- Nothing. - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- [#64](https://github.com/zendframework/zend-expressive-skeleton/pull/64) - fixes the installer script to correctly rewrite the `require-dev` section - and ensure only the development dependencies selected, as well as base - requirements such as PHPUnit and PHP_CodeSniffer, are installed. As such, - the `--no-dev` flag is no longer required, and development dependencies - such as whoops are properly installed. - -## 1.0.0rc6 - 2016-01-19 - -Sixth release candidate. - -### Added - -- Nothing. - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- [#56](https://github.com/zendframework/zend-expressive-skeleton/pull/56) - updates the `composer serve` command to include the `public/index.php` script - as an argument. This ensures that asset paths that the application could - intercept and serve will be passed to the application (previously, the - built-in server would treat these as 404s, and never pass them to the - application). -- [#57](https://github.com/zendframework/zend-expressive-skeleton/pull/57) - updates the Apache configuration rules defined in `public/.htaccess` to omit - several that could prevent the application from intercepting requests for - assets. -- [#52](https://github.com/zendframework/zend-expressive-skeleton/pull/52) - fixes the switch statement in the `HomePageAction` class to ensure the - template name and documentation link are accurately found. -- [#59](https://github.com/zendframework/zend-expressive-skeleton/pull/59) - updates the `config/container.php` implementation for zend-servicemanager such - that it can work with either v2 or v3 of that library. -- [#60](https://github.com/zendframework/zend-expressive-skeleton/pull/60) - updates the zend-expressive-helpers dependency to `^2.0`, and updates the - `config/autoload/middleware-pipeline.global.php` to follow the changes in - middleware configuration introduced in [zend-expressive #270](https://github.com/zendframework/zend-expressive/pull/270). - The change introduces convention-based keys for "always" (execute before - routing), "routing" (routing, listeners that act on the route result, and - dispatching), and "error", with reasonable priorities to ensure execution - order. -- [#60](https://github.com/zendframework/zend-expressive-skeleton/pull/60) - fixes the documentation for `composer create-project` to include the - `--no-dev` flag; this is done as composer currently installs the development - dependencies listed before the installer script rewrites the `composer.json` - file. Running `composer update` or `composer install` within the project - directory after the initial installation will install the development - dependencies. - -## 1.0.0rc5 - 2015-12-22 - -Fifth release candidate. - -### Added - -- Nothing. - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- [#42](https://github.com/zendframework/zend-expressive-skeleton/pull/42) - fixes some grammatical issues in the questions presented by the installer. -- [#45](https://github.com/zendframework/zend-expressive-skeleton/pull/45) - fixes how JS and CSS assets are added to zend-view templates. -- [#48](https://github.com/zendframework/zend-expressive-skeleton/pull/48) - adds unit tests for the `OptionalPackages` class (which provides the Composer - installer scripts). -- [#49](https://github.com/zendframework/zend-expressive-skeleton/pull/49) - updates the Pimple support to Pimple v3, ensuring Pimple users are using the - latest stable release. - -## 1.0.0rc4 - 2015-12-09 - -Fourth release candidate. - -### Added - -- [#34](https://github.com/zendframework/zend-expressive-skeleton/pull/34) - updates the zend-view configuration to register a factory for - `Zend\View\HelperPluginManager`, as well as a `view_helpers` sub-key for - registering custom view helpers. -- [#37](https://github.com/zendframework/zend-expressive-skeleton/pull/37) - creates the subdirectories `src/App/` and `test/AppTest/`, moving the - subdirectories of each under those, and updating the `composer.json` - autoloading directives accordingly. This change will allow new projects to - implement a "modular" structure if desired, with a subdirectory per namespace. -- [#41](https://github.com/zendframework/zend-expressive-skeleton/pull/41) adds - the composer script "serve", which fires up the built-in PHP webserver on port - 8080; invoke using `composer serve`. - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- [#23](https://github.com/zendframework/zend-expressive-skeleton/pull/23) - updates the comment for the glob statements to ensure all 4 (not just 2!) - possible matches are detailed. -- [#24](https://github.com/zendframework/zend-expressive-skeleton/pull/24) - updates the `config/config.php` file to store cached configuration as a plain - PHP file, so that it can simply `include()`; this will be faster than using - JSON-serialized structures. -- [#30](https://github.com/zendframework/zend-expressive-skeleton/pull/30) - updates the Twig configuration to follow the changes made for - [zendframework/zend-expressive-twigrenderer 0.3.0](https://github.com/zendframework/zend-expressive-twigrenderer/releases/tag/0.3.0). - The old configuration format will still work, though users *should* update - their configuration to the new format. The change in this patch only affects - new installs. -- [#33](https://github.com/zendframework/zend-expressive-skeleton/pull/33) - updates to zendframework/zend-expressive-helpers `^1.2`. -- [#33](https://github.com/zendframework/zend-expressive-skeleton/pull/33) adds - configuration for auto-registering the new `Zend\Expressive\Helper\UrlHelperMiddleware` - as pipeline middleware; this fixes an issue when using the zend-view renderer - with the `url()` helper whereby the `UrlHelper` was being registered as a - route result observer too late to receive the `RouteResult`. -- [#40](https://github.com/zendframework/zend-expressive-skeleton/pull/40) - renames the namespace for the installer to `ExpressiveInstaller`. - -## 1.0.0rc3 - 2015-12-07 - -Third release candidate. - -### Added - -- [#20](https://github.com/zendframework/zend-expressive-skeleton/pull/20) adds - the ability to specify a "minimal" install; when selected, the installer will - install modified configuration, omit some files, and remove the default - middleware and public assets. -- [#27](https://github.com/zendframework/zend-expressive-skeleton/pull/27) adds - [zendframework/zend-expressive-helpers](https://github.com/zendframework/zend-expressive-helpers) - as a dependency, and integrates the helpers into the configuration. - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- [#13](https://github.com/zendframework/zend-expressive-skeleton/pull/13) - updates the installer to also remove the dependency on composer/composer - on completion. -- [#11](https://github.com/zendframework/zend-expressive-skeleton/pull/11) - moves the route middleware service definitions into the routes configuration - files. -- [#21](https://github.com/zendframework/zend-expressive-skeleton/pull/21) - updates `require` statements in generated configuration files to use the - `__DIR__` constant to ensure files are located relative to the origin file. -- [#25](https://github.com/zendframework/zend-expressive-skeleton/pull/25) and - [#29](https://github.com/zendframework/zend-expressive-skeleton/pull/29) - update minimum versions for each router and template implementation (final - versions for RC3 are all at `^1.0`). -- [#29](https://github.com/zendframework/zend-expressive-skeleton/pull/29) sets - the zend-expressive required version to `~1.0.0@rc || ^1.0`, to ensure a - stable version is always installed. - -## 1.0.0rc2 - 2015-10-20 - -Second release candidate. - -### Added - -- Nothing. - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- Updated expressive to RC2. -- Updated subcomponent versions in installer to `^0.2` - -## 1.0.0rc1 - 2015-10-19 - -First release candidate. - -### Added - -- Nothing. - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- Nothing. - -## 0.5.3 - 2015-10-16 - -### Added - -- [#8](https://github.com/zendframework/zend-expressive-skeleton/pull/8) adds a - routine to the installer that recursively removes the `src/Composer/` - directory of the skeleton, ensuring you have a clean start when creating a - project. - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- Nothing. - -## 0.5.2 - 2015-10-13 +Initial tagged release ### Added - -- [#7](https://github.com/zendframework/zend-expressive-skeleton/pull/7) adds a - dependency on zend-stdlib for the purposes of globbing and merging - configuration. +* Everything ### Deprecated - -- Nothing. +* Nothing ### Removed - -- Nothing. +* Nothing ### Fixed - -- Nothing. - -## 0.5.1 - 2015-10-11 - -### Added - -- Nothing. - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- [#6](https://github.com/zendframework/zend-expressive-skeleton/pull/6) updates - the zendframework/zend-view package configuration to remove the dependency on - zendframework/zend-i18n, as it is now handled in the standalone - zend-expressive-zendviewrenderer package. - -## 0.5.0 - 2015-10-10 - -### Added - -- Nothing. - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- [#3](https://github.com/zendframework/zend-expressive-skeleton/pull/3) updates - the skeleton to use zendframework/zend-expressive 0.4.0. - -## 0.4.0 - 2015-10-09 - -First release as zend-expressive-skeleton. - -### Added - -- Nothing. - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- Nothing. - -## 0.3.0 - 2015-09-12 - -### Added - -- Use zend-expressive template factories. -- Use the zend view url helper in the layout template. - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- Nothing. - -## 0.2.0 - 2015-09-11 - -### Added - -- [#bbb2e60](https://github.com/xtreamwayz/expressive-composer-installer/commit/bbb2e607af23e3ae23f6a9c71eb97c3c651c0ca1) adds PHPUnit tests. -- [#791c1c6](https://github.com/xtreamwayz/expressive-composer-installer/commit/791c1c63f324ca08d08e26375f3a356102bf2ad9) adds Whoops error handler. -- [e1d8d7bf](https://github.com/xtreamwayz/expressive-composer-installer/commit/e1d8d7bf5d5e2f51863fa59a37d1963405743201) adds config caching in production mode. - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- Nothing. - -## 0.1.1 - 2015-09-08 - -### Added - -- [#b4a0923](https://github.com/xtreamwayz/expressive-composer-installer/commit/b4a092386993227f8057d7ad4e0d9762659eefb0) adds support for Pimple 3.0.x. Still needs testing! - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- [#11](https://github.com/xtreamwayz/expressive-composer-installer/issues/11) fixes an issues where non stable packages are not being installed correctly. - -## 0.1.0 - 2015-09-07 - -Initial tagged release. - -### Added - -- Everything. - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- Nothing. +* Nothing diff --git a/CONDUCT.md b/CONDUCT.md index c663d2b..870544d 100644 --- a/CONDUCT.md +++ b/CONDUCT.md @@ -1,6 +1,6 @@ # Contributor Code of Conduct -The Zend Framework project adheres to [The Code Manifesto](http://codemanifesto.com) +DotKernel project adheres to [The Code Manifesto](http://codemanifesto.com) as its guidelines for contributor interactions. ## The Code Manifesto diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b6e2158..e87f9b8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,233 +1,3 @@ # CONTRIBUTING -## RESOURCES - -If you wish to contribute to Zend Framework, please be sure to -read/subscribe to the following resources: - - - [Coding Standards](https://github.com/zendframework/zf2/wiki/Coding-Standards) - - [Contributor's Guide](CONTRIBUTING.md) - - ZF Contributor's mailing list: - Archives: http://zend-framework-community.634137.n4.nabble.com/ZF-Contributor-f680267.html - Subscribe: zf-contributors-subscribe@lists.zend.com - - ZF Contributor's IRC channel: - #zftalk.dev on Freenode.net - -If you are working on new features or refactoring [create a proposal](https://github.com/zendframework/zend-expressive-skeleton/issues/new). - -## Reporting Potential Security Issues - -If you have encountered a potential security vulnerability, please **DO NOT** report it on the public -issue tracker: send it to us at [zf-security@zend.com](mailto:zf-security@zend.com) instead. -We will work with you to verify the vulnerability and patch it as soon as possible. - -When reporting issues, please provide the following information: - -- Component(s) affected -- A description indicating how to reproduce the issue -- A summary of the security vulnerability and impact - -We request that you contact us via the email address above and give the project -contributors a chance to resolve the vulnerability and issue a new release prior -to any public exposure; this helps protect users and provides them with a chance -to upgrade and/or update in order to protect their applications. - -For sensitive email communications, please use [our PGP key](http://framework.zend.com/zf-security-pgp-key.asc). - -## RUNNING TESTS - -To run tests: - -- Clone the repository: - - ```console - $ git clone git@github.com:zendframework/zend-expressive-skeleton.git - $ cd zend-expressive-skeleton - ``` - -- Install dependencies via composer: - - ```console - $ composer install - ``` - - **NOTE:** If you are wanting to test the installer itself, add the - `--no-scripts` flag to the `composer install` command. - - If you don't have `curl` installed, you can also download `composer.phar` from - https://getcomposer.org/: - - ```console - $ curl -sS https://getcomposer.org/installer | php -- - $ ln -s composer.phar composer - ``` - -- Run the tests using the "test" command shipped in the `composer.json`: - - ```console - $ composer test - ``` - -You can turn on conditional tests with the `phpunit.xml` file. -To do so: - - - Copy `phpunit.xml.dist` file to `phpunit.xml` - - Edit `phpunit.xml` to enable any specific functionality you - want to test, as well as to provide test values to utilize. - -## Running Coding Standards Checks - -First, ensure you've installed dependencies via composer, per the previous -section on running tests. - -To run CS checks only: - -```console -$ composer cs -``` - -To attempt to automatically fix common CS issues: - - -```console -$ composer cs-fix -``` - -If the above fixes any CS issues, please re-run the tests to ensure -they pass, and make sure you add and commit the changes after verification. - -## Recommended Workflow for Contributions - -Your first step is to establish a public repository from which we can -pull your work into the master repository. We recommend using -[GitHub](https://github.com), as that is where the component is already hosted. - -1. Setup a [GitHub account](http://github.com/), if you haven't yet -2. Fork the repository (http://github.com/zendframework/zend-expressive-skeleton) -3. Clone the canonical repository locally and enter it. - - ```console - $ git clone git://github.com:zendframework/zend-expressive-skeleton.git - $ cd zend-expressive-skeleton - ``` - -4. Add a remote to your fork; substitute your GitHub username in the command - below. - - ```console - $ git remote add {username} git@github.com:{username}/zend-expressive-skeleton.git - $ git fetch {username} - ``` - -### Keeping Up-to-Date - -Periodically, you should update your fork or personal repository to -match the canonical ZF repository. Assuming you have setup your local repository -per the instructions above, you can do the following: - - -```console -$ git checkout master -$ git fetch origin -$ git rebase origin/master -# OPTIONALLY, to keep your remote up-to-date - -$ git push {username} master:master -``` - -If you're tracking other branches -- for example, the "develop" branch, where -new feature development occurs -- you'll want to do the same operations for that -branch; simply substitute "develop" for "master". - -### Working on a patch - -We recommend you do each new feature or bugfix in a new branch. This simplifies -the task of code review as well as the task of merging your changes into the -canonical repository. - -A typical workflow will then consist of the following: - -1. Create a new local branch based off either your master or develop branch. -2. Switch to your new local branch. (This step can be combined with the - previous step with the use of `git checkout -b`.) -3. Do some work, commit, repeat as necessary. -4. Push the local branch to your remote repository. -5. Send a pull request. - -The mechanics of this process are actually quite trivial. Below, we will -create a branch for fixing an issue in the tracker. - -```console -$ git checkout -b hotfix/9295 -Switched to a new branch 'hotfix/9295' -``` - -... do some work ... - - -```console -$ git commit -``` - -... write your log message ... - - -```console -$ git push {username} hotfix/9295:hotfix/9295 -Counting objects: 38, done. -Delta compression using up to 2 threads. -Compression objects: 100% (18/18), done. -Writing objects: 100% (20/20), 8.19KiB, done. -Total 20 (delta 12), reused 0 (delta 0) -To ssh://git@github.com/{username}/zend-expressive-skeleton.git - b5583aa..4f51698 HEAD -> master -``` - -To send a pull request, you have two options. - -If using GitHub, you can do the pull request from there. Navigate to -your repository, select the branch you just created, and then select the -"Pull Request" button in the upper right. Select the user/organization -"zendframework" as the recipient. - -If using your own repository - or even if using GitHub - you can use `git -format-patch` to create a patchset for us to apply; in fact, this is -**recommended** for security-related patches. If you use `format-patch`, please -send the patches as attachments to: - -- zf-devteam@zend.com for patches without security implications -- zf-security@zend.com for security patches - -#### What branch to issue the pull request against? - -Which branch should you issue a pull request against? - -- For fixes against the stable release, issue the pull request against the - "master" branch. -- For new features, or fixes that introduce new elements to the public API (such - as new public methods or properties), issue the pull request against the - "develop" branch. - -### Branch Cleanup - -As you might imagine, if you are a frequent contributor, you'll start to -get a ton of branches both locally and on your remote. - -Once you know that your changes have been accepted to the master -repository, we suggest doing some cleanup of these branches. - -- Local branch cleanup - - ```console - $ git branch -d - ``` - -- Remote branch removal - - ```console - $ git push {username} : - ``` - - -## Conduct - -Please see our [CONDUCT.md](CONDUCT.md) to understand expected behavior when interacting with others in the project. +## RESOURCES \ No newline at end of file diff --git a/README.md b/README.md index 4a6ca0b..c734ec5 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,37 @@ # dot-admin -Admin DK application \ No newline at end of file + +Dotkernel web starter package suitable for admin applications. + +## Installation + +Create a new project directory and change dir to it. Run the following composer command +```bash +$ composer create-project -s dev dotkernel/dot-admin . +``` + +## Configuration + +* import the database schema, if you are using mysql, found in `data/dot-frontend.sql` +* if using admin and frontend, you can import the `dot-admin+frontend.sql` database +* remove the `.dist` extension of the files `local.php.dist` and `errorhandler.local.php.dist` located in `config/autoload` +* edit `local.php` according to your dev machine. Fill in the `database` configuration and mail configuration +* run the following command in your project root dir +```bash +$ composer development-enable +``` +This will enable dev mode having debug flag true and configuration caching off. It also make sure that any previously config cache is cleared. + +**Do not enable dev mode in production** + +* Next, run the following command in your project's directory +```bash +$ php -S 0.0.0.0:8080 -t public +``` +* visit `http://localhost:8080` in your browser + +**NOTE:** +If you still get exceptions or errors regarding some missing services, try running the following command +```bash +$ composer clear-config-cache +``` + diff --git a/bin/clear-config-cache.php b/bin/clear-config-cache.php index 9735ba3..dd5516a 100644 --- a/bin/clear-config-cache.php +++ b/bin/clear-config-cache.php @@ -4,10 +4,11 @@ * * Can also be invoked as `composer clear-config-cache`. * - * @see https://github.com/zendframework/zend-expressive-skeleton for the canonical source repository - * @copyright Copyright (c) 2017 Zend Technologies USA Inc. (http://www.zend.com) - * @license https://github.com/zendframework/zend-expressive-skeleton/blob/master/LICENSE.md New BSD License + * @see https://github.com/dotkernel/dot-admin/ for the canonical source repository + * @copyright Copyright (c) 2017 Apidemia (https://www.apidemia.com) + * @license https://github.com/dotkernel/dot-admin/blob/master/LICENSE.md MIT License */ + chdir(__DIR__ . '/../'); require 'vendor/autoload.php'; $config = include 'config/config.php'; diff --git a/composer.json b/composer.json index 38e8011..ef2be76 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,7 @@ } ], "require": { - "php": "^5.5 || ^7.0", + "php": "^7.1", "roave/security-advisories": "dev-master", "zendframework/zend-expressive": "^1.0", "zendframework/zend-expressive-fastroute": "^1.0", @@ -20,35 +20,43 @@ "zendframework/zend-i18n": "^2.7", "zendframework/zend-i18n-resources": "^2.5", "zendframework/zend-captcha": "^2.6", - "zendframework/zendservice-recaptcha": "^2.0", + "zendframework/zendservice-recaptcha": "^3.0", "zendframework/zend-text": "^2.6", "zendframework/zend-stdlib": "^3.0", "zendframework/zend-psr7bridge": "^0.2.2", - "zendframework/zend-config": "^2.6", + "zendframework/zend-config": "^3.1", "zendframework/zend-config-aggregator": "^0.2.0", "zendframework/zend-component-installer": "^0.6.0 || ~1.0", - "dotkernel/dot-helpers": "0.6.x-dev", - "dotkernel/dot-ems": "0.6.x-dev", - "dotkernel/dot-event": "0.6.x-dev", - "dotkernel/dot-session": "0.6.x-dev", - "dotkernel/dot-flashmessenger": "0.6.x-dev", - "dotkernel/dot-controller": "0.6.x-dev", - "dotkernel/dot-controller-plugin-flashmessenger": "0.6.x-dev", - "dotkernel/dot-controller-plugin-authentication": "0.6.x-dev", - "dotkernel/dot-controller-plugin-authorization": "0.6.x-dev", - "dotkernel/dot-controller-plugin-mail": "0.6.x-dev", - "dotkernel/dot-authentication": "0.6.x-dev", - "dotkernel/dot-authentication-service": "0.6.x-dev", - "dotkernel/dot-authentication-web": "0.6.x-dev", - "dotkernel/dot-authorization": "0.6.x-dev", - "dotkernel/dot-rbac": "0.6.x-dev", - "dotkernel/dot-rbac-guard": "0.6.x-dev", - "dotkernel/dot-user": "0.6.x-dev", - "dotkernel/dot-mail": "0.6.x-dev", - "dotkernel/dot-navigation": "0.6.x-dev", - "dotkernel/dot-twigrenderer": "0.6.x-dev", - "dotkernel/dot-log": "0.6.x-dev" + "dotkernel/dot-annotated-services": "^1.0", + "dotkernel/dot-authentication-service": "^0.1", + "dotkernel/dot-authentication-web": "^0.1", + "dotkernel/dot-cache": "^1.0", + "dotkernel/dot-controller": "^0.1", + "dotkernel/dot-controller-plugin-flashmessenger": "^0.1", + "dotkernel/dot-controller-plugin-authentication": "^0.1", + "dotkernel/dot-controller-plugin-authorization": "^0.1", + "dotkernel/dot-controller-plugin-forms": "^0.1", + "dotkernel/dot-controller-plugin-mail": "^0.1", + "dotkernel/dot-controller-plugin-session": "^0.1", + "dotkernel/dot-mapper": "^0.1", + "dotkernel/dot-event": "^0.1", + "dotkernel/dot-filter": "^1.0", + "dotkernel/dot-flashmessenger": "^0.1", + "dotkernel/dot-form": "^1.0", + "dotkernel/dot-helpers": "^0.1", + "dotkernel/dot-hydrator": "^1.0", + "dotkernel/dot-inputfilter": "^1.0", + "dotkernel/dot-log": "^1.0", + "dotkernel/dot-mail": "^0.1", + "dotkernel/dot-navigation": "^0.1", + "dotkernel/dot-paginator": "^1.0", + "dotkernel/dot-rbac": "^0.1", + "dotkernel/dot-rbac-guard": "^0.1", + "dotkernel/dot-session": "^1.0", + "dotkernel/dot-twigrenderer": "^0.1", + "dotkernel/dot-user": "^0.1", + "dotkernel/dot-validator": "^1.0" }, "require-dev": { "phpunit/phpunit": "^4.8", @@ -57,24 +65,16 @@ "mikey179/vfsstream": "^1.6", "zfcampus/zf-development-mode": "^3.1" }, - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/dotkernel/dot-ems" - }, - { - "type": "vcs", - "url": "https://github.com/dotkernel/dot-log" - } - ], "autoload": { "psr-4": { - "Dot\\Admin\\": "src/Admin/" + "Admin\\App\\": "src/App/", + "Admin\\Admin\\": "src/Admin/", + "Admin\\User\\": "src/User/" } }, "autoload-dev": { "psr-4": { - "Dot\\AdminTest\\": "test/AdminTest/" + "AdminTest\\App\\": "test/AppTest/" } }, "scripts": { @@ -92,5 +92,10 @@ "test": "phpunit --colors=always", "test-coverage": "phpunit --colors=always --coverage-clover clover.xml", "upload-coverage": "coveralls -v" + }, + "extra": { + "branch-alias": { + "dev-master": "0.2-dev" + } } } diff --git a/composer.lock b/composer.lock index e0b709a..a817914 100644 --- a/composer.lock +++ b/composer.lock @@ -4,22 +4,25 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "e713294c87ac9d49e60b5b04844aa867", + "content-hash": "fe3c6792853effcc704247a20b087f95", "packages": [ { "name": "container-interop/container-interop", - "version": "1.1.0", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/container-interop/container-interop.git", - "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e" + "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/container-interop/container-interop/zipball/fc08354828f8fd3245f77a66b9e23a6bca48297e", - "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e", + "url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8", + "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8", "shasum": "" }, + "require": { + "psr/container": "^1.0" + }, "type": "library", "autoload": { "psr-4": { @@ -31,24 +34,277 @@ "MIT" ], "description": "Promoting the interoperability of container objects (DIC, SL, etc.)", - "time": "2014-12-30T15:22:37+00:00" + "homepage": "https://github.com/container-interop/container-interop", + "time": "2017-02-14T19:40:03+00:00" + }, + { + "name": "doctrine/annotations", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "54cacc9b81758b14e3ce750f205a393d52339e97" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/54cacc9b81758b14e3ce750f205a393d52339e97", + "reference": "54cacc9b81758b14e3ce750f205a393d52339e97", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "doctrine/cache": "1.*", + "phpunit/phpunit": "^5.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "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": "2017-02-24T16:22:25+00:00" + }, + { + "name": "doctrine/cache", + "version": "v1.6.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/cache.git", + "reference": "b6f544a20f4807e81f7044d31e679ccbb1866dc3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/cache/zipball/b6f544a20f4807e81f7044d31e679ccbb1866dc3", + "reference": "b6f544a20f4807e81f7044d31e679ccbb1866dc3", + "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": "2016-10-29T11:16:17+00:00" + }, + { + "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-09T13:34:57+00:00" + }, + { + "name": "dotkernel/dot-annotated-services", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/dotkernel/dot-annotated-services.git", + "reference": "ef50f79d5cff31265b1b500d331d826e50ccbe92" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dotkernel/dot-annotated-services/zipball/ef50f79d5cff31265b1b500d331d826e50ccbe92", + "reference": "ef50f79d5cff31265b1b500d331d826e50ccbe92", + "shasum": "" + }, + "require": { + "doctrine/annotations": "^1.3", + "doctrine/cache": "^1.6", + "php": "^7.1", + "zendframework/zend-servicemanager": "^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.7", + "squizlabs/php_codesniffer": "^2.5" + }, + "suggest": { + "doctrine/cache": "To cache the result of processing the annotations and speed-up your application" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Dot\\AnnotatedServices\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Popa Tiberiu", + "email": "tibi@apidemia.com" + } + ], + "description": "DotKernel service creation component through zend-servicemanager and annotations", + "keywords": [ + "annotations", + "container", + "factories", + "service-manager", + "services", + "zf2", + "zf3" + ], + "time": "2017-03-10T20:58:45+00:00" }, { "name": "dotkernel/dot-authentication", - "version": "dev-master", + "version": "0.1.1", "source": { "type": "git", "url": "https://github.com/dotkernel/dot-authentication.git", - "reference": "ea271a2c71c8dcdb0e415b6c7d4a221012a3ef3f" + "reference": "f151ed11d6173b08cf66101f8e50d27ec914f52b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dotkernel/dot-authentication/zipball/ea271a2c71c8dcdb0e415b6c7d4a221012a3ef3f", - "reference": "ea271a2c71c8dcdb0e415b6c7d4a221012a3ef3f", + "url": "https://api.github.com/repos/dotkernel/dot-authentication/zipball/f151ed11d6173b08cf66101f8e50d27ec914f52b", + "reference": "f151ed11d6173b08cf66101f8e50d27ec914f52b", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", + "php": "^7.1", "psr/http-message": "^1.0" }, "require-dev": { @@ -58,7 +314,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "0.6-dev" + "dev-master": "0.2-dev" } }, "autoload": { @@ -77,27 +333,27 @@ } ], "description": "Core interfaces for authentication implementations", - "time": "2017-01-17 16:47:31" + "time": "2017-03-10T21:06:47+00:00" }, { "name": "dotkernel/dot-authentication-service", - "version": "dev-master", + "version": "0.1.1", "source": { "type": "git", "url": "https://github.com/dotkernel/dot-authentication-service.git", - "reference": "6222de2ab27e3f2104a04493be8abc08eafe9431" + "reference": "4a780b87deef94ccd67a6a256f38984d7522e29e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dotkernel/dot-authentication-service/zipball/6222de2ab27e3f2104a04493be8abc08eafe9431", - "reference": "6222de2ab27e3f2104a04493be8abc08eafe9431", + "url": "https://api.github.com/repos/dotkernel/dot-authentication-service/zipball/4a780b87deef94ccd67a6a256f38984d7522e29e", + "reference": "4a780b87deef94ccd67a6a256f38984d7522e29e", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", - "dotkernel/dot-authentication": "0.6.x-dev", - "dotkernel/dot-helpers": "0.6.x-dev", - "php": "^5.6 || ^7.0", + "dotkernel/dot-authentication": "^0.1", + "dotkernel/dot-hydrator": "^1.0", + "php": "^7.1", "psr/http-message": "^1.0", "zendframework/zend-authentication": "^2.5", "zendframework/zend-servicemanager": "^3.1" @@ -106,20 +362,18 @@ "phpunit/phpunit": "^4.8", "squizlabs/php_codesniffer": "^2.3", "zendframework/zend-db": "^2.8", - "zendframework/zend-hydrator": "^2.2", "zendframework/zend-psr7bridge": "^0.2", "zendframework/zend-session": "^2.7" }, "suggest": { "zendframework/zend-db": "Needed if you are using the CallbackCheckAdapter", - "zendframework/zend-hydrator": "Used by the authentication adapters to hydrate identity objects", "zendframework/zend-psr7bridge": "Util lib for converting ZF3 HTTP messages to PSR7 and vice-versa", "zendframework/zend-session": "Used by the SessionStorage to store authentication identity" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.6-dev" + "dev-master": "0.2-dev" } }, "autoload": { @@ -137,30 +391,30 @@ "email": "tibi@apidemia.com" } ], - "description": "Concrete implementation of an AuthenticationInterface based on Zend Authentication", - "time": "2017-01-19 18:46:59" + "description": "DotKernel authentication service component based on zend-authentication", + "time": "2017-03-10T21:25:17+00:00" }, { "name": "dotkernel/dot-authentication-web", - "version": "dev-master", + "version": "0.1.1", "source": { "type": "git", "url": "https://github.com/dotkernel/dot-authentication-web.git", - "reference": "4eb770e6dd901d27f8e9a050b3583b22936cd4e5" + "reference": "c1420b0e79c9d57981a2cbde25936fbfe1038563" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dotkernel/dot-authentication-web/zipball/4eb770e6dd901d27f8e9a050b3583b22936cd4e5", - "reference": "4eb770e6dd901d27f8e9a050b3583b22936cd4e5", + "url": "https://api.github.com/repos/dotkernel/dot-authentication-web/zipball/c1420b0e79c9d57981a2cbde25936fbfe1038563", + "reference": "c1420b0e79c9d57981a2cbde25936fbfe1038563", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", - "dotkernel/dot-authentication": "0.6.x-dev", - "dotkernel/dot-event": "0.6.x-dev", - "dotkernel/dot-flashmessenger": "0.6.x-dev", - "dotkernel/dot-helpers": "0.6.x-dev", - "php": "^5.6 || ^7.0", + "dotkernel/dot-authentication": "^0.1", + "dotkernel/dot-event": "^0.1", + "dotkernel/dot-flashmessenger": "^0.1", + "dotkernel/dot-helpers": "^0.1", + "php": "^7.1", "psr/http-message": "^1.0" }, "require-dev": { @@ -173,12 +427,400 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "0.6-dev" + "dev-master": "0.2-dev" + } + }, + "autoload": { + "psr-4": { + "Dot\\Authentication\\Web\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "n3vrax", + "email": "tibi@apidemia.com" + } + ], + "description": "DotKernel login/logout authentication flow for web based applications", + "time": "2017-03-10T21:40:04+00:00" + }, + { + "name": "dotkernel/dot-authorization", + "version": "0.1.1", + "source": { + "type": "git", + "url": "https://github.com/dotkernel/dot-authorization.git", + "reference": "382b9e6199bab9695199d6900c6852e62ad37507" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dotkernel/dot-authorization/zipball/382b9e6199bab9695199d6900c6852e62ad37507", + "reference": "382b9e6199bab9695199d6900c6852e62ad37507", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^4.8", + "squizlabs/php_codesniffer": "^2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.2-dev" + } + }, + "autoload": { + "psr-4": { + "Dot\\Authorization\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "n3vrax", + "email": "tibi@apidemia.com" + } + ], + "description": "DotKernel authorization service abstractions", + "time": "2017-03-10T21:45:36+00:00" + }, + { + "name": "dotkernel/dot-cache", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/dotkernel/dot-cache.git", + "reference": "e142c594973d2cccfa0cc1947d5e8f92027e5297" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dotkernel/dot-cache/zipball/e142c594973d2cccfa0cc1947d5e8f92027e5297", + "reference": "e142c594973d2cccfa0cc1947d5e8f92027e5297", + "shasum": "" + }, + "require": { + "php": "^7.1", + "zendframework/zend-cache": "^2.7", + "zendframework/zend-servicemanager": "^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.7", + "squizlabs/php_codesniffer": "^2.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Dot\\Cache\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "n3vrax", + "email": "tibi@apidemia.com" + } + ], + "description": "DotKernel cache component extending zend-cache", + "keywords": [ + "cache", + "services" + ], + "time": "2017-03-10T21:58:23+00:00" + }, + { + "name": "dotkernel/dot-controller", + "version": "0.1.1", + "source": { + "type": "git", + "url": "https://github.com/dotkernel/dot-controller.git", + "reference": "8de640e969070b292d3b07971bcf1aaeea2212c0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dotkernel/dot-controller/zipball/8de640e969070b292d3b07971bcf1aaeea2212c0", + "reference": "8de640e969070b292d3b07971bcf1aaeea2212c0", + "shasum": "" + }, + "require": { + "container-interop/container-interop": "^1.1", + "dotkernel/dot-event": "^0.1", + "php": "^7.1", + "psr/http-message": "^1.0", + "zendframework/zend-servicemanager": "^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8", + "squizlabs/php_codesniffer": "^2.3", + "zendframework/zend-expressive-helpers": "^2.0", + "zendframework/zend-expressive-template": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.2-dev" + } + }, + "autoload": { + "psr-4": { + "Dot\\Controller\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "n3vrax", + "email": "tibi@apidemia.com" + } + ], + "description": "DotKernel controller like middleware component with plugin support", + "time": "2017-03-10T22:05:02+00:00" + }, + { + "name": "dotkernel/dot-controller-plugin-authentication", + "version": "0.1.1", + "source": { + "type": "git", + "url": "https://github.com/dotkernel/dot-controller-plugin-authentication.git", + "reference": "4b637c3e9c6067d40f51a16e2d91cc549fcc64e9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dotkernel/dot-controller-plugin-authentication/zipball/4b637c3e9c6067d40f51a16e2d91cc549fcc64e9", + "reference": "4b637c3e9c6067d40f51a16e2d91cc549fcc64e9", + "shasum": "" + }, + "require": { + "container-interop/container-interop": "^1.1", + "dotkernel/dot-authentication": "^0.1", + "dotkernel/dot-controller": "^0.1", + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^4.8", + "squizlabs/php_codesniffer": "^2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.2-dev" + } + }, + "autoload": { + "psr-4": { + "Dot\\Controller\\Plugin\\Authentication\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "n3vrax", + "email": "tibi@apidemia.com" + } + ], + "description": "DotKernel authentication controller plugin", + "time": "2017-03-10T22:13:15+00:00" + }, + { + "name": "dotkernel/dot-controller-plugin-authorization", + "version": "0.1.1", + "source": { + "type": "git", + "url": "https://github.com/dotkernel/dot-controller-plugin-authorization.git", + "reference": "3c5e054608d3a6a6347723f046c0b4fcc60ef23f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dotkernel/dot-controller-plugin-authorization/zipball/3c5e054608d3a6a6347723f046c0b4fcc60ef23f", + "reference": "3c5e054608d3a6a6347723f046c0b4fcc60ef23f", + "shasum": "" + }, + "require": { + "container-interop/container-interop": "^1.1", + "dotkernel/dot-authorization": "^0.1", + "dotkernel/dot-controller": "^0.1", + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^4.8", + "squizlabs/php_codesniffer": "^2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.2-dev" + } + }, + "autoload": { + "psr-4": { + "Dot\\Controller\\Plugin\\Authorization\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "n3vrax", + "email": "tibi@apidemia.com" + } + ], + "description": "DotKernel authorization controller plugin", + "time": "2017-03-10T22:20:42+00:00" + }, + { + "name": "dotkernel/dot-controller-plugin-flashmessenger", + "version": "0.1.1", + "source": { + "type": "git", + "url": "https://github.com/dotkernel/dot-controller-plugin-flashmessenger.git", + "reference": "13c6b40263b726a0a9b1e6770cf5aad51f14976f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dotkernel/dot-controller-plugin-flashmessenger/zipball/13c6b40263b726a0a9b1e6770cf5aad51f14976f", + "reference": "13c6b40263b726a0a9b1e6770cf5aad51f14976f", + "shasum": "" + }, + "require": { + "container-interop/container-interop": "^1.1", + "dotkernel/dot-controller": "^0.1", + "dotkernel/dot-flashmessenger": "^0.1", + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^4.8", + "squizlabs/php_codesniffer": "^2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.2-dev" + } + }, + "autoload": { + "psr-4": { + "Dot\\Controller\\Plugin\\FlashMessenger\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "n3vrax", + "email": "tibi@apidemia.com" + } + ], + "description": "DotKernel flash messenger controller plugin", + "time": "2017-03-10T22:31:23+00:00" + }, + { + "name": "dotkernel/dot-controller-plugin-forms", + "version": "0.1.1", + "source": { + "type": "git", + "url": "https://github.com/dotkernel/dot-controller-plugin-forms.git", + "reference": "6865153f196e7788419219eb014a789f30779b6f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dotkernel/dot-controller-plugin-forms/zipball/6865153f196e7788419219eb014a789f30779b6f", + "reference": "6865153f196e7788419219eb014a789f30779b6f", + "shasum": "" + }, + "require": { + "container-interop/container-interop": "^1.1", + "dotkernel/dot-controller": "^0.1", + "dotkernel/dot-flashmessenger": "^0.1", + "dotkernel/dot-form": "^1.0", + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^4.8", + "squizlabs/php_codesniffer": "^2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.2-dev" + } + }, + "autoload": { + "psr-4": { + "Dot\\Controller\\Plugin\\Forms\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "n3vrax", + "email": "tibi@apidemia.com" + } + ], + "description": "DotKernel controller forms plugin for easy access to forms inside controllers", + "time": "2017-03-10T22:37:15+00:00" + }, + { + "name": "dotkernel/dot-controller-plugin-mail", + "version": "0.1.1", + "source": { + "type": "git", + "url": "https://github.com/dotkernel/dot-controller-plugin-mail.git", + "reference": "398a6f6b095bf3d94b056ef2dc349f3983f0e38d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dotkernel/dot-controller-plugin-mail/zipball/398a6f6b095bf3d94b056ef2dc349f3983f0e38d", + "reference": "398a6f6b095bf3d94b056ef2dc349f3983f0e38d", + "shasum": "" + }, + "require": { + "container-interop/container-interop": "^1.1", + "dotkernel/dot-controller": "^0.1", + "dotkernel/dot-mail": "^0.1", + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^4.8", + "squizlabs/php_codesniffer": "^2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.2-dev" } }, "autoload": { "psr-4": { - "Dot\\Authentication\\Web\\": "src/" + "Dot\\Controller\\Plugin\\Mail\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -191,25 +833,28 @@ "email": "tibi@apidemia.com" } ], - "description": "Login/logout authentication flow for web based applications", - "time": "2017-01-19 19:10:51" + "description": "DotKernel mail controller plugin component", + "time": "2017-03-10T22:51:48+00:00" }, { - "name": "dotkernel/dot-authorization", - "version": "dev-master", + "name": "dotkernel/dot-controller-plugin-session", + "version": "0.1.1", "source": { "type": "git", - "url": "https://github.com/dotkernel/dot-authorization.git", - "reference": "4f734502001439d95239ec67179ef8b8b44185a9" + "url": "https://github.com/dotkernel/dot-controller-plugin-session.git", + "reference": "e90b34afe60c1ed4e279627e60993146ab086810" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dotkernel/dot-authorization/zipball/4f734502001439d95239ec67179ef8b8b44185a9", - "reference": "4f734502001439d95239ec67179ef8b8b44185a9", + "url": "https://api.github.com/repos/dotkernel/dot-controller-plugin-session/zipball/e90b34afe60c1ed4e279627e60993146ab086810", + "reference": "e90b34afe60c1ed4e279627e60993146ab086810", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "container-interop/container-interop": "^1.1", + "dotkernel/dot-controller": "^0.1", + "dotkernel/dot-session": "^1.0", + "php": "^7.1" }, "require-dev": { "phpunit/phpunit": "^4.8", @@ -218,12 +863,12 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "0.6-dev" + "dev-master": "0.2-dev" } }, "autoload": { "psr-4": { - "Dot\\Authorization\\": "src/" + "Dot\\Controller\\Plugin\\Session\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -236,44 +881,43 @@ "email": "tibi@apidemia.com" } ], - "description": "Authorization service abstractions", - "time": "2017-01-19 19:13:31" + "description": "DotKernel session controller plugin", + "time": "2017-03-10T22:55:53+00:00" }, { - "name": "dotkernel/dot-controller", - "version": "dev-master", + "name": "dotkernel/dot-event", + "version": "0.1.1", "source": { "type": "git", - "url": "https://github.com/dotkernel/dot-controller.git", - "reference": "bfe7326d2cccdc9a78efe1e58c4ca25d402d1b8d" + "url": "https://github.com/dotkernel/dot-event.git", + "reference": "1f2822d5de1ad9a620b4498546b3f0d8d0cc64e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dotkernel/dot-controller/zipball/bfe7326d2cccdc9a78efe1e58c4ca25d402d1b8d", - "reference": "bfe7326d2cccdc9a78efe1e58c4ca25d402d1b8d", + "url": "https://api.github.com/repos/dotkernel/dot-event/zipball/1f2822d5de1ad9a620b4498546b3f0d8d0cc64e5", + "reference": "1f2822d5de1ad9a620b4498546b3f0d8d0cc64e5", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", - "php": "^5.6 || ^7.0", + "php": "^7.1", "psr/http-message": "^1.0", + "zendframework/zend-eventmanager": "^3.0", "zendframework/zend-servicemanager": "^3.0" }, "require-dev": { "phpunit/phpunit": "^4.8", - "squizlabs/php_codesniffer": "^2.3", - "zendframework/zend-expressive-helpers": "^2.0", - "zendframework/zend-expressive-template": "^1.0" + "squizlabs/php_codesniffer": "^2.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.6-dev" + "dev-master": "0.2-dev" } }, "autoload": { "psr-4": { - "Dot\\Controller\\": "src/" + "Dot\\Event\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -286,42 +930,41 @@ "email": "tibi@apidemia.com" } ], - "description": "Controller like middleware with plugin support", - "time": "2017-01-19 19:19:14" + "description": "DotKernel event component extending and customizing zend-eventmanager", + "time": "2017-03-11T02:04:37+00:00" }, { - "name": "dotkernel/dot-controller-plugin-authentication", - "version": "dev-master", + "name": "dotkernel/dot-filter", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/dotkernel/dot-controller-plugin-authentication.git", - "reference": "a34a8abed145ca3892321dc926ac8ade7c1c0ec1" + "url": "https://github.com/dotkernel/dot-filter.git", + "reference": "297b62f39481cf1cb48639c9135d1d004b7c3571" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dotkernel/dot-controller-plugin-authentication/zipball/a34a8abed145ca3892321dc926ac8ade7c1c0ec1", - "reference": "a34a8abed145ca3892321dc926ac8ade7c1c0ec1", + "url": "https://api.github.com/repos/dotkernel/dot-filter/zipball/297b62f39481cf1cb48639c9135d1d004b7c3571", + "reference": "297b62f39481cf1cb48639c9135d1d004b7c3571", "shasum": "" }, "require": { - "container-interop/container-interop": "^1.1", - "dotkernel/dot-controller": "0.6.x-dev", - "php": "^5.6 || ^7.0" + "php": "^7.1", + "zendframework/zend-filter": "^2.7", + "zendframework/zend-servicemanager": "^3.0" }, "require-dev": { - "dotkernel/dot-authentication": "0.6.x-dev", - "phpunit/phpunit": "^4.8", - "squizlabs/php_codesniffer": "^2.3" + "phpunit/phpunit": "^4.7", + "squizlabs/php_codesniffer": "^2.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.6-dev" + "dev-master": "1.1-dev" } }, "autoload": { "psr-4": { - "Dot\\Controller\\Plugin\\Authentication\\": "src/" + "Dot\\Filter\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -334,42 +977,53 @@ "email": "tibi@apidemia.com" } ], - "description": "Authentication DK3 controller plugin", - "time": "2017-01-19 19:22:54" + "description": "DotKernel filter component customizing and extending zend-filter", + "keywords": [ + "container", + "dotkernel", + "factories", + "filter", + "service-manager", + "zf2", + "zf3" + ], + "time": "2017-03-11T02:09:29+00:00" }, { - "name": "dotkernel/dot-controller-plugin-authorization", - "version": "dev-master", + "name": "dotkernel/dot-flashmessenger", + "version": "0.1.1", "source": { "type": "git", - "url": "https://github.com/dotkernel/dot-controller-plugin-authorization.git", - "reference": "bf312185c3944e7e60d44b568a9bf764ff574178" + "url": "https://github.com/dotkernel/dot-flashmessenger.git", + "reference": "c803b00487c1526911204140172f1f39ffd7c141" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dotkernel/dot-controller-plugin-authorization/zipball/bf312185c3944e7e60d44b568a9bf764ff574178", - "reference": "bf312185c3944e7e60d44b568a9bf764ff574178", + "url": "https://api.github.com/repos/dotkernel/dot-flashmessenger/zipball/c803b00487c1526911204140172f1f39ffd7c141", + "reference": "c803b00487c1526911204140172f1f39ffd7c141", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", - "dotkernel/dot-controller": "0.6.x-dev", - "php": "^5.6 || ^7.0" + "dotkernel/dot-session": "^1.0", + "php": "^7.1", + "psr/http-message": "^1.0", + "zendframework/zend-servicemanager": "^3.0" }, "require-dev": { - "dotkernel/dot-authorization": "0.6.x-dev", "phpunit/phpunit": "^4.8", - "squizlabs/php_codesniffer": "^2.3" + "squizlabs/php_codesniffer": "^2.3", + "zendframework/zend-expressive-template": "^1.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.6-dev" + "dev-master": "0.2-dev" } }, "autoload": { "psr-4": { - "Dot\\Controller\\Plugin\\Authorization\\": "src/" + "Dot\\FlashMessenger\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -382,42 +1036,47 @@ "email": "tibi@apidemia.com" } ], - "description": "Authorization controller plugin for easy access to authorization service inside DK controllers", - "time": "2017-01-19 19:25:12" + "description": "DotKernel flash messenger component for session messages between redirects", + "time": "2017-03-11T02:13:58+00:00" }, { - "name": "dotkernel/dot-controller-plugin-flashmessenger", - "version": "dev-master", + "name": "dotkernel/dot-form", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/dotkernel/dot-controller-plugin-flashmessenger.git", - "reference": "49338082748c5eb02e85630fd04e13ad43a7210a" + "url": "https://github.com/dotkernel/dot-form.git", + "reference": "9862d67a15f2ff236eb13516d617e22acc409f37" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dotkernel/dot-controller-plugin-flashmessenger/zipball/49338082748c5eb02e85630fd04e13ad43a7210a", - "reference": "49338082748c5eb02e85630fd04e13ad43a7210a", + "url": "https://api.github.com/repos/dotkernel/dot-form/zipball/9862d67a15f2ff236eb13516d617e22acc409f37", + "reference": "9862d67a15f2ff236eb13516d617e22acc409f37", "shasum": "" }, "require": { - "container-interop/container-interop": "^1.1", - "dotkernel/dot-controller": "0.6.x-dev", - "php": "^5.6 || ^7.0" + "dotkernel/dot-filter": "^1.0", + "dotkernel/dot-hydrator": "^1.0", + "dotkernel/dot-inputfilter": "^1.0", + "dotkernel/dot-validator": "^1.0", + "php": "^7.1", + "zendframework/zend-form": "^2.9", + "zendframework/zend-servicemanager": "^3.0" }, "require-dev": { - "dotkernel/dot-flashmessenger": "0.6.x-dev", - "phpunit/phpunit": "^4.8", - "squizlabs/php_codesniffer": "^2.3" + "doctrine/annotations": "^1.3", + "dotkernel/dot-mapper": "^0.1", + "phpunit/phpunit": "^4.7", + "squizlabs/php_codesniffer": "^2.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.6-dev" + "dev-master": "1.1-dev" } }, "autoload": { "psr-4": { - "Dot\\Controller\\Plugin\\FlashMessenger\\": "src/" + "Dot\\Form\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -430,44 +1089,49 @@ "email": "tibi@apidemia.com" } ], - "description": "Flashmessenger DK controller plugin", - "time": "2017-01-19 19:29:11" + "description": "DotKernel form component extending and customizing zend-form", + "keywords": [ + "form", + "input filter", + "services" + ], + "time": "2017-03-11T02:17:20+00:00" }, { - "name": "dotkernel/dot-controller-plugin-mail", - "version": "dev-master", + "name": "dotkernel/dot-helpers", + "version": "0.1.1", "source": { "type": "git", - "url": "https://github.com/dotkernel/dot-controller-plugin-mail.git", - "reference": "c1b98ea6f21673010387ea20fb0bd6fc064e965e" + "url": "https://github.com/dotkernel/dot-helpers.git", + "reference": "de0700f0e0c5ea90702897edccb6a20728a071a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dotkernel/dot-controller-plugin-mail/zipball/c1b98ea6f21673010387ea20fb0bd6fc064e965e", - "reference": "c1b98ea6f21673010387ea20fb0bd6fc064e965e", + "url": "https://api.github.com/repos/dotkernel/dot-helpers/zipball/de0700f0e0c5ea90702897edccb6a20728a071a3", + "reference": "de0700f0e0c5ea90702897edccb6a20728a071a3", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", - "dotkernel/dot-controller": "0.6.x-dev", - "php": "^5.6 || ^7.0" + "php": "^7.1", + "psr/http-message": "^1.0", + "zendframework/zend-expressive-helpers": "^2.0", + "zendframework/zend-servicemanager": "^3.1" }, "require-dev": { - "dotkernel/dot-event": "0.6.x-dev", - "dotkernel/dot-helpers": "0.6.x-dev", - "dotkernel/dot-mail": "0.6.x-dev", "phpunit/phpunit": "^4.8", - "squizlabs/php_codesniffer": "^2.3" + "squizlabs/php_codesniffer": "^2.3", + "zendframework/zend-diactoros": "^1.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.6-dev" + "dev-master": "0.2-dev" } }, "autoload": { "psr-4": { - "Dot\\Controller\\Plugin\\Mail\\": "src/" + "Dot\\Helpers\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -480,58 +1144,44 @@ "email": "tibi@apidemia.com" } ], - "description": "Mail controller plugin for easy access of mail services inside DK controllers", - "time": "2017-01-19 19:33:27" + "description": "Various helper structures and classes", + "time": "2017-03-11T02:20:17+00:00" }, { - "name": "dotkernel/dot-ems", - "version": "dev-master", + "name": "dotkernel/dot-hydrator", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/dotkernel/dot-ems.git", - "reference": "db2f576999ce703322618246cf1ce8f6660b94ee" + "url": "https://github.com/dotkernel/dot-hydrator.git", + "reference": "ce1ff7ebd112e078d0b19abec5d356cdc94890c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dotkernel/dot-ems/zipball/db2f576999ce703322618246cf1ce8f6660b94ee", - "reference": "db2f576999ce703322618246cf1ce8f6660b94ee", + "url": "https://api.github.com/repos/dotkernel/dot-hydrator/zipball/ce1ff7ebd112e078d0b19abec5d356cdc94890c3", + "reference": "ce1ff7ebd112e078d0b19abec5d356cdc94890c3", "shasum": "" }, "require": { - "container-interop/container-interop": "^1.1", - "dotkernel/dot-event": "0.6.x-dev", - "dotkernel/dot-helpers": "0.6.x-dev", - "php": "^5.6 || ^7.0", + "php": "^7.1", "zendframework/zend-hydrator": "^2.2", - "zendframework/zend-paginator": "^2.7", "zendframework/zend-servicemanager": "^3.0" }, "require-dev": { - "phpunit/phpunit": "^4.8", - "squizlabs/php_codesniffer": "^2.3", - "zendframework/zend-db": "^2.8", - "zendframework/zend-validator": "^2.8" - }, - "suggest": { - "zendframework/zend-db": "Needed if you use the db mappers", - "zendframework/zend-validator": "Needed if you use the provided custom validators" + "phpunit/phpunit": "^4.7", + "squizlabs/php_codesniffer": "^2.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.6-dev" + "dev-master": "1.1-dev" } }, "autoload": { "psr-4": { - "Dot\\Ems\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "DotTest\\Ems\\": "test/" + "Dot\\Hydrator\\": "src" } }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -541,46 +1191,52 @@ "email": "tibi@apidemia.com" } ], - "description": "Entity-mapper-service abstraction layer", - "support": { - "source": "https://github.com/dotkernel/dot-ems/tree/master", - "issues": "https://github.com/dotkernel/dot-ems/issues" - }, - "time": "2017-01-19 19:40:34" + "description": "DotKernel hydrator component extending and customizing zend-hydrator", + "keywords": [ + "container", + "factories", + "hydrator", + "service-manager", + "services", + "zf2", + "zf3" + ], + "time": "2017-03-11T02:23:42+00:00" }, { - "name": "dotkernel/dot-event", - "version": "dev-master", + "name": "dotkernel/dot-inputfilter", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/dotkernel/dot-event.git", - "reference": "77828a61d71341f442256d7d6985543fac02ccac" + "url": "https://github.com/dotkernel/dot-inputfilter.git", + "reference": "0223cc8d821f4eef716729f0e2892279c1d75521" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dotkernel/dot-event/zipball/77828a61d71341f442256d7d6985543fac02ccac", - "reference": "77828a61d71341f442256d7d6985543fac02ccac", + "url": "https://api.github.com/repos/dotkernel/dot-inputfilter/zipball/0223cc8d821f4eef716729f0e2892279c1d75521", + "reference": "0223cc8d821f4eef716729f0e2892279c1d75521", "shasum": "" }, "require": { - "container-interop/container-interop": "^1.1", - "php": "^5.6 || ^7.0", - "psr/http-message": "^1.0", - "zendframework/zend-eventmanager": "^3.0" + "dotkernel/dot-filter": "^1.0", + "dotkernel/dot-validator": "^1.0", + "php": "^7.1", + "zendframework/zend-inputfilter": "^2.7", + "zendframework/zend-servicemanager": "^3.0" }, "require-dev": { - "phpunit/phpunit": "^4.8", - "squizlabs/php_codesniffer": "^2.3" + "phpunit/phpunit": "^4.7", + "squizlabs/php_codesniffer": "^2.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.6-dev" + "dev-master": "1.1-dev" } }, "autoload": { "psr-4": { - "Dot\\Event\\": "src/" + "Dot\\InputFilter\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -593,44 +1249,53 @@ "email": "tibi@apidemia.com" } ], - "description": "Common base event objects based on Zend Event Manager", - "time": "2017-01-19 20:34:03" + "description": "DotKernel input filter component extending and customizing zend-inputfilter", + "keywords": [ + "container", + "dotkernel", + "factories", + "input filter", + "service-manager", + "zf2", + "zf3" + ], + "time": "2017-03-11T02:26:01+00:00" }, { - "name": "dotkernel/dot-flashmessenger", - "version": "dev-master", + "name": "dotkernel/dot-log", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/dotkernel/dot-flashmessenger.git", - "reference": "99b97c23c8d49c7ad5b1c697b700ce6d391b7527" + "url": "https://github.com/dotkernel/dot-log.git", + "reference": "bcd1d2838d9bf61874e60be8c150e50ccab321a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dotkernel/dot-flashmessenger/zipball/99b97c23c8d49c7ad5b1c697b700ce6d391b7527", - "reference": "99b97c23c8d49c7ad5b1c697b700ce6d391b7527", + "url": "https://api.github.com/repos/dotkernel/dot-log/zipball/bcd1d2838d9bf61874e60be8c150e50ccab321a9", + "reference": "bcd1d2838d9bf61874e60be8c150e50ccab321a9", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", - "php": "^5.6 || ^7.0", + "php": "^7.1", "psr/http-message": "^1.0", - "zendframework/zend-servicemanager": "^3.0", - "zendframework/zend-session": "^2.7" + "zendframework/zend-log": "^2.9.0", + "zendframework/zend-servicemanager": "^3.0" }, "require-dev": { + "dotkernel/dot-mail": "^0.1", "phpunit/phpunit": "^4.8", - "squizlabs/php_codesniffer": "^2.3", - "zendframework/zend-expressive-template": "^1.0" + "squizlabs/php_codesniffer": "^2.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.6-dev" + "dev-master": "1.1-dev" } }, "autoload": { "psr-4": { - "Dot\\FlashMessenger\\": "src/" + "Dot\\Log\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -643,44 +1308,45 @@ "email": "tibi@apidemia.com" } ], - "description": "Flash messenger library for session messages between redirects", - "time": "2017-01-19 20:35:28" + "description": "DotKernel log component extending and customizing zend-log", + "time": "2017-03-11T02:29:00+00:00" }, { - "name": "dotkernel/dot-helpers", - "version": "dev-master", + "name": "dotkernel/dot-mail", + "version": "0.1.1", "source": { "type": "git", - "url": "https://github.com/dotkernel/dot-helpers.git", - "reference": "3aa60b7513658953f459bcbf421ecdf3d32ab2b9" + "url": "https://github.com/dotkernel/dot-mail.git", + "reference": "d6e66cadf3f691d318112131e615b228c7dea529" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dotkernel/dot-helpers/zipball/3aa60b7513658953f459bcbf421ecdf3d32ab2b9", - "reference": "3aa60b7513658953f459bcbf421ecdf3d32ab2b9", + "url": "https://api.github.com/repos/dotkernel/dot-mail/zipball/d6e66cadf3f691d318112131e615b228c7dea529", + "reference": "d6e66cadf3f691d318112131e615b228c7dea529", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", - "php": "^5.6 || ^7.0", - "psr/http-message": "^1.0", - "zendframework/zend-expressive-helpers": "^2.0", - "zendframework/zend-servicemanager": "^3.1" + "dotkernel/dot-event": "^0.1", + "dotkernel/dot-helpers": "^0.1", + "php": "^7.1", + "zendframework/zend-expressive-template": "^1.0", + "zendframework/zend-mail": "^2.7", + "zendframework/zend-servicemanager": "^3.0" }, "require-dev": { "phpunit/phpunit": "^4.8", - "squizlabs/php_codesniffer": "^2.3", - "zendframework/zend-diactoros": "^1.3" + "squizlabs/php_codesniffer": "^2.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.6-dev" + "dev-master": "0.2-dev" } }, "autoload": { "psr-4": { - "Dot\\Helpers\\": "src/" + "Dot\\Mail\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -693,53 +1359,52 @@ "email": "tibi@apidemia.com" } ], - "description": "Various helper structures and classes", - "time": "2017-01-19 20:42:47" + "description": "DotKernel mail component based on zend-mail", + "time": "2017-03-11T02:35:09+00:00" }, { - "name": "dotkernel/dot-log", - "version": "dev-master", + "name": "dotkernel/dot-mapper", + "version": "0.1.2", "source": { "type": "git", - "url": "https://github.com/dotkernel/dot-log.git", - "reference": "4f6a917c3702e8fb7d2fa80e384e0231b0de7145" + "url": "https://github.com/dotkernel/dot-mapper.git", + "reference": "8401b0ab623dec41b1624b268c5c88a78ee9718f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dotkernel/dot-log/zipball/4f6a917c3702e8fb7d2fa80e384e0231b0de7145", - "reference": "4f6a917c3702e8fb7d2fa80e384e0231b0de7145", + "url": "https://api.github.com/repos/dotkernel/dot-mapper/zipball/8401b0ab623dec41b1624b268c5c88a78ee9718f", + "reference": "8401b0ab623dec41b1624b268c5c88a78ee9718f", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", - "php": "^5.6 || ^7.0", - "psr/http-message": "^1.0", - "zendframework/zend-log": "^2.9.0", + "dotkernel/dot-event": "^0.1", + "dotkernel/dot-helpers": "^0.1", + "dotkernel/dot-hydrator": "^1.0", + "php": "^7.1", + "zendframework/zend-paginator": "^2.7", "zendframework/zend-servicemanager": "^3.0" }, "require-dev": { - "dotkernel/dot-event": "0.6.x-dev", - "dotkernel/dot-helpers": "0.6.x-dev", - "dotkernel/dot-mail": "0.6.x-dev", "phpunit/phpunit": "^4.8", - "squizlabs/php_codesniffer": "^2.3" + "squizlabs/php_codesniffer": "^2.3", + "zendframework/zend-db": "^2.8" + }, + "suggest": { + "zendframework/zend-db": "Needed if you use the db mappers" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.6-dev" + "dev-master": "0.2-dev" } }, "autoload": { "psr-4": { - "Dot\\Log\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "DotTest\\Log\\": "test/" + "Dot\\Mapper\\": "src/" } }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -749,49 +1414,47 @@ "email": "tibi@apidemia.com" } ], - "description": "Log component for DotKernel", - "support": { - "source": "https://github.com/dotkernel/dot-log/tree/master", - "issues": "https://github.com/dotkernel/dot-log/issues" - }, - "time": "2017-01-19 20:44:25" + "description": "DotKernel mapper pattern implementation", + "time": "2017-03-11T02:42:36+00:00" }, { - "name": "dotkernel/dot-mail", - "version": "dev-master", + "name": "dotkernel/dot-navigation", + "version": "0.1.1", "source": { "type": "git", - "url": "https://github.com/dotkernel/dot-mail.git", - "reference": "85d2c0fc155b58dac0c02029895d4dca1a678efe" + "url": "https://github.com/dotkernel/dot-navigation.git", + "reference": "8d1ec2d3ccf32c5a3072143bfad4c6af42114393" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dotkernel/dot-mail/zipball/85d2c0fc155b58dac0c02029895d4dca1a678efe", - "reference": "85d2c0fc155b58dac0c02029895d4dca1a678efe", + "url": "https://api.github.com/repos/dotkernel/dot-navigation/zipball/8d1ec2d3ccf32c5a3072143bfad4c6af42114393", + "reference": "8d1ec2d3ccf32c5a3072143bfad4c6af42114393", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", - "dotkernel/dot-event": "0.6.x-dev", - "dotkernel/dot-helpers": "0.6.x-dev", - "php": "^5.6 || ^7.0", + "php": "^7.1", + "psr/http-message": "^1.0", + "zendframework/zend-escaper": "^2.5", + "zendframework/zend-expressive-helpers": "^2.0", "zendframework/zend-expressive-template": "^1.0", - "zendframework/zend-mail": "^2.7", - "zendframework/zend-servicemanager": "^3.0" + "zendframework/zend-servicemanager": "^3.1" }, "require-dev": { + "dotkernel/dot-authorization": "^0.1", "phpunit/phpunit": "^4.8", - "squizlabs/php_codesniffer": "^2.3" + "squizlabs/php_codesniffer": "^2.3", + "zendframework/zend-stdlib": "^3.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.6-dev" + "dev-master": "0.2-dev" } }, "autoload": { "psr-4": { - "Dot\\Mail\\": "src/" + "Dot\\Navigation\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -804,47 +1467,44 @@ "email": "tibi@apidemia.com" } ], - "description": "Mail module for DotKernel based on Zend Mail", - "time": "2017-01-19 20:55:28" + "description": "DotKernel navigation component", + "time": "2017-03-11T12:30:28+00:00" }, { - "name": "dotkernel/dot-navigation", - "version": "dev-master", + "name": "dotkernel/dot-paginator", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/dotkernel/dot-navigation.git", - "reference": "eb164c14c5b0d4406ea8547c2c3263d2b724bf6d" + "url": "https://github.com/dotkernel/dot-paginator.git", + "reference": "4e9ff3a5751d8dbc7e8b313dcf93d36478247905" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dotkernel/dot-navigation/zipball/eb164c14c5b0d4406ea8547c2c3263d2b724bf6d", - "reference": "eb164c14c5b0d4406ea8547c2c3263d2b724bf6d", + "url": "https://api.github.com/repos/dotkernel/dot-paginator/zipball/4e9ff3a5751d8dbc7e8b313dcf93d36478247905", + "reference": "4e9ff3a5751d8dbc7e8b313dcf93d36478247905", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", - "php": "^5.6 || ^7.0", - "psr/http-message": "^1.0", - "zendframework/zend-escaper": "^2.5", - "zendframework/zend-expressive-helpers": "^2.0", - "zendframework/zend-expressive-template": "^1.0", - "zendframework/zend-servicemanager": "^3.1" + "php": "^7.1", + "zendframework/zend-paginator": "^2.7", + "zendframework/zend-servicemanager": "^3.0" }, "require-dev": { - "dotkernel/dot-authorization": "0.6.x-dev", + "dotkernel/dot-mapper": "^0.1", "phpunit/phpunit": "^4.8", "squizlabs/php_codesniffer": "^2.3", - "zendframework/zend-stdlib": "^3.1" + "zendframework/zend-db": "^2.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.6-dev" + "dev-master": "1.1-dev" } }, "autoload": { "psr-4": { - "Dot\\Navigation\\": "src/" + "Dot\\Paginator\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -857,31 +1517,31 @@ "email": "tibi@apidemia.com" } ], - "description": "Navigation library for easy menu parsing", - "time": "2017-01-19 21:01:37" + "description": "DotKernel paginator component extending and customizing zend-paginator", + "time": "2017-03-11T12:40:19+00:00" }, { "name": "dotkernel/dot-rbac", - "version": "dev-master", + "version": "0.1.1", "source": { "type": "git", "url": "https://github.com/dotkernel/dot-rbac.git", - "reference": "8f31ddca002170cf2b02c6bb93f9d58712991c19" + "reference": "476b42d98db7510b741362720296409ca061fc0f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dotkernel/dot-rbac/zipball/8f31ddca002170cf2b02c6bb93f9d58712991c19", - "reference": "8f31ddca002170cf2b02c6bb93f9d58712991c19", + "url": "https://api.github.com/repos/dotkernel/dot-rbac/zipball/476b42d98db7510b741362720296409ca061fc0f", + "reference": "476b42d98db7510b741362720296409ca061fc0f", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", - "dotkernel/dot-authorization": "0.6.x-dev", - "php": "^5.6 || ^7.0", + "dotkernel/dot-authorization": "^0.1", + "php": "^7.1", "zendframework/zend-servicemanager": "^3.1" }, "require-dev": { - "dotkernel/dot-authentication": "0.6.x-dev", + "dotkernel/dot-authentication": "^0.1", "phpunit/phpunit": "^4.8", "squizlabs/php_codesniffer": "^2.3", "zendframework/zend-stdlib": "^3.1" @@ -892,7 +1552,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "0.6-dev" + "dev-master": "0.2-dev" } }, "autoload": { @@ -910,38 +1570,35 @@ "email": "tibi@apidemia.com" } ], - "description": "Rbac authorization library for DK", - "time": "2017-01-19 21:04:42" + "description": "DotKernel RBAC authorization component", + "time": "2017-03-11T12:47:53+00:00" }, { "name": "dotkernel/dot-rbac-guard", - "version": "dev-master", + "version": "0.1.1", "source": { "type": "git", "url": "https://github.com/dotkernel/dot-rbac-guard.git", - "reference": "d111f4513858f7715a6455f4a3efbe5e9cbef821" + "reference": "d179d6250d9cfc63e406b253652e11cc15879bb9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dotkernel/dot-rbac-guard/zipball/d111f4513858f7715a6455f4a3efbe5e9cbef821", - "reference": "d111f4513858f7715a6455f4a3efbe5e9cbef821", + "url": "https://api.github.com/repos/dotkernel/dot-rbac-guard/zipball/d179d6250d9cfc63e406b253652e11cc15879bb9", + "reference": "d179d6250d9cfc63e406b253652e11cc15879bb9", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", - "dotkernel/dot-authorization": "0.6.x-dev", - "dotkernel/dot-event": "0.6.x-dev", - "dotkernel/dot-helpers": "0.6.x-dev", - "dotkernel/dot-rbac": "0.6.x-dev", - "php": "^5.6 || ^7.0", + "dotkernel/dot-rbac": "^0.1", + "php": "^7.1", "psr/http-message": "^1.0", "zendframework/zend-expressive-router": "^1.0", "zendframework/zend-servicemanager": "^3.0" }, "require-dev": { - "dotkernel/dot-authentication": "0.6.x-dev", - "dotkernel/dot-controller": "0.6.x-dev", - "dotkernel/dot-flashmessenger": "0.6.x-dev", + "dotkernel/dot-authentication": "^0.1", + "dotkernel/dot-controller": "^0.1", + "dotkernel/dot-flashmessenger": "^0.1", "phpunit/phpunit": "^4.8", "squizlabs/php_codesniffer": "^2.3", "zendframework/zend-diactoros": "^1.3", @@ -950,7 +1607,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "0.6-dev" + "dev-master": "0.2-dev" } }, "autoload": { @@ -968,26 +1625,26 @@ "email": "tibi@apidemia.com" } ], - "description": "Various authorization guards for route/controller authorization", - "time": "2017-01-19 21:09:56" + "description": "DotKernel RBAC guards component", + "time": "2017-03-11T13:10:10+00:00" }, { "name": "dotkernel/dot-session", - "version": "dev-master", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/dotkernel/dot-session.git", - "reference": "1b3dd3a8daf16e387d8b79fe66ef163cfbd88484" + "reference": "da09162e9b04dca29ffea019a3d3dcf278b4c235" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dotkernel/dot-session/zipball/1b3dd3a8daf16e387d8b79fe66ef163cfbd88484", - "reference": "1b3dd3a8daf16e387d8b79fe66ef163cfbd88484", + "url": "https://api.github.com/repos/dotkernel/dot-session/zipball/da09162e9b04dca29ffea019a3d3dcf278b4c235", + "reference": "da09162e9b04dca29ffea019a3d3dcf278b4c235", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", - "php": "^5.6 || ^7.0", + "php": "^7.1", "psr/http-message": "^1.0", "zendframework/zend-servicemanager": "^3.0", "zendframework/zend-session": "^2.7" @@ -1000,7 +1657,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "0.6-dev" + "dev-master": "1.1-dev" } }, "autoload": { @@ -1018,36 +1675,36 @@ "email": "tibi@apidemia.com" } ], - "description": "Session library for DotKernel based on ZF3 session library", - "time": "2017-01-19 21:12:24" + "description": "DotKernel session component extending and customizing zend-session", + "time": "2017-03-11T13:17:16+00:00" }, { "name": "dotkernel/dot-twigrenderer", - "version": "dev-master", + "version": "0.1.1", "source": { "type": "git", "url": "https://github.com/dotkernel/dot-twigrenderer.git", - "reference": "1cec6c7b70bfcf80f082bde44f07fc2c4ec42b70" + "reference": "f3ac7b8f08d16043dff8561b3f3ea3e571ff2623" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dotkernel/dot-twigrenderer/zipball/1cec6c7b70bfcf80f082bde44f07fc2c4ec42b70", - "reference": "1cec6c7b70bfcf80f082bde44f07fc2c4ec42b70", + "url": "https://api.github.com/repos/dotkernel/dot-twigrenderer/zipball/f3ac7b8f08d16043dff8561b3f3ea3e571ff2623", + "reference": "f3ac7b8f08d16043dff8561b3f3ea3e571ff2623", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", "ocramius/proxy-manager": "^2.0", - "php": "^5.6 || ^7.0", + "php": "^7.1", "zendframework/zend-expressive-twigrenderer": "^1.1", "zendframework/zend-servicemanager": "^3.1", "zendframework/zend-view": "^2.8" }, "require-dev": { - "dotkernel/dot-authentication": "0.6.x-dev", - "dotkernel/dot-authorization": "0.6.x-dev", - "dotkernel/dot-flashmessenger": "0.6.x-dev", - "dotkernel/dot-navigation": "0.6.x-dev", + "dotkernel/dot-authentication": "^0.1", + "dotkernel/dot-authorization": "^0.1", + "dotkernel/dot-flashmessenger": "^0.1", + "dotkernel/dot-navigation": "^0.1", "phpunit/phpunit": "^4.8", "squizlabs/php_codesniffer": "^2.3", "zendframework/zend-form": "^2.9" @@ -1055,7 +1712,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "0.6-dev" + "dev-master": "0.2-dev" } }, "autoload": { @@ -1073,57 +1730,60 @@ "email": "tibi@apidemia.com" } ], - "description": "DK useful twig extensions and ZF3 view helpers made available for twig", - "time": "2017-01-19 21:15:34" + "description": "DotKernel component providing twig extensions and customization", + "time": "2017-03-11T13:23:04+00:00" }, { "name": "dotkernel/dot-user", - "version": "dev-master", + "version": "0.1.2", "source": { "type": "git", "url": "https://github.com/dotkernel/dot-user.git", - "reference": "1b013d9db0d8c2564942d266c52be84085a325f2" + "reference": "90f29b056d9dc90f22bb9ea03b45462819aa3565" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dotkernel/dot-user/zipball/1b013d9db0d8c2564942d266c52be84085a325f2", - "reference": "1b013d9db0d8c2564942d266c52be84085a325f2", + "url": "https://api.github.com/repos/dotkernel/dot-user/zipball/90f29b056d9dc90f22bb9ea03b45462819aa3565", + "reference": "90f29b056d9dc90f22bb9ea03b45462819aa3565", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", - "dotkernel/dot-authentication": "0.6.x-dev", - "dotkernel/dot-authentication-web": "0.6.x-dev", - "dotkernel/dot-authorization": "0.6.x-dev", - "dotkernel/dot-controller": "0.6.x-dev", - "dotkernel/dot-controller-plugin-authentication": "0.6.x-dev", - "dotkernel/dot-controller-plugin-authorization": "0.6.x-dev", - "dotkernel/dot-controller-plugin-flashmessenger": "0.6.x-dev", - "dotkernel/dot-ems": "0.6.x-dev", - "dotkernel/dot-event": "0.6.x-dev", - "dotkernel/dot-flashmessenger": "0.6.x-dev", - "dotkernel/dot-helpers": "0.6.x-dev", - "php": "^5.6 || ^7.0", + "dotkernel/dot-authentication": "^0.1", + "dotkernel/dot-authentication-web": "^0.1", + "dotkernel/dot-authorization": "^0.1", + "dotkernel/dot-controller": "^0.1", + "dotkernel/dot-controller-plugin-authentication": "^0.1", + "dotkernel/dot-controller-plugin-authorization": "^0.1", + "dotkernel/dot-controller-plugin-flashmessenger": "^0.1", + "dotkernel/dot-controller-plugin-forms": "^0.1", + "dotkernel/dot-event": "^0.1", + "dotkernel/dot-filter": "^1.0", + "dotkernel/dot-flashmessenger": "^0.1", + "dotkernel/dot-form": "^1.0", + "dotkernel/dot-helpers": "^0.1", + "dotkernel/dot-hydrator": "^1.0", + "dotkernel/dot-inputfilter": "^1.0", + "dotkernel/dot-mapper": "^0.1", + "dotkernel/dot-validator": "^1.0", + "php": "^7.1", "psr/http-message": "^1.0", "zendframework/zend-crypt": "^3.1", "zendframework/zend-db": "^2.8", "zendframework/zend-diactoros": "^1.3", "zendframework/zend-expressive-template": "^1.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-form": "^2.9", - "zendframework/zend-hydrator": "^2.2", - "zendframework/zend-inputfilter": "^2.7", "zendframework/zend-math": "^3.0", "zendframework/zend-servicemanager": "^3.0" }, "require-dev": { "phpunit/phpunit": "^4.8", - "squizlabs/php_codesniffer": "^2.3" + "squizlabs/php_codesniffer": "^2.3", + "zendframework/zend-captcha": "^2.7" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.6-dev" + "dev-master": "0.2-dev" } }, "autoload": { @@ -1141,21 +1801,79 @@ "email": "tibi@apidemia.com" } ], - "description": "Complete configurable user module for DK built around dot-authentication-web", - "time": "2017-01-19 21:21:39" + "description": "DotKernel configurable user component providing common user functions and extending the authentication flow", + "time": "2017-03-11T13:58:33+00:00" + }, + { + "name": "dotkernel/dot-validator", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/dotkernel/dot-validator.git", + "reference": "2e69abe0cf92269e50bc93316a680026983077f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dotkernel/dot-validator/zipball/2e69abe0cf92269e50bc93316a680026983077f1", + "reference": "2e69abe0cf92269e50bc93316a680026983077f1", + "shasum": "" + }, + "require": { + "php": "^7.1", + "zendframework/zend-servicemanager": "^3.0", + "zendframework/zend-validator": "^2.8" + }, + "require-dev": { + "dotkernel/dot-hydrator": "^1.0", + "dotkernel/dot-mapper": "^0.1", + "phpunit/phpunit": "^4.7", + "squizlabs/php_codesniffer": "^2.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Dot\\Validator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "n3vrax", + "email": "tibi@apidemia.com" + } + ], + "description": "DotKernel validator component extending and customizing zend-validator", + "keywords": [ + "container", + "factories", + "service-manager", + "services", + "validator", + "zf2", + "zf3" + ], + "time": "2017-03-11T13:41:07+00:00" }, { "name": "fig/http-message-util", - "version": "1.1.0", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/php-fig/http-message-util.git", - "reference": "70899e53776d4d65fec9f90f0f88ba6c4d0f7b88" + "reference": "20b2c280cb6914b7b83089720df44e490f4b42f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message-util/zipball/70899e53776d4d65fec9f90f0f88ba6c4d0f7b88", - "reference": "70899e53776d4d65fec9f90f0f88ba6c4d0f7b88", + "url": "https://api.github.com/repos/php-fig/http-message-util/zipball/20b2c280cb6914b7b83089720df44e490f4b42f0", + "reference": "20b2c280cb6914b7b83089720df44e490f4b42f0", "shasum": "" }, "require": { @@ -1192,7 +1910,7 @@ "request", "response" ], - "time": "2016-09-19T14:52:54+00:00" + "time": "2017-02-09T16:10:21+00:00" }, { "name": "http-interop/http-middleware", @@ -1248,16 +1966,16 @@ }, { "name": "nikic/fast-route", - "version": "v1.1.0", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/nikic/FastRoute.git", - "reference": "f3dcf5130e634b6123d40727d612ec6aa4f61fb3" + "reference": "b5f95749071c82a8e0f58586987627054400cdf6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/FastRoute/zipball/f3dcf5130e634b6123d40727d612ec6aa4f61fb3", - "reference": "f3dcf5130e634b6123d40727d612ec6aa4f61fb3", + "url": "https://api.github.com/repos/nikic/FastRoute/zipball/b5f95749071c82a8e0f58586987627054400cdf6", + "reference": "b5f95749071c82a8e0f58586987627054400cdf6", "shasum": "" }, "require": { @@ -1287,7 +2005,7 @@ "router", "routing" ], - "time": "2016-10-20T17:36:47+00:00" + "time": "2017-01-19T11:35:12+00:00" }, { "name": "ocramius/package-versions", @@ -1339,29 +2057,31 @@ }, { "name": "ocramius/proxy-manager", - "version": "2.0.4", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/Ocramius/ProxyManager.git", - "reference": "a55d08229f4f614bf335759ed0cf63378feeb2e6" + "reference": "d9e5a00ca2d87b7e0f1bff36b897e02afd7d5435" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Ocramius/ProxyManager/zipball/a55d08229f4f614bf335759ed0cf63378feeb2e6", - "reference": "a55d08229f4f614bf335759ed0cf63378feeb2e6", + "url": "https://api.github.com/repos/Ocramius/ProxyManager/zipball/d9e5a00ca2d87b7e0f1bff36b897e02afd7d5435", + "reference": "d9e5a00ca2d87b7e0f1bff36b897e02afd7d5435", "shasum": "" }, "require": { - "ocramius/package-versions": "^1.0", - "php": "7.0.0 - 7.0.5 || ^7.0.7", - "zendframework/zend-code": "3.0.0 - 3.0.2 || ^3.0.4" + "ocramius/package-versions": "^1.1.1", + "php": "^7.1.0", + "zendframework/zend-code": "^3.1.0" }, "require-dev": { - "couscous/couscous": "^1.4.0", + "couscous/couscous": "^1.5.2", "ext-phar": "*", - "phpbench/phpbench": "^0.11.2", - "phpunit/phpunit": "^5.4.6", - "squizlabs/php_codesniffer": "^2.6.0" + "humbug/humbug": "dev-master@DEV", + "phpbench/phpbench": "^0.12.2", + "phpunit/phpunit": "^5.6.4", + "phpunit/phpunit-mock-objects": "^3.4.1", + "squizlabs/php_codesniffer": "^2.7.0" }, "suggest": { "ocramius/generated-hydrator": "To have very fast object to array to object conversion for ghost objects", @@ -1400,20 +2120,20 @@ "proxy pattern", "service proxies" ], - "time": "2016-11-04T15:53:15+00:00" + "time": "2016-11-30T15:45:00+00:00" }, { "name": "paragonie/random_compat", - "version": "v2.0.4", + "version": "v2.0.9", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "a9b97968bcde1c4de2a5ec6cbd06a0f6c919b46e" + "reference": "6968531206671f94377b01dc7888d5d1b858a01b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/a9b97968bcde1c4de2a5ec6cbd06a0f6c919b46e", - "reference": "a9b97968bcde1c4de2a5ec6cbd06a0f6c919b46e", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/6968531206671f94377b01dc7888d5d1b858a01b", + "reference": "6968531206671f94377b01dc7888d5d1b858a01b", "shasum": "" }, "require": { @@ -1448,7 +2168,56 @@ "pseudorandom", "random" ], - "time": "2016-11-07T23:38:38+00:00" + "time": "2017-03-03T20:43:42+00:00" + }, + { + "name": "psr/container", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "time": "2017-02-14T16:28:37+00:00" }, { "name": "psr/http-message", @@ -1553,12 +2322,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "1991086b35313dd4e3c5b351f4b22760deea0910" + "reference": "73603e2e613ddc14e2e11ec6da4e837bf0a2149a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/1991086b35313dd4e3c5b351f4b22760deea0910", - "reference": "1991086b35313dd4e3c5b351f4b22760deea0910", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/73603e2e613ddc14e2e11ec6da4e837bf0a2149a", + "reference": "73603e2e613ddc14e2e11ec6da4e837bf0a2149a", "shasum": "" }, "conflict": { @@ -1567,6 +2336,7 @@ "aws/aws-sdk-php": ">=3,<3.2.1", "bugsnag/bugsnag-laravel": ">=2,<2.0.2", "cakephp/cakephp": ">=3,<3.0.15|>=2,<2.4.99|>=2.5,<2.5.99|>=2.6,<2.6.12|>=1.3,<1.3.18|>=2.7,<2.7.6|>=3.1,<3.1.4", + "cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4", "cartalyst/sentry": "<2.1", "codeigniter/framework": "<=3.0.6", "composer/composer": "<=1.0.0-alpha11", @@ -1594,6 +2364,8 @@ "joomla/session": "<1.3.1", "laravel/framework": ">=4,<4.0.99|>=4.1,<4.1.29", "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", + "magento/magento1ce": ">=1.5.0.1,<1.9.3.2", + "magento/magento1ee": ">=1.9,<1.14.3.2", "magento/magento2ce": ">=2,<2.2", "monolog/monolog": ">=1.8,<1.12", "namshi/jose": "<2.2", @@ -1602,7 +2374,7 @@ "phpmailer/phpmailer": ">=5,<5.2.22", "pusher/pusher-php-server": "<2.2.1", "sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9", - "shopware/shopware": "<4.3.7|>=5,<5.1.5", + "shopware/shopware": "<4.4|>=5,<5.2.16", "silverstripe/cms": ">=3.1,<3.1.11|>=3,<=3.0.11", "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", "silverstripe/framework": ">=3,<3.3", @@ -1611,6 +2383,7 @@ "simplesamlphp/simplesamlphp": "<1.14.11", "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", "socalnick/scn-social-auth": "<1.15.2", + "squizlabs/php_codesniffer": ">=1,<2.8.1", "swiftmailer/swiftmailer": ">=4,<5.4.5", "symfony/dependency-injection": ">=2,<2.0.17", "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.7", @@ -1630,7 +2403,7 @@ "thelia/backoffice-default-template": ">=2.1,<2.1.2", "thelia/thelia": ">=2.1.0-beta1,<2.1.3|>=2.1,<2.1.2", "twig/twig": "<1.20", - "typo3/cms": ">=6.2,<6.2.30|>=8,<8.4.1|>=7,<7.6.13", + "typo3/cms": ">=6.2,<6.2.30|>=8,<8.6.1|>=7,<7.6.16", "typo3/flow": ">=2.3,<2.3.16|>=3,<3.0.10|>=3.1,<3.1.7|>=3.2,<3.2.7|>=3.3,<3.3.5|>=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1", "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4", "willdurand/js-translation-bundle": "<2.1.1", @@ -1676,33 +2449,94 @@ } ], "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", - "time": "2017-01-19T15:04:18+00:00" + "time": "2017-03-02T15:23:30+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/e79d363049d1c2128f133a2667e4f4190904f7f4", + "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-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-11-14T01:06:16+00:00" }, { "name": "twig/twig", - "version": "v1.31.0", + "version": "v2.2.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "ddc9e3e20ee9c0b6908f401ac8353635b750eca7" + "reference": "29bb02dde09ff56291d30f7687eb8696918023af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/ddc9e3e20ee9c0b6908f401ac8353635b750eca7", - "reference": "ddc9e3e20ee9c0b6908f401ac8353635b750eca7", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/29bb02dde09ff56291d30f7687eb8696918023af", + "reference": "29bb02dde09ff56291d30f7687eb8696918023af", "shasum": "" }, "require": { - "php": ">=5.2.7" + "php": "^7.0", + "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { + "psr/container": "^1.0", "symfony/debug": "~2.7", "symfony/phpunit-bridge": "~3.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.31-dev" + "dev-master": "2.2-dev" } }, "autoload": { @@ -1737,7 +2571,7 @@ "keywords": [ "templating" ], - "time": "2017-01-11T19:36:15+00:00" + "time": "2017-02-27T00:16:20+00:00" }, { "name": "zendframework/zend-authentication", @@ -1801,18 +2635,87 @@ ], "time": "2016-02-28T15:02:34+00:00" }, + { + "name": "zendframework/zend-cache", + "version": "2.7.2", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-cache.git", + "reference": "c98331b96d3b9d9b24cf32d02660602edb34d039" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-cache/zipball/c98331b96d3b9d9b24cf32d02660602edb34d039", + "reference": "c98331b96d3b9d9b24cf32d02660602edb34d039", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", + "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", + "zendframework/zend-stdlib": "^2.7 || ^3.0" + }, + "require-dev": { + "phpbench/phpbench": "^0.10.0", + "phpunit/phpunit": "^4.8", + "zendframework/zend-coding-standard": "~1.0.0", + "zendframework/zend-serializer": "^2.6", + "zendframework/zend-session": "^2.6.2" + }, + "suggest": { + "ext-apc": "APC or compatible extension, to use the APC storage adapter", + "ext-apcu": "APCU >= 5.1.0, to use the APCu storage adapter", + "ext-dba": "DBA, to use the DBA storage adapter", + "ext-memcache": "Memcache >= 2.0.0 to use the Memcache storage adapter", + "ext-memcached": "Memcached >= 1.0.0 to use the Memcached storage adapter", + "ext-mongo": "Mongo, to use MongoDb storage adapter", + "ext-redis": "Redis, to use Redis storage adapter", + "ext-wincache": "WinCache, to use the WinCache storage adapter", + "ext-xcache": "XCache, to use the XCache storage adapter", + "mongofill/mongofill": "Alternative to ext-mongo - a pure PHP implementation designed as a drop in replacement", + "zendframework/zend-serializer": "Zend\\Serializer component", + "zendframework/zend-session": "Zend\\Session component" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev", + "dev-develop": "2.8-dev" + }, + "zf": { + "component": "Zend\\Cache", + "config-provider": "Zend\\Cache\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Zend\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides a generic way to cache any data", + "homepage": "https://github.com/zendframework/zend-cache", + "keywords": [ + "cache", + "zf2" + ], + "time": "2016-12-16T11:35:47+00:00" + }, { "name": "zendframework/zend-captcha", - "version": "2.6.0", + "version": "2.7.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-captcha.git", - "reference": "9a1197bc5b8aa4fad104c22f6d9b2a3d4bdda0c6" + "reference": "2d56293a5ae3e45e7c8ee7030aa8b305768d8014" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-captcha/zipball/9a1197bc5b8aa4fad104c22f6d9b2a3d4bdda0c6", - "reference": "9a1197bc5b8aa4fad104c22f6d9b2a3d4bdda0c6", + "url": "https://api.github.com/repos/zendframework/zend-captcha/zipball/2d56293a5ae3e45e7c8ee7030aa8b305768d8014", + "reference": "2d56293a5ae3e45e7c8ee7030aa8b305768d8014", "shasum": "" }, "require": { @@ -1822,11 +2725,11 @@ }, "require-dev": { "phpunit/phpunit": "~4.8", - "squizlabs/php_codesniffer": "^2.3.1", + "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-session": "^2.6", "zendframework/zend-text": "^2.6", "zendframework/zend-validator": "^2.6", - "zendframework/zendservice-recaptcha": "*" + "zendframework/zendservice-recaptcha": "^3.0" }, "suggest": { "zendframework/zend-i18n-resources": "Translations of captcha messages", @@ -1838,8 +2741,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6-dev", - "dev-develop": "2.7-dev" + "dev-master": "2.7-dev", + "dev-develop": "2.8-dev" } }, "autoload": { @@ -1856,7 +2759,7 @@ "captcha", "zf2" ], - "time": "2016-06-21T17:32:09+00:00" + "time": "2017-02-23T08:09:44+00:00" }, { "name": "zendframework/zend-code", @@ -1957,41 +2860,45 @@ }, { "name": "zendframework/zend-config", - "version": "2.6.0", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-config.git", - "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d" + "reference": "a12e4a592bf66d9629b84960e268f3752e53abe4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-config/zipball/2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", - "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", + "url": "https://api.github.com/repos/zendframework/zend-config/zipball/a12e4a592bf66d9629b84960e268f3752e53abe4", + "reference": "a12e4a592bf66d9629b84960e268f3752e53abe4", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "ext-json": "*", + "php": "^5.6 || ^7.0", + "psr/container": "^1.0", + "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + }, + "conflict": { + "container-interop/container-interop": "<1.2.0" }, "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-i18n": "^2.5", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" + "malukenho/docheader": "^0.1.5", + "phpunit/phpunit": "^5.7 || ^6.0", + "zendframework/zend-coding-standard": "~1.0.0", + "zendframework/zend-filter": "^2.7.1", + "zendframework/zend-i18n": "^2.7.3", + "zendframework/zend-servicemanager": "^2.7.8 || ^3.2.1" }, "suggest": { - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-json": "Zend\\Json to use the Json reader or writer classes", - "zendframework/zend-servicemanager": "Zend\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" + "zendframework/zend-filter": "^2.7.1; install if you want to use the Filter processor", + "zendframework/zend-i18n": "^2.7.3; install if you want to use the Translator processor", + "zendframework/zend-servicemanager": "^2.7.8 || ^3.2.1; if you need an extensible plugin manager for use with the Config Factory" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6-dev", - "dev-develop": "2.7-dev" + "dev-master": "3.1-dev", + "dev-develop": "3.2-dev" } }, "autoload": { @@ -2009,7 +2916,7 @@ "config", "zf2" ], - "time": "2016-02-04T23:01:10+00:00" + "time": "2017-02-22T14:31:10+00:00" }, { "name": "zendframework/zend-config-aggregator", @@ -2317,16 +3224,16 @@ }, { "name": "zendframework/zend-expressive", - "version": "1.0.6", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-expressive.git", - "reference": "b315ea9269ea3ee2e5fd3adb712fc278a2904eb1" + "reference": "891507003240cec0404ca3b157a88a311c1f9c26" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-expressive/zipball/b315ea9269ea3ee2e5fd3adb712fc278a2904eb1", - "reference": "b315ea9269ea3ee2e5fd3adb712fc278a2904eb1", + "url": "https://api.github.com/repos/zendframework/zend-expressive/zipball/891507003240cec0404ca3b157a88a311c1f9c26", + "reference": "891507003240cec0404ca3b157a88a311c1f9c26", "shasum": "" }, "require": { @@ -2334,18 +3241,19 @@ "php": "^5.6 || ^7.0", "psr/http-message": "^1.0", "zendframework/zend-diactoros": "^1.1", - "zendframework/zend-expressive-router": "^1.1", + "zendframework/zend-expressive-router": "^1.1 || ^2.0", "zendframework/zend-expressive-template": "^1.0.1", - "zendframework/zend-stratigility": ">=1.1.0 < 1.3.0 || >=1.3.1 <2.0.0" + "zendframework/zend-stratigility": "^1.3.3" }, "require-dev": { "filp/whoops": "^1.1 || ^2.0", "malukenho/docheader": "^0.1.5", + "mockery/mockery": "^0.9.5", "phpunit/phpunit": "^4.7", "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-expressive-aurarouter": "^1.0", - "zendframework/zend-expressive-fastroute": "^1.0", - "zendframework/zend-expressive-zendrouter": "^1.0", + "zendframework/zend-expressive-aurarouter": "^1.0 || ^2.0", + "zendframework/zend-expressive-fastroute": "^1.0 || ^2.0", + "zendframework/zend-expressive-zendrouter": "^1.0 || ^2.0", "zendframework/zend-servicemanager": "^2.6" }, "suggest": { @@ -2358,8 +3266,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev", - "dev-develop": "1.1-dev" + "dev-master": "1.1-dev", + "dev-develop": "2.0-dev" } }, "autoload": { @@ -2378,7 +3286,7 @@ "psr", "psr-7" ], - "time": "2017-01-09T16:08:29+00:00" + "time": "2017-02-14T14:11:19+00:00" }, { "name": "zendframework/zend-expressive-fastroute", @@ -2597,41 +3505,41 @@ }, { "name": "zendframework/zend-expressive-twigrenderer", - "version": "1.2.1", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-expressive-twigrenderer.git", - "reference": "786c2b971856c810106b37e9e9b8a90cc28ae454" + "reference": "a1583749cc55b79b81939175281e56a5c9449213" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-expressive-twigrenderer/zipball/786c2b971856c810106b37e9e9b8a90cc28ae454", - "reference": "786c2b971856c810106b37e9e9b8a90cc28ae454", + "url": "https://api.github.com/repos/zendframework/zend-expressive-twigrenderer/zipball/a1583749cc55b79b81939175281e56a5c9449213", + "reference": "a1583749cc55b79b81939175281e56a5c9449213", "shasum": "" }, "require": { - "container-interop/container-interop": "^1.1", + "container-interop/container-interop": "^1.2", "php": "^5.6 || ^7.0", - "twig/twig": "^1.26", - "zendframework/zend-expressive-helpers": "^1.1 || ^2.0 || ^3.0", - "zendframework/zend-expressive-router": "^1.3.2 || ^2.0", + "twig/twig": "^1.32 || ^2.1", + "zendframework/zend-expressive-helpers": "^1.4 || ^2.2 || ^3.0.1", + "zendframework/zend-expressive-router": "^1.3.2 || ^2.1", "zendframework/zend-expressive-template": "^1.0.4" }, "require-dev": { "malukenho/docheader": "^0.1.5", - "phpunit/phpunit": "^5.7", + "phpunit/phpunit": "^6.0.7 || ^5.7.14", "zendframework/zend-coding-standard": "~1.0.0" }, "suggest": { - "aura/di": "3.0.*@beta to make use of Aura.Di dependency injection container", + "aura/di": "^3.2 to make use of Aura.Di dependency injection container", "mouf/pimple-interop": "^1.0 to use Pimple for dependency injection", - "zendframework/zend-servicemanager": "^2.5 to use zend-servicemanager for dependency injection" + "zendframework/zend-servicemanager": "^3.3 to use zend-servicemanager for dependency injection" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev", - "dev-develop": "1.3-dev" + "dev-master": "1.3-dev", + "dev-develop": "1.4-dev" } }, "autoload": { @@ -2652,7 +3560,7 @@ "psr-7", "twig" ], - "time": "2017-01-12T10:57:50+00:00" + "time": "2017-03-02T17:50:19+00:00" }, { "name": "zendframework/zend-filter", @@ -2716,16 +3624,16 @@ }, { "name": "zendframework/zend-form", - "version": "2.9.2", + "version": "2.10.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-form.git", - "reference": "2d076100e4c6a779b7676d098192e3d1cf74f34e" + "reference": "ca2b2f9e1bdc5511d06967d755cbb793708a2d7b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-form/zipball/2d076100e4c6a779b7676d098192e3d1cf74f34e", - "reference": "2d076100e4c6a779b7676d098192e3d1cf74f34e", + "url": "https://api.github.com/repos/zendframework/zend-form/zipball/ca2b2f9e1bdc5511d06967d755cbb793708a2d7b", + "reference": "ca2b2f9e1bdc5511d06967d755cbb793708a2d7b", "shasum": "" }, "require": { @@ -2736,11 +3644,11 @@ }, "require-dev": { "doctrine/annotations": "~1.0", - "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "^4.8", "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-captcha": "^2.5.4", + "zendframework/zend-captcha": "^2.7.1", "zendframework/zend-code": "^2.6 || ^3.0", + "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-escaper": "^2.5", "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", "zendframework/zend-filter": "^2.6", @@ -2750,10 +3658,10 @@ "zendframework/zend-text": "^2.6", "zendframework/zend-validator": "^2.6", "zendframework/zend-view": "^2.6.2", - "zendframework/zendservice-recaptcha": "*" + "zendframework/zendservice-recaptcha": "^3.0.0" }, "suggest": { - "zendframework/zend-captcha": "^2.5.4, required for using CAPTCHA form elements", + "zendframework/zend-captcha": "^2.7.1, required for using CAPTCHA form elements", "zendframework/zend-code": "^2.6 || ^3.0, required to use zend-form annotations support", "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, reuired for zend-form annotations support", "zendframework/zend-i18n": "^2.6, required when using zend-form view helpers", @@ -2764,8 +3672,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.9-dev", - "dev-develop": "2.10-dev" + "dev-master": "2.10-dev", + "dev-develop": "2.11-dev" }, "zf": { "component": "Zend\\Form", @@ -2789,20 +3697,20 @@ "form", "zf2" ], - "time": "2016-09-22T15:56:15+00:00" + "time": "2017-02-23T16:03:25+00:00" }, { "name": "zendframework/zend-http", - "version": "2.5.5", + "version": "2.6.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-http.git", - "reference": "98b1cac0bc7a91497c5898184281abcd0e24c8d6" + "reference": "09f4d279f46d86be63171ff62ee0f79eca878678" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-http/zipball/98b1cac0bc7a91497c5898184281abcd0e24c8d6", - "reference": "98b1cac0bc7a91497c5898184281abcd0e24c8d6", + "url": "https://api.github.com/repos/zendframework/zend-http/zipball/09f4d279f46d86be63171ff62ee0f79eca878678", + "reference": "09f4d279f46d86be63171ff62ee0f79eca878678", "shasum": "" }, "require": { @@ -2813,15 +3721,15 @@ "zendframework/zend-validator": "^2.5" }, "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "^4.0", + "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-config": "^2.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev", - "dev-develop": "2.6-dev" + "dev-master": "2.6-dev", + "dev-develop": "2.7-dev" } }, "autoload": { @@ -2839,7 +3747,7 @@ "http", "zf2" ], - "time": "2016-08-08T15:01:54+00:00" + "time": "2017-01-31T14:41:02+00:00" }, { "name": "zendframework/zend-hydrator", @@ -3062,40 +3970,35 @@ }, { "name": "zendframework/zend-json", - "version": "2.6.1", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-json.git", - "reference": "4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28" + "reference": "f42a1588e75c2a3e338cd94c37906231e616daab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-json/zipball/4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", - "reference": "4c8705dbe4ad7d7e51b2876c5b9eea0ef916ba28", + "url": "https://api.github.com/repos/zendframework/zend-json/zipball/f42a1588e75c2a3e338cd94c37906231e616daab", + "reference": "f42a1588e75c2a3e338cd94c37906231e616daab", "shasum": "" }, "require": { "php": "^5.5 || ^7.0" }, "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-http": "^2.5.4", - "zendframework/zend-server": "^2.6.1", - "zendframework/zend-stdlib": "^2.5 || ^3.0", - "zendframework/zendxml": "^1.0.2" + "squizlabs/php_codesniffer": "^2.3", + "zendframework/zend-stdlib": "^2.7 || ^3.0" }, "suggest": { - "zendframework/zend-http": "Zend\\Http component, required to use Zend\\Json\\Server", - "zendframework/zend-server": "Zend\\Server component, required to use Zend\\Json\\Server", - "zendframework/zend-stdlib": "Zend\\Stdlib component, for use with caching Zend\\Json\\Server responses", - "zendframework/zendxml": "To support Zend\\Json\\Json::fromXml() usage" + "zendframework/zend-json-server": "For implementing JSON-RPC servers", + "zendframework/zend-xml2json": "For converting XML documents to JSON" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6-dev", - "dev-develop": "2.7-dev" + "dev-master": "3.0-dev", + "dev-develop": "3.1-dev" } }, "autoload": { @@ -3113,7 +4016,7 @@ "json", "zf2" ], - "time": "2016-02-04T21:20:26+00:00" + "time": "2016-04-01T02:34:00+00:00" }, { "name": "zendframework/zend-loader", @@ -3232,16 +4135,16 @@ }, { "name": "zendframework/zend-mail", - "version": "2.7.2", + "version": "2.7.3", "source": { "type": "git", "url": "https://github.com/zendframework/zend-mail.git", - "reference": "8210faa6865f94962f9a5c76269703bfdcf2fa4c" + "reference": "e92b4bc1cf6fe0fdad571bd7b4af2762414d58ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mail/zipball/8210faa6865f94962f9a5c76269703bfdcf2fa4c", - "reference": "8210faa6865f94962f9a5c76269703bfdcf2fa4c", + "url": "https://api.github.com/repos/zendframework/zend-mail/zipball/e92b4bc1cf6fe0fdad571bd7b4af2762414d58ff", + "reference": "e92b4bc1cf6fe0fdad571bd7b4af2762414d58ff", "shasum": "" }, "require": { @@ -3253,7 +4156,7 @@ }, "require-dev": { "phpunit/phpunit": "^4.8", - "squizlabs/php_codesniffer": "^2.3.1", + "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-config": "^2.6", "zendframework/zend-crypt": "^2.6", "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" @@ -3288,7 +4191,7 @@ "mail", "zf2" ], - "time": "2016-12-19T22:46:35+00:00" + "time": "2017-02-14T18:03:34+00:00" }, { "name": "zendframework/zend-math", @@ -3504,31 +4407,33 @@ }, { "name": "zendframework/zend-servicemanager", - "version": "3.2.0", + "version": "3.3.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-servicemanager.git", - "reference": "596a2cde85a92c3366514ae0f55ea32ef59536ac" + "reference": "c3036efb81f71bfa36cc9962ee5d4474f36581d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/596a2cde85a92c3366514ae0f55ea32ef59536ac", - "reference": "596a2cde85a92c3366514ae0f55ea32ef59536ac", + "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/c3036efb81f71bfa36cc9962ee5d4474f36581d0", + "reference": "c3036efb81f71bfa36cc9962ee5d4474f36581d0", "shasum": "" }, "require": { - "container-interop/container-interop": "~1.0", + "container-interop/container-interop": "^1.2", "php": "^5.6 || ^7.0", + "psr/container": "^1.0", "zendframework/zend-stdlib": "^3.1" }, "provide": { - "container-interop/container-interop-implementation": "^1.1" + "container-interop/container-interop-implementation": "^1.2", + "psr/container-implementation": "^1.0" }, "require-dev": { "mikey179/vfsstream": "^1.6", "ocramius/proxy-manager": "^1.0 || ^2.0", "phpbench/phpbench": "^0.10.0", - "phpunit/phpunit": "^4.6 || ^5.2.10", + "phpunit/phpunit": "^5.7 || ^6.0.6", "zendframework/zend-coding-standard": "~1.0.0" }, "suggest": { @@ -3542,8 +4447,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev", - "dev-develop": "3.3-dev" + "dev-master": "3.3-dev", + "dev-develop": "3.4-dev" } }, "autoload": { @@ -3561,7 +4466,7 @@ "servicemanager", "zf" ], - "time": "2016-12-19T20:04:51+00:00" + "time": "2017-03-01T22:08:02+00:00" }, { "name": "zendframework/zend-session", @@ -3676,16 +4581,16 @@ }, { "name": "zendframework/zend-stratigility", - "version": "1.3.2", + "version": "1.3.3", "source": { "type": "git", "url": "https://github.com/zendframework/zend-stratigility.git", - "reference": "ffbf29ceb92a9223b8383ea2b021b242591141d2" + "reference": "2c4120d2af215c8261a36e0bc3aa8e179e05e148" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-stratigility/zipball/ffbf29ceb92a9223b8383ea2b021b242591141d2", - "reference": "ffbf29ceb92a9223b8383ea2b021b242591141d2", + "url": "https://api.github.com/repos/zendframework/zend-stratigility/zipball/2c4120d2af215c8261a36e0bc3aa8e179e05e148", + "reference": "2c4120d2af215c8261a36e0bc3aa8e179e05e148", "shasum": "" }, "require": { @@ -3725,7 +4630,7 @@ "middleware", "psr-7" ], - "time": "2017-01-05T22:22:10+00:00" + "time": "2017-01-23T22:59:03+00:00" }, { "name": "zendframework/zend-text", @@ -3823,16 +4728,16 @@ }, { "name": "zendframework/zend-validator", - "version": "2.8.1", + "version": "2.8.2", "source": { "type": "git", "url": "https://github.com/zendframework/zend-validator.git", - "reference": "8ec9f57a717dd37340308aa632f148a2c2be1cfc" + "reference": "99b528e01276054458da9553b587cfb959dfa436" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/8ec9f57a717dd37340308aa632f148a2c2be1cfc", - "reference": "8ec9f57a717dd37340308aa632f148a2c2be1cfc", + "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/99b528e01276054458da9553b587cfb959dfa436", + "reference": "99b528e01276054458da9553b587cfb959dfa436", "shasum": "" }, "require": { @@ -3890,57 +4795,7 @@ "validator", "zf2" ], - "time": "2016-06-23T13:44:31+00:00" - }, - { - "name": "zendframework/zend-version", - "version": "2.5.1", - "source": { - "type": "git", - "url": "https://github.com/zendframework/zend-version.git", - "reference": "e30c55dc394eaf396f0347887af0a7bef471fe08" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-version/zipball/e30c55dc394eaf396f0347887af0a7bef471fe08", - "reference": "e30c55dc394eaf396f0347887af0a7bef471fe08", - "shasum": "" - }, - "require": { - "php": ">=5.3.23", - "zendframework/zend-json": "~2.5" - }, - "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0", - "zendframework/zend-http": "~2.5" - }, - "suggest": { - "zendframework/zend-http": "Allows use of Zend\\Http\\Client to check version information", - "zendframework/zend-json": "To check latest version hosted in GitHub" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.5-dev", - "dev-develop": "2.6-dev" - } - }, - "autoload": { - "psr-4": { - "Zend\\Version\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "homepage": "https://github.com/zendframework/zend-version", - "keywords": [ - "version", - "zf2" - ], - "time": "2015-06-04T15:41:05+00:00" + "time": "2017-01-29T17:24:24+00:00" }, { "name": "zendframework/zend-view", @@ -4031,33 +4886,42 @@ }, { "name": "zendframework/zendservice-recaptcha", - "version": "2.0.1", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/zendframework/ZendService_ReCaptcha.git", - "reference": "4324cca8502d9f47b3b43a18acdd3fdbeb965536" + "reference": "6c6877c07c8ac73b187911ea5d264a640b234361" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/ZendService_ReCaptcha/zipball/4324cca8502d9f47b3b43a18acdd3fdbeb965536", - "reference": "4324cca8502d9f47b3b43a18acdd3fdbeb965536", + "url": "https://api.github.com/repos/zendframework/ZendService_ReCaptcha/zipball/6c6877c07c8ac73b187911ea5d264a640b234361", + "reference": "6c6877c07c8ac73b187911ea5d264a640b234361", "shasum": "" }, "require": { - "php": ">=5.3.3", - "zendframework/zend-http": ">=2.0.0", - "zendframework/zend-uri": ">=2.0.0", - "zendframework/zend-version": ">=2.0.0" + "php": "^5.6 || ^7.0", + "zendframework/zend-http": "^2.5.4", + "zendframework/zend-json": "^2.6.1 || ^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7 || ^6.0", + "zendframework/zend-coding-standard": "~1.0.0", + "zendframework/zend-config": "^2.0", + "zendframework/zend-validator": "^2.8.2" + }, + "suggest": { + "zendframework/zend-validator": "~2.0, if using ReCaptcha's Mailhide API" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.0-dev", + "dev-develop": "3.1-dev" } }, "autoload": { - "psr-0": { - "ZendService": "library/" + "psr-4": { + "ZendService\\ReCaptcha\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4070,7 +4934,7 @@ "recaptcha", "zf2" ], - "time": "2012-09-24T15:18:29+00:00" + "time": "2017-02-09T21:38:25+00:00" } ], "packages-dev": [ @@ -4130,20 +4994,21 @@ }, { "name": "filp/whoops", - "version": "2.1.5", + "version": "2.1.8", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "2abce9d956589122c6443d6265f01cf7e9388e3c" + "reference": "f2950be7da8b8d6c4e77821b6c9d486e36cdc4f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/2abce9d956589122c6443d6265f01cf7e9388e3c", - "reference": "2abce9d956589122c6443d6265f01cf7e9388e3c", + "url": "https://api.github.com/repos/filp/whoops/zipball/f2950be7da8b8d6c4e77821b6c9d486e36cdc4f3", + "reference": "f2950be7da8b8d6c4e77821b6c9d486e36cdc4f3", "shasum": "" }, "require": { - "php": "^5.5.9 || ^7.0" + "php": "^5.5.9 || ^7.0", + "psr/log": "^1.0.1" }, "require-dev": { "mockery/mockery": "0.9.*", @@ -4186,7 +5051,7 @@ "whoops", "zf2" ], - "time": "2016-12-26T16:13:31+00:00" + "time": "2017-03-07T09:04:45+00:00" }, { "name": "mikey179/vfsStream", @@ -4382,27 +5247,27 @@ }, { "name": "phpspec/prophecy", - "version": "v1.6.2", + "version": "v1.7.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "6c52c2722f8460122f96f86346600e1077ce22cb" + "reference": "93d39f1f7f9326d746203c7c056f300f7f126073" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/6c52c2722f8460122f96f86346600e1077ce22cb", - "reference": "6c52c2722f8460122f96f86346600e1077ce22cb", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/93d39f1f7f9326d746203c7c056f300f7f126073", + "reference": "93d39f1f7f9326d746203c7c056f300f7f126073", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", - "sebastian/comparator": "^1.1", - "sebastian/recursion-context": "^1.0|^2.0" + "sebastian/comparator": "^1.1|^2.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, "require-dev": { - "phpspec/phpspec": "^2.0", + "phpspec/phpspec": "^2.5|^3.2", "phpunit/phpunit": "^4.8 || ^5.6.5" }, "type": "library", @@ -4441,7 +5306,7 @@ "spy", "stub" ], - "time": "2016-11-21T14:58:47+00:00" + "time": "2017-03-02T20:05:34+00:00" }, { "name": "phpunit/php-code-coverage", @@ -4595,25 +5460,30 @@ }, { "name": "phpunit/php-timer", - "version": "1.0.8", + "version": "1.0.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260" + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/38e9124049cf1a164f1e4537caf19c99bf1eb260", - "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.3.3 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~4|~5" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, "autoload": { "classmap": [ "src/" @@ -4635,20 +5505,20 @@ "keywords": [ "timer" ], - "time": "2016-05-12T18:03:57+00:00" + "time": "2017-02-26T11:10:40+00:00" }, { "name": "phpunit/php-token-stream", - "version": "1.4.9", + "version": "1.4.11", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b" + "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3b402f65a4cc90abf6e1104e388b896ce209631b", - "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7", + "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7", "shasum": "" }, "require": { @@ -4684,20 +5554,20 @@ "keywords": [ "tokenizer" ], - "time": "2016-11-15T14:06:22+00:00" + "time": "2017-02-27T10:12:30+00:00" }, { "name": "phpunit/phpunit", - "version": "4.8.32", + "version": "4.8.35", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "f5e1941a8dacf0d904753ff2895c6f68e54bcee1" + "reference": "791b1a67c25af50e230f841ee7a9c6eba507dc87" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f5e1941a8dacf0d904753ff2895c6f68e54bcee1", - "reference": "f5e1941a8dacf0d904753ff2895c6f68e54bcee1", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/791b1a67c25af50e230f841ee7a9c6eba507dc87", + "reference": "791b1a67c25af50e230f841ee7a9c6eba507dc87", "shasum": "" }, "require": { @@ -4756,7 +5626,7 @@ "testing", "xunit" ], - "time": "2017-01-22T08:37:05+00:00" + "time": "2017-02-06T05:18:07+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -4816,16 +5686,16 @@ }, { "name": "sebastian/comparator", - "version": "1.2.2", + "version": "1.2.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f" + "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/6a1ed12e8b2409076ab22e3897126211ff8b1f7f", - "reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", + "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", "shasum": "" }, "require": { @@ -4876,7 +5746,7 @@ "compare", "equality" ], - "time": "2016-11-19T09:18:40+00:00" + "time": "2017-01-29T09:50:25+00:00" }, { "name": "sebastian/diff", @@ -5100,16 +5970,16 @@ }, { "name": "sebastian/recursion-context", - "version": "1.0.4", + "version": "1.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "938df7a6478e72795e5f8266cff24d06e3136f2e" + "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/938df7a6478e72795e5f8266cff24d06e3136f2e", - "reference": "938df7a6478e72795e5f8266cff24d06e3136f2e", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b19cc3298482a335a95f3016d2f8a6950f0fbcd7", + "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7", "shasum": "" }, "require": { @@ -5149,7 +6019,7 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2016-11-15T06:55:36+00:00" + "time": "2016-10-03T07:41:43+00:00" }, { "name": "sebastian/version", @@ -5188,16 +6058,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "2.7.1", + "version": "2.8.1", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "9b324f3a1132459a7274a0ace2e1b766ba80930f" + "reference": "d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/9b324f3a1132459a7274a0ace2e1b766ba80930f", - "reference": "9b324f3a1132459a7274a0ace2e1b766ba80930f", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d", + "reference": "d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d", "shasum": "" }, "require": { @@ -5262,20 +6132,20 @@ "phpcs", "standards" ], - "time": "2016-11-30T04:02:31+00:00" + "time": "2017-03-01T22:17:45+00:00" }, { "name": "symfony/yaml", - "version": "v3.2.2", + "version": "v3.2.6", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "50eadbd7926e31842893c957eca362b21592a97d" + "reference": "093e416ad096355149e265ea2e4cc1f9ee40ab1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/50eadbd7926e31842893c957eca362b21592a97d", - "reference": "50eadbd7926e31842893c957eca362b21592a97d", + "url": "https://api.github.com/repos/symfony/yaml/zipball/093e416ad096355149e265ea2e4cc1f9ee40ab1a", + "reference": "093e416ad096355149e265ea2e4cc1f9ee40ab1a", "shasum": "" }, "require": { @@ -5317,7 +6187,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2017-01-03T13:51:32+00:00" + "time": "2017-03-07T16:47:02+00:00" }, { "name": "webmozart/assert", @@ -5422,33 +6292,12 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": { - "roave/security-advisories": 20, - "dotkernel/dot-helpers": 20, - "dotkernel/dot-ems": 20, - "dotkernel/dot-event": 20, - "dotkernel/dot-session": 20, - "dotkernel/dot-flashmessenger": 20, - "dotkernel/dot-controller": 20, - "dotkernel/dot-controller-plugin-flashmessenger": 20, - "dotkernel/dot-controller-plugin-authentication": 20, - "dotkernel/dot-controller-plugin-authorization": 20, - "dotkernel/dot-controller-plugin-mail": 20, - "dotkernel/dot-authentication": 20, - "dotkernel/dot-authentication-service": 20, - "dotkernel/dot-authentication-web": 20, - "dotkernel/dot-authorization": 20, - "dotkernel/dot-rbac": 20, - "dotkernel/dot-rbac-guard": 20, - "dotkernel/dot-user": 20, - "dotkernel/dot-mail": 20, - "dotkernel/dot-navigation": 20, - "dotkernel/dot-twigrenderer": 20, - "dotkernel/dot-log": 20 + "roave/security-advisories": 20 }, "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^5.5 || ^7.0" + "php": "^7.1" }, "platform-dev": [] } diff --git a/config/autoload/authentication.global.php b/config/autoload/authentication.global.php index 8889923..859e9d6 100644 --- a/config/autoload/authentication.global.php +++ b/config/autoload/authentication.global.php @@ -2,41 +2,28 @@ return [ 'dot_authentication' => [ - //required by the auth adapters, it may be optional for your custom adapters - //specify the identity entity to use and its hydrator - 'identity_class' => \Dot\Admin\Entity\AdminEntity::class, - 'identity_hydrator_class' => \Dot\User\Entity\UserEntityHydrator::class, - - //this is adapter specific - //currently we support HTTP basic and digest - //below is config template for callbackcheck adapter which is for mysql 'adapter' => [ - \Dot\Authentication\Adapter\DbTable\CallbackCheckAdapter::class => [ - //zend db adapter service name - 'db_adapter' => 'database', + 'type' => 'CallbackCheck', + 'options' => [ + 'adapter' => 'database', - //your user table name - 'table_name' => 'admin', + 'identity_prototype' => \Admin\Admin\Entity\AdminEntity::class, + 'identity_hydrator' => \Dot\Hydrator\ClassMethodsCamelCase::class, - //what user fields should use for authentication(db fields) - 'identity_columns' => ['username', 'email'], + 'table' => 'admin', - //name of the password db field + 'identity_columns' => ['username', 'email'], 'credential_column' => 'password', - 'callback_check' => \Dot\User\Service\PasswordCheck::class, - ], + 'callback_check' => \Dot\User\Service\PasswordCheck::class + ] ], - - //storage specific options, example below, for session storage 'storage' => [ - \Dot\Authentication\Storage\SessionStorage::class => [ - //session namespace + 'type' => 'Session', + 'options' => [ 'namespace' => 'admin_authentication', - - //what session member to use - 'member' => 'storage' - ], + 'member' => 'storage', + ] ], 'adapter_manager' => [ diff --git a/config/autoload/authorization-guards.global.php b/config/autoload/authorization-guards.global.php index 3f5e20e..ceaea95 100644 --- a/config/autoload/authorization-guards.global.php +++ b/config/autoload/authorization-guards.global.php @@ -1,82 +1,80 @@ [ + 'protection_policy' => \Dot\Rbac\Guard\Guard\GuardInterface::POLICY_DENY, - //define how it will treat non-matching guard rules, allow all by default - 'protection_policy' => \Dot\Rbac\Guard\GuardInterface::POLICY_DENY, + 'event_listeners' => [], - //register custom guards providers here 'guards_provider_manager' => [], - - //define custom guards here 'guard_manager' => [], - //list of guards 'guards_provider' => [ - //the list of guards to use. Custom guards need to be registered in the guard manager first - \Dot\Rbac\Guard\Provider\ArrayGuardsProvider::class => [ - - \Dot\Rbac\Guard\Route\RouteGuard::class => [ - 'login' => ['*'], - 'logout' => ['*'], - ], - - \Dot\Rbac\Guard\Controller\ControllerPermissionGuard::class => [ - [ - 'route' => 'user', - 'actions' => [], - 'permissions' => ['authenticated'] - ], + 'type' => 'ArrayGuards', + 'options' => [ + 'guards' => [ [ - 'route' => 'user', - 'actions' => ['register', 'forgot-password', 'reset-password', 'confirm-account'], - 'permissions' => ['unauthenticated'] + 'type' => 'Route', + 'options' => [ + 'rules' => [ + 'login' => ['*'], + 'logout' => ['*'] + ] + ] ], [ - 'route' => 'user', - 'actions' => ['add', 'edit', 'delete'], - 'permissions' => [ - 'permissions' => ['superuser', 'admin-manager'], - 'condition' => \Dot\Rbac\Guard\GuardInterface::CONDITION_OR, - ], + 'type' => 'Controller', + 'options' => [ + 'rules' => [ + [ + 'route' => 'user', + 'actions' => [ + 'register', + 'reset-password', + 'forgot-password', + 'confirm-account', + 'opt-out' + ], + 'roles' => ['guest'], + ] + ] + ] ], - [ - 'route' => 'dashboard', - 'actions' => [], - 'permissions' => ['authenticated'] - ], - - [ - 'route' => 'f_user', - 'actions' => [], - 'permissions' => [ - 'permissions' => ['superuser', 'admin'], - 'condition' => \Dot\Rbac\Guard\GuardInterface::CONDITION_OR + 'type' => 'ControllerPermission', + 'options' => [ + 'rules' => [ + [ + 'route' => 'user', + 'actions' => [], + 'permissions' => ['authenticated'] + ], + [ + 'route' => 'user', + 'actions' => ['add', 'edit', 'delete'], + 'permissions' => [ + 'permissions' => ['superuser', 'admin-manager'], + 'condition' => \Dot\Rbac\Guard\Guard\GuardInterface::CONDITION_OR, + ], + ], + [ + 'route' => 'dashboard', + 'actions' => [], + 'permissions' => ['authenticated'] + ], + [ + 'route' => 'f_user', + 'actions' => [], + 'permissions' => [ + 'permissions' => ['superuser', 'admin'], + 'condition' => \Dot\Rbac\Guard\Guard\GuardInterface::CONDITION_OR + ] + ], + ] ] - ], - ], + ] + ] ] ], - - //enable wanted url appending, used in the redirect on forbidden handler for now - 'allow_redirect_param' => true, - - //the name of the query param appended for the wanted url - 'redirect_query_name' => 'redirect', - - //options for the redirect on forbidden handler - 'redirect_options' => [ - - 'enable' => false, - - 'redirect_route' => ['name' => 'login', 'params' => []], - - ], - - //overwrite default messages - //'messages_options' => [], ] ]; diff --git a/config/autoload/authorization.global.php b/config/autoload/authorization.global.php index a2d9827..887383d 100644 --- a/config/autoload/authorization.global.php +++ b/config/autoload/authorization.global.php @@ -1,51 +1,40 @@ [ - //maybe you want to change the place where the identity is retrieved - //change this line and add you own IdentityProvider - //default is AuthenticationIdentityProvider which gets the identity from th dot-authentication service. - //IdentityProviderInterface::class => \Your\Identity\Provider, - ], + 'dependencies' => [], 'dot_authorization' => [ - 'assertion_map' => [ - //map permissions to assertions - //'edit' => EditAssertion::class, - ], - - 'assertion_manager' => [], - - //name of the guest role to use if no identity is provided 'guest_role' => 'guest', 'role_provider_manager' => [], - //example for a flat RBAC model using the InMemoryRoleProvider, hierarchical is also supported 'role_provider' => [ - \Dot\Rbac\Role\Provider\InMemoryRoleProvider::class => [ - 'superuser' => [ - 'permissions' => [ - 'superuser', - 'authenticated' - ] - ], - 'admin' => [ - 'permissions' => [ - 'admin', - 'authenticated', - ] - ], - - 'guest' => [ - 'permissions' => [ - 'unauthenticated' + 'type' => 'InMemory', + 'options' => [ + 'roles' => [ + 'superuser' => [ + 'permissions' => [ + 'superuser', + 'admin', + 'authenticated', + ] + ], + 'admin' => [ + 'permissions' => [ + 'admin', + 'authenticated' + ] + ], + 'guest' => [ + 'permissions' => [ + 'unauthenticated', + ] ] ] ] ], + + 'assertion_manager' => [], + 'assertions' => [] ] ]; diff --git a/config/autoload/cache.global.php b/config/autoload/cache.global.php new file mode 100644 index 0000000..2d4a904 --- /dev/null +++ b/config/autoload/cache.global.php @@ -0,0 +1,14 @@ + __DIR__ . '/../../data/cache/annotations', + + 'dependencies' => [ + 'factories' => [ + // used by dot-annotated-services to cache annotations + // needs to return a cache instance from Doctrine\Common\Cache + \Dot\AnnotatedServices\Factory\AbstractAnnotatedFactory::CACHE_SERVICE => + \Admin\App\Factory\AnnotationsCacheFactory::class, + ] + ], +]; diff --git a/config/autoload/controllers.global.php b/config/autoload/controllers.global.php index b51fbcb..a702621 100644 --- a/config/autoload/controllers.global.php +++ b/config/autoload/controllers.global.php @@ -1,22 +1,11 @@ [ - 'factories' => [ - \Dot\Admin\Controller\DashboardController::class => - \Dot\Admin\Factory\DashboardControllerFactory::class, - - \Dot\Admin\Controller\UserController::class => - \Dot\Admin\Factory\User\UserControllerFactory::class, - - \Dot\Admin\Controller\AdminController::class => - \Dot\Admin\Factory\Admin\AdminControllerFactory::class, - ] - ], + 'dependencies' => [], 'dot_controller' => [ + 'plugin_manager' => [], - 'plugin_manager' => [] + 'event_listeners' => [], ], ]; diff --git a/config/autoload/dependencies.global.php b/config/autoload/dependencies.global.php index 1630037..f2f2ae6 100644 --- a/config/autoload/dependencies.global.php +++ b/config/autoload/dependencies.global.php @@ -1,63 +1,20 @@ [ - // Use 'invokables' for constructor-less services, or services that do - // not require arguments to the constructor. Map a service name to the - // class name. - 'invokables' => [ - // Fully\Qualified\InterfaceName::class => Fully\Qualified\ClassName::class, - Helper\ServerUrlHelper::class => Helper\ServerUrlHelper::class, - ], - // Use 'factories' for services provided by callbacks/factory classes. 'factories' => [ Application::class => ApplicationFactory::class, Helper\UrlHelper::class => Helper\UrlHelperFactory::class, - - /** Admin entity related dependencies */ - \Dot\Admin\Form\Admin\AdminForm::class => - \Dot\Admin\Factory\Admin\AdminFormFactory::class, - - \Dot\Admin\Form\Admin\AdminFieldset::class => - \Dot\Admin\Factory\Admin\AdminFieldsetFactory::class, - - \Dot\Admin\Form\Admin\AdminInputFilter::class => - \Dot\Admin\Factory\Admin\AdminInputFilterFactory::class, - - /** User entity related dependencies */ - \Dot\Admin\Form\User\UserDetailsInputFilter::class => - \Dot\Admin\Factory\User\UserDetailsInputFilterFactory::class, - - \Dot\Admin\Form\User\UserInputFilter::class => - \Dot\Admin\Factory\User\UserInputFilterFactory::class, - - \Dot\Admin\Form\User\UserDetailsFieldset::class => - \Dot\Admin\Factory\User\UserDetailsFieldsetFactory::class, - - \Dot\Admin\Form\User\UserFieldset::class => - \Dot\Admin\Factory\User\UserFieldsetFactory::class, - - \Dot\Admin\Form\User\UserForm::class => - \Dot\Admin\Factory\User\UserFormFactory::class, - - \Dot\Admin\Service\Listener\AdminServiceListener::class => - \Dot\Admin\Factory\AdminServiceListenerFactory::class, - + Helper\ServerUrlHelper::class => \Zend\ServiceManager\Factory\InvokableFactory::class, ], - 'delegators' => [ - 'dot-ems.service.user' => [ - \Dot\Admin\Factory\User\UserServiceDelegator::class, - ], - 'dot-ems.service.admin' => [ - \Dot\Admin\Factory\Admin\AdminServiceDelegator::class, - ] - ], + 'lazy_services' => [ + 'proxies_target_dir' => 'data/proxies', + 'proxies_namespace' => 'DotProxy', + ] ], ]; diff --git a/config/autoload/ems.global.php b/config/autoload/ems.global.php index 4ad4d55..e9ec1c5 100644 --- a/config/autoload/ems.global.php +++ b/config/autoload/ems.global.php @@ -2,61 +2,6 @@ return [ 'dot_ems' => [ - 'service' => [ - 'user' => [ - 'atomic_operations' => true, - 'type' => \Dot\Admin\Service\UserService::class, - - 'mapper' => [ - \Dot\Ems\Mapper\RelationalDbMapper::class => [ - 'adapter' => 'database', - 'table' => 'user', - - 'entity_prototype' => \Dot\Admin\Entity\UserEntity::class, - 'entity_hydrator' => \Dot\User\Entity\UserEntityHydrator::class, - - 'relations' => [ - \Dot\Ems\Mapper\Relation\OneToOneRelation::class => [ - 'field_name' => 'details', - 'ref_name' => 'userId', - - 'delete_refs' => true, - 'change_refs' => true, - - 'mapper' => [ - \Dot\Ems\Mapper\DbMapper::class => [ - 'adapter' => 'database', - 'table' => 'user_details', - - 'identifier_name' => 'userId', - 'entity_prototype' => \Dot\Admin\Entity\UserDetailsEntity::class, - ], - ], - ], - ], - ], - ], - ], - - 'admin' => [ - 'atomic_operations' => true, - 'type' => \Dot\Admin\Service\AdminService::class, - - 'service_listeners' => [ - \Dot\Admin\Service\Listener\AdminServiceListener::class, - ], - - 'mapper' => [ - \Dot\Ems\Mapper\DbMapper::class => [ - 'adapter' => 'database', - 'table' => 'admin', - - 'entity_prototype' => \Dot\Admin\Entity\AdminEntity::class, - 'entity_hydrator' => \Dot\User\Entity\UserEntityHydrator::class, - ] - ], - ], - - ], + 'default_adapter' => 'database', ] ]; diff --git a/config/autoload/forms.global.php b/config/autoload/forms.global.php new file mode 100644 index 0000000..e27fd9e --- /dev/null +++ b/config/autoload/forms.global.php @@ -0,0 +1,16 @@ + [ + 'hydrator_manager' => [], + ], + + 'dot_input_filter' => [ + 'input_filter_manager' => [], + ], + + 'dot_form' => [ + 'form_manager' => [], + 'forms' => [], + ], +]; diff --git a/config/autoload/log.global.php b/config/autoload/log.global.php index 0ab9fdd..acbdba7 100644 --- a/config/autoload/log.global.php +++ b/config/autoload/log.global.php @@ -2,8 +2,7 @@ return [ 'dot_log' => [ - 'service' => [ - + 'loggers' => [ 'action_logger' => [ 'writers' => [ [ diff --git a/config/autoload/mail.global.php b/config/autoload/mail.global.php index ab06fe1..777ff0a 100644 --- a/config/autoload/mail.global.php +++ b/config/autoload/mail.global.php @@ -34,7 +34,7 @@ 'message_options' => [ //from email address of the email - 'from' => 'pgt.dot.work@gmail.com', + 'from' => 'support@dotkernel.com', //from name to be displayed instead of from address 'from_name' => 'DotKernel Team', @@ -89,7 +89,7 @@ 'smtp_options' => [ //hostname or IP address of the mail server - 'host' => 'smtp.gmail.com', + 'host' => '', //port of the mail server - default 25 'port' => 587, @@ -115,18 +115,18 @@ //file options that will be used only if the adapter is Zend\Mail\Transport\File /*'file_options' => [ - //this is the folder where the file is going to be saved - //default value is 'data/mail/output' + // this is the folder where the file is going to be saved + // default value is 'data/mail/output' 'path' => 'data/mail/output', - // a callable that will get the Zend\Mail\Transport\File object - // as an argument and should return the filename + // a callable that will get the Zend\Mail\Transport\File object as an argument + // and should return the filename // if null is used, and empty callable will be used - //'callback' => null, + // 'callback' => null, ],*/ //listeners to register with the mail service, for mail events - 'mail_listeners' => [ + 'event_listeners' => [ ], ], diff --git a/config/autoload/middleware-pipeline.global.php b/config/autoload/middleware-pipeline.global.php index 73301df..1735bf0 100644 --- a/config/autoload/middleware-pipeline.global.php +++ b/config/autoload/middleware-pipeline.global.php @@ -1,4 +1,5 @@ [ Helper\ServerUrlMiddleware::class => Helper\ServerUrlMiddlewareFactory::class, Helper\UrlHelperMiddleware::class => Helper\UrlHelperMiddlewareFactory::class, - - \Dot\Admin\Middleware\AdminIndexMiddleware::class => \Dot\Admin\Factory\AdminIndexMiddlewareFactory::class, ], ], // This can be used to seed pre- and/or post-routing middleware @@ -43,7 +42,6 @@ // - modifications to outgoing responses Helper\ServerUrlMiddleware::class, \Dot\Session\SessionMiddleware::class, - \Dot\FlashMessenger\FlashMessengerMiddleware::class, ], 'priority' => 10000, ], @@ -53,7 +51,7 @@ ApplicationFactory::ROUTING_MIDDLEWARE, Helper\UrlHelperMiddleware::class, - \Dot\Admin\Middleware\AdminIndexMiddleware::class, + \Admin\App\Middleware\AdminIndexMiddleware::class, //DK after-routing middleware \Dot\Navigation\NavigationMiddleware::class, diff --git a/config/autoload/navigation.global.php b/config/autoload/navigation.global.php index 1586ece..6ae352e 100644 --- a/config/autoload/navigation.global.php +++ b/config/autoload/navigation.global.php @@ -6,71 +6,71 @@ //enable menu item active if any child is active 'active_recursion' => true, - //map a provider name to a provider type - 'providers_map' => [ - 'main_menu' => \Dot\Navigation\Provider\ArrayProvider::class, - 'account_menu' => \Dot\Navigation\Provider\ArrayProvider::class, - ], - - //map a provider name to its config - //this is for bootstrap navbar - even if we support multi-level menus, bootstrap is limited to one level 'containers' => [ 'main_menu' => [ - [ - 'options' => [ - 'label' => 'Dashboard', - 'route' => 'dashboard', - 'icon' => 'fa fa-tachometer', + 'type' => 'ArrayProvider', + 'options' => [ + 'items' => [ + [ + 'options' => [ + 'label' => 'Dashboard', + 'route' => 'dashboard', + 'params' => ['action' => ''], + 'icon' => 'fa fa-tachometer', + ] + ], + [ + 'options' => [ + 'label' => 'Manage admins', + 'route' => 'user', + 'params' => ['action' => 'manage'], + 'icon' => 'fa fa-user-circle-o', + ], + ], + [ + 'options' => [ + 'label' => 'Manage users', + 'route' => 'f_user', + 'params' => ['action' => 'manage'], + 'icon' => 'fa fa-user-o', + ], + ], ] - ], - [ - 'options' => [ - 'label' => 'Manage admins', - 'route' => 'user', - 'params' => ['action' => 'manage'], - 'icon' => 'fa fa-user-circle-o', - ], - ], - [ - 'options' => [ - 'label' => 'Manage users', - 'route' => 'f_user', - 'params' => ['action' => 'manage'], - 'icon' => 'fa fa-user-o', - ], - ], + ] ], 'account_menu' => [ - [ - 'options' => [ - 'label' => 'Profile', - 'route' => 'user', - 'params' => ['action' => 'account'], - 'icon' => 'fa fa-user', - ] - ], - [ - 'options' => [ - 'label' => 'Settings', - 'route' => 'dashboard', - 'icon' => 'fa fa-cog', - ] - ], - [ - 'options' => [ - 'label' => 'Sign Out', - 'route' => 'logout', - 'icon' => 'fa fa-sign-out', + 'type' => 'ArrayProvider', + 'options' => [ + 'items' => [ + [ + 'options' => [ + 'label' => 'Profile', + 'route' => 'user', + 'params' => ['action' => 'account'], + 'icon' => 'fa fa-user', + ] + ], + [ + 'options' => [ + 'label' => 'Settings', + 'route' => 'dashboard', + 'icon' => 'fa fa-cog', + ] + ], + [ + 'options' => [ + 'label' => 'Sign Out', + 'route' => 'logout', + 'icon' => 'fa fa-sign-out', + ] + ] ] ] ] - ], //register custom providers here - 'provider_manager' => [ - - ] + 'provider_manager' => [] ], ]; diff --git a/config/autoload/routes.global.php b/config/autoload/routes.global.php index 2049d15..d047c27 100644 --- a/config/autoload/routes.global.php +++ b/config/autoload/routes.global.php @@ -6,34 +6,10 @@ Zend\Expressive\Router\RouterInterface::class => Zend\Expressive\Router\FastRouteRouter::class, ], // Map middleware -> factories here - 'factories' => [ - ], + 'factories' => [], ], 'routes' => [ - [ - 'name' => 'dashboard', - 'path' => '/dashboard[/{action}]', - 'middleware' => \Dot\Admin\Controller\DashboardController::class, - ], - [ - // there is already a route named `user` from dot-user package, - // so we use a diff one for the frontend user management - 'name' => 'f_user', - 'path' => '/user[/{action}[/{id:\d+}]]', - 'middleware' => \Dot\Admin\Controller\UserController::class, - ], - //change default route paths for user related stuff into admin - //we will use 'user' for frontend users - 'login_route' => [ - 'path' => '/admin/login', - ], - 'logout_route' => [ - 'path' => '/admin/logout', - ], - 'user_route' => [ - 'path' => '/admin[/{action}[/{id:\d+}]]', - 'middleware' => [\Dot\Admin\Controller\AdminController::class], - ] + // see Admin\App\ConfigProvider or similar... ], ]; diff --git a/config/autoload/session.global.php b/config/autoload/session.global.php index 3ab0345..3adf3a6 100644 --- a/config/autoload/session.global.php +++ b/config/autoload/session.global.php @@ -1,12 +1,13 @@ [ - 'session_namespace' => 'admin_session', - ], - 'dot_flashmessenger' => [ - 'namespace' => 'admin_flashmessenger' + 'options' => [ + 'namespace' => 'admin_messenger' + ] ], + + 'session_config' => [ + 'name' => 'ADMIN_SESSID', + ] ]; diff --git a/config/autoload/templates.global.php b/config/autoload/templates.global.php index 9e1267c..92577f0 100644 --- a/config/autoload/templates.global.php +++ b/config/autoload/templates.global.php @@ -17,16 +17,17 @@ 'templates' => [ 'extension' => 'html.twig', 'paths' => [ - 'app' => ['templates/app'], - 'layout' => ['templates/layout'], - 'error' => ['templates/error'], - 'partial' => ['templates/partial'], - 'entity-manage' => ['templates/entity-manage'], + 'app' => [__DIR__ . '/../../templates/app'], + 'layout' => [__DIR__ . '/../../templates/layout'], + 'error' => [__DIR__ . '/../../templates/error'], + 'partial' => [__DIR__ . '/../../templates/partial'], + 'admin' => [__DIR__ . '/../../templates/app/admin'], + 'user' => [__DIR__ . '/../../templates/app/user'], ], ], 'twig' => [ - 'cache_dir' => 'data/cache/twig', + 'cache_dir' => __DIR__ . '/../../data/cache/twig', 'assets_url' => '/', 'assets_version' => null, 'extensions' => [ diff --git a/config/autoload/user.global.php b/config/autoload/user.global.php deleted file mode 100644 index a89055b..0000000 --- a/config/autoload/user.global.php +++ /dev/null @@ -1,88 +0,0 @@ - [ - //whatever dependencies you need additionally - 'factories' => [ - \Dot\Admin\Authentication\Listener\AuthenticationListener::class => - \Zend\ServiceManager\Factory\InvokableFactory::class, - ], - ], - - 'dot_user' => [ - //listeners for various user related events - 'user_event_listeners' => [ - - ], - - //user entity and its hydrator to use for user transactions - 'user_entity' => \Dot\Admin\Entity\AdminEntity::class, - - 'enable_user_status' => true, - - //enable user form label display - 'show_form_input_labels' => true, - - //db config - 'db_options' => [ - 'db_adapter' => 'database', - - 'user_table' => 'admin', - ], - - //disable registration, we are not interested in other options - 'register_options' => [ - 'enable_registration' => false, - ], - - 'login_options' => [ - 'enable_remember_me' => false, - - 'auth_identity_fields' => ['username', 'email'], - - 'allowed_login_statuses' => ['active'], - ], - - 'password_recovery_options' => [ - 'enable_password_recovery' => false, - - //'reset_password_token_timeout' => 3600, - ], - - 'confirm_account_options' => [ - 'enable_account_confirmation' => false, - - //'active_user_status' => 'active' - ], - - 'template_options' => [ - 'login_template' => 'app::login' - ], - - 'form_manager' => [ - 'factories' => [ - - ] - ], - - 'messages_options' => [ - 'messages' => [ - - ] - ], - ], - - 'dot_authentication' => [ - //this package specific configuration template - 'web' => [ - //template name to use for the login form - 'login_template' => 'app::login', - - //where to redirect after login success - 'after_login_route' => 'dashboard', - //where to redirect after logging out - 'after_logout_route' => 'login', - ] - ], -]; diff --git a/config/autoload/zend-expressive.global.php b/config/autoload/zend-expressive.global.php index fdbf127..49d01bb 100644 --- a/config/autoload/zend-expressive.global.php +++ b/config/autoload/zend-expressive.global.php @@ -20,4 +20,10 @@ 'template_error' => 'error::error', ], ], + + 'dependencies' => [ + 'lazy_services' => [ + 'write_proxy_files' => true, + ] + ] ]; diff --git a/config/bootstrap.php b/config/bootstrap.php deleted file mode 100644 index 54aec0b..0000000 --- a/config/bootstrap.php +++ /dev/null @@ -1,25 +0,0 @@ -get(\Zend\EventManager\EventManagerInterface::class); - -/** - * Register event listeners - * This authentication listener prepares the request for authentication adapter - */ -/** @var $authenticationListeners */ -$authenticationListeners = $container->get(\Dot\Admin\Authentication\Listener\AuthenticationListener::class); -$authenticationListeners->attach($eventManager); - -$sharedEventManager = $eventManager->getSharedManager(); -$sharedEventManager->attach( - \Dot\Authentication\Web\ErrorHandler\UnauthorizedHandler::class, - \Dot\Authentication\Web\Event\AuthenticationEvent::EVENT_AUTHENTICATION_UNAUTHORIZED, - new \Dot\Admin\Authentication\Listener\UnauthorizedListener(), - 10 -); diff --git a/config/config.php b/config/config.php index 92e1b3a..8c924ef 100644 --- a/config/config.php +++ b/config/config.php @@ -1,4 +1,5 @@ setService('config', $config); -/** - * include the bootstrap script, used to do pre application run setup - * like registering event listeners - */ -require __DIR__ . '/bootstrap.php'; - return $container; diff --git a/config/development.config.php.dist b/config/development.config.php.dist index a339639..30a6dfa 100644 --- a/config/development.config.php.dist +++ b/config/development.config.php.dist @@ -20,4 +20,10 @@ use Zend\ConfigAggregator\ConfigAggregator; return [ 'debug' => true, ConfigAggregator::ENABLE_CACHE => false, + + 'dependencies' => [ + 'lazy_services' => [ + 'write_proxy_files' => false, + ] + ] ]; diff --git a/data/.gitignore b/data/.gitignore index be92eeb..7b8eac6 100644 --- a/data/.gitignore +++ b/data/.gitignore @@ -1,4 +1,9 @@ * !.gitignore +!cache +!cache/README.md +!proxies +!proxies/README.md !dot-admin.sql +!dot-admin+frontend.sql diff --git a/data/cache/README.md b/data/cache/README.md new file mode 100644 index 0000000..93dfd2b --- /dev/null +++ b/data/cache/README.md @@ -0,0 +1 @@ +folder for all sorts of caches diff --git a/data/dot-admin+frontend.sql b/data/dot-admin+frontend.sql new file mode 100644 index 0000000..9ad0eff --- /dev/null +++ b/data/dot-admin+frontend.sql @@ -0,0 +1,299 @@ +-- DOT_FRONTEND EXPORT + +-- +-- Table structure for table `user` +-- + +CREATE TABLE `user` ( + `id` int(11) NOT NULL, + `username` varchar(150) DEFAULT NULL, + `email` varchar(150) NOT NULL, + `password` varchar(150) NOT NULL, + `status` enum('pending','active','inactive','deleted') NOT NULL DEFAULT 'pending', + `dateCreated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `user_details` +-- + +CREATE TABLE `user_details` ( + `userId` int(11) NOT NULL, + `firstName` varchar(150) DEFAULT NULL, + `lastName` varchar(150) DEFAULT NULL, + `phone` varchar(150) DEFAULT NULL, + `address` text +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `user_role` +-- + +CREATE TABLE `user_role` ( + `id` int(11) NOT NULL, + `name` varchar(150) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- +-- Dumping data for table `user_role` +-- + +INSERT INTO `user_role` (`id`, `name`) VALUES +(1, 'user'), +(2, 'subscriber'); + +-- -------------------------------------------------------- + +-- +-- Table structure for table `user_roles` +-- + +CREATE TABLE `user_roles` ( + `userId` int(11) NOT NULL, + `roleId` int(11) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `user_token` +-- + +CREATE TABLE `user_token` ( + `id` int(11) NOT NULL, + `userId` int(11) NOT NULL, + `selector` varchar(150) DEFAULT NULL, + `token` varchar(150) NOT NULL, + `expire` int(11) DEFAULT NULL, + `type` enum('confirm','reset','remember') NOT NULL, + `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- +-- Indexes for dumped tables +-- + +-- +-- Indexes for table `user` +-- +ALTER TABLE `user` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `email` (`email`), + ADD UNIQUE KEY `username` (`username`); + +-- +-- Indexes for table `user_details` +-- +ALTER TABLE `user_details` + ADD PRIMARY KEY (`userId`); + +-- +-- Indexes for table `user_role` +-- +ALTER TABLE `user_role` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `name` (`name`); + +-- +-- Indexes for table `user_roles` +-- +ALTER TABLE `user_roles` + ADD PRIMARY KEY (`userId`,`roleId`), + ADD KEY `userId` (`userId`), + ADD KEY `roleId` (`roleId`); + +-- +-- Indexes for table `user_token` +-- +ALTER TABLE `user_token` + ADD PRIMARY KEY (`id`), + ADD KEY `userId` (`userId`), + ADD KEY `selector` (`selector`), + ADD KEY `token` (`token`); + +-- +-- AUTO_INCREMENT for dumped tables +-- + +-- +-- AUTO_INCREMENT for table `user` +-- +ALTER TABLE `user` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1; +-- +-- AUTO_INCREMENT for table `user_role` +-- +ALTER TABLE `user_role` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3; +-- +-- AUTO_INCREMENT for table `user_token` +-- +ALTER TABLE `user_token` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1; +-- +-- Constraints for dumped tables +-- + +-- +-- Constraints for table `user_details` +-- +ALTER TABLE `user_details` + ADD CONSTRAINT `user_details_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- +-- Constraints for table `user_roles` +-- +ALTER TABLE `user_roles` + ADD CONSTRAINT `user_roles_ibfk_2` FOREIGN KEY (`roleId`) REFERENCES `user_role` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + ADD CONSTRAINT `user_roles_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- +-- Constraints for table `user_token` +-- +ALTER TABLE `user_token` + ADD CONSTRAINT `user_token_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- DOT_ADMIN EXPORT + +-- +-- Table structure for table `admin` +-- + +CREATE TABLE IF NOT EXISTS `admin` ( +`id` int(11) NOT NULL, + `username` varchar(150) NOT NULL, + `email` varchar(150) NOT NULL, + `password` varchar(150) NOT NULL, + `firstName` varchar(150) DEFAULT NULL, + `lastName` varchar(150) DEFAULT NULL, + `status` enum('active','inactive','deleted') NOT NULL DEFAULT 'active', + `dateCreated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=2 ; + +-- +-- Dumping data for table `admin` +-- + +INSERT INTO `admin` (`id`, `username`, `email`, `password`, `firstName`, `lastName`, `status`, `dateCreated`) VALUES +(1, 'admin', 'admin@dotkernel.com', '$2y$11$OwMimRB1aTrv.VH0uRIDFeU3eh7NNraKncCRruhW.lKOPyz/R7Fq6', 'DotKernel', 'Admin', 'active', '2016-10-28 17:42:40'); + +-- -------------------------------------------------------- + +-- +-- Table structure for table `admin_role` +-- + +CREATE TABLE IF NOT EXISTS `admin_role` ( +`id` int(11) NOT NULL, + `name` varchar(150) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=3 ; + +-- +-- Dumping data for table `admin_role` +-- + +INSERT INTO `admin_role` (`id`, `name`) VALUES +(1, 'superuser'), +(2, 'admin'); + +-- -------------------------------------------------------- + +-- +-- Table structure for table `admin_roles` +-- + +CREATE TABLE IF NOT EXISTS `admin_roles` ( + `userId` int(11) NOT NULL, + `roleId` int(11) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- +-- Dumping data for table `admin_roles` +-- + +INSERT INTO `admin_roles` (`userId`, `roleId`) VALUES +(1, 1); + +-- -------------------------------------------------------- + +-- +-- Table structure for table `admin_token` +-- + +CREATE TABLE IF NOT EXISTS `admin_token` ( +`id` int(11) NOT NULL, + `userId` int(11) NOT NULL, + `selector` varchar(150) DEFAULT NULL, + `token` varchar(150) NOT NULL, + `expire` int(11) DEFAULT NULL, + `type` enum('confirm','reset','remember') NOT NULL, + `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ; + +-- +-- Indexes for dumped tables +-- + +-- +-- Indexes for table `admin` +-- +ALTER TABLE `admin` + ADD PRIMARY KEY (`id`), ADD UNIQUE KEY `email_UNIQUE` (`email`), ADD UNIQUE KEY `username_UNIQUE` (`username`); + +-- +-- Indexes for table `admin_role` +-- +ALTER TABLE `admin_role` + ADD PRIMARY KEY (`id`); + +-- +-- Indexes for table `admin_roles` +-- +ALTER TABLE `admin_roles` + ADD PRIMARY KEY (`userId`,`roleId`), ADD KEY `userId` (`userId`), ADD KEY `roleId` (`roleId`); + +-- +-- Indexes for table `admin_token` +-- +ALTER TABLE `admin_token` + ADD PRIMARY KEY (`id`), ADD KEY `userId` (`userId`), ADD KEY `selector` (`selector`), ADD KEY `token` (`token`); + +-- +-- AUTO_INCREMENT for dumped tables +-- + +-- +-- AUTO_INCREMENT for table `admin` +-- +ALTER TABLE `admin` +MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=6; +-- +-- AUTO_INCREMENT for table `admin_role` +-- +ALTER TABLE `admin_role` +MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=3; +-- +-- AUTO_INCREMENT for table `admin_token` +-- +ALTER TABLE `admin_token` +MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; +-- +-- Constraints for dumped tables +-- + +-- +-- Constraints for table `admin_roles` +-- +ALTER TABLE `admin_roles` +ADD CONSTRAINT `admin_roles_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `admin` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, +ADD CONSTRAINT `admin_roles_ibfk_2` FOREIGN KEY (`roleId`) REFERENCES `admin_role` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- +-- Constraints for table `admin_token` +-- +ALTER TABLE `admin_token` +ADD CONSTRAINT `admin_token_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `admin` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/data/dot-admin.sql b/data/dot-admin.sql index d363ee3..a194196 100644 --- a/data/dot-admin.sql +++ b/data/dot-admin.sql @@ -2,53 +2,137 @@ -- Table structure for table `admin` -- -DROP TABLE IF EXISTS `admin`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `admin` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `username` varchar(150) DEFAULT NULL, +CREATE TABLE IF NOT EXISTS `admin` ( +`id` int(11) NOT NULL, + `username` varchar(150) NOT NULL, `email` varchar(150) NOT NULL, `password` varchar(150) NOT NULL, `firstName` varchar(150) DEFAULT NULL, `lastName` varchar(150) DEFAULT NULL, - `role` enum('superuser','admin') NOT NULL DEFAULT 'admin', - `status` enum('pending','active','inactive','deleted') NOT NULL DEFAULT 'active', - `dateCreated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `email_UNIQUE` (`email`), - UNIQUE KEY `username_UNIQUE` (`username`) -) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4; -/*!40101 SET character_set_client = @saved_cs_client */; + `status` enum('active','inactive','deleted') NOT NULL DEFAULT 'active', + `dateCreated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=2 ; -- -- Dumping data for table `admin` -- -LOCK TABLES `admin` WRITE; -/*!40000 ALTER TABLE `admin` DISABLE KEYS */; -INSERT INTO `admin` VALUES (1,'admin','admin@admin.com','$2y$11$mPxSiRXjgCRFnyzCsS/85epxA0uiOJnZsAxzk28sBj63rszgC0XRi','Admin','Admin','superuser','active','2016-10-28 17:42:40'); -/*!40000 ALTER TABLE `admin` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `log_admin_action` --- - -DROP TABLE IF EXISTS `log_admin_action`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `log_admin_action` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `ip` varchar(150) NOT NULL, - `agentId` int(11) DEFAULT NULL, - `agentName` varchar(150) DEFAULT NULL, - `type` enum('login','logout','create','update','delete') NOT NULL, - `targetId` varchar(150) DEFAULT NULL, - `target` mediumtext, - `message` mediumtext NOT NULL, - `status` enum('ok','error') DEFAULT 'ok', - `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4; -/*!40101 SET character_set_client = @saved_cs_client */; \ No newline at end of file +INSERT INTO `admin` (`id`, `username`, `email`, `password`, `firstName`, `lastName`, `status`, `dateCreated`) VALUES +(1, 'admin', 'admin@dotkernel.com', '$2y$11$OwMimRB1aTrv.VH0uRIDFeU3eh7NNraKncCRruhW.lKOPyz/R7Fq6', 'DotKernel', 'Admin', 'active', '2016-10-28 17:42:40'); + +-- -------------------------------------------------------- + +-- +-- Table structure for table `admin_role` +-- + +CREATE TABLE IF NOT EXISTS `admin_role` ( +`id` int(11) NOT NULL, + `name` varchar(150) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=3 ; + +-- +-- Dumping data for table `admin_role` +-- + +INSERT INTO `admin_role` (`id`, `name`) VALUES +(1, 'superuser'), +(2, 'admin'); + +-- -------------------------------------------------------- + +-- +-- Table structure for table `admin_roles` +-- + +CREATE TABLE IF NOT EXISTS `admin_roles` ( + `userId` int(11) NOT NULL, + `roleId` int(11) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- +-- Dumping data for table `admin_roles` +-- + +INSERT INTO `admin_roles` (`userId`, `roleId`) VALUES +(1, 1); + +-- -------------------------------------------------------- + +-- +-- Table structure for table `admin_token` +-- + +CREATE TABLE IF NOT EXISTS `admin_token` ( +`id` int(11) NOT NULL, + `userId` int(11) NOT NULL, + `selector` varchar(150) DEFAULT NULL, + `token` varchar(150) NOT NULL, + `expire` int(11) DEFAULT NULL, + `type` enum('confirm','reset','remember') NOT NULL, + `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ; + +-- +-- Indexes for dumped tables +-- + +-- +-- Indexes for table `admin` +-- +ALTER TABLE `admin` + ADD PRIMARY KEY (`id`), ADD UNIQUE KEY `email_UNIQUE` (`email`), ADD UNIQUE KEY `username_UNIQUE` (`username`); + +-- +-- Indexes for table `admin_role` +-- +ALTER TABLE `admin_role` + ADD PRIMARY KEY (`id`); + +-- +-- Indexes for table `admin_roles` +-- +ALTER TABLE `admin_roles` + ADD PRIMARY KEY (`userId`,`roleId`), ADD KEY `userId` (`userId`), ADD KEY `roleId` (`roleId`); + +-- +-- Indexes for table `admin_token` +-- +ALTER TABLE `admin_token` + ADD PRIMARY KEY (`id`), ADD KEY `userId` (`userId`), ADD KEY `selector` (`selector`), ADD KEY `token` (`token`); + +-- +-- AUTO_INCREMENT for dumped tables +-- + +-- +-- AUTO_INCREMENT for table `admin` +-- +ALTER TABLE `admin` +MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=6; +-- +-- AUTO_INCREMENT for table `admin_role` +-- +ALTER TABLE `admin_role` +MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=3; +-- +-- AUTO_INCREMENT for table `admin_token` +-- +ALTER TABLE `admin_token` +MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; +-- +-- Constraints for dumped tables +-- + +-- +-- Constraints for table `admin_roles` +-- +ALTER TABLE `admin_roles` +ADD CONSTRAINT `admin_roles_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `admin` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, +ADD CONSTRAINT `admin_roles_ibfk_2` FOREIGN KEY (`roleId`) REFERENCES `admin_role` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- +-- Constraints for table `admin_token` +-- +ALTER TABLE `admin_token` +ADD CONSTRAINT `admin_token_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `admin` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/data/proxies/README.md b/data/proxies/README.md new file mode 100644 index 0000000..3264384 --- /dev/null +++ b/data/proxies/README.md @@ -0,0 +1 @@ +folder for class proxies diff --git a/public/css/bootstrap-multiselect.css b/public/css/bootstrap-multiselect.css new file mode 100644 index 0000000..13de57b --- /dev/null +++ b/public/css/bootstrap-multiselect.css @@ -0,0 +1 @@ +.multiselect-container{position:absolute;list-style-type:none;margin:0;padding:0}.multiselect-container .input-group{margin:5px}.multiselect-container>li{padding:0}.multiselect-container>li>a.multiselect-all label{font-weight:700}.multiselect-container>li.multiselect-group label{margin:0;padding:3px 20px 3px 20px;height:100%;font-weight:700}.multiselect-container>li.multiselect-group-clickable label{cursor:pointer}.multiselect-container>li>a{padding:0}.multiselect-container>li>a>label{margin:0;height:100%;cursor:pointer;font-weight:400;padding:3px 20px 3px 40px}.multiselect-container>li>a>label.radio,.multiselect-container>li>a>label.checkbox{margin:0}.multiselect-container>li>a>label>input[type=checkbox]{margin-bottom:5px}.btn-group>.btn-group:nth-child(2)>.multiselect.btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.form-inline .multiselect-container label.checkbox,.form-inline .multiselect-container label.radio{padding:3px 20px 3px 40px}.form-inline .multiselect-container li a label.checkbox input[type=checkbox],.form-inline .multiselect-container li a label.radio input[type=radio]{margin-left:-20px;margin-right:0} \ No newline at end of file diff --git a/public/css/bootstrap-table.css b/public/css/bootstrap-table.css index f3ef858..ebf5e95 100644 --- a/public/css/bootstrap-table.css +++ b/public/css/bootstrap-table.css @@ -1,6 +1,6 @@ /** * @author zhixin wen - * version: 1.11.0 + * version: 1.11.1 * https://github.com/wenzhixin/bootstrap-table/ */ @@ -285,7 +285,7 @@ padding: 0 !important; } -.pull-right .dropdown-menu { +.bootstrap-table .pull-right .dropdown-menu { right: 0; left: auto; } @@ -304,3 +304,10 @@ div.fixed-table-scroll-outer { height: 150px; overflow: hidden; } + +/* for get correct heights */ +.fixed-table-toolbar:after, .fixed-table-pagination:after { + content: ""; + display: block; + clear: both; +} diff --git a/public/css/styles.css b/public/css/styles.css index 824cd49..211251d 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -46,11 +46,24 @@ h3, h4 { margin-top: 15px; } +.alert > ul { + list-style-type: square; + padding-left: 20px; +} + .modal { -webkit-overflow-scrolling: auto !important; overflow-y: auto !important; } +.has-error .multiselect { + border-color: #a94442; +} + +.has-error .checkbox { + color: black; +} + #modalAlertIcon { padding-right: 10px; } @@ -174,7 +187,6 @@ fieldset[disabled] .btn-default:active, .btn-default.disabled.active, .btn-default[disabled].active, fieldset[disabled] .btn-default.active { - background-color: #e9ecf2; border-color: #e9ecf2; color: #1b3548; } @@ -715,11 +727,11 @@ a.bg-danger:hover { padding: 10px 15px; background: #e9ecf2; box-shadow: 0 1px 1px rgba(0, 0, 0, .05); - margin: 0; + margin: 0 0 10px; } h1.page-header { - margin-top: 30px; + margin-top: 15px; border-bottom: 0; } diff --git a/public/js/bootstrap-multiselect-collapsible-groups.js b/public/js/bootstrap-multiselect-collapsible-groups.js new file mode 100644 index 0000000..195e4f1 --- /dev/null +++ b/public/js/bootstrap-multiselect-collapsible-groups.js @@ -0,0 +1,92 @@ +! function(e) { + "use strict"; + jQuery.fn.multiselect.Constructor.prototype.selectAll = (function() { + var cached_function = jQuery.fn.multiselect.Constructor.prototype.selectAll; + + return function() { + var args = Array.prototype.slice.call( arguments ); + args[0] = (this.options.enableCollapsibleOptGroups && this.options.multiple) ? false : args[0]; + cached_function.apply(this, args); + }; + }()); + + jQuery.fn.multiselect.Constructor.prototype.deselectAll = (function() { + var cached_function = jQuery.fn.multiselect.Constructor.prototype.deselectAll; + + return function() { + var args = Array.prototype.slice.call( arguments ); + args[0] = (this.options.enableCollapsibleOptGroups && this.options.multiple) ? false : args[0]; + cached_function.apply(this, args); + }; + }()); + + jQuery.fn.multiselect.Constructor.prototype.createOptgroup = (function() { + var cached_function = jQuery.fn.multiselect.Constructor.prototype.createOptgroup; + + return function() { + var args = Array.prototype.slice.call( arguments ); + var t = args[0]; + if (this.options.enableCollapsibleOptGroups && this.options.multiple) { + var n = e(t).attr("label"); + var v = e(t).attr("value"); + var r = e('
  • ' + n + '
  • '); + + if (this.options.enableClickableOptGroups) { + r.addClass("multiselect-group-clickable") + } + this.$ul.append(r); + if (e(t).is(":disabled")) { + r.addClass("disabled") + } + e("option", t).each(e.proxy(function(e, t) { + this.createOptionValue(t) + }, this)) + } else { + cached_function.apply(this, arguments); + } + }; + }()); + + jQuery.fn.multiselect.Constructor.prototype.buildDropdownOptions = (function() { + var cached_function = jQuery.fn.multiselect.Constructor.prototype.buildDropdownOptions; + + return function() { + cached_function.apply(this, arguments); + + if (this.options.enableCollapsibleOptGroups && this.options.multiple) { + + e("li.multiselect-group input", this.$ul).off(); + e("li.multiselect-group", this.$ul).siblings().not("li.multiselect-group, li.multiselect-all", this.$ul).each( function () { + $(this).toggleClass('hidden', true); + }); + e("li.multiselect-group", this.$ul).on("click", e.proxy(function(t) { + t.stopPropagation(); + }, this)); + e("li.multiselect-group > a > b", this.$ul).on("click", e.proxy(function(t) { + t.stopPropagation(); + var n = e(t.target).closest('li'); + var r = n.nextUntil("li.multiselect-group"); + var i = true; + r.each(function() { + i = i && e(this).hasClass('hidden'); + }); + r.toggleClass('hidden', !i); + }, this)); + e("li.multiselect-group > a > input", this.$ul).on("change", e.proxy(function(t) { + t.stopPropagation(); + var n = e(t.target).closest('li'); + var r = n.nextUntil("li.multiselect-group"); + var i = true; + var s = r.find("input"); + s.each(function() { + i = i && e(this).prop("checked") + }); + s.prop("checked", !i).trigger("change") + }, this)); + e("li.multiselect-all", this.$ul).css('background', '#f3f3f3').css('border-bottom', '1px solid #eaeaea'); + e("li.multiselect-group > a, li.multiselect-all > a > label.checkbox", this.$ul).css('padding', '3px 20px 3px 35px'); + e("li.multiselect-group > a > input", this.$ul).css('margin', '4px 0px 5px -20px'); + } + }; + }()); +}(window.jQuery) diff --git a/public/js/bootstrap-multiselect.js b/public/js/bootstrap-multiselect.js new file mode 100644 index 0000000..5fb4c18 --- /dev/null +++ b/public/js/bootstrap-multiselect.js @@ -0,0 +1,1416 @@ +/** + * Bootstrap Multiselect (https://github.com/davidstutz/bootstrap-multiselect) + * + * Apache License, Version 2.0: + * Copyright (c) 2012 - 2015 David Stutz + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a + * copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * BSD 3-Clause License: + * Copyright (c) 2012 - 2015 David Stutz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of David Stutz nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +!function ($) { + "use strict";// jshint ;_; + + if (typeof ko !== 'undefined' && ko.bindingHandlers && !ko.bindingHandlers.multiselect) { + ko.bindingHandlers.multiselect = { + after: ['options', 'value', 'selectedOptions'], + + init: function(element, valueAccessor, allBindings, viewModel, bindingContext) { + var $element = $(element); + var config = ko.toJS(valueAccessor()); + + $element.multiselect(config); + + if (allBindings.has('options')) { + var options = allBindings.get('options'); + if (ko.isObservable(options)) { + ko.computed({ + read: function() { + options(); + setTimeout(function() { + var ms = $element.data('multiselect'); + if (ms) + ms.updateOriginalOptions();//Not sure how beneficial this is. + $element.multiselect('rebuild'); + }, 1); + }, + disposeWhenNodeIsRemoved: element + }); + } + } + + //value and selectedOptions are two-way, so these will be triggered even by our own actions. + //It needs some way to tell if they are triggered because of us or because of outside change. + //It doesn't loop but it's a waste of processing. + if (allBindings.has('value')) { + var value = allBindings.get('value'); + if (ko.isObservable(value)) { + ko.computed({ + read: function() { + value(); + setTimeout(function() { + $element.multiselect('refresh'); + }, 1); + }, + disposeWhenNodeIsRemoved: element + }).extend({ rateLimit: 100, notifyWhenChangesStop: true }); + } + } + + //Switched from arrayChange subscription to general subscription using 'refresh'. + //Not sure performance is any better using 'select' and 'deselect'. + if (allBindings.has('selectedOptions')) { + var selectedOptions = allBindings.get('selectedOptions'); + if (ko.isObservable(selectedOptions)) { + ko.computed({ + read: function() { + selectedOptions(); + setTimeout(function() { + $element.multiselect('refresh'); + }, 1); + }, + disposeWhenNodeIsRemoved: element + }).extend({ rateLimit: 100, notifyWhenChangesStop: true }); + } + } + + ko.utils.domNodeDisposal.addDisposeCallback(element, function() { + $element.multiselect('destroy'); + }); + }, + + update: function(element, valueAccessor, allBindings, viewModel, bindingContext) { + var $element = $(element); + var config = ko.toJS(valueAccessor()); + + $element.multiselect('setOptions', config); + $element.multiselect('rebuild'); + } + }; + } + + function forEach(array, callback) { + for (var index = 0; index < array.length; ++index) { + callback(array[index], index); + } + } + + /** + * Constructor to create a new multiselect using the given select. + * + * @param {jQuery} select + * @param {Object} options + * @returns {Multiselect} + */ + function Multiselect(select, options) { + + this.$select = $(select); + + // Placeholder via data attributes + if (this.$select.attr("data-placeholder")) { + options.nonSelectedText = this.$select.data("placeholder"); + } + + this.options = this.mergeOptions($.extend({}, options, this.$select.data())); + + // Initialization. + // We have to clone to create a new reference. + this.originalOptions = this.$select.clone()[0].options; + this.query = ''; + this.searchTimeout = null; + this.lastToggledInput = null + + this.options.multiple = this.$select.attr('multiple') === "multiple"; + this.options.onChange = $.proxy(this.options.onChange, this); + this.options.onDropdownShow = $.proxy(this.options.onDropdownShow, this); + this.options.onDropdownHide = $.proxy(this.options.onDropdownHide, this); + this.options.onDropdownShown = $.proxy(this.options.onDropdownShown, this); + this.options.onDropdownHidden = $.proxy(this.options.onDropdownHidden, this); + + // Build select all if enabled. + this.buildContainer(); + this.buildButton(); + this.buildDropdown(); + this.buildSelectAll(); + this.buildDropdownOptions(); + this.buildFilter(); + + this.updateButtonText(); + this.updateSelectAll(); + + if (this.options.disableIfEmpty && $('option', this.$select).length <= 0) { + this.disable(); + } + + this.$select.hide().after(this.$container); + }; + + Multiselect.prototype = { + + defaults: { + /** + * Default text function will either print 'None selected' in case no + * option is selected or a list of the selected options up to a length + * of 3 selected options. + * + * @param {jQuery} options + * @param {jQuery} select + * @returns {String} + */ + buttonText: function(options, select) { + if (options.length === 0) { + return this.nonSelectedText; + } + else if (this.allSelectedText + && options.length === $('option', $(select)).length + && $('option', $(select)).length !== 1 + && this.multiple) { + + if (this.selectAllNumber) { + return this.allSelectedText + ' (' + options.length + ')'; + } + else { + return this.allSelectedText; + } + } + else if (options.length > this.numberDisplayed) { + return options.length + ' ' + this.nSelectedText; + } + else { + var selected = ''; + var delimiter = this.delimiterText; + + options.each(function() { + var label = ($(this).attr('label') !== undefined) ? $(this).attr('label') : $(this).text(); + selected += label + delimiter; + }); + + return selected.substr(0, selected.length - 2); + } + }, + /** + * Updates the title of the button similar to the buttonText function. + * + * @param {jQuery} options + * @param {jQuery} select + * @returns {@exp;selected@call;substr} + */ + buttonTitle: function(options, select) { + if (options.length === 0) { + return this.nonSelectedText; + } + else { + var selected = ''; + var delimiter = this.delimiterText; + + options.each(function () { + var label = ($(this).attr('label') !== undefined) ? $(this).attr('label') : $(this).text(); + selected += label + delimiter; + }); + return selected.substr(0, selected.length - 2); + } + }, + /** + * Create a label. + * + * @param {jQuery} element + * @returns {String} + */ + optionLabel: function(element){ + return $(element).attr('label') || $(element).text(); + }, + /** + * Triggered on change of the multiselect. + * + * Not triggered when selecting/deselecting options manually. + * + * @param {jQuery} option + * @param {Boolean} checked + */ + onChange : function(option, checked) { + + }, + /** + * Triggered when the dropdown is shown. + * + * @param {jQuery} event + */ + onDropdownShow: function(event) { + + }, + /** + * Triggered when the dropdown is hidden. + * + * @param {jQuery} event + */ + onDropdownHide: function(event) { + + }, + /** + * Triggered after the dropdown is shown. + * + * @param {jQuery} event + */ + onDropdownShown: function(event) { + + }, + /** + * Triggered after the dropdown is hidden. + * + * @param {jQuery} event + */ + onDropdownHidden: function(event) { + + }, + /** + * Triggered on select all. + */ + onSelectAll: function() { + + }, + enableHTML: false, + buttonClass: 'btn btn-default', + inheritClass: false, + buttonWidth: 'auto', + buttonContainer: '
    ', + dropRight: false, + selectedClass: 'active', + // Maximum height of the dropdown menu. + // If maximum height is exceeded a scrollbar will be displayed. + maxHeight: false, + checkboxName: false, + includeSelectAllOption: false, + includeSelectAllIfMoreThan: 0, + selectAllText: ' Select all', + selectAllValue: 'multiselect-all', + selectAllName: false, + selectAllNumber: true, + enableFiltering: false, + enableCaseInsensitiveFiltering: false, + enableClickableOptGroups: false, + filterPlaceholder: 'Search', + // possible options: 'text', 'value', 'both' + filterBehavior: 'text', + includeFilterClearBtn: true, + preventInputChangeEvent: false, + nonSelectedText: 'None selected', + nSelectedText: 'selected', + allSelectedText: 'All selected', + numberDisplayed: 3, + disableIfEmpty: false, + delimiterText: ', ', + templates: { + button: '', + ul: '', + filter: '
  • ', + filterClearBtn: '', + li: '
  • ', + divider: '
  • ', + liGroup: '
  • ' + } + }, + + constructor: Multiselect, + + /** + * Builds the container of the multiselect. + */ + buildContainer: function() { + this.$container = $(this.options.buttonContainer); + this.$container.on('show.bs.dropdown', this.options.onDropdownShow); + this.$container.on('hide.bs.dropdown', this.options.onDropdownHide); + this.$container.on('shown.bs.dropdown', this.options.onDropdownShown); + this.$container.on('hidden.bs.dropdown', this.options.onDropdownHidden); + }, + + /** + * Builds the button of the multiselect. + */ + buildButton: function() { + this.$button = $(this.options.templates.button).addClass(this.options.buttonClass); + if (this.$select.attr('class') && this.options.inheritClass) { + this.$button.addClass(this.$select.attr('class')); + } + // Adopt active state. + if (this.$select.prop('disabled')) { + this.disable(); + } + else { + this.enable(); + } + + // Manually add button width if set. + if (this.options.buttonWidth && this.options.buttonWidth !== 'auto') { + this.$button.css({ + 'width' : this.options.buttonWidth, + 'overflow' : 'hidden', + 'text-overflow' : 'ellipsis' + }); + this.$container.css({ + 'width': this.options.buttonWidth + }); + } + + // Keep the tab index from the select. + var tabindex = this.$select.attr('tabindex'); + if (tabindex) { + this.$button.attr('tabindex', tabindex); + } + + this.$container.prepend(this.$button); + }, + + /** + * Builds the ul representing the dropdown menu. + */ + buildDropdown: function() { + + // Build ul. + this.$ul = $(this.options.templates.ul); + + if (this.options.dropRight) { + this.$ul.addClass('pull-right'); + } + + // Set max height of dropdown menu to activate auto scrollbar. + if (this.options.maxHeight) { + // TODO: Add a class for this option to move the css declarations. + this.$ul.css({ + 'max-height': this.options.maxHeight + 'px', + 'overflow-y': 'auto', + 'overflow-x': 'hidden' + }); + } + + this.$container.append(this.$ul); + }, + + /** + * Build the dropdown options and binds all nessecary events. + * + * Uses createDivider and createOptionValue to create the necessary options. + */ + buildDropdownOptions: function() { + + this.$select.children().each($.proxy(function(index, element) { + + var $element = $(element); + // Support optgroups and options without a group simultaneously. + var tag = $element.prop('tagName') + .toLowerCase(); + + if ($element.prop('value') === this.options.selectAllValue) { + return; + } + + if (tag === 'optgroup') { + this.createOptgroup(element); + } + else if (tag === 'option') { + + if ($element.data('role') === 'divider') { + this.createDivider(); + } + else { + this.createOptionValue(element); + } + + } + + // Other illegal tags will be ignored. + }, this)); + + // Bind the change event on the dropdown elements. + $('li input', this.$ul).on('change', $.proxy(function(event) { + var $target = $(event.target); + + var checked = $target.prop('checked') || false; + var isSelectAllOption = $target.val() === this.options.selectAllValue; + + // Apply or unapply the configured selected class. + if (this.options.selectedClass) { + if (checked) { + $target.closest('li') + .addClass(this.options.selectedClass); + } + else { + $target.closest('li') + .removeClass(this.options.selectedClass); + } + } + + // Get the corresponding option. + var value = $target.val(); + var $option = this.getOptionByValue(value); + + var $optionsNotThis = $('option', this.$select).not($option); + var $checkboxesNotThis = $('input', this.$container).not($target); + + if (isSelectAllOption) { + if (checked) { + this.selectAll(); + } + else { + this.deselectAll(); + } + } + + if(!isSelectAllOption){ + if (checked) { + $option.prop('selected', true); + + if (this.options.multiple) { + // Simply select additional option. + $option.prop('selected', true); + } + else { + // Unselect all other options and corresponding checkboxes. + if (this.options.selectedClass) { + $($checkboxesNotThis).closest('li').removeClass(this.options.selectedClass); + } + + $($checkboxesNotThis).prop('checked', false); + $optionsNotThis.prop('selected', false); + + // It's a single selection, so close. + this.$button.click(); + } + + if (this.options.selectedClass === "active") { + $optionsNotThis.closest("a").css("outline", ""); + } + } + else { + // Unselect option. + $option.prop('selected', false); + } + } + + this.$select.change(); + + this.updateButtonText(); + this.updateSelectAll(); + + this.options.onChange($option, checked); + + if(this.options.preventInputChangeEvent) { + return false; + } + }, this)); + + $('li a', this.$ul).on('mousedown', function(e) { + if (e.shiftKey) { + // Prevent selecting text by Shift+click + return false; + } + }); + + $('li a', this.$ul).on('touchstart click', $.proxy(function(event) { + event.stopPropagation(); + + var $target = $(event.target); + + if (event.shiftKey && this.options.multiple) { + if($target.is("label")){ // Handles checkbox selection manually (see https://github.com/davidstutz/bootstrap-multiselect/issues/431) + event.preventDefault(); + $target = $target.find("input"); + $target.prop("checked", !$target.prop("checked")); + } + var checked = $target.prop('checked') || false; + + if (this.lastToggledInput !== null && this.lastToggledInput !== $target) { // Make sure we actually have a range + var from = $target.closest("li").index(); + var to = this.lastToggledInput.closest("li").index(); + + if (from > to) { // Swap the indices + var tmp = to; + to = from; + from = tmp; + } + + // Make sure we grab all elements since slice excludes the last index + ++to; + + // Change the checkboxes and underlying options + var range = this.$ul.find("li").slice(from, to).find("input"); + + range.prop('checked', checked); + + if (this.options.selectedClass) { + range.closest('li') + .toggleClass(this.options.selectedClass, checked); + } + + for (var i = 0, j = range.length; i < j; i++) { + var $checkbox = $(range[i]); + + var $option = this.getOptionByValue($checkbox.val()); + + $option.prop('selected', checked); + } + } + + // Trigger the select "change" event + $target.trigger("change"); + } + + // Remembers last clicked option + if($target.is("input") && !$target.closest("li").is(".multiselect-item")){ + this.lastToggledInput = $target; + } + + $target.blur(); + }, this)); + + // Keyboard support. + this.$container.off('keydown.multiselect').on('keydown.multiselect', $.proxy(function(event) { + if ($('input[type="text"]', this.$container).is(':focus')) { + return; + } + + if (event.keyCode === 9 && this.$container.hasClass('open')) { + this.$button.click(); + } + else { + var $items = $(this.$container).find("li:not(.divider):not(.disabled) a").filter(":visible"); + + if (!$items.length) { + return; + } + + var index = $items.index($items.filter(':focus')); + + // Navigation up. + if (event.keyCode === 38 && index > 0) { + index--; + } + // Navigate down. + else if (event.keyCode === 40 && index < $items.length - 1) { + index++; + } + else if (!~index) { + index = 0; + } + + var $current = $items.eq(index); + $current.focus(); + + if (event.keyCode === 32 || event.keyCode === 13) { + var $checkbox = $current.find('input'); + + $checkbox.prop("checked", !$checkbox.prop("checked")); + $checkbox.change(); + } + + event.stopPropagation(); + event.preventDefault(); + } + }, this)); + + if(this.options.enableClickableOptGroups && this.options.multiple) { + $('li.multiselect-group', this.$ul).on('click', $.proxy(function(event) { + event.stopPropagation(); + + var group = $(event.target).parent(); + + // Search all option in optgroup + var $options = group.nextUntil('li.multiselect-group'); + var $visibleOptions = $options.filter(":visible:not(.disabled)"); + + // check or uncheck items + var allChecked = true; + var optionInputs = $visibleOptions.find('input'); + optionInputs.each(function() { + allChecked = allChecked && $(this).prop('checked'); + }); + + optionInputs.prop('checked', !allChecked).trigger('change'); + }, this)); + } + }, + + /** + * Create an option using the given select option. + * + * @param {jQuery} element + */ + createOptionValue: function(element) { + var $element = $(element); + if ($element.is(':selected')) { + $element.prop('selected', true); + } + + // Support the label attribute on options. + var label = this.options.optionLabel(element); + var value = $element.val(); + var inputType = this.options.multiple ? "checkbox" : "radio"; + + var $li = $(this.options.templates.li); + var $label = $('label', $li); + $label.addClass(inputType); + + if (this.options.enableHTML) { + $label.html(" " + label); + } + else { + $label.text(" " + label); + } + + var $checkbox = $('').attr('type', inputType); + + if (this.options.checkboxName) { + $checkbox.attr('name', this.options.checkboxName); + } + $label.prepend($checkbox); + + var selected = $element.prop('selected') || false; + $checkbox.val(value); + + if (value === this.options.selectAllValue) { + $li.addClass("multiselect-item multiselect-all"); + $checkbox.parent().parent() + .addClass('multiselect-all'); + } + + $label.attr('title', $element.attr('title')); + + this.$ul.append($li); + + if ($element.is(':disabled')) { + $checkbox.attr('disabled', 'disabled') + .prop('disabled', true) + .closest('a') + .attr("tabindex", "-1") + .closest('li') + .addClass('disabled'); + } + + $checkbox.prop('checked', selected); + + if (selected && this.options.selectedClass) { + $checkbox.closest('li') + .addClass(this.options.selectedClass); + } + }, + + /** + * Creates a divider using the given select option. + * + * @param {jQuery} element + */ + createDivider: function(element) { + var $divider = $(this.options.templates.divider); + this.$ul.append($divider); + }, + + /** + * Creates an optgroup. + * + * @param {jQuery} group + */ + createOptgroup: function(group) { + var groupName = $(group).prop('label'); + + // Add a header for the group. + var $li = $(this.options.templates.liGroup); + + if (this.options.enableHTML) { + $('label', $li).html(groupName); + } + else { + $('label', $li).text(groupName); + } + + if (this.options.enableClickableOptGroups) { + $li.addClass('multiselect-group-clickable'); + } + + this.$ul.append($li); + + if ($(group).is(':disabled')) { + $li.addClass('disabled'); + } + + // Add the options of the group. + $('option', group).each($.proxy(function(index, element) { + this.createOptionValue(element); + }, this)); + }, + + /** + * Build the selct all. + * + * Checks if a select all has already been created. + */ + buildSelectAll: function() { + if (typeof this.options.selectAllValue === 'number') { + this.options.selectAllValue = this.options.selectAllValue.toString(); + } + + var alreadyHasSelectAll = this.hasSelectAll(); + + if (!alreadyHasSelectAll && this.options.includeSelectAllOption && this.options.multiple + && $('option', this.$select).length > this.options.includeSelectAllIfMoreThan) { + + // Check whether to add a divider after the select all. + if (this.options.includeSelectAllDivider) { + this.$ul.prepend($(this.options.templates.divider)); + } + + var $li = $(this.options.templates.li); + $('label', $li).addClass("checkbox"); + + if (this.options.enableHTML) { + $('label', $li).html(" " + this.options.selectAllText); + } + else { + $('label', $li).text(" " + this.options.selectAllText); + } + + if (this.options.selectAllName) { + $('label', $li).prepend(''); + } + else { + $('label', $li).prepend(''); + } + + var $checkbox = $('input', $li); + $checkbox.val(this.options.selectAllValue); + + $li.addClass("multiselect-item multiselect-all"); + $checkbox.parent().parent() + .addClass('multiselect-all'); + + this.$ul.prepend($li); + + $checkbox.prop('checked', false); + } + }, + + /** + * Builds the filter. + */ + buildFilter: function() { + + // Build filter if filtering OR case insensitive filtering is enabled and the number of options exceeds (or equals) enableFilterLength. + if (this.options.enableFiltering || this.options.enableCaseInsensitiveFiltering) { + var enableFilterLength = Math.max(this.options.enableFiltering, this.options.enableCaseInsensitiveFiltering); + + if (this.$select.find('option').length >= enableFilterLength) { + + this.$filter = $(this.options.templates.filter); + $('input', this.$filter).attr('placeholder', this.options.filterPlaceholder); + + // Adds optional filter clear button + if(this.options.includeFilterClearBtn){ + var clearBtn = $(this.options.templates.filterClearBtn); + clearBtn.on('click', $.proxy(function(event){ + clearTimeout(this.searchTimeout); + this.$filter.find('.multiselect-search').val(''); + $('li', this.$ul).show().removeClass("filter-hidden"); + this.updateSelectAll(); + }, this)); + this.$filter.find('.input-group').append(clearBtn); + } + + this.$ul.prepend(this.$filter); + + this.$filter.val(this.query).on('click', function(event) { + event.stopPropagation(); + }).on('input keydown', $.proxy(function(event) { + // Cancel enter key default behaviour + if (event.which === 13) { + event.preventDefault(); + } + + // This is useful to catch "keydown" events after the browser has updated the control. + clearTimeout(this.searchTimeout); + + this.searchTimeout = this.asyncFunction($.proxy(function() { + + if (this.query !== event.target.value) { + this.query = event.target.value; + + var currentGroup, currentGroupVisible; + $.each($('li', this.$ul), $.proxy(function(index, element) { + var value = $('input', element).length > 0 ? $('input', element).val() : ""; + var text = $('label', element).text(); + + var filterCandidate = ''; + if ((this.options.filterBehavior === 'text')) { + filterCandidate = text; + } + else if ((this.options.filterBehavior === 'value')) { + filterCandidate = value; + } + else if (this.options.filterBehavior === 'both') { + filterCandidate = text + '\n' + value; + } + + if (value !== this.options.selectAllValue && text) { + // By default lets assume that element is not + // interesting for this search. + var showElement = false; + + if (this.options.enableCaseInsensitiveFiltering && filterCandidate.toLowerCase().indexOf(this.query.toLowerCase()) > -1) { + showElement = true; + } + else if (filterCandidate.indexOf(this.query) > -1) { + showElement = true; + } + + // Toggle current element (group or group item) according to showElement boolean. + $(element).toggle(showElement).toggleClass('filter-hidden', !showElement); + + // Differentiate groups and group items. + if ($(element).hasClass('multiselect-group')) { + // Remember group status. + currentGroup = element; + currentGroupVisible = showElement; + } + else { + // Show group name when at least one of its items is visible. + if (showElement) { + $(currentGroup).show().removeClass('filter-hidden'); + } + + // Show all group items when group name satisfies filter. + if (!showElement && currentGroupVisible) { + $(element).show().removeClass('filter-hidden'); + } + } + } + }, this)); + } + + this.updateSelectAll(); + }, this), 300, this); + }, this)); + } + } + }, + + /** + * Unbinds the whole plugin. + */ + destroy: function() { + this.$container.remove(); + this.$select.show(); + this.$select.data('multiselect', null); + }, + + /** + * Refreshs the multiselect based on the selected options of the select. + */ + refresh: function() { + $('option', this.$select).each($.proxy(function(index, element) { + var $input = $('li input', this.$ul).filter(function() { + return $(this).val() === $(element).val(); + }); + + if ($(element).is(':selected')) { + $input.prop('checked', true); + + if (this.options.selectedClass) { + $input.closest('li') + .addClass(this.options.selectedClass); + } + } + else { + $input.prop('checked', false); + + if (this.options.selectedClass) { + $input.closest('li') + .removeClass(this.options.selectedClass); + } + } + + if ($(element).is(":disabled")) { + $input.attr('disabled', 'disabled') + .prop('disabled', true) + .closest('li') + .addClass('disabled'); + } + else { + $input.prop('disabled', false) + .closest('li') + .removeClass('disabled'); + } + }, this)); + + this.updateButtonText(); + this.updateSelectAll(); + }, + + /** + * Select all options of the given values. + * + * If triggerOnChange is set to true, the on change event is triggered if + * and only if one value is passed. + * + * @param {Array} selectValues + * @param {Boolean} triggerOnChange + */ + select: function(selectValues, triggerOnChange) { + if(!$.isArray(selectValues)) { + selectValues = [selectValues]; + } + + for (var i = 0; i < selectValues.length; i++) { + var value = selectValues[i]; + + if (value === null || value === undefined) { + continue; + } + + var $option = this.getOptionByValue(value); + var $checkbox = this.getInputByValue(value); + + if($option === undefined || $checkbox === undefined) { + continue; + } + + if (!this.options.multiple) { + this.deselectAll(false); + } + + if (this.options.selectedClass) { + $checkbox.closest('li') + .addClass(this.options.selectedClass); + } + + $checkbox.prop('checked', true); + $option.prop('selected', true); + + if (triggerOnChange) { + this.options.onChange($option, true); + } + } + + this.updateButtonText(); + this.updateSelectAll(); + }, + + /** + * Clears all selected items. + */ + clearSelection: function () { + this.deselectAll(false); + this.updateButtonText(); + this.updateSelectAll(); + }, + + /** + * Deselects all options of the given values. + * + * If triggerOnChange is set to true, the on change event is triggered, if + * and only if one value is passed. + * + * @param {Array} deselectValues + * @param {Boolean} triggerOnChange + */ + deselect: function(deselectValues, triggerOnChange) { + if(!$.isArray(deselectValues)) { + deselectValues = [deselectValues]; + } + + for (var i = 0; i < deselectValues.length; i++) { + var value = deselectValues[i]; + + if (value === null || value === undefined) { + continue; + } + + var $option = this.getOptionByValue(value); + var $checkbox = this.getInputByValue(value); + + if($option === undefined || $checkbox === undefined) { + continue; + } + + if (this.options.selectedClass) { + $checkbox.closest('li') + .removeClass(this.options.selectedClass); + } + + $checkbox.prop('checked', false); + $option.prop('selected', false); + + if (triggerOnChange) { + this.options.onChange($option, false); + } + } + + this.updateButtonText(); + this.updateSelectAll(); + }, + + /** + * Selects all enabled & visible options. + * + * If justVisible is true or not specified, only visible options are selected. + * + * @param {Boolean} justVisible + * @param {Boolean} triggerOnSelectAll + */ + selectAll: function (justVisible, triggerOnSelectAll) { + var justVisible = typeof justVisible === 'undefined' ? true : justVisible; + var allCheckboxes = $("li input[type='checkbox']:enabled", this.$ul); + var visibleCheckboxes = allCheckboxes.filter(":visible"); + var allCheckboxesCount = allCheckboxes.length; + var visibleCheckboxesCount = visibleCheckboxes.length; + + if(justVisible) { + visibleCheckboxes.prop('checked', true); + $("li:not(.divider):not(.disabled)", this.$ul).filter(":visible").addClass(this.options.selectedClass); + } + else { + allCheckboxes.prop('checked', true); + $("li:not(.divider):not(.disabled)", this.$ul).addClass(this.options.selectedClass); + } + + if (allCheckboxesCount === visibleCheckboxesCount || justVisible === false) { + $("option:enabled", this.$select).prop('selected', true); + } + else { + var values = visibleCheckboxes.map(function() { + return $(this).val(); + }).get(); + + $("option:enabled", this.$select).filter(function(index) { + return $.inArray($(this).val(), values) !== -1; + }).prop('selected', true); + } + + if (triggerOnSelectAll) { + this.options.onSelectAll(); + } + }, + + /** + * Deselects all options. + * + * If justVisible is true or not specified, only visible options are deselected. + * + * @param {Boolean} justVisible + */ + deselectAll: function (justVisible) { + var justVisible = typeof justVisible === 'undefined' ? true : justVisible; + + if(justVisible) { + var visibleCheckboxes = $("li input[type='checkbox']:not(:disabled)", this.$ul).filter(":visible"); + visibleCheckboxes.prop('checked', false); + + var values = visibleCheckboxes.map(function() { + return $(this).val(); + }).get(); + + $("option:enabled", this.$select).filter(function(index) { + return $.inArray($(this).val(), values) !== -1; + }).prop('selected', false); + + if (this.options.selectedClass) { + $("li:not(.divider):not(.disabled)", this.$ul).filter(":visible").removeClass(this.options.selectedClass); + } + } + else { + $("li input[type='checkbox']:enabled", this.$ul).prop('checked', false); + $("option:enabled", this.$select).prop('selected', false); + + if (this.options.selectedClass) { + $("li:not(.divider):not(.disabled)", this.$ul).removeClass(this.options.selectedClass); + } + } + }, + + /** + * Rebuild the plugin. + * + * Rebuilds the dropdown, the filter and the select all option. + */ + rebuild: function() { + this.$ul.html(''); + + // Important to distinguish between radios and checkboxes. + this.options.multiple = this.$select.attr('multiple') === "multiple"; + + this.buildSelectAll(); + this.buildDropdownOptions(); + this.buildFilter(); + + this.updateButtonText(); + this.updateSelectAll(); + + if (this.options.disableIfEmpty && $('option', this.$select).length <= 0) { + this.disable(); + } + else { + this.enable(); + } + + if (this.options.dropRight) { + this.$ul.addClass('pull-right'); + } + }, + + /** + * The provided data will be used to build the dropdown. + */ + dataprovider: function(dataprovider) { + + var groupCounter = 0; + var $select = this.$select.empty(); + + $.each(dataprovider, function (index, option) { + var $tag; + + if ($.isArray(option.children)) { // create optiongroup tag + groupCounter++; + + $tag = $('').attr({ + label: option.label || 'Group ' + groupCounter, + disabled: !!option.disabled + }); + + forEach(option.children, function(subOption) { // add children option tags + $tag.append($('
    "),e.push("")}}),this.$tableFooter.find("tr").html(e.join("")),this.$tableFooter.show(),clearTimeout(this.timeoutFooter_),this.timeoutFooter_=setTimeout(a.proxy(this.fitFooter,this),this.$el.is(":hidden")?100:0))},o.prototype.fitFooter=function(){var b,c,d;return clearTimeout(this.timeoutFooter_),this.$el.is(":hidden")?void(this.timeoutFooter_=setTimeout(a.proxy(this.fitFooter,this),100)):(c=this.$el.css("width"),d=c>this.$tableBody.width()?g():0,this.$tableFooter.css({"margin-right":d}).find("table").css("width",c).attr("class",this.$el.attr("class")),b=this.$tableFooter.find("td"),void this.$body.find(">tr:first-child:not(.no-records-found) > *").each(function(c){var d=a(this);b.eq(c).find(".fht-cell").width(d.innerWidth())}))},o.prototype.toggleColumn=function(a,b,d){if(-1!==a&&(this.columns[a].visible=b,this.initHeader(),this.initSearch(),this.initPagination(),this.initBody(),this.options.showColumns)){var e=this.$toolbar.find(".keep-open input").prop("disabled",!1);d&&e.filter(c('[value="%s"]',a)).prop("checked",b),e.filter(":checked").length<=this.options.minimumCountColumns&&e.filter(":checked").prop("disabled",!0)}},o.prototype.getVisibleFields=function(){var b=this,c=[];return a.each(this.header.fields,function(a,d){var f=b.columns[e(b.columns,d)];f.visible&&c.push(d)}),c},o.prototype.resetView=function(a){var b=0;if(a&&a.height&&(this.options.height=a.height),this.$selectAll.prop("checked",this.$selectItem.length>0&&this.$selectItem.length===this.$selectItem.filter(":checked").length),this.options.height){var c=this.$toolbar.outerHeight(!0),d=this.$pagination.outerHeight(!0),e=this.options.height-c-d;this.$tableContainer.css("height",e+"px")}return this.options.cardView?(this.$el.css("margin-top","0"),this.$tableContainer.css("padding-bottom","0"),void this.$tableFooter.hide()):(this.options.showHeader&&this.options.height?(this.$tableHeader.show(),this.resetHeader(),b+=this.$header.outerHeight()):(this.$tableHeader.hide(),this.trigger("post-header")),this.options.showFooter&&(this.resetFooter(),this.options.height&&(b+=this.$tableFooter.outerHeight()+1)),this.getCaret(),this.$tableContainer.css("padding-bottom",b+"px"),void this.trigger("reset-view"))},o.prototype.getData=function(b){return!this.searchText&&a.isEmptyObject(this.filterColumns)&&a.isEmptyObject(this.filterColumnsPartial)?b?this.options.data.slice(this.pageFrom-1,this.pageTo):this.options.data:b?this.data.slice(this.pageFrom-1,this.pageTo):this.data},o.prototype.load=function(b){var c=!1;"server"===this.options.sidePagination?(this.options.totalRows=b[this.options.totalField],c=b.fixedScroll,b=b[this.options.dataField]):a.isArray(b)||(c=b.fixedScroll,b=b.data),this.initData(b),this.initSearch(),this.initPagination(),this.initBody(c)},o.prototype.append=function(a){this.initData(a,"append"),this.initSearch(),this.initPagination(),this.initSort(),this.initBody(!0)},o.prototype.prepend=function(a){this.initData(a,"prepend"),this.initSearch(),this.initPagination(),this.initSort(),this.initBody(!0)},o.prototype.remove=function(b){var c,d,e=this.options.data.length;if(b.hasOwnProperty("field")&&b.hasOwnProperty("values")){for(c=e-1;c>=0;c--)d=this.options.data[c],d.hasOwnProperty(b.field)&&-1!==a.inArray(d[b.field],b.values)&&(this.options.data.splice(c,1),"server"===this.options.sidePagination&&(this.options.totalRows-=1));e!==this.options.data.length&&(this.initSearch(),this.initPagination(),this.initSort(),this.initBody(!0))}},o.prototype.removeAll=function(){this.options.data.length>0&&(this.options.data.splice(0,this.options.data.length),this.initSearch(),this.initPagination(),this.initBody(!0))},o.prototype.getRowByUniqueId=function(a){var b,c,d,e=this.options.uniqueId,f=this.options.data.length,g=null;for(b=f-1;b>=0;b--){if(c=this.options.data[b],c.hasOwnProperty(e))d=c[e];else{if(!c._data.hasOwnProperty(e))continue;d=c._data[e]}if("string"==typeof d?a=a.toString():"number"==typeof d&&(Number(d)===d&&d%1===0?a=parseInt(a):d===Number(d)&&0!==d&&(a=parseFloat(a))),d===a){g=c;break}}return g},o.prototype.removeByUniqueId=function(a){var b=this.options.data.length,c=this.getRowByUniqueId(a);c&&this.options.data.splice(this.options.data.indexOf(c),1),b!==this.options.data.length&&(this.initSearch(),this.initPagination(),this.initBody(!0))},o.prototype.updateByUniqueId=function(b){var c=this,d=a.isArray(b)?b:[b];a.each(d,function(b,d){var e;d.hasOwnProperty("id")&&d.hasOwnProperty("row")&&(e=a.inArray(c.getRowByUniqueId(d.id),c.options.data),-1!==e&&a.extend(c.options.data[e],d.row))}),this.initSearch(),this.initPagination(),this.initSort(),this.initBody(!0)},o.prototype.insertRow=function(a){a.hasOwnProperty("index")&&a.hasOwnProperty("row")&&(this.data.splice(a.index,0,a.row),this.initSearch(),this.initPagination(),this.initSort(),this.initBody(!0))},o.prototype.updateRow=function(b){var c=this,d=a.isArray(b)?b:[b];a.each(d,function(b,d){d.hasOwnProperty("index")&&d.hasOwnProperty("row")&&a.extend(c.options.data[d.index],d.row)}),this.initSearch(),this.initPagination(),this.initSort(),this.initBody(!0)},o.prototype.initHiddenRows=function(){this.hiddenRows=[]},o.prototype.showRow=function(a){this.toggleRow(a,!0)},o.prototype.hideRow=function(a){this.toggleRow(a,!1)},o.prototype.toggleRow=function(b,c){var d,e;b.hasOwnProperty("index")?d=this.getData()[b.index]:b.hasOwnProperty("uniqueId")&&(d=this.getRowByUniqueId(b.uniqueId)),d&&(e=a.inArray(d,this.hiddenRows),c||-1!==e?c&&e>-1&&this.hiddenRows.splice(e,1):this.hiddenRows.push(d),this.initBody(!0))},o.prototype.getHiddenRows=function(){var b=this,c=this.getData(),d=[];return a.each(c,function(c,e){a.inArray(e,b.hiddenRows)>-1&&d.push(e)}),this.hiddenRows=d,d},o.prototype.mergeCells=function(b){var c,d,e,f=b.index,g=a.inArray(b.field,this.getVisibleFields()),h=b.rowspan||1,i=b.colspan||1,j=this.$body.find(">tr");if(this.options.detailView&&!this.options.cardView&&(g+=1),e=j.eq(f).find(">td").eq(g),!(0>f||0>g||f>=this.data.length)){for(c=f;f+h>c;c++)for(d=g;g+i>d;d++)j.eq(c).find(">td").eq(d).hide();e.attr("rowspan",h).attr("colspan",i).show()}},o.prototype.updateCell=function(a){a.hasOwnProperty("index")&&a.hasOwnProperty("field")&&a.hasOwnProperty("value")&&(this.data[a.index][a.field]=a.value,a.reinit!==!1&&(this.initSort(),this.initBody(!0)))},o.prototype.getOptions=function(){return this.options},o.prototype.getSelections=function(){var b=this;return a.grep(this.options.data,function(a){return a[b.header.stateField]===!0})},o.prototype.getAllSelections=function(){var b=this;return a.grep(this.options.data,function(a){return a[b.header.stateField]})},o.prototype.checkAll=function(){this.checkAll_(!0)},o.prototype.uncheckAll=function(){this.checkAll_(!1)},o.prototype.checkInvert=function(){var b=this,c=b.$selectItem.filter(":enabled"),d=c.filter(":checked");c.each(function(){a(this).prop("checked",!a(this).prop("checked"))}),b.updateRows(),b.updateSelected(),b.trigger("uncheck-some",d),d=b.getSelections(),b.trigger("check-some",d)},o.prototype.checkAll_=function(a){var b;a||(b=this.getSelections()),this.$selectAll.add(this.$selectAll_).prop("checked",a),this.$selectItem.filter(":enabled").prop("checked",a),this.updateRows(),a&&(b=this.getSelections()),this.trigger(a?"check-all":"uncheck-all",b)},o.prototype.check=function(a){this.check_(!0,a)},o.prototype.uncheck=function(a){this.check_(!1,a)},o.prototype.check_=function(a,b){var d=this.$selectItem.filter(c('[data-index="%s"]',b)).prop("checked",a);this.data[b][this.header.stateField]=a,this.updateSelected(),this.trigger(a?"check":"uncheck",this.data[b],d)},o.prototype.checkBy=function(a){this.checkBy_(!0,a)},o.prototype.uncheckBy=function(a){this.checkBy_(!1,a)},o.prototype.checkBy_=function(b,d){if(d.hasOwnProperty("field")&&d.hasOwnProperty("values")){var e=this,f=[];a.each(this.options.data,function(g,h){if(!h.hasOwnProperty(d.field))return!1;if(-1!==a.inArray(h[d.field],d.values)){var i=e.$selectItem.filter(":enabled").filter(c('[data-index="%s"]',g)).prop("checked",b);h[e.header.stateField]=b,f.push(h),e.trigger(b?"check":"uncheck",h,i)}}),this.updateSelected(),this.trigger(b?"check-some":"uncheck-some",f)}},o.prototype.destroy=function(){this.$el.insertBefore(this.$container),a(this.options.toolbar).insertBefore(this.$el),this.$container.next().remove(),this.$container.remove(),this.$el.html(this.$el_.html()).css("margin-top","0").attr("class",this.$el_.attr("class")||"")},o.prototype.showLoading=function(){this.$tableLoading.show()},o.prototype.hideLoading=function(){this.$tableLoading.hide()},o.prototype.togglePagination=function(){this.options.pagination=!this.options.pagination;var a=this.$toolbar.find('button[name="paginationSwitch"] i');this.options.pagination?a.attr("class",this.options.iconsPrefix+" "+this.options.icons.paginationSwitchDown):a.attr("class",this.options.iconsPrefix+" "+this.options.icons.paginationSwitchUp),this.updatePagination()},o.prototype.refresh=function(a){a&&a.url&&(this.options.url=a.url),a&&a.pageNumber&&(this.options.pageNumber=a.pageNumber),a&&a.pageSize&&(this.options.pageSize=a.pageSize),this.initServer(a&&a.silent,a&&a.query,a&&a.url),this.trigger("refresh",a)},o.prototype.resetWidth=function(){this.options.showHeader&&this.options.height&&this.fitHeader(),this.options.showFooter&&this.fitFooter()},o.prototype.showColumn=function(a){this.toggleColumn(e(this.columns,a),!0,!0)},o.prototype.hideColumn=function(a){this.toggleColumn(e(this.columns,a),!1,!0)},o.prototype.getHiddenColumns=function(){return a.grep(this.columns,function(a){return!a.visible})},o.prototype.getVisibleColumns=function(){return a.grep(this.columns,function(a){return a.visible})},o.prototype.toggleAllColumns=function(b){if(a.each(this.columns,function(a){this.columns[a].visible=b}),this.initHeader(),this.initSearch(),this.initPagination(),this.initBody(),this.options.showColumns){var c=this.$toolbar.find(".keep-open input").prop("disabled",!1);c.filter(":checked").length<=this.options.minimumCountColumns&&c.filter(":checked").prop("disabled",!0)}},o.prototype.showAllColumns=function(){this.toggleAllColumns(!0)},o.prototype.hideAllColumns=function(){this.toggleAllColumns(!1)},o.prototype.filterBy=function(b){this.filterColumns=a.isEmptyObject(b)?{}:b,this.options.pageNumber=1,this.initSearch(),this.updatePagination()},o.prototype.scrollTo=function(a){return"string"==typeof a&&(a="bottom"===a?this.$tableBody[0].scrollHeight:0),"number"==typeof a&&this.$tableBody.scrollTop(a),"undefined"==typeof a?this.$tableBody.scrollTop():void 0},o.prototype.getScrollPosition=function(){return this.scrollTo()},o.prototype.selectPage=function(a){a>0&&a<=this.options.totalPages&&(this.options.pageNumber=a,this.updatePagination())},o.prototype.prevPage=function(){this.options.pageNumber>1&&(this.options.pageNumber--,this.updatePagination())},o.prototype.nextPage=function(){this.options.pageNumber tr[data-index="%s"]',b));d.next().is("tr.detail-view")===(a?!1:!0)&&d.find("> td > .detail-icon").click()},o.prototype.expandRow=function(a){this.expandRow_(!0,a)},o.prototype.collapseRow=function(a){this.expandRow_(!1,a)},o.prototype.expandAllRows=function(b){if(b){var d=this.$body.find(c('> tr[data-index="%s"]',0)),e=this,f=null,g=!1,h=-1;if(d.next().is("tr.detail-view")?d.next().next().is("tr.detail-view")||(d.next().find(".detail-icon").click(),g=!0):(d.find("> td > .detail-icon").click(),g=!0),g)try{h=setInterval(function(){f=e.$body.find("tr.detail-view").last().find(".detail-icon"),f.length>0?f.click():clearInterval(h)},1)}catch(i){clearInterval(h)}}else for(var j=this.$body.children(),k=0;k0&&a.each(b.columns,function(d,e){-1!==b.options.columnsHidden.indexOf(e.field)&&e.visible!==c&&b.toggleColumn(a.fn.bootstrapTable.utils.getFieldIndex(b.columns,e.field),c,!0)})},c=function(a){(a.options.height||a.options.showFooter)&&setTimeout(function(){a.resetView.call(a)},1)},d=function(a,b,d){a.options.minHeight?b<=a.options.minWidth&&d<=a.options.minHeight?e(a):b>a.options.minWidth&&d>a.options.minHeight&&f(a):b<=a.options.minWidth?e(a):b>a.options.minWidth&&f(a),c(a)},e=function(a){g(a,!1),b(a,!1)},f=function(a){g(a,!0),b(a,!0)},g=function(a,b){a.options.cardView=b,a.toggleView()},h=function(a,b){var c;return function(){var d=this,e=arguments,f=function(){c=null,a.apply(d,e)};clearTimeout(c),c=setTimeout(f,b)}};a.extend(a.fn.bootstrapTable.defaults,{mobileResponsive:!1,minWidth:562,minHeight:void 0,heightThreshold:100,checkOnInit:!0,columnsHidden:[]});var i=a.fn.bootstrapTable.Constructor,j=i.prototype.init;i.prototype.init=function(){if(j.apply(this,Array.prototype.slice.apply(arguments)),this.options.mobileResponsive&&this.options.minWidth){this.options.minWidth<100&&this.options.resizable&&(console.log("The minWidth when the resizable extension is active should be greater or equal than 100"),this.options.minWidth=100);var b=this,c={width:a(window).width(),height:a(window).height()};if(a(window).on("resize orientationchange",h(function(){var e=a(this).height(),f=a(this).width();(Math.abs(c.height-e)>b.options.heightThreshold||c.width!=f)&&(d(b,f,e),c={width:f,height:e})},200)),this.options.checkOnInit){var e=a(window).height(),f=a(window).width();d(this,f,e),c={width:f,height:e}}}}}(jQuery); \ No newline at end of file diff --git a/src/Admin/Authentication/AuthenticationListener.php b/src/Admin/Authentication/AuthenticationListener.php new file mode 100644 index 0000000..907123c --- /dev/null +++ b/src/Admin/Authentication/AuthenticationListener.php @@ -0,0 +1,47 @@ +getParam('request'); + $error = $e->getParam('error'); + + if (empty($error)) { + /** @var array $data */ + $data = $e->getParam('data'); + $identity = $data['identity'] ?? ''; + $credential = $data['password'] ?? ''; + + if (empty($identity) || empty($credential)) { + $e->setParam('error', 'Credentials are required and cannot be empty'); + return; + } + + $dbCredentials = new DbCredentials($identity, $credential); + $e->setParam('request', $request->withAttribute(DbCredentials::class, $dbCredentials)); + } + } +} diff --git a/src/Admin/Authentication/Listener/AuthenticationListener.php b/src/Admin/Authentication/Listener/AuthenticationListener.php deleted file mode 100644 index e377266..0000000 --- a/src/Admin/Authentication/Listener/AuthenticationListener.php +++ /dev/null @@ -1,70 +0,0 @@ -getSharedManager(); - - //this will be called after the default prepare listener and the actual authentication call - //use it for additional checks and data insertions into template - $this->listeners[] = $sharedEvents->attach( - LoginAction::class, - AuthenticationEvent::EVENT_AUTHENTICATION_AUTHENTICATE, - [$this, 'prepareAdapter'], - 10 - ); - - //more listeners if you want to customize the flow... - } - - /** - * Pre authentication listener that happens before the default authentication - * Can be used to prepare some data for the form and pre-validate data - * - * @param AuthenticationEvent $e - */ - public function prepareAdapter(AuthenticationEvent $e) - { - $request = $e->getRequest(); - $error = $e->getError(); - - //disable login page input labels - $e->setParam('showLabels', false); - - if ($request->getMethod() === 'POST' && empty($error)) { - $identity = $e->getParam('identity', ''); - $credential = $e->getParam('password', ''); - if (empty($identity) || empty($credential)) { - $e->setError('Credentials are required and cannot be empty'); - return; - } - - $dbCredentials = new DbCredentials($identity, $credential); - $e->setRequest($request->withAttribute(DbCredentials::class, $dbCredentials)); - } - } -} diff --git a/src/Admin/Authentication/Listener/UnauthorizedListener.php b/src/Admin/Authentication/Listener/UnauthorizedListener.php deleted file mode 100644 index c42a6c2..0000000 --- a/src/Admin/Authentication/Listener/UnauthorizedListener.php +++ /dev/null @@ -1,47 +0,0 @@ -getRequest(); - /** @var RouteResult $routeMatch */ - $routeMatch = $request->getAttribute(RouteResult::class, null); - - if ($routeMatch) { - $routeName = $routeMatch->getMatchedRouteName(); - $params = $routeMatch->getMatchedParams(); - $action = isset($params['action']) ? $params['action'] : ''; - - if (in_array($routeName, $this->routes) && in_array($action, $this->actions)) { - return new Response\EmptyResponse(401); - } - } - - return true; - } -} diff --git a/src/Admin/Authentication/UnauthorizedListener.php b/src/Admin/Authentication/UnauthorizedListener.php new file mode 100644 index 0000000..c1c3486 --- /dev/null +++ b/src/Admin/Authentication/UnauthorizedListener.php @@ -0,0 +1,54 @@ +getParam('request'); + /** @var RouteResult $routeMatch */ + $routeMatch = $request->getAttribute(RouteResult::class); + + if ($routeMatch) { + $routeName = $routeMatch->getMatchedRouteName(); + $params = $routeMatch->getMatchedParams(); + $action = $params['action'] ?? ''; + + if (in_array($routeName, $this->routes) && in_array($action, $this->actions)) { + return new Response\EmptyResponse(401); + } + } + + return true; + } +} diff --git a/src/Admin/ConfigProvider.php b/src/Admin/ConfigProvider.php new file mode 100644 index 0000000..4682033 --- /dev/null +++ b/src/Admin/ConfigProvider.php @@ -0,0 +1,172 @@ + $this->getDependenciesConfig(), + + 'dot_ems' => $this->getMapperConfig(), + + 'dot_authentication' => $this->getAuthenticationConfig(), + + 'dot_form' => $this->getFormsConfig(), + + 'dot_hydrator' => $this->getHydratorsConfig(), + + 'dot_user' => [ + 'user_entity' => AdminEntity::class, + 'role_entity' => RoleEntity::class, + + 'default_roles' => ['admin'], + 'route_default' => ['route_name' => 'dashboard', 'route_params' => ['action' => '']], + + 'enable_account_confirmation' => false, + + 'login_options' => [ + 'enable_remember' => false, + 'allowed_status' => [AdminEntity::STATUS_ACTIVE] + ], + 'register_options' => [ + 'enable_registration' => false, + ], + 'password_recovery_options' => [ + 'enable_recovery' => false, + ], + 'template_options' => [ + 'login_template' => 'admin::login', + 'account_template' => 'admin::account', + ], + 'messages_options' => [ + 'messages' => [ + + ] + ], + + 'event_listeners' => [ + 'controller' => [ + AdminController::class, + ] + ] + ] + ]; + } + + public function getDependenciesConfig(): array + { + return [ + + ]; + } + + public function getMapperConfig(): array + { + return [ + 'mapper_manager' => [ + 'factories' => [ + AdminDbMapper::class => UserDbMapperFactory::class, + RoleDbMapper::class => DbMapperFactory::class, + TokenDbMapper::class => DbMapperFactory::class, + ], + 'aliases' => [ + AdminEntity::class => AdminDbMapper::class, + RoleEntity::class => RoleDbMapper::class, + + ConfirmTokenEntity::class => TokenDbMapper::class, + RememberTokenEntity::class => TokenDbMapper::class, + ResetTokenEntity::class => TokenDbMapper::class, + ] + ], + ]; + } + + public function getHydratorsConfig(): array + { + return [ + 'hydrator_manager' => [ + 'factories' => [ + AdminHydrator::class => AdminHydratorFactory::class, + ], + 'aliases' => [ + 'AdminHydrator' => AdminHydrator::class, + ] + ] + ]; + } + + public function getFormsConfig(): array + { + return [ + 'form_manager' => [ + 'factories' => [ + AdminFieldset::class => UserFieldsetFactory::class, + AdminForm::class => InvokableFactory::class, + AccountForm::class => InvokableFactory::class, + //ChangePasswordForm::class => InvokableFactory::class, + ], + 'aliases' => [ + 'UserFieldset' => AdminFieldset::class, + 'AdminFieldset' => AdminFieldset::class, + 'Admin' => AdminForm::class, + 'Account' => AccountForm::class, + //'ChangePassword' => ChangePasswordForm::class, + ] + ] + ]; + } + + public function getAuthenticationConfig(): array + { + return [ + 'web' => [ + 'after_login_route' => ['route_name' => 'dashboard', 'route_params' => ['action' => '']], + + 'event_listeners' => [ + [ + 'type' => AuthenticationListener::class, + 'priority' => 100, + ], + [ + 'type' => UnauthorizedListener::class, + 'priority' => 1000, + ] + ] + ] + ]; + } +} diff --git a/src/Admin/Controller/AdminController.php b/src/Admin/Controller/AdminController.php deleted file mode 100644 index d8aa13f..0000000 --- a/src/Admin/Controller/AdminController.php +++ /dev/null @@ -1,51 +0,0 @@ -getInputFilter()->get('admin')->get('password')->setRequired(false); - $form->getInputFilter()->get('admin')->get('passwordVerify')->setRequired(false); - } - - //remove username and email checks if the value has not changed relative to the original - if ($entity->getUsername() === $data['admin']['username']) { - $form->removeUsernameValidation(); - } - - if ($entity->getEmail() === $data['admin']['email']) { - $form->removeEmailValidation(); - } - - $form->applyValidationGroup(); - } -} diff --git a/src/Admin/Controller/DashboardController.php b/src/Admin/Controller/DashboardController.php deleted file mode 100644 index 654cb0c..0000000 --- a/src/Admin/Controller/DashboardController.php +++ /dev/null @@ -1,29 +0,0 @@ -template()->render('app::dashboard')); - } -} diff --git a/src/Admin/Controller/UserController.php b/src/Admin/Controller/UserController.php deleted file mode 100644 index 9cb3bc8..0000000 --- a/src/Admin/Controller/UserController.php +++ /dev/null @@ -1,50 +0,0 @@ -getInputFilter()->get('user')->get('password')->setRequired(false); - $form->getInputFilter()->get('user')->get('passwordVerify')->setRequired(false); - } - - //remove username and email checks if the value has not changed relative to the original - if ($entity->getUsername() === $data['user']['username']) { - $form->removeUsernameValidation(); - } - - if ($entity->getEmail() === $data['user']['email']) { - $form->removeEmailValidation(); - } - - $form->applyValidationGroup(); - } -} diff --git a/src/Admin/Entity/AdminEntity.php b/src/Admin/Entity/AdminEntity.php index 858da01..dc493a3 100644 --- a/src/Admin/Entity/AdminEntity.php +++ b/src/Admin/Entity/AdminEntity.php @@ -1,26 +1,33 @@ firstName = $firstName; - return $this; } /** @@ -55,37 +60,35 @@ public function getLastName() /** * @param string $lastName - * @return AdminEntity */ public function setLastName($lastName) { $this->lastName = $lastName; - return $this; } /** * @return array */ - public function jsonSerialize() + public function jsonSerialize(): array { - return get_object_vars($this); + $fields = parent::jsonSerialize(); + return array_merge($fields, [ + 'firstName' => $this->getFirstName(), + 'lastName' => $this->getLastName(), + ]); } /** - * @return array + * @param bool|null $value + * @return mixed */ - public function ignoredProperties() - { - return ['roles', 'name', 'dateCreated']; - } - - public function sortableColumns() - { - return ['id', 'username', 'email', 'dateCreated', 'role', 'status', 'firstName', 'lastName']; - } - - public function searchableColumns() + public function needsPasswordRehash(bool $value = null) { - return ['id', 'username', 'email', 'firstName', 'lastName', 'role', 'status']; + if ($value !== null) { + $this->needsPasswordRehash = $value; + return $this; + } else { + return $this->needsPasswordRehash; + } } } diff --git a/src/Admin/Entity/RoleEntity.php b/src/Admin/Entity/RoleEntity.php new file mode 100644 index 0000000..a3682fe --- /dev/null +++ b/src/Admin/Entity/RoleEntity.php @@ -0,0 +1,19 @@ +details) { - $this->details = new UserDetailsEntity(); - } - return $this->details; - } - - /** - * @param UserDetailsEntity $details - * @return UserEntity - */ - public function setDetails(UserDetailsEntity $details = null) - { - $this->details = $details; - return $this; - } - - /** - * @return array - */ - public function jsonSerialize() - { - return get_object_vars($this); - } - - /** - * @return array - */ - public function ignoredProperties() - { - return ['roles', 'name', 'dateCreated']; - } - - public function sortableColumns() - { - return ['id', 'username', 'email', 'dateCreated', 'role', 'status']; - } - - public function searchableColumns() - { - return ['id', 'username', 'email', 'role', 'status']; - } -} diff --git a/src/Admin/Factory/Admin/AdminControllerFactory.php b/src/Admin/Factory/Admin/AdminControllerFactory.php deleted file mode 100644 index 286f375..0000000 --- a/src/Admin/Factory/Admin/AdminControllerFactory.php +++ /dev/null @@ -1,42 +0,0 @@ -get('dot-ems.service.admin'); - $userForm = $container->get(AdminForm::class); - - $confirmDeleteForm = new ConfirmDeleteForm(); - $confirmDeleteForm->init(); - - $controller = new AdminController($service, $userForm, $confirmDeleteForm); - $controller->setDebug($container->get('config')['debug']); - - return $controller; - } -} diff --git a/src/Admin/Factory/Admin/AdminFieldsetFactory.php b/src/Admin/Factory/Admin/AdminFieldsetFactory.php deleted file mode 100644 index 5d538bd..0000000 --- a/src/Admin/Factory/Admin/AdminFieldsetFactory.php +++ /dev/null @@ -1,54 +0,0 @@ -get(UserOptions::class); - - $prototype = $this->getDependencyObject($container, $options->getUserEntity()); - if (!$prototype instanceof UserEntityInterface) { - throw new \Exception('User entity prototype not valid'); - } - $hydrator = $this->getDependencyObject($container, $options->getUserEntityHydrator()); - if (!$hydrator instanceof HydratorInterface) { - $hydrator = new ClassMethods(false); - } - - $adminFieldset = new AdminFieldset(); - $adminFieldset->setObject($prototype); - $adminFieldset->setHydrator($hydrator); - $adminFieldset->init(); - - return $adminFieldset; - } -} diff --git a/src/Admin/Factory/Admin/AdminFormFactory.php b/src/Admin/Factory/Admin/AdminFormFactory.php deleted file mode 100644 index 7eedfb3..0000000 --- a/src/Admin/Factory/Admin/AdminFormFactory.php +++ /dev/null @@ -1,38 +0,0 @@ -get(AdminInputFilter::class); - $fieldset = $container->get(AdminFieldset::class); - - $form = new AdminForm($fieldset); - $form->getInputFilter()->add($filter, 'admin'); - $form->init(); - - return $form; - } -} diff --git a/src/Admin/Factory/Admin/AdminInputFilterFactory.php b/src/Admin/Factory/Admin/AdminInputFilterFactory.php deleted file mode 100644 index ac7df49..0000000 --- a/src/Admin/Factory/Admin/AdminInputFilterFactory.php +++ /dev/null @@ -1,42 +0,0 @@ -get('dot-ems.service.admin'); - $filter = new AdminInputFilter( - new NoRecordsExists([ - 'service' => $service, - 'key' => 'email' - ]), - new NoRecordsExists([ - 'service' => $service, - 'key' => 'username' - ]) - ); - $filter->init(); - return $filter; - } -} diff --git a/src/Admin/Factory/Admin/AdminServiceDelegator.php b/src/Admin/Factory/Admin/AdminServiceDelegator.php deleted file mode 100644 index e963462..0000000 --- a/src/Admin/Factory/Admin/AdminServiceDelegator.php +++ /dev/null @@ -1,37 +0,0 @@ -get('database'); - $entityOperationsMapper = new EntityOperationsDbMapper('admin', $dbAdapter); - $service->setEntityOperationsMapper($entityOperationsMapper); - - $service->setPasswordService($container->get(PasswordInterface::class)); - } - - return $service; - } -} diff --git a/src/Admin/Factory/AdminHydratorFactory.php b/src/Admin/Factory/AdminHydratorFactory.php new file mode 100644 index 0000000..29c37b2 --- /dev/null +++ b/src/Admin/Factory/AdminHydratorFactory.php @@ -0,0 +1,28 @@ +get(RolesHydratingStrategy::class)); + return $hydrator; + } +} diff --git a/src/Admin/Factory/AdminIndexMiddlewareFactory.php b/src/Admin/Factory/AdminIndexMiddlewareFactory.php deleted file mode 100644 index b881577..0000000 --- a/src/Admin/Factory/AdminIndexMiddlewareFactory.php +++ /dev/null @@ -1,34 +0,0 @@ -get(AuthenticationInterface::class), - $container->get(UrlHelper::class) - ); - } -} diff --git a/src/Admin/Factory/AdminServiceListenerFactory.php b/src/Admin/Factory/AdminServiceListenerFactory.php deleted file mode 100644 index a7a676b..0000000 --- a/src/Admin/Factory/AdminServiceListenerFactory.php +++ /dev/null @@ -1,29 +0,0 @@ -get('action_logger'), - $container->get(AuthenticationInterface::class) - ); - } -} diff --git a/src/Admin/Factory/DashboardControllerFactory.php b/src/Admin/Factory/DashboardControllerFactory.php deleted file mode 100644 index 33c08d2..0000000 --- a/src/Admin/Factory/DashboardControllerFactory.php +++ /dev/null @@ -1,21 +0,0 @@ -get('dot-ems.service.user'); - $userForm = $container->get(UserForm::class); - - $confirmDeleteForm = new ConfirmDeleteForm(); - $confirmDeleteForm->init(); - - $controller = new UserController($service, $userForm, $confirmDeleteForm); - $controller->setDebug($container->get('config')['debug']); - - return $controller; - } -} diff --git a/src/Admin/Factory/User/UserDetailsFieldsetFactory.php b/src/Admin/Factory/User/UserDetailsFieldsetFactory.php deleted file mode 100644 index 26be52f..0000000 --- a/src/Admin/Factory/User/UserDetailsFieldsetFactory.php +++ /dev/null @@ -1,32 +0,0 @@ -setObject(new UserDetailsEntity()); - $fieldset->setHydrator(new ClassMethods(false)); - $fieldset->init(); - - return $fieldset; - } -} diff --git a/src/Admin/Factory/User/UserDetailsInputFilterFactory.php b/src/Admin/Factory/User/UserDetailsInputFilterFactory.php deleted file mode 100644 index c1b71cb..0000000 --- a/src/Admin/Factory/User/UserDetailsInputFilterFactory.php +++ /dev/null @@ -1,27 +0,0 @@ -init(); - return $inputFilter; - } -} diff --git a/src/Admin/Factory/User/UserFieldsetFactory.php b/src/Admin/Factory/User/UserFieldsetFactory.php deleted file mode 100644 index 7f0aa34..0000000 --- a/src/Admin/Factory/User/UserFieldsetFactory.php +++ /dev/null @@ -1,33 +0,0 @@ -setObject(new UserEntity()); - $fieldset->setHydrator(new UserEntityHydrator()); - $fieldset->init(); - - return $fieldset; - } -} diff --git a/src/Admin/Factory/User/UserFormFactory.php b/src/Admin/Factory/User/UserFormFactory.php deleted file mode 100644 index 3d3ff16..0000000 --- a/src/Admin/Factory/User/UserFormFactory.php +++ /dev/null @@ -1,43 +0,0 @@ -get(UserDetailsFieldset::class); - $userFieldset = $container->get(UserFieldset::class); - - /** @var InputFilter $userInputFilter */ - $userInputFilter = $container->get(UserInputFilter::class); - $userDetailsInputFilter = $container->get(UserDetailsInputFilter::class); - - $userInputFilter->add($userDetailsInputFilter, 'details'); - - $form = new UserForm($userFieldset, $userDetailsFieldset); - $form->getInputFilter()->add($userInputFilter, 'user'); - $form->init(); - - return $form; - } -} diff --git a/src/Admin/Factory/User/UserInputFilterFactory.php b/src/Admin/Factory/User/UserInputFilterFactory.php deleted file mode 100644 index ab061aa..0000000 --- a/src/Admin/Factory/User/UserInputFilterFactory.php +++ /dev/null @@ -1,39 +0,0 @@ -get('dot-ems.service.user'); - $inputFilter = new UserInputFilter( - new NoRecordsExists([ - 'service' => $service, - 'key' => 'email' - ]), - new NoRecordsExists([ - 'service' => $service, - 'key' => 'username' - ]) - ); - $inputFilter->init(); - - return $inputFilter; - } -} diff --git a/src/Admin/Factory/User/UserServiceDelegator.php b/src/Admin/Factory/User/UserServiceDelegator.php deleted file mode 100644 index 259b6d4..0000000 --- a/src/Admin/Factory/User/UserServiceDelegator.php +++ /dev/null @@ -1,37 +0,0 @@ -get('database'); - $entityOperationsMapper = new EntityOperationsDbMapper('user', $dbAdapter); - $service->setEntityOperationsMapper($entityOperationsMapper); - - $service->setPasswordService($container->get(PasswordInterface::class)); - } - - return $service; - } -} diff --git a/src/Admin/Form/AccountForm.php b/src/Admin/Form/AccountForm.php new file mode 100644 index 0000000..fb7df6a --- /dev/null +++ b/src/Admin/Form/AccountForm.php @@ -0,0 +1,101 @@ +setAttribute('method', 'post'); + $this->setInputFilter(new InputFilter()); + } + + public function init() + { + $this->add([ + 'type' => 'AdminFieldset', + 'options' => [ + 'use_as_base_fieldset' => true, + ] + ]); + + $this->add([ + 'name' => 'account_csrf', + 'type' => 'csrf', + 'options' => [ + 'timeout' => 3600, + 'message' => Messages::CSRF_EXPIRED + ] + ]); + + $this->add([ + 'name' => 'submit', + 'attributes' => [ + 'type' => 'submit', + 'value' => 'Update account' + ] + ], ['priority' => -100]); + + $this->setValidationGroup([ + 'account_csrf', + 'user' => [ + 'username', + 'email', + 'firstName', + 'lastName', + ] + ]); + } + + /** + * @param object $object + * @param int $flags + * @return Form + */ + public function bind($object, $flags = FormInterface::VALUES_NORMALIZED) + { + if ($object instanceof AdminEntity) { + $usernameValidators = $this->getInputFilter()->get('user')->get('username') + ->getValidatorChain()->getValidators(); + foreach ($usernameValidators as $validator) { + $validator = $validator['instance']; + if ($validator instanceof NoRecordExists) { + $validator->setExclude(['field' => 'id', 'value' => $object->getId()]); + break; + } + } + $emailValidators = $this->getInputFilter()->get('user')->get('email') + ->getValidatorChain()->getValidators(); + foreach ($emailValidators as $validator) { + $validator = $validator['instance']; + if ($validator instanceof NoRecordExists) { + $validator->setExclude(['field' => 'id', 'value' => $object->getId()]); + break; + } + } + } + return parent::bind($object, $flags); + } +} diff --git a/src/Admin/Form/Admin/AdminFieldset.php b/src/Admin/Form/Admin/AdminFieldset.php deleted file mode 100644 index dd62f2e..0000000 --- a/src/Admin/Form/Admin/AdminFieldset.php +++ /dev/null @@ -1,132 +0,0 @@ -add([ - 'name' => 'id', - 'type' => 'hidden', - ]); - - $this->add([ - 'name' => 'username', - 'type' => 'text', - 'options' => [ - 'label' => 'Username', - ], - 'attributes' => [ - 'id' => 'username', - 'placeholder' => 'Username...' - ] - ]); - - $this->add([ - 'name' => 'email', - 'type' => 'text', - 'options' => [ - 'label' => 'Email', - ], - 'attributes' => [ - 'placeholder' => 'Email...' - ] - ]); - - $this->add([ - 'name' => 'firstName', - 'type' => 'text', - 'options' => [ - 'label' => 'First name', - ], - 'attributes' => [ - 'placeholder' => 'First name...' - ] - ]); - - $this->add([ - 'name' => 'lastName', - 'type' => 'text', - 'options' => [ - 'label' => 'Last name', - ], - 'attributes' => [ - 'placeholder' => 'Last name...' - ] - ]); - - $this->add(array( - 'type' => 'password', - 'name' => 'password', - 'options' => [ - 'label' => 'Password' - ], - 'attributes' => array( - 'placeholder' => 'Password', - //'required' => true, - ), - )); - - $this->add(array( - 'type' => 'password', - 'name' => 'passwordVerify', - 'options' => [ - 'label' => 'Confirm Password' - ], - 'attributes' => array( - 'placeholder' => 'Confirm Password', - //'required' => true, - ), - )); - - $this->add([ - 'name' => 'role', - 'type' => 'select', - 'options' => [ - 'label' => 'Authentication Role', - 'value_options' => [ - ['value' => 'superuser', 'label' => 'superuser'], - ['value' => 'admin', 'label' => 'admin'], - ] - ], - ]); - - $this->add([ - 'name' => 'status', - 'type' => 'select', - 'options' => [ - 'label' => 'Account Status', - 'value_options' => [ - ['value' => 'pending', 'label' => 'pending'], - ['value' => 'active', 'label' => 'active'], - ['value' => 'inactive', 'label' => 'inactive'], - ['value' => 'deleted', 'label' => 'deleted'], - ] - ], - ]); - } -} diff --git a/src/Admin/Form/Admin/AdminForm.php b/src/Admin/Form/Admin/AdminForm.php deleted file mode 100644 index c644abf..0000000 --- a/src/Admin/Form/Admin/AdminForm.php +++ /dev/null @@ -1,110 +0,0 @@ - true, - 'username' => true, - 'email' => true, - 'firstName' => true, - 'lastName' => true, - 'password' => true, - 'passwordVerify' => true, - 'role' => true, - 'status' => true - ]; - - /** - * AdminForm constructor. - * @param Fieldset $adminFieldset - * @param string $name - * @param array $options - */ - public function __construct(Fieldset $adminFieldset, $name = 'admin_form', array $options = []) - { - $this->adminFieldset = $adminFieldset; - parent::__construct($name, $options); - } - - public function init() - { - $this->adminFieldset->setName('admin'); - $this->adminFieldset->setUseAsBaseFieldset(true); - $this->add($this->adminFieldset); - - $csrf = new Csrf('admin_csrf', [ - 'csrf_options' => [ - 'timeout' => 3600, - 'message' => 'The form used to make the request has expired. Please try again now' - ] - ]); - $this->add($csrf); - } - - public function removeUsernameValidation() - { - $this->currentValidationGroup['username'] = false; - } - - public function removeEmailValidation() - { - $this->currentValidationGroup['email'] = false; - } - - public function resetValidationGroup() - { - foreach ($this->currentValidationGroup as $key => $value) { - $this->currentValidationGroup[$key] = true; - } - $this->setValidationGroup(FormInterface::VALIDATE_ALL); - } - - public function applyValidationGroup() - { - $validationGroup = $this->getActiveValidationGroup($this->currentValidationGroup, $this->getBaseFieldset()); - $this->setValidationGroup(['admin' => $validationGroup]); - } - - /** - * @param $groups - * @param ElementInterface $prevElement - * @return array - */ - public function getActiveValidationGroup($groups, ElementInterface $prevElement) - { - $validationGroup = []; - foreach ($groups as $key => $value) { - if (is_array($value) && $prevElement instanceof FieldsetInterface) { - if ($prevElement->has($key)) { - $validationGroup[$key] = $this->getActiveValidationGroup($value, $prevElement->get($key)); - } - } elseif ($value === true && $prevElement->has($key)) { - $validationGroup[] = $key; - } - } - return $validationGroup; - } -} diff --git a/src/Admin/Form/Admin/AdminInputFilter.php b/src/Admin/Form/Admin/AdminInputFilter.php deleted file mode 100644 index a9e960e..0000000 --- a/src/Admin/Form/Admin/AdminInputFilter.php +++ /dev/null @@ -1,218 +0,0 @@ -usernameValidator = $usernameValidator; - $this->emailValidator = $emailValidator; - } - - public function init() - { - $this->add([ - 'name' => 'id', - 'required' => false, - ]); - - $username = [ - 'name' => 'username', - 'filters' => [ - ['name' => 'StringTrim'], - ], - 'validators' => [ - [ - 'name' => 'NotEmpty', - 'break_chain_on_failure' => true, - 'options' => [ - 'message' => static::USERNAME_REQUIRED, - ] - ], - [ - 'name' => 'StringLength', - 'options' => [ - 'min' => 3, - 'max' => 150, - 'message' => static::USERNAME_LENGTH_LIMIT, - ] - ], - [ - 'name' => 'Regex', - 'options' => [ - 'pattern' => '/^[a-zA-Z0-9-_]+$/', - 'message' => static::USERNAME_INVALID, - ] - ] - ], - ]; - - if ($this->usernameValidator) { - $this->usernameValidator->setMessage(static::USERNAME_TAKEN); - $username['validators'][] = $this->usernameValidator; - } - - $this->add($username); - - $email = [ - 'name' => 'email', - 'filters' => [ - ['name' => 'StringTrim'], - ], - 'validators' => [ - [ - 'name' => 'NotEmpty', - 'break_chain_on_failure' => true, - 'options' => [ - 'message' => static::EMAIL_REQUIRED, - ] - ], - [ - 'name' => 'EmailAddress', - 'options' => [ - 'message' => static::EMAIL_INVALID, - ] - ], - ], - ]; - - if ($this->emailValidator) { - $this->emailValidator->setMessage(static::EMAIL_TAKEN); - $email['validators'][] = $this->emailValidator; - } - - $this->add($email); - - $this->add([ - 'name' => 'firstName', - 'required' => false, - 'filters' => [ - ['name' => 'StringTrim'] - ], - 'validators' => [ - [ - 'name' => 'StringLength', - 'options' => [ - 'max' => 150, - 'message' => static::FIRSTNAME_LIMIT, - ] - ] - ], - ]); - - $this->add([ - 'name' => 'lastName', - 'required' => false, - 'filters' => [ - ['name' => 'StringTrim'] - ], - 'validators' => [ - [ - 'name' => 'StringLength', - 'options' => [ - 'max' => 150, - 'message' => static::LASTNAME_LIMIT, - ] - ] - ], - ]); - - $this->add([ - 'name' => 'password', - 'filters' => [ - ['name' => 'StringTrim'], - ], - 'validators' => [ - [ - 'name' => 'NotEmpty', - 'break_chain_on_failure' => true, - 'options' => [ - 'message' => static::PASSWORD_REQUIRED - ] - ], - [ - 'name' => 'StringLength', - 'options' => [ - 'min' => 4, - 'max' => 150, - 'message' => static::PASSWORD_LENGTH_LIMIT, - ], - ], - ], - ]); - - $this->add([ - 'name' => 'passwordVerify', - 'filters' => [ - ['name' => 'StringTrim'], - ], - 'validators' => [ - [ - 'name' => 'NotEmpty', - 'break_chain_on_failure' => true, - 'options' => [ - 'message' => static::PASSWORD_VERIFY_REQUIRED, - ] - ], - [ - 'name' => 'Identical', - 'options' => [ - 'token' => 'password', - 'message' => static::PASSWORD_VERIFY_MISMATCH - ], - ], - ], - ]); - - $this->add([ - 'name' => 'role', - ]); - - $this->add([ - 'name' => 'status', - ]); - } -} diff --git a/src/Admin/Form/AdminFieldset.php b/src/Admin/Form/AdminFieldset.php new file mode 100644 index 0000000..9020abb --- /dev/null +++ b/src/Admin/Form/AdminFieldset.php @@ -0,0 +1,151 @@ +First name is required and cannot be empty'; + const MESSAGE_FIRST_NAME_LIMIT = 'First name character limit of 150 exceeded'; + const MESSAGE_LAST_NAME_EMPTY = 'Last name is required and cannot be empty'; + const MESSAGE_LAST_NAME_LIMIT = 'Last name character limit of 150 exceeded'; + + const MESSAGE_ROLES_EMPTY = 'Roles should have at least one role selected'; + + /** + * AdminFieldset constructor. + */ + public function __construct() + { + parent::__construct(); + } + + public function init() + { + parent::init(); + + $this->add([ + 'name' => 'firstName', + 'type' => 'text', + 'options' => [ + 'label' => 'First name' + ], + 'attributes' => [ + 'placeholder' => 'First name...' + ] + ], ['priority' => -10]); + + $this->add([ + 'name' => 'lastName', + 'type' => 'text', + 'options' => [ + 'label' => 'Last name' + ], + 'attributes' => [ + 'placeholder' => 'Last name...' + ] + ], ['priority' => -11]); + + $this->add([ + 'name' => 'roles', + 'type' => 'EntitySelect', + 'options' => [ + 'label' => 'Roles', + 'use_hidden_element' => true, + 'target' => RoleEntity::class, + 'property' => 'name', + ], + 'attributes' => [ + 'multiple' => true, + 'id' => 'rolesSelect' + ] + ], ['priority' => -25]); + + $this->add([ + 'name' => 'status', + 'type' => 'select', + 'options' => [ + 'label' => 'Account Status', + 'value_options' => [ + ['value' => AdminEntity::STATUS_ACTIVE, 'label' => AdminEntity::STATUS_ACTIVE], + ['value' => AdminEntity::STATUS_INACTIVE, 'label' => AdminEntity::STATUS_INACTIVE], + ['value' => AdminEntity::STATUS_DELETED, 'label' => AdminEntity::STATUS_DELETED], + ] + ], + ], ['priority' => -30]); + } + + public function getInputFilterSpecification() + { + $specs = parent::getInputFilterSpecification(); + $specs['firstName'] = [ + 'filters' => [ + ['name' => 'StringTrim'] + ], + 'validators' => [ + [ + 'name' => 'NotEmpty', + 'break_chain_on_failure' => true, + 'options' => [ + 'message' => static::MESSAGE_FIRST_NAME_EMPTY + ] + ], + [ + 'name' => 'StringLength', + 'options' => [ + 'max' => 150, + 'message' => static::MESSAGE_FIRST_NAME_LIMIT, + ], + ] + ] + ]; + $specs['lastName'] = [ + 'filters' => [ + ['name' => 'StringTrim'] + ], + 'validators' => [ + [ + 'name' => 'NotEmpty', + 'break_chain_on_failure' => true, + 'options' => [ + 'message' => static::MESSAGE_LAST_NAME_EMPTY + ] + ], + [ + 'name' => 'StringLength', + 'options' => [ + 'max' => 150, + 'message' => static::MESSAGE_LAST_NAME_LIMIT, + ], + ] + ], + ]; + $specs['roles'] = [ + 'validators' => [ + [ + 'name' => 'NotEmpty', + 'break_chain_on_failure' => true, + 'options' => [ + 'message' => static::MESSAGE_ROLES_EMPTY, + ] + ] + ] + ]; + + return $specs; + } +} diff --git a/src/Admin/Form/AdminForm.php b/src/Admin/Form/AdminForm.php new file mode 100644 index 0000000..5f4519f --- /dev/null +++ b/src/Admin/Form/AdminForm.php @@ -0,0 +1,122 @@ + [ + 'username', + 'email', + 'firstName', + 'lastName', + 'password', + 'passwordConfirm', + 'roles', + 'status', + ] + ]; + + protected $noPasswordValidationGroup = [ + 'admin_csrf', + 'user' => [ + 'username', + 'email', + 'firstName', + 'lastName', + 'roles', + 'status', + ] + ]; + + /** + * AdminForm constructor. + */ + public function __construct() + { + parent::__construct('adminForm'); + + $this->setAttribute('method', 'post'); + $this->setInputFilter(new InputFilter()); + } + + public function init() + { + $this->add([ + 'type' => 'AdminFieldset', + 'options' => [ + 'use_as_base_fieldset' => true, + ] + ]); + + $this->add([ + 'name' => 'admin_csrf', + 'type' => 'csrf', + 'options' => [ + 'timeout' => 3600, + 'message' => Messages::CSRF_EXPIRED + ] + ]); + + $this->setValidationGroup($this->validationGroup); + } + + public function disablePasswordValidation() + { + $this->setValidationGroup($this->noPasswordValidationGroup); + } + + public function resetValidation() + { + $this->setValidationGroup($this->validationGroup); + } + + /** + * @param object $object + * @param int $flags + * @return Form + */ + public function bind($object, $flags = FormInterface::VALUES_NORMALIZED) + { + if ($object instanceof AdminEntity) { + $usernameValidators = $this->getInputFilter()->get('user')->get('username') + ->getValidatorChain()->getValidators(); + foreach ($usernameValidators as $validator) { + $validator = $validator['instance']; + if ($validator instanceof NoRecordExists) { + $validator->setExclude(['field' => 'id', 'value' => $object->getId()]); + break; + } + } + $emailValidators = $this->getInputFilter()->get('user')->get('email') + ->getValidatorChain()->getValidators(); + foreach ($emailValidators as $validator) { + $validator = $validator['instance']; + if ($validator instanceof NoRecordExists) { + $validator->setExclude(['field' => 'id', 'value' => $object->getId()]); + break; + } + } + } + return parent::bind($object, $flags); + } +} diff --git a/src/Admin/Form/ChangePasswordForm.php b/src/Admin/Form/ChangePasswordForm.php new file mode 100644 index 0000000..679a13e --- /dev/null +++ b/src/Admin/Form/ChangePasswordForm.php @@ -0,0 +1,107 @@ +Current password is required and cannot be empty'; + + /** + * ChangePasswordForm constructor. + */ + public function __construct() + { + parent::__construct('changePasswordForm'); + $this->setAttribute('method', 'post'); + } + + public function init() + { + $this->add([ + 'name' => 'currentPassword', + 'type' => 'Password', + 'options' => [ + 'label' => 'Your current password', + ], + 'attributes' => [ + 'placeholder' => 'Current password...', + //'required' => 'required', + ] + ]); + + $this->add([ + 'type' => 'AdminFieldset', + 'options' => [ + 'use_as_base_fieldset' => true, + ] + ]); + + $this->add([ + 'name' => 'change_password_csrf', + 'type' => 'Csrf', + 'options' => [ + 'csrf_options' => [ + 'timeout' => 3600, + 'message' => Messages::CSRF_EXPIRED + ] + ] + ]); + + $this->add([ + 'name' => 'submit', + 'attributes' => [ + 'type' => 'submit', + 'value' => 'Change password' + ] + ]); + + $this->getBaseFieldset()->get('password') + ->setLabel('New password') + ->setAttribute('placeholder', 'New password...'); + + $this->getBaseFieldset()->get('passwordConfirm') + ->setLabel('Confirm new password') + ->setAttribute('placeholder', 'New password confirm...'); + + $this->setValidationGroup([ + 'change_password_csrf', + 'currentPassword', + 'user' => [ + 'password', + 'passwordConfirm' + ] + ]); + } + + public function getInputFilterSpecification() + { + return [ + 'currentPassword' => [ + 'validators' => [ + [ + 'name' => 'NotEmpty', + 'break_chain_on_failure' => true, + 'options' => [ + 'message' => static::CURRENT_PASSWORD_REQUIRED, + ] + ] + ] + ] + ]; + } +} diff --git a/src/Admin/Form/ConfirmDeleteForm.php b/src/Admin/Form/ConfirmDeleteForm.php deleted file mode 100644 index 0770058..0000000 --- a/src/Admin/Form/ConfirmDeleteForm.php +++ /dev/null @@ -1,36 +0,0 @@ - [ - 'timeout' => 3600, - 'message' => 'The form used to make the request has expired. Please try again now' - ] - ]); - $this->add($csrf); - } -} diff --git a/src/Admin/Form/User/UserDetailsFieldset.php b/src/Admin/Form/User/UserDetailsFieldset.php deleted file mode 100644 index 681efd8..0000000 --- a/src/Admin/Form/User/UserDetailsFieldset.php +++ /dev/null @@ -1,76 +0,0 @@ -add([ - 'type' => 'text', - 'name' => 'firstName', - 'options' => [ - 'label' => 'First Name', - ], - 'attributes' => [ - 'placeholder' => 'First Name' - ] - ]); - - $this->add([ - 'type' => 'text', - 'name' => 'lastName', - 'options' => [ - 'label' => 'Last Name' - ], - 'attributes' => [ - 'placeholder' => 'Last Name' - ] - ]); - - $this->add([ - 'type' => 'text', - 'name' => 'address', - 'options' => [ - 'label' => 'Address' - ], - 'attributes' => [ - 'placeholder' => 'Address' - ] - ]); - - $this->add([ - 'type' => 'text', - 'name' => 'phone', - 'options' => [ - 'label' => 'Phone number' - ], - 'attributes' => [ - 'placeholder' => 'Phone number' - ] - ]); - } -} diff --git a/src/Admin/Form/User/UserDetailsInputFilter.php b/src/Admin/Form/User/UserDetailsInputFilter.php deleted file mode 100644 index df6502c..0000000 --- a/src/Admin/Form/User/UserDetailsInputFilter.php +++ /dev/null @@ -1,101 +0,0 @@ -add([ - 'name' => 'firstName', - 'filters' => [ - ['name' => 'StringTrim'] - ], - 'validators' => [ - [ - 'name' => 'NotEmpty', - 'break_chain_on_failure' => true, - 'options' => [ - 'message' => static::FIRSTNAME_REQUIRED - ] - ], - [ - 'name' => 'StringLength', - 'options' => [ - 'max' => 150, - 'message' => static::FIRSTNAME_LIMIT - ] - ] - ], - ]); - - $this->add([ - 'name' => 'lastName', - 'filters' => [ - ['name' => 'StringTrim'] - ], - 'validators' => [ - [ - 'name' => 'NotEmpty', - 'break_chain_on_failure' => true, - 'options' => [ - 'message' => static::LASTNAME_REQUIRED - ] - ], - [ - 'name' => 'StringLength', - 'options' => [ - 'max' => 150, - 'message' => static::LASTNAME_LIMIT - ] - ] - ], - ]); - - $this->add([ - 'name' => 'address', - 'required' => false, - 'filters' => [ - ['name' => 'StringTrim'] - ], - 'validators' => [], - ]); - - $this->add([ - 'name' => 'phone', - 'required' => false, - 'filters' => [ - ['name' => 'StringTrim'] - ], - 'validators' => [ - [ - 'name' => 'Regex', - 'options' => [ - 'pattern' => '/^\+?\d+$/', - 'message' => static::PHONE_INVALID - ] - ], - ], - ]); - } -} diff --git a/src/Admin/Form/User/UserFieldset.php b/src/Admin/Form/User/UserFieldset.php deleted file mode 100644 index 1560d81..0000000 --- a/src/Admin/Form/User/UserFieldset.php +++ /dev/null @@ -1,110 +0,0 @@ -add([ - 'name' => 'id', - 'type' => 'hidden', - ]); - - $this->add([ - 'name' => 'username', - 'type' => 'text', - 'options' => [ - 'label' => 'Username', - ], - 'attributes' => [ - 'id' => 'username', - 'placeholder' => 'Username...' - ] - ]); - - $this->add([ - 'name' => 'email', - 'type' => 'text', - 'options' => [ - 'label' => 'Email', - ], - 'attributes' => [ - 'placeholder' => 'Email...' - ] - ]); - - $this->add(array( - 'type' => 'password', - 'name' => 'password', - 'options' => [ - 'label' => 'Password' - ], - 'attributes' => array( - 'placeholder' => 'Password', - //'required' => true, - ), - ), ['priority' => -10]); - - $this->add(array( - 'type' => 'password', - 'name' => 'passwordVerify', - 'options' => [ - 'label' => 'Confirm Password' - ], - 'attributes' => array( - 'placeholder' => 'Confirm Password', - //'required' => true, - ), - ), ['priority' => -11]); - - $this->add([ - 'name' => 'role', - 'type' => 'select', - 'options' => [ - 'label' => 'User Role', - 'value_options' => [ - ['value' => 'member', 'label' => 'member'], - ['value' => 'user', 'label' => 'user'], - ] - ], - ], ['priority' => -20]); - - $this->add([ - 'name' => 'status', - 'type' => 'select', - 'options' => [ - 'label' => 'Account Status', - 'value_options' => [ - ['value' => 'pending', 'label' => 'pending'], - ['value' => 'active', 'label' => 'active'], - ['value' => 'inactive', 'label' => 'inactive'], - ['value' => 'deleted', 'label' => 'deleted'], - ] - ], - ], ['priority' => -21]); - } -} diff --git a/src/Admin/Form/User/UserForm.php b/src/Admin/Form/User/UserForm.php deleted file mode 100644 index fc92477..0000000 --- a/src/Admin/Form/User/UserForm.php +++ /dev/null @@ -1,111 +0,0 @@ - true, - 'username' => true, - 'email' => true, - 'details' => ['firstName' => true, 'lastName' => true, 'address' => true, 'phone' => true], - 'password' => true, - 'passwordVerify' => true, - 'role' => true, - 'status' => true - ]; - - public function __construct(Fieldset $userFieldset, Fieldset $userDetailsFieldset, $options = []) - { - $this->userFieldset = $userFieldset; - $this->userDetailsFieldset = $userDetailsFieldset; - parent::__construct('user_form', $options); - } - - public function init() - { - $this->userFieldset->setName('user'); - $this->userFieldset->setUseAsBaseFieldset(true); - - $this->userDetailsFieldset->setName('details'); - $this->userFieldset->add($this->userDetailsFieldset); - $this->userFieldset->setPriority('details', -5); - - $this->add($this->userFieldset); - - $csrf = new Csrf('user_csrf', [ - 'csrf_options' => [ - 'timeout' => 3600, - 'message' => 'The form used to make the request has expired. Please try again now' - ] - ]); - $this->add($csrf); - } - - public function removeUsernameValidation() - { - $this->currentValidationGroup['username'] = false; - } - - public function removeEmailValidation() - { - $this->currentValidationGroup['email'] = false; - } - - public function resetValidationGroup() - { - foreach ($this->currentValidationGroup as $key => $value) { - $this->currentValidationGroup[$key] = true; - } - $this->setValidationGroup(FormInterface::VALIDATE_ALL); - } - - public function applyValidationGroup() - { - $validationGroup = $this->getActiveValidationGroup($this->currentValidationGroup, $this->getBaseFieldset()); - $this->setValidationGroup(['user' => $validationGroup]); - } - - /** - * @param $groups - * @param ElementInterface $prevElement - * @return array - */ - public function getActiveValidationGroup($groups, ElementInterface $prevElement) - { - $validationGroup = []; - foreach ($groups as $key => $value) { - if (is_array($value) && $prevElement instanceof FieldsetInterface) { - if ($prevElement->has($key)) { - $validationGroup[$key] = $this->getActiveValidationGroup($value, $prevElement->get($key)); - } - } elseif ($value === true && $prevElement->has($key)) { - $validationGroup[] = $key; - } - } - return $validationGroup; - } -} diff --git a/src/Admin/Form/User/UserInputFilter.php b/src/Admin/Form/User/UserInputFilter.php deleted file mode 100644 index 4fd1291..0000000 --- a/src/Admin/Form/User/UserInputFilter.php +++ /dev/null @@ -1,181 +0,0 @@ -usernameValidator = $usernameValidator; - $this->emailValidator = $emailValidator; - } - - public function init() - { - $this->add([ - 'name' => 'id', - 'required' => false, - ]); - - $username = [ - 'name' => 'username', - 'filters' => [ - ['name' => 'StringTrim'], - ], - 'validators' => [ - [ - 'name' => 'NotEmpty', - 'break_chain_on_failure' => true, - 'options' => [ - 'message' => static::USERNAME_REQUIRED, - ] - ], - [ - 'name' => 'StringLength', - 'options' => [ - 'min' => 3, - 'max' => 150, - 'message' => static::USERNAME_LENGTH_LIMIT, - ] - ], - [ - 'name' => 'Regex', - 'options' => [ - 'pattern' => '/^[a-zA-Z0-9-_]+$/', - 'message' => static::USERNAME_INVALID, - ] - ] - ], - ]; - - if ($this->usernameValidator) { - $this->usernameValidator->setMessage(static::USERNAME_TAKEN); - $username['validators'][] = $this->usernameValidator; - } - - $this->add($username); - - $email = [ - 'name' => 'email', - 'filters' => [ - ['name' => 'StringTrim'], - ], - 'validators' => [ - [ - 'name' => 'NotEmpty', - 'break_chain_on_failure' => true, - 'options' => [ - 'message' => static::EMAIL_REQUIRED, - ] - ], - [ - 'name' => 'EmailAddress', - 'options' => [ - 'message' => static::EMAIL_INVALID, - ] - ], - ], - ]; - - if ($this->emailValidator) { - $this->emailValidator->setMessage(static::EMAIL_TAKEN); - $email['validators'][] = $this->emailValidator; - } - - $this->add($email); - - $this->add([ - 'name' => 'password', - 'filters' => [ - ['name' => 'StringTrim'], - ], - 'validators' => [ - [ - 'name' => 'NotEmpty', - 'break_chain_on_failure' => true, - 'options' => [ - 'message' => static::PASSWORD_REQUIRED - ] - ], - [ - 'name' => 'StringLength', - 'options' => [ - 'min' => 4, - 'max' => 150, - 'message' => static::PASSWORD_LENGTH_LIMIT, - ], - ], - ], - ]); - - $this->add([ - 'name' => 'passwordVerify', - 'filters' => [ - ['name' => 'StringTrim'], - ], - 'validators' => [ - [ - 'name' => 'NotEmpty', - 'break_chain_on_failure' => true, - 'options' => [ - 'message' => static::PASSWORD_VERIFY_REQUIRED, - ] - ], - [ - 'name' => 'Identical', - 'options' => [ - 'token' => 'password', - 'message' => static::PASSWORD_VERIFY_MISMATCH - ], - ], - ], - ]); - - $this->add([ - 'name' => 'role', - ]); - - $this->add([ - 'name' => 'status', - ]); - } -} diff --git a/src/Admin/Hydrator/AdminHydrator.php b/src/Admin/Hydrator/AdminHydrator.php new file mode 100644 index 0000000..ccd71d2 --- /dev/null +++ b/src/Admin/Hydrator/AdminHydrator.php @@ -0,0 +1,21 @@ +getMapperManager()->get(RoleEntity::class); + $identifier = current($mapper->getPrimaryKey()); + + $roles = []; + $roleIds = []; + + // if value is an array of role ids, replace them with RoleEntity instances + $value = (array) $value; + foreach ($value as $role) { + if ($role instanceof RoleEntity) { + $roles[] = $role; + continue; + } + + if (is_string($role) || is_numeric($role)) { + $roleIds[] = (int) $role; + } + } + + if (!empty($roleIds)) { + $collectedRoles = $mapper->find('all', ['conditions' => [$identifier => $roleIds]]); + $roles = array_merge($roles, $collectedRoles); + } + + if (count($roles) !== count($value)) { + throw new RuntimeException('Could not load all roles for entity'); + } + + return $roles; + } + + public function extract($value) + { + // no need for extract strategy in this case + return $value; + } +} diff --git a/src/Admin/Mapper/AdminDbMapper.php b/src/Admin/Mapper/AdminDbMapper.php new file mode 100644 index 0000000..8ab7f02 --- /dev/null +++ b/src/Admin/Mapper/AdminDbMapper.php @@ -0,0 +1,28 @@ +adapter = $adapter; - $this->table = $table; - $this->sql = new Sql($this->adapter, $table); - } - - /** - * @param $fieldName - * @param string $deletedValue - * @param $ids - * @return int - */ - public function markAsDeleted(array $ids, $fieldName, $deletedValue = 'deleted') - { - $update = $this->sql->update()->set([$fieldName => $deletedValue])->where(function (Where $where) use ($ids) { - $where->in('id', $ids); - }); - $stmt = $this->sql->prepareStatementForSqlObject($update); - return $stmt->execute()->getAffectedRows(); - } - - /** - * @param $ids - * @return int - */ - public function bulkDelete(array $ids) - { - $delete = $this->sql->delete()->where(function (Where $where) use ($ids) { - $where->in('id', $ids); - }); - $stmt = $this->sql->prepareStatementForSqlObject($delete); - return $stmt->execute()->getAffectedRows(); - } -} diff --git a/src/Admin/Mapper/EntityOperationsMapperInterface.php b/src/Admin/Mapper/EntityOperationsMapperInterface.php deleted file mode 100644 index 7154a05..0000000 --- a/src/Admin/Mapper/EntityOperationsMapperInterface.php +++ /dev/null @@ -1,31 +0,0 @@ -entityOperationsMapper; - } - - /** - * @param EntityOperationsMapperInterface $entityOperationsMapper - * @return AbstractEntityService - */ - public function setEntityOperationsMapper(EntityOperationsMapperInterface $entityOperationsMapper) - { - $this->entityOperationsMapper = $entityOperationsMapper; - return $this; - } - - /** - * @param $ids - * @return mixed - */ - public function markAsDeleted($ids) - { - return $this->entityOperationsMapper->markAsDeleted($ids, 'status', 'deleted'); - } - - /** - * @param array $ids - * @return mixed - */ - public function bulkDelete($ids) - { - return $this->entityOperationsMapper->bulkDelete($ids); - } -} diff --git a/src/Admin/Service/AdminService.php b/src/Admin/Service/AdminService.php index d2945f6..1ca5221 100644 --- a/src/Admin/Service/AdminService.php +++ b/src/Admin/Service/AdminService.php @@ -1,36 +1,56 @@ getPassword())) { + if ($entity->needsPasswordRehash()) { $entity->setPassword($this->passwordService->create($entity->getPassword())); } + + $entity->needsPasswordRehash(true); + return parent::save($entity); } @@ -45,6 +65,8 @@ public function getPasswordService() /** * @param PasswordInterface $passwordService * @return $this + * + * @Inject({PasswordInterface::class}) */ public function setPasswordService(PasswordInterface $passwordService) { diff --git a/src/Admin/Service/EntityServiceInterface.php b/src/Admin/Service/EntityServiceInterface.php deleted file mode 100644 index 5976968..0000000 --- a/src/Admin/Service/EntityServiceInterface.php +++ /dev/null @@ -1,31 +0,0 @@ -actionLogger = $actionLogger; - $this->authenticationService = $authenticationService; - } - - /** - * @param EntityServiceEvent $e - * @return void - */ - public function onPreCreate(EntityServiceEvent $e) - { - // TODO: Implement onPreCreate() method. - } - - /** - * @param EntityServiceEvent $e - * @return void - */ - public function onPostCreate(EntityServiceEvent $e) - { - /** @var EntityService $service */ - $service = $e->getTarget(); - $target = $e->getData(); - - $extra = [ - 'type' => 'create', - 'status' => 'ok', - ]; - - $this->actionLogger->info( - 'Entity created successfully', - array_merge( - $extra, - $this->getConstantExtra(), - $this->getTargetExtra($service, $target) - ) - ); - } - - /** - * @return array - */ - protected function getConstantExtra() - { - return [ - 'ip' => $_SERVER['REMOTE_ADDR'], - 'agentId' => $this->authenticationService->hasIdentity() - ? $this->authenticationService->getIdentity()->getId() - : null, - 'agentName' => $this->authenticationService->hasIdentity() - ? $this->authenticationService->getIdentity()->getName() - : null, - ]; - } - - /** - * @param EntityService $service - * @param $target - * @return array - */ - protected function getTargetExtra(EntityService $service, $target) - { - $targetId = null; - if (is_object($target)) { - $getter = 'get' . ucfirst($service->getMapper()->getIdentifierName()); - if (method_exists($target, $getter)) { - $targetId = call_user_func([$target, $getter]); - } - $target = get_class($target); - } - - return [ - 'target' => $target, - 'targetId' => $targetId, - ]; - } - - /** - * @param EntityServiceEvent $e - * @return void - */ - public function onCreateError(EntityServiceEvent $e) - { - // TODO: Implement onCreateError() method. - } - - public function onPreUpdate(EntityServiceEvent $e) - { - // TODO: Implement onPreUpdate() method. - } - - /** - * @param EntityServiceEvent $e - * @return void - */ - public function onPostUpdate(EntityServiceEvent $e) - { - /** @var EntityService $service */ - $service = $e->getTarget(); - $target = $e->getData(); - - $extra = [ - 'type' => 'update', - 'status' => 'ok', - ]; - - $this->actionLogger->info( - 'Entity updated successfully', - array_merge( - $extra, - $this->getConstantExtra(), - $this->getTargetExtra($service, $target) - ) - ); - } - - /** - * @param EntityServiceEvent $e - * @return void - */ - public function onUpdateError(EntityServiceEvent $e) - { - // TODO: Implement onUpdateError() method. - } - - /** - * @param EntityServiceEvent $e - * @return void - */ - public function onPreDelete(EntityServiceEvent $e) - { - // TODO: Implement onPreDelete() method. - } - - /** - * @param EntityServiceEvent $e - * @return void - */ - public function onPostDelete(EntityServiceEvent $e) - { - // TODO: Implement onPostDelete() method. - } - - /** - * @param EntityServiceEvent $e - * @return void - */ - public function onDeleteError(EntityServiceEvent $e) - { - // TODO: Implement onDeleteError() method. - } -} diff --git a/src/Admin/Service/UserService.php b/src/Admin/Service/UserService.php deleted file mode 100644 index e945e09..0000000 --- a/src/Admin/Service/UserService.php +++ /dev/null @@ -1,54 +0,0 @@ -getPassword())) { - $entity->setPassword($this->passwordService->create($entity->getPassword())); - } - return parent::save($entity); - } - - /** - * @return PasswordInterface - */ - public function getPasswordService() - { - return $this->passwordService; - } - - /** - * @param PasswordInterface $passwordService - * @return $this - */ - public function setPasswordService(PasswordInterface $passwordService) - { - $this->passwordService = $passwordService; - return $this; - } -} diff --git a/src/App/ConfigProvider.php b/src/App/ConfigProvider.php new file mode 100644 index 0000000..979e95a --- /dev/null +++ b/src/App/ConfigProvider.php @@ -0,0 +1,87 @@ + $this->getDependenciesConfig(), + + 'dot_form' => $this->getFormsConfig(), + + 'routes' => $this->getRoutesConfig(), + ]; + } + + public function getDependenciesConfig(): array + { + return [ + + ]; + } + + public function getFormsConfig(): array + { + return [ + 'form_manager' => [ + 'factories' => [ + ConfirmDeleteForm::class => InvokableFactory::class, + ], + 'aliases' => [ + 'ConfirmDelete' => ConfirmDeleteForm::class, + ] + ] + ]; + } + + public function getRoutesConfig(): array + { + return [ + [ + 'name' => 'dashboard', + 'path' => '/dashboard[/[{action}]]', + 'middleware' => DashboardController::class, + ], + [ + // there is already a route named `user` from dot-user package, + // so we use a diff one for the frontend user management + 'name' => 'f_user', + 'path' => '/user[/{action}[/{id:\d+}]]', + 'middleware' => UserManageController::class, + ], + + //change default route paths for user related stuff into admin + //we will use 'user' for frontend users + 'login_route' => [ + 'path' => '/admin/login', + ], + 'logout_route' => [ + 'path' => '/admin/logout', + ], + 'user_route' => [ + 'path' => '/admin[/{action}[/{id:\d+}]]', + 'middleware' => [AdminController::class, UserController::class], + ] + ]; + } +} diff --git a/src/App/Controller/AdminController.php b/src/App/Controller/AdminController.php new file mode 100644 index 0000000..0222e59 --- /dev/null +++ b/src/App/Controller/AdminController.php @@ -0,0 +1,95 @@ +disablePasswordValidation(); + $entity->needsPasswordRehash(false); + } + } + + /** + * @return ResponseInterface + */ + public function changePasswordAction(): ResponseInterface + { + // change uri to account uri, as there is the form, so PRG will go there + $this->request = $this->request->withUri(new Uri($this->url('user', ['action' => 'account']))); + + // this overwrites the original action, in order to redirect to /admin/account, where change password is moved + // this happens only on GET, POST will go through the original action in order to process the request + // as the original action does a PRG(post-redirect-get) it will go to the account action after processing update + // this is what we actually want + if ($this->getRequest()->getMethod() === 'GET') { + return new RedirectResponse($this->url('user', ['action' => 'account'])); + } + + $next = $this->getNext(); + return $next($this->getRequest(), $this->getResponse()); + } + + /** + * @param UserControllerEvent $e + */ + public function onBeforeAccountRender(UserControllerEvent $e) + { + // inject the ChangePassword form + $e->setParam('changePasswordForm', $this->forms('ChangePassword')); + $e->setParam('changePasswordAction', $this->url('user', ['action' => 'change-password'])); + } +} diff --git a/src/App/Controller/DashboardController.php b/src/App/Controller/DashboardController.php new file mode 100644 index 0000000..2d993a7 --- /dev/null +++ b/src/App/Controller/DashboardController.php @@ -0,0 +1,47 @@ +template('app::dashboard')); + } +} diff --git a/src/Admin/Controller/EntityManageBaseController.php b/src/App/Controller/EntityManageBaseController.php similarity index 59% rename from src/Admin/Controller/EntityManageBaseController.php rename to src/App/Controller/EntityManageBaseController.php index 2231e2a..bb2d404 100644 --- a/src/Admin/Controller/EntityManageBaseController.php +++ b/src/App/Controller/EntityManageBaseController.php @@ -1,26 +1,41 @@ service = $service; - $this->entityForm = $entityForm; - $this->deleteForm = $deleteForm; } /** @@ -62,7 +71,7 @@ public function __construct( */ public function indexAction() { - return new RedirectResponse($this->url()->generate(static::ENTITY_ROUTE_NAME, ['action' => 'manage'])); + return new RedirectResponse($this->url(static::ENTITY_ROUTE_NAME, ['action' => 'manage'])); } /** @@ -70,15 +79,16 @@ public function indexAction() */ public function manageAction() { - $listUri = $this->url()->generate(static::ENTITY_ROUTE_NAME, ['action' => 'list']); - $addUri = $this->url()->generate(static::ENTITY_ROUTE_NAME, ['action' => 'add']); - $editUri = $this->url()->generate(static::ENTITY_ROUTE_NAME, ['action' => 'edit']); - $deleteUri = $this->url()->generate(static::ENTITY_ROUTE_NAME, ['action' => 'delete']); + $listUri = $this->url(static::ENTITY_ROUTE_NAME, ['action' => 'list']); + $addUri = $this->url(static::ENTITY_ROUTE_NAME, ['action' => 'add']); + $editUri = $this->url(static::ENTITY_ROUTE_NAME, ['action' => 'edit']); + $deleteUri = $this->url(static::ENTITY_ROUTE_NAME, ['action' => 'delete']); return new HtmlResponse( - $this->template()->render( + $this->template( static::ENTITY_TEMPLATE_NAME, [ + 'defaultSortedColumn' => static::DEFAULT_SORTED_COLUMN, 'listUri' => $listUri, 'editUri' => $editUri, 'addUri' => $addUri, @@ -91,19 +101,36 @@ public function manageAction() } /** - * @return JsonResponse + * @return ResponseInterface */ - public function listAction() + public function listAction(): ResponseInterface { //get query params as sent by bootstrap-table $params = $this->request->getQueryParams(); - $limit = isset($params['limit']) ? (int)$params['limit'] : 30; - $offset = isset($params['offset']) ? (int)$params['offset'] : 0; + + $options = []; + $sort = $params['sort'] ?? ''; + $order = $params['order'] ?? 'asc'; + + $search = $params['search'] ?? ''; + $search = trim($search); + + if (!empty($sort) && !empty($order)) { + $options['order'] = [$sort => $order]; + } + + if (!empty($search)) { + $options['search'] = $search; + } + + $limit = (int) $params['limit'] ?? 30; + $offset = (int) $params['offset'] ?? 0; /** @var Paginator $paginator */ - $paginator = $this->service->findAll([], $params, true); + $paginator = $this->service->findAll($options, true); $paginator->setItemCountPerPage($limit); - $paginator->setCurrentPageNumber(intval($offset / $limit)); + $paginator->setCurrentPageNumber(intval($offset / $limit) + 1); + return new JsonResponse([ 'total' => $paginator->getTotalItemCount(), @@ -112,25 +139,22 @@ public function listAction() } /** - * @return HtmlResponse|JsonResponse + * @return ResponseInterface */ - public function addAction() + public function addAction(): ResponseInterface { $request = $this->request; - $form = $this->entityForm; - $form->getBaseFieldset()->remove('id'); + /** @var Form $form */ + $form = $this->forms(static::ENTITY_FORM_NAME); if ($request->getMethod() === 'POST') { $data = $request->getParsedBody(); - - $form->bind($this->service->getMapper()->getPrototype()); $form->setData($data); - if ($form->isValid()) { $entity = $form->getData(); try { - $id = $this->service->save($entity); - if ($id) { + $entity = $this->service->save($entity); + if ($entity) { return $this->generateJsonOutput($this->getEntityCreateSuccessMessage()); } else { return $this->generateJsonOutput($this->getEntityCreateErrorMessage(), 'error'); @@ -143,142 +167,26 @@ public function addAction() return $this->generateJsonOutput($message, 'error'); } } else { - return $this->generateJsonOutput($this->getFormErrors($form->getMessages()), 'validation', $form); + return $this->generateJsonOutput($this->forms()->getErrors($form), 'validation', $form); } } return new HtmlResponse( - $this->template()->render( + $this->template( 'partial::ajax-form', [ 'form' => $form, 'formAction' => - $this->url()->generate(static::ENTITY_ROUTE_NAME, ['action' => 'add']) + $this->url(static::ENTITY_ROUTE_NAME, ['action' => 'add']) ] ) ); } /** - * @param array $messages - * @param string $type - * @param Form|null $form - * @return JsonResponse - */ - protected function generateJsonOutput(array $messages, $type = 'success', Form $form = null) - { - $dismissible = true; - $typeToNamespace = [ - 'success' => FlashMessengerInterface::SUCCESS_NAMESPACE, - 'error' => FlashMessengerInterface::ERROR_NAMESPACE, - 'info' => FlashMessengerInterface::INFO_NAMESPACE, - 'warning' => FlashMessengerInterface::WARNING_NAMESPACE, - 'validation' => FlashMessengerInterface::ERROR_NAMESPACE - ]; - - $alerts = $messages; - if ($type === 'validation' && $form) { - $alerts = $this->getFormMessages($form->getMessages()); - $dismissible = false; - } - - $output = [$type => $messages]; - //render the alerts partial to send it through ajax to be inserted into the DOM - $output['alerts'] = $this->template()->render( - 'dot-partial::alerts', - ['dismissible' => $dismissible, 'messages' => [$typeToNamespace[$type] => $alerts]] - ); - - return new JsonResponse($output); - } - - /** - * @param array $formMessages - * @return array + * @return ResponseInterface */ - protected function getFormMessages(array $formMessages) - { - $messages = []; - foreach ($formMessages as $message) { - if (is_array($message)) { - foreach ($message as $m) { - if (is_string($m)) { - $messages[] = $m; - } elseif (is_array($m)) { - $messages = array_merge($messages, $this->getFormMessages($m)); - } - } - } elseif (is_string($message)) { - $messages[] = $message; - } - } - - return $messages; - } - - protected function getEntityCreateSuccessMessage() - { - return [ucfirst(static::ENTITY_NAME_SINGULAR) . ' was successfully created']; - } - - protected function getEntityCreateErrorMessage() - { - return [ - ucfirst(static::ENTITY_NAME_SINGULAR) . - ' could not be created due to a server error. Please try again' - ]; - } - - /** - * @return boolean - */ - public function isDebug() - { - return $this->debug; - } - - /** - * @param boolean $debug - * @return $this - */ - public function setDebug($debug) - { - $this->debug = $debug; - return $this; - } - - /** - * @param array $formMessages - * @return array - */ - protected function getFormErrors(array $formMessages) - { - $errors = []; - foreach ($formMessages as $key => $message) { - if (is_array($message)) { - if (!isset($errors[$key])) { - $errors[$key] = array(); - } - - foreach ($message as $k => $m) { - if (is_string($m)) { - $errors[$key][] = $m; - } elseif (is_array($m)) { - $errors[$key][$k] = $this->getFormErrors($m); - } - } - } elseif (is_string($message)) { - $errors[] = $message; - } - } - - return $errors; - } - - /** - * @return HtmlResponse|JsonResponse - */ - public function editAction() + public function editAction(): ResponseInterface { $request = $this->getRequest(); $id = $request->getAttribute('id'); @@ -286,12 +194,13 @@ public function editAction() return $this->generateJsonOutput($this->getEntityEditNoIdErrorMessage(), 'error'); } - $entity = $this->service->find([$this->service->getMapper()->getIdentifierName() => $id]); + $entity = $this->service->find($id); if (!$entity) { return $this->generateJsonOutput($this->getEntityIdInvalidErrorMessage(), 'error'); } - $form = $this->entityForm; + /** @var Form $form */ + $form = $this->forms(static::ENTITY_FORM_NAME); $form->bind($entity); if ($request->getMethod() === 'POST') { @@ -307,8 +216,12 @@ public function editAction() if ($form->isValid()) { $entity = $form->getData(); try { - $this->service->save($entity); - return $this->generateJsonOutput($this->getEntityUpdateSuccessMessage()); + $r = $this->service->save($entity); + if ($r) { + return $this->generateJsonOutput($this->getEntityUpdateSuccessMessage()); + } else { + return $this->generateJsonOutput($this->getEntityUpdateErrorMessage(), 'error'); + } } catch (\Exception $e) { $message = $this->getEntityUpdateErrorMessage(); if ($this->isDebug()) { @@ -317,16 +230,16 @@ public function editAction() return $this->generateJsonOutput($message, 'error'); } } else { - return $this->generateJsonOutput($this->getFormErrors($form->getMessages()), 'validation', $form); + return $this->generateJsonOutput($this->forms()->getErrors($form), 'validation', $form); } } return new HtmlResponse( - $this->template()->render( + $this->template( 'partial::ajax-form', [ 'form' => $form, - 'formAction' => $this->url()->generate( + 'formAction' => $this->url( static::ENTITY_ROUTE_NAME, ['action' => 'edit', 'id' => $id] ) @@ -335,36 +248,14 @@ public function editAction() ); } - protected function getEntityEditNoIdErrorMessage() - { - return [ucfirst(static::ENTITY_NAME_SINGULAR) . ' id parameter is missing']; - } - - protected function getEntityIdInvalidErrorMessage() - { - return [ucfirst(static::ENTITY_NAME_SINGULAR) . ' id is invalid.']; - } - - protected function getEntityUpdateSuccessMessage() - { - return [ucfirst(static::ENTITY_NAME_SINGULAR) . ' was successfully updated']; - } - - protected function getEntityUpdateErrorMessage() - { - return [ - ucfirst(static::ENTITY_NAME_SINGULAR) . - ' could not be updated due to a server error. Please try again' - ]; - } - /** - * @return HtmlResponse|JsonResponse|RedirectResponse + * @return ResponseInterface */ - public function deleteAction() + public function deleteAction(): ResponseInterface { $request = $this->getRequest(); - $form = $this->deleteForm; + /** @var Form $form */ + $form = $this->forms(static::ENTITY_DELETE_FORM_NAME); if ($request->getMethod() === 'POST') { $data = $request->getParsedBody(); @@ -372,10 +263,10 @@ public function deleteAction() if (isset($data[static::ENTITY_NAME_PLURAL]) && is_array($data[static::ENTITY_NAME_PLURAL])) { return new HtmlResponse( $this->template()->render( - 'partial::delete-form', + 'partial::confirm-delete', [ 'form' => $form, - 'deleteUri' => $this->url()->generate(static::ENTITY_ROUTE_NAME, ['action' => 'delete']), + 'deleteUri' => $this->url(static::ENTITY_ROUTE_NAME, ['action' => 'delete']), 'entities' => $data[static::ENTITY_NAME_PLURAL] ] ) @@ -396,7 +287,7 @@ public function deleteAction() if ($markAsDeleted) { $result = $this->service->markAsDeleted($ids); } else { - $result = $this->service->bulkDelete($ids); + $result = $this->service->deleteAll($ids); } if ($result) { @@ -416,30 +307,126 @@ public function deleteAction() return $this->generateJsonOutput($this->getEntityDeleteNoChangesMessage(), 'info'); } } else { - return $this->generateJsonOutput($this->getFormErrors($form->getMessages()), 'validation', $form); + return $this->generateJsonOutput($this->forms()->getErrors($form), 'validation', $form); } } } //redirect to manage page if trying to access this action via GET - return new RedirectResponse($this->url()->generate(static::ENTITY_ROUTE_NAME, ['action' => 'manage'])); + return new RedirectResponse($this->url(static::ENTITY_ROUTE_NAME, ['action' => 'manage'])); + } + + /** + * @param array $messages + * @param string $type + * @param Form|null $form + * @return JsonResponse + */ + protected function generateJsonOutput(array $messages, $type = 'success', Form $form = null): JsonResponse + { + $dismissible = true; + $typeToNamespace = [ + 'success' => FlashMessengerInterface::SUCCESS, + 'error' => FlashMessengerInterface::ERROR, + 'info' => FlashMessengerInterface::INFO, + 'warning' => FlashMessengerInterface::WARNING, + 'validation' => FlashMessengerInterface::ERROR + ]; + + $alerts = $messages; + if ($type === 'validation' && $form) { + $alerts = $this->forms()->getMessages($form); + $dismissible = false; + } + + $output = [$type => $messages]; + //render the alerts partial to send it through ajax to be inserted into the DOM + $output['alerts'] = $this->template( + 'partial::alerts', + ['dismissible' => $dismissible, 'messages' => [$typeToNamespace[$type] => $alerts]] + ); + + return new JsonResponse($output); + } + + /** + * @return array + */ + protected function getEntityCreateSuccessMessage() + { + return [ucfirst(static::ENTITY_NAME_SINGULAR) . ' entity was successfully created']; } + /** + * @return array + */ + protected function getEntityCreateErrorMessage() + { + return [ + ucfirst(static::ENTITY_NAME_SINGULAR) . + ' entity could not be created due to a server error. Please try again' + ]; + } + + /** + * @return array + */ + protected function getEntityEditNoIdErrorMessage() + { + return [ucfirst(static::ENTITY_NAME_SINGULAR) . ' entity id parameter is missing']; + } + + /** + * @return array + */ + protected function getEntityIdInvalidErrorMessage() + { + return [ucfirst(static::ENTITY_NAME_SINGULAR) . ' entity id is invalid.']; + } + + /** + * @return array + */ + protected function getEntityUpdateSuccessMessage() + { + return [ucfirst(static::ENTITY_NAME_SINGULAR) . ' entity was successfully updated']; + } + + /** + * @return array + */ + protected function getEntityUpdateErrorMessage() + { + return [ + ucfirst(static::ENTITY_NAME_SINGULAR) . + ' entity could not be updated due to a server error. Please try again' + ]; + } + + /** + * @return array + */ protected function getEntityDeleteSuccessMessage() { - return [ucfirst(static::ENTITY_NAME_SINGULAR) . ' was successfully removed']; + return [ucfirst(static::ENTITY_NAME_SINGULAR) . ' entity was successfully removed']; } + /** + * @return array + */ protected function getEntityDeleteNoChangesMessage() { - return ['Delete operation was canceled. No changes were made']; + return ['Deletion not confirmed or no changes were made']; } + /** + * @return array + */ protected function getEntityDeleteErrorMessage() { return [ ucfirst(static::ENTITY_NAME_SINGULAR) . - ' could not be removed due to a server error. Please try again' + ' entity could not be removed due to a server error. Please try again' ]; } } diff --git a/src/App/Controller/UserController.php b/src/App/Controller/UserController.php new file mode 100644 index 0000000..8e55946 --- /dev/null +++ b/src/App/Controller/UserController.php @@ -0,0 +1,57 @@ +disablePasswordValidation(); + $entity->needsPasswordRehash(false); + } + } +} diff --git a/src/App/Exception/ExceptionInterface.php b/src/App/Exception/ExceptionInterface.php new file mode 100644 index 0000000..89f6798 --- /dev/null +++ b/src/App/Exception/ExceptionInterface.php @@ -0,0 +1,19 @@ +get('config')['annotations_cache_dir']); + } +} diff --git a/src/App/Form/ConfirmDeleteForm.php b/src/App/Form/ConfirmDeleteForm.php new file mode 100644 index 0000000..53cc05c --- /dev/null +++ b/src/App/Form/ConfirmDeleteForm.php @@ -0,0 +1,36 @@ +setAttribute('method', 'post'); + } + + public function init() + { + $csrf = new Csrf('confirm_delete_csrf', [ + 'csrf_options' => [ + 'timeout' => 3600, + 'message' => Messages::CSRF_EXPIRED + ] + ]); + $this->add($csrf); + } +} diff --git a/src/App/Mapper/SearchFinderMapperTrait.php b/src/App/Mapper/SearchFinderMapperTrait.php new file mode 100644 index 0000000..8feae67 --- /dev/null +++ b/src/App/Mapper/SearchFinderMapperTrait.php @@ -0,0 +1,41 @@ +where(function (Where $where) use ($search) { + $where = $where->nest(); + foreach ($search as $column => $value) { + $where->like($column, $value)->or; + } + $where->unnest()->and; + }); + } + + return $select; + } +} diff --git a/src/App/Messages.php b/src/App/Messages.php new file mode 100644 index 0000000..8749011 --- /dev/null +++ b/src/App/Messages.php @@ -0,0 +1,19 @@ +urlHelper->generate('dashboard')); } - } return $next($request, $response); diff --git a/src/App/Service/AbstractEntityService.php b/src/App/Service/AbstractEntityService.php new file mode 100644 index 0000000..a7bade2 --- /dev/null +++ b/src/App/Service/AbstractEntityService.php @@ -0,0 +1,141 @@ +getEntityMapper()->updateAll(['status' => 'deleted'], [$this->entityIdentifier => $ids]); + } + + /** + * @param array $ids + * @return int + */ + public function deleteAll(array $ids): int + { + return $this->getEntityMapper()->deleteAll([$this->entityIdentifier => $ids]); + } + + /** + * @param $id + * @param array $options + * @return mixed|null + */ + public function find($id, array $options = []) + { + return $this->getEntityMapper()->get($id, $options); + } + + /** + * @param array $options + * @param bool $paginated + * @return array|Paginator + */ + public function findAll(array $options = [], bool $paginated = false) + { + $finder = 'all'; + if (isset($options['search']) && is_string($options['search']) && !empty($this->searchableColumns)) { + $finder = 'search'; + $search = []; + foreach ($this->searchableColumns as $column) { + $search[$column] = $options['search']; + } + + $options['search'] = $search; + } + + if (!$paginated) { + return $this->getEntityMapper()->find($finder, $options); + } + + //returns paginated results + $options['finder'] = $finder; + return $this->getPaginator($this->getEntityMapper(), $options); + } + + /** + * @param mixed $entity + * @param array $options + * @return mixed + */ + public function save($entity, array $options = []) + { + return $this->getEntityMapper()->save($entity, $options); + } + + /** + * @param mixed $entity + * @param array $options + * @return mixed + */ + public function delete($entity, array $options = []) + { + return $this->getEntityMapper()->delete($entity, $options); + } + + /** + * @return MapperInterface + */ + public function getEntityMapper(): MapperInterface + { + if (!$this->entityMapper) { + $this->entityMapper = $this->getMapperManager()->get($this->entityClass); + } + + return $this->entityMapper; + } + + /** + * @param MapperInterface $mapper + * @param array $options + * @return Paginator + */ + public function getPaginator(MapperInterface $mapper, array $options = []): Paginator + { + if (!class_exists($this->paginatorClass)) { + throw new RuntimeException(sprintf('Paginator class %s does not exists', $this->paginatorClass)); + } + + $class = $this->paginatorClass; + return new $class(new MapperAdapter($mapper, $options)); + } +} diff --git a/src/App/Service/EntityServiceInterface.php b/src/App/Service/EntityServiceInterface.php new file mode 100644 index 0000000..8dc342e --- /dev/null +++ b/src/App/Service/EntityServiceInterface.php @@ -0,0 +1,57 @@ + $this->getDependenciesConfig(), + + 'dot_form' => $this->getFormsConfig(), + + 'dot_hydrator' => $this->getHydratorsConfig(), + + 'dot_ems' => $this->getMappersConfig(), + ]; + } + + public function getDependenciesConfig(): array + { + return [ + + ]; + } + + public function getFormsConfig(): array + { + return [ + 'form_manager' => [ + 'factories' => [ + UserFieldset::class => UserFieldsetFactory::class, + UserDetailsFieldset::class => InvokableFactory::class, + UserForm::class => InvokableFactory::class, + ], + 'aliases' => [ + 'F_UserFieldset' => UserFieldset::class, + 'F_UserDetailsFieldset' => UserDetailsFieldset::class, + 'User' => UserForm::class, + ] + ] + ]; + } + + public function getHydratorsConfig(): array + { + return [ + 'hydrator_manager' => [ + 'factories' => [ + UserHydrator::class => UserHydratorFactory::class, + ], + 'aliases' => [ + 'UserHydrator' => UserHydrator::class, + ] + ] + ]; + } + + public function getMappersConfig(): array + { + return [ + 'mapper_manager' => [ + 'factories' => [ + UserDbMapper::class => UserDbMapperFactory::class, + ], + 'aliases' => [ + UserEntity::class => UserDbMapper::class, + ] + ] + ]; + } +} diff --git a/src/Admin/Entity/UserDetailsEntity.php b/src/User/Entity/UserDetailsEntity.php similarity index 77% rename from src/Admin/Entity/UserDetailsEntity.php rename to src/User/Entity/UserDetailsEntity.php index 9eabcf1..e88677f 100644 --- a/src/Admin/Entity/UserDetailsEntity.php +++ b/src/User/Entity/UserDetailsEntity.php @@ -1,17 +1,15 @@ $this->getUserId(), + 'firstName' => $this->getFirstName(), + 'lastName' => $this->getLastName(), + 'phone' => $this->getPhone(), + 'address' => $this->getAddress() + ]; } } diff --git a/src/User/Entity/UserEntity.php b/src/User/Entity/UserEntity.php new file mode 100644 index 0000000..d3e6ef3 --- /dev/null +++ b/src/User/Entity/UserEntity.php @@ -0,0 +1,72 @@ +details) { + $this->details = new UserDetailsEntity(); + } + return $this->details; + } + + /** + * @param UserDetailsEntity $details + */ + public function setDetails(UserDetailsEntity $details) + { + $this->details = $details; + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + return array_merge( + parent::jsonSerialize(), + [ + 'details' => $this->getDetails(), + ] + ); + } + + /** + * @param bool|null $value + * @return mixed + */ + public function needsPasswordRehash(bool $value = null) + { + if ($value !== null) { + $this->needsPasswordRehash = $value; + return $this; + } else { + return $this->needsPasswordRehash; + } + } +} diff --git a/src/User/Factory/UserDbMapperFactory.php b/src/User/Factory/UserDbMapperFactory.php new file mode 100644 index 0000000..ffb7a7f --- /dev/null +++ b/src/User/Factory/UserDbMapperFactory.php @@ -0,0 +1,41 @@ +get(UserOptions::class); + $userOptions = clone $userOptions; + $userOptions->setUserEntity(UserEntity::class); + $userOptions->setRoleEntity(RoleEntity::class); + $userOptions->setDefaultRoles(['user']); + + $options['user_options'] = $userOptions; + + return parent::__invoke($container, $requestedName, $options); + } +} diff --git a/src/User/Factory/UserFieldsetFactory.php b/src/User/Factory/UserFieldsetFactory.php new file mode 100644 index 0000000..e05fe15 --- /dev/null +++ b/src/User/Factory/UserFieldsetFactory.php @@ -0,0 +1,49 @@ +getUserOptions(); + $userOptions = clone $userOptions; + + $userOptions->setUserEntity(UserEntity::class); + $userOptions->setRoleEntity(RoleEntity::class); + $userOptions->setDefaultRoles(['user']); + + /** @var HydratorPluginManager $hydratorManager */ + $hydratorManager = $container->get('HydratorManager'); + $entity = new UserEntity(); + + $fieldset->setUserOptions($userOptions); + $fieldset->setObject($entity); + $fieldset->setHydrator($hydratorManager->get($entity->hydrator())); + + return $fieldset; + } +} diff --git a/src/User/Factory/UserHydratorFactory.php b/src/User/Factory/UserHydratorFactory.php new file mode 100644 index 0000000..9aa9a25 --- /dev/null +++ b/src/User/Factory/UserHydratorFactory.php @@ -0,0 +1,28 @@ +get(RolesHydratingStrategy::class)); + return $hydrator; + } +} diff --git a/src/User/Form/UserDetailsFieldset.php b/src/User/Form/UserDetailsFieldset.php new file mode 100644 index 0000000..a7d1ced --- /dev/null +++ b/src/User/Form/UserDetailsFieldset.php @@ -0,0 +1,165 @@ +First name is required and cannot be empty'; + const MESSAGE_FIRST_NAME_LIMIT = 'First name character limit of 150 exceeded'; + const MESSAGE_LAST_NAME_EMPTY = 'Last name is required and cannot be empty'; + const MESSAGE_LAST_NAME_LIMIT = 'Last name character limit of 150 exceeded'; + + const PHONE_INVALID = 'Phone number is invalid'; + const PHONE_LIMIT = 'Phone number character limit exceeded'; + + /** + * UserDetailsFieldset constructor. + */ + public function __construct() + { + parent::__construct('details'); + $this->setObject(new UserDetailsEntity()); + $this->setHydrator(new ClassMethodsCamelCase()); + } + + public function init() + { + $this->add([ + 'name' => 'firstName', + 'type' => 'text', + 'options' => [ + 'label' => 'First name' + ], + 'attributes' => [ + 'placeholder' => 'First name...' + ] + ]); + $this->add([ + 'name' => 'lastName', + 'type' => 'text', + 'options' => [ + 'label' => 'Last name' + ], + 'attributes' => [ + 'placeholder' => 'Last name...' + ] + ]); + $this->add([ + 'name' => 'phone', + 'type' => 'text', + 'options' => [ + 'label' => 'Phone' + ], + 'attributes' => [ + 'placeholder' => 'Phone...' + ] + ]); + $this->add([ + 'name' => 'address', + 'type' => 'text', + 'options' => [ + 'label' => 'Address' + ], + 'attributes' => [ + 'placeholder' => 'Address...' + ] + ]); + } + + public function getInputFilterSpecification(): array + { + return [ + 'firstName' => [ + 'filters' => [ + ['name' => 'StringTrim'] + ], + 'validators' => [ + [ + 'name' => 'NotEmpty', + 'break_chain_on_failure' => true, + 'options' => [ + 'message' => static::MESSAGE_FIRST_NAME_EMPTY + ] + ], + [ + 'name' => 'StringLength', + 'options' => [ + 'max' => 150, + 'message' => static::MESSAGE_FIRST_NAME_LIMIT, + ], + ] + ] + ], + 'lastName' => [ + 'filters' => [ + ['name' => 'StringTrim'] + ], + 'validators' => [ + [ + 'name' => 'NotEmpty', + 'break_chain_on_failure' => true, + 'options' => [ + 'message' => static::MESSAGE_LAST_NAME_EMPTY + ] + ], + [ + 'name' => 'StringLength', + 'options' => [ + 'max' => 150, + 'message' => static::MESSAGE_LAST_NAME_LIMIT, + ], + ] + ], + ], + 'phone' => [ + 'required' => false, + 'filters' => [ + ['name' => 'StringTrim'], + ['name' => 'ToNull'] + ], + 'validators' => [ + [ + 'name' => 'Regex', + 'options' => [ + 'pattern' => '/^\+?\d+$/', + 'message' => static::PHONE_INVALID + ] + ], + [ + 'name' => 'StringLength', + 'options' => [ + 'max' => 150, + 'message' => static::PHONE_LIMIT, + ], + ] + ], + ], + 'address' => [ + 'required' => false, + 'filters' => [ + ['name' => 'StringTrim'], + ['name' => 'ToNull'] + ], + 'validators' => [ + + ], + ], + ]; + } +} diff --git a/src/User/Form/UserFieldset.php b/src/User/Form/UserFieldset.php new file mode 100644 index 0000000..84a804e --- /dev/null +++ b/src/User/Form/UserFieldset.php @@ -0,0 +1,85 @@ +Roles should have at least one role selected'; + + public function __construct() + { + parent::__construct(); + $this->setName('f_user'); + } + + public function init() + { + parent::init(); + + $this->add([ + 'name' => 'details', + 'type' => 'F_UserDetailsFieldset', + ], ['priority' => -10]); + + $this->add([ + 'name' => 'roles', + 'type' => 'EntitySelect', + 'options' => [ + 'label' => 'Roles', + 'use_hidden_element' => true, + 'target' => RoleEntity::class, + 'property' => 'name', + ], + 'attributes' => [ + 'multiple' => true, + 'id' => 'rolesSelect' + ] + ], ['priority' => -25]); + + $this->add([ + 'name' => 'status', + 'type' => 'select', + 'options' => [ + 'label' => 'Account Status', + 'value_options' => [ + ['value' => UserEntity::STATUS_PENDING, 'label' => UserEntity::STATUS_PENDING], + ['value' => UserEntity::STATUS_ACTIVE, 'label' => UserEntity::STATUS_ACTIVE], + ['value' => UserEntity::STATUS_INACTIVE, 'label' => UserEntity::STATUS_INACTIVE], + ['value' => UserEntity::STATUS_DELETED, 'label' => UserEntity::STATUS_DELETED], + ] + ], + ], ['priority' => -30]); + } + + public function getInputFilterSpecification() + { + $specs = parent::getInputFilterSpecification(); + $specs['roles'] = [ + 'validators' => [ + [ + 'name' => 'NotEmpty', + 'break_chain_on_failure' => true, + 'options' => [ + 'message' => static::MESSAGE_ROLES_EMPTY, + ] + ] + ] + ]; + + return $specs; + } +} diff --git a/src/User/Form/UserForm.php b/src/User/Form/UserForm.php new file mode 100644 index 0000000..87e1f8e --- /dev/null +++ b/src/User/Form/UserForm.php @@ -0,0 +1,130 @@ + [ + 'username', + 'email', + 'details' => [ + 'firstName', + 'lastName', + 'phone', + 'address' + ], + 'password', + 'passwordConfirm', + 'roles', + 'status', + ] + ]; + + protected $noPasswordValidationGroup = [ + 'user_csrf', + 'f_user' => [ + 'username', + 'email', + 'details' => [ + 'firstName', + 'lastName', + 'phone', + 'address' + ], + 'roles', + 'status', + ] + ]; + + /** + * UserForm constructor. + */ + public function __construct() + { + parent::__construct('userForm'); + + $this->setAttribute('method', 'post'); + $this->setInputFilter(new InputFilter()); + } + + public function init() + { + $this->add([ + 'type' => 'F_UserFieldset', + 'options' => [ + 'use_as_base_fieldset' => true, + ] + ]); + + $this->add([ + 'name' => 'user_csrf', + 'type' => 'csrf', + 'options' => [ + 'timeout' => 3600, + 'message' => Messages::CSRF_EXPIRED + ] + ]); + + $this->setValidationGroup($this->validationGroup); + } + + public function disablePasswordValidation() + { + $this->setValidationGroup($this->noPasswordValidationGroup); + } + + public function resetValidation() + { + $this->setValidationGroup($this->validationGroup); + } + + /** + * @param object $object + * @param int $flags + * @return Form + */ + public function bind($object, $flags = FormInterface::VALUES_NORMALIZED) + { + if ($object instanceof UserEntity) { + $usernameValidators = $this->getInputFilter()->get('f_user')->get('username') + ->getValidatorChain()->getValidators(); + foreach ($usernameValidators as $validator) { + $validator = $validator['instance']; + if ($validator instanceof NoRecordExists) { + $validator->setExclude(['field' => 'id', 'value' => $object->getId()]); + break; + } + } + $emailValidators = $this->getInputFilter()->get('f_user')->get('email') + ->getValidatorChain()->getValidators(); + foreach ($emailValidators as $validator) { + $validator = $validator['instance']; + if ($validator instanceof NoRecordExists) { + $validator->setExclude(['field' => 'id', 'value' => $object->getId()]); + break; + } + } + } + return parent::bind($object, $flags); + } +} diff --git a/src/User/Hydrator/RolesHydratingStrategy.php b/src/User/Hydrator/RolesHydratingStrategy.php new file mode 100644 index 0000000..5bed265 --- /dev/null +++ b/src/User/Hydrator/RolesHydratingStrategy.php @@ -0,0 +1,73 @@ +getMapperManager()->get(RoleEntity::class); + $identifier = current($mapper->getPrimaryKey()); + + $roles = []; + $roleIds = []; + + // if value is an array of role ids, replace them with RoleEntity instances + $value = (array) $value; + foreach ($value as $role) { + if ($role instanceof RoleEntity) { + $roles[] = $role; + continue; + } + + if (is_string($role) || is_numeric($role)) { + $roleIds[] = (int) $role; + } + } + + if (!empty($roleIds)) { + $collectedRoles = $mapper->find('all', ['conditions' => [$identifier => $roleIds]]); + $roles = array_merge($roles, $collectedRoles); + } + + if (count($roles) !== count($value)) { + throw new RuntimeException('Could not load all roles for entity'); + } + + return $roles; + } + + public function extract($value) + { + // no need for extract strategy in this case + return $value; + } +} diff --git a/src/User/Hydrator/UserHydrator.php b/src/User/Hydrator/UserHydrator.php new file mode 100644 index 0000000..ce24777 --- /dev/null +++ b/src/User/Hydrator/UserHydrator.php @@ -0,0 +1,35 @@ +rolesStrategy = $rolesStrategy; + + $this->addStrategy('roles', $rolesStrategy); + } +} diff --git a/src/User/Mapper/UserDbMapper.php b/src/User/Mapper/UserDbMapper.php new file mode 100644 index 0000000..86e9b7b --- /dev/null +++ b/src/User/Mapper/UserDbMapper.php @@ -0,0 +1,179 @@ +userDetailsPrototype = new UserDetailsEntity(); + $this->userDetailsHydrator = new ClassMethodsCamelCase(); + } + + /** + * The user details join is dependent, and we add it on every user find + * + * @param string $type + * @param array $options + * @return array + */ + public function find(string $type = 'all', array $options = []): array + { + $this->insertUserDetailsJoin($options); + return parent::find($type, $options); + } + + /** + * We override the count select too, in order to add the join + * + * @param string $type + * @param array $options + * @return int + */ + public function count(string $type = 'all', array $options = []): int + { + $this->insertUserDetailsJoin($options); + return parent::count($type, $options); + } + + /** + * @param array $options + */ + protected function insertUserDetailsJoin(array &$options) + { + $options['joins'] = $options['joins'] ?? []; + // append a join condition to the options + // for user details every time we fetch users + $options['joins'] += [ + 'UserDetails' => [ + 'on' => 'UserDetails.userId = User.id', + 'table' => $this->userDetailsTable, + 'type' => Select::JOIN_LEFT + ] + ]; + } + + /** + * @param MapperEvent $e + */ + public function onAfterLoad(MapperEvent $e) + { + parent::onAfterLoad($e); + + /** @var UserEntity $entity */ + $user = $e->getParam('entity'); + + /** @var array $data */ + $data = $e->getParam('data'); + + $detailsData = array_filter($data['UserDetails']); + if (!empty($detailsData)) { + //load user details into user entity + $details = $this->userDetailsHydrator->hydrate( + array_filter($data['UserDetails']), + clone $this->userDetailsPrototype + ); + $user->setDetails($details); + } else { + $user->setDetails(new UserDetailsEntity()); + } + } + + /** + * @param MapperEvent $e + */ + public function onAfterSave(MapperEvent $e) + { + parent::onAfterSave($e); + + //we save user details too, as a dependent mapping + /** @var UserEntity $entity */ + $entity = $e->getParam('entity'); + /** @var UserDetailsEntity $details */ + $details = $entity->getDetails(); + $detailsData = $this->userDetailsHydrator->extract($details); + + $detailsColumns = $this->getDetailsColumns(); + $detailsData = array_intersect_key($detailsData, array_flip($detailsColumns)); + + if ($details->getUserId()) { + unset($detailsData['userId']); + // update details + $query = $this->getSql()->update($this->userDetailsTable) + ->set($detailsData) + ->where(['userId' => (int) $details->getUserId()]); + } else { + $details->setUserId($entity->getId()); + $detailsData['userId'] = $entity->getId(); + // insert + $query = $this->getSql()->insert($this->userDetailsTable) + ->columns(array_keys($detailsData)) + ->values($detailsData); + } + + $stmt = $this->getSql()->prepareStatementForSqlObject($query); + $result = $stmt->execute(); + if (!$result->valid()) { + throw new \RuntimeException('Could not save user details data'); + } + } + + /** + * @return array + */ + public function getDetailsColumns(): array + { + if (!$this->detailsColumns) { + /** @var ColumnObject[] $detailsColumns */ + $detailsColumns = $this->getMetadata()->getTable($this->userDetailsTable)->getColumns(); + $this->detailsColumns = []; + foreach ($detailsColumns as $column) { + $this->detailsColumns[] = $column->getName(); + } + } + + return $this->detailsColumns; + } +} diff --git a/src/User/Service/UserService.php b/src/User/Service/UserService.php new file mode 100644 index 0000000..4f32eca --- /dev/null +++ b/src/User/Service/UserService.php @@ -0,0 +1,77 @@ +needsPasswordRehash()) { + $entity->setPassword($this->passwordService->create($entity->getPassword())); + } + + $entity->needsPasswordRehash(true); + + return parent::save($entity); + } + + /** + * @return PasswordInterface + */ + public function getPasswordService() + { + return $this->passwordService; + } + + /** + * @param PasswordInterface $passwordService + * @return $this + * + * @Inject({PasswordInterface::class}) + */ + public function setPasswordService(PasswordInterface $passwordService) + { + $this->passwordService = $passwordService; + return $this; + } +} diff --git a/templates/app/admin/account.html.twig b/templates/app/admin/account.html.twig new file mode 100644 index 0000000..ee97f7c --- /dev/null +++ b/templates/app/admin/account.html.twig @@ -0,0 +1,101 @@ +{% extends '@layout/default.html.twig' %} + +{% block title %}Account{% endblock %} + +{% block content %} + {{ messagesPartial('partial::alerts') }} +
    +
    +
    +
    Update personal data
    +
    + {{ messagesPartial('partial::alerts', {}, null, 'account') }} + + {% set dummy = form.prepare() %} + {{ form().openTag(form) | raw }} + + {% set username = form.getBaseFieldset().get('username') %} +
    + {% set dummy = username.setAttribute('class', 'form-control') %} + {{ formLabel(username) }} + {{ formElement(username) }} +
    + + {% set email = form.getBaseFieldset().get('email') %} +
    + {% set dummy = email.setAttribute('class', 'form-control') %} + {{ formLabel(email) }} + {{ formElement(email) }} +
    + + {% set firstName = form.getBaseFieldset().get('firstName') %} +
    + {% set dummy = firstName.setAttribute('class', 'form-control') %} + {{ formLabel(firstName) }} + {{ formElement(firstName) }} +
    + + {% set lastName = form.getBaseFieldset().get('lastName') %} +
    + {% set dummy = lastName.setAttribute('class', 'form-control') %} + {{ formLabel(lastName) }} + {{ formElement(lastName) }} +
    + + {{ formElement(form.get('account_csrf')) }} + + {% set submit = form.get('submit') %} + {% set dummy = submit.setAttribute('class', 'btn btn-lg btn-primary btn-block') %} + {{ formSubmit(submit) }} + + {{ form().closeTag() | raw }} + +
    +
    +
    + +
    +
    +
    Change password
    +
    + {{ messagesPartial('partial::alerts', {}, null, 'change-password') }} + + {% set dummy = changePasswordForm.setAttribute('action', changePasswordAction) %} + {% set dummy = changePasswordForm.prepare() %} + {{ form().openTag(changePasswordForm) | raw }} + + {% set currentPassword = changePasswordForm.get('currentPassword') %} +
    + {% set dummy = currentPassword.setAttribute('class', 'form-control') %} + {{ formLabel(currentPassword) }} + {{ formElement(currentPassword) }} +
    + + {% set password = changePasswordForm.getBaseFieldset().get('password') %} +
    + {% set dummy = password.setAttribute('class', 'form-control') %} + {{ formLabel(password) }} + {{ formElement(password) }} +
    + + {% set passwordConfirm = changePasswordForm.getBaseFieldset().get('passwordConfirm') %} +
    + {% set dummy = passwordConfirm.setAttribute('class', 'form-control') %} + {{ formLabel(passwordConfirm) }} + {{ formElement(passwordConfirm) }} +
    + + {{ formElement(changePasswordForm.get('change_password_csrf')) }} + + {% set submit = changePasswordForm.get('submit') %} + {% set dummy = submit.setAttribute('class', 'btn btn-lg btn-primary btn-block') %} + {{ formSubmit(submit) }} + + {{ form().closeTag() | raw }} + +
    +
    +
    +
    + +{% endblock %} diff --git a/templates/entity-manage/admin-table.html.twig b/templates/app/admin/admin-table.html.twig similarity index 64% rename from templates/entity-manage/admin-table.html.twig rename to templates/app/admin/admin-table.html.twig index abc06be..c8741ab 100644 --- a/templates/entity-manage/admin-table.html.twig +++ b/templates/app/admin/admin-table.html.twig @@ -1,4 +1,4 @@ -{% extends '@entity-manage/bs-table.html.twig' %} +{% extends '@app/bs-table.html.twig' %} {% block title %}Manage admins{% endblock %} @@ -24,8 +24,26 @@ E-mail First Name Last Name - Role + Roles Status Created {% endblock %} + +{% block javascript %} + + {{ parent() }} + + +{% endblock %} diff --git a/templates/app/admin/login.html.twig b/templates/app/admin/login.html.twig new file mode 100644 index 0000000..9dd35db --- /dev/null +++ b/templates/app/admin/login.html.twig @@ -0,0 +1,21 @@ +{% extends '@layout/default.html.twig' %} + +{% block title %}Sign In{% endblock %} + +{% block content %} +
    +
    + + +
    +
    + +{% endblock %} diff --git a/templates/entity-manage/bs-table.html.twig b/templates/app/bs-table.html.twig similarity index 92% rename from templates/entity-manage/bs-table.html.twig rename to templates/app/bs-table.html.twig index 1e3fbf4..e8c0ba6 100644 --- a/templates/entity-manage/bs-table.html.twig +++ b/templates/app/bs-table.html.twig @@ -24,8 +24,9 @@ data-mobile-responsive="true" data-check-on-init="true" data-min-width="800" data-id-field="id" data-show-refresh="true" data-show-toggle="true" data-show-columns="true" data-search="true" data-pagination="true" data-toolbar="#tableToolbar" - data-side-pagination="server" data-page-list="[10, 30, 50, 100]" data-page-size="30" - data-sort-name="dateCreated" data-sort-order="desc" id="bsTable"> + data-silent-sort="false" data-search-on-enter-key="true" data-pagination-loop="false" + data-side-pagination="server" data-page-list="[10, 30, 50, 100, 200]" data-page-size="30" + data-sort-name="{{ defaultSortedColumn }}" data-sort-order="desc" id="bsTable"> {% block tableFields %}{% endblock %} @@ -121,7 +122,7 @@ $("#formPlaceholder").html(data); $("#loading").on('hidden.bs.modal', function () { - $("#loading").off('hidden') + $("#loading").off('hidden'); $("#formModal").modal('show'); }); @@ -129,7 +130,7 @@ }) .fail(function (data) { $("#loading").on('hidden.bs.modal', function () { - $("#loading").off('hidden') + $("#loading").off('hidden'); showFailDialog(data); }); hideLoading(); @@ -152,14 +153,14 @@ $("#formPlaceholder").html(data); $("#loading").on('hidden.bs.modal', function () { - $("#loading").off('hidden') + $("#loading").off('hidden'); $("#formModal").modal('show'); }); hideLoading(); }) .fail(function (data) { $("#loading").on('hidden.bs.modal', function () { - $("#loading").off('hidden') + $("#loading").off('hidden'); showFailDialog(data); }); hideLoading(); @@ -181,14 +182,14 @@ $("#formPlaceholder").html(data); $("#loading").on('hidden.bs.modal', function () { - $("#loading").off('hidden') + $("#loading").off('hidden'); $("#formModal").modal('show'); }); hideLoading(); }) .fail(function (data) { $("#loading").on('hidden.bs.modal', function () { - $("#loading").off('hidden') + $("#loading").off('hidden'); showFailDialog(data); }); hideLoading(); @@ -204,7 +205,12 @@ .closest('.form-group').addClass('has-error'); } else if ($.isPlainObject(value)) { - formInputErrors(value, prevName + "[" + name + "]"); + if (prevName == '') { + formInputErrors(value, name); + } else { + formInputErrors(value, prevName + "[" + name + "]"); + } + } }); }; @@ -230,7 +236,7 @@ else if (typeof data.validation !== 'undefined') { hideLoading(); $("#formMessages").html(data.alerts); - formInputErrors(data.validation.{{ entityNameSingular }}, "{{ entityNameSingular }}"); + formInputErrors(data.validation, ""); $('#formModal').modal('handleUpdate'); } else { diff --git a/templates/app/dashboard.html.twig b/templates/app/dashboard.html.twig index ca1d6ce..5572300 100644 --- a/templates/app/dashboard.html.twig +++ b/templates/app/dashboard.html.twig @@ -9,6 +9,8 @@ + {{ messagesPartial('partial::alerts', {dismissible: true}) }} +
    diff --git a/templates/app/login.html.twig b/templates/app/login.html.twig deleted file mode 100644 index 25c00b2..0000000 --- a/templates/app/login.html.twig +++ /dev/null @@ -1,36 +0,0 @@ -{% extends '@layout/default.html.twig' %} - -{% if showLabels is not defined %} - {% set showLabels = false %} -{% endif %} - -{% block title %}Sign In{% endblock %} - -{% block content %} -
    -
    - - -
    -
    - -{% endblock %} diff --git a/templates/entity-manage/user-table.html.twig b/templates/app/user/user-table.html.twig similarity index 64% rename from templates/entity-manage/user-table.html.twig rename to templates/app/user/user-table.html.twig index c1fd9f1..b63967f 100644 --- a/templates/entity-manage/user-table.html.twig +++ b/templates/app/user/user-table.html.twig @@ -1,4 +1,4 @@ -{% extends '@entity-manage/bs-table.html.twig' %} +{% extends '@app/bs-table.html.twig' %} {% block title %}Manage users{% endblock %} @@ -22,9 +22,9 @@ #ID Username E-mail - First name - Last name - Role + First name + Last name + Roles Status Created @@ -37,13 +37,24 @@ diff --git a/templates/layout/default.html.twig b/templates/layout/default.html.twig index 59af0b6..dfc94ad 100644 --- a/templates/layout/default.html.twig +++ b/templates/layout/default.html.twig @@ -10,6 +10,7 @@ + @@ -33,7 +34,7 @@ -  DotKernelAdmin +  DotKernelAdmin
    - {{ formElement(form.get('delete_confirm_csrf')) }} + {{ formElement(form.get('confirm_delete_csrf')) }}
    {{ form().closeTag() | raw }} diff --git a/templates/partial/form-display-validationgroup.html.twig b/templates/partial/form-display-validationgroup.html.twig new file mode 100644 index 0000000..13499d8 --- /dev/null +++ b/templates/partial/form-display-validationgroup.html.twig @@ -0,0 +1,79 @@ +{% if showLabels is not defined %} + {% set showLabels = false %} +{% endif %} + +{% if validationGroup is not defined %} + {% set validationGroup = form.getValidationGroup() %} +{% endif %} + +{% for k,v in validationGroup %} + + {% if form.has(k) %} + + {% include '@partial/form-display-validationgroup.html.twig' with { + 'form' : form.get(k), + 'validationGroup': validationGroup[k], + 'showLabels' : showLabels} only + %} + + {% else %} + + {% if form.has(v) %} + + {% set element = form.get(v) %} + {% if element is Captcha %} + +
    + + {% set dummy = element.setAttribute('class', 'form-control') %} + + {{ formLabel(element) }} + {{ formCaptcha(element) }} + +
    + + {% elseif element is Checkbox %} + +
    + +
    + + {% elseif element is Hidden %} + + {{ formElement(element) }} + + {% elseif element is Button and element.getName() != 'submit' %} + + {% set dummy = element.setAttribute('class', 'btn btn-block') %} + {{ formButton(element) }} + + {% elseif element is Submit or element is Button and element.getName() == 'submit' %} + + {% set dummy = element.setAttribute('class', 'btn btn-lg btn-primary btn-block') %} + {{ formSubmit(element) }} + + {% else %} + +
    + + {% set dummy = element.setAttribute('class', 'form-control') %} + + {% if showLabels and element.getLabel() is defined and element.getLabel() is not empty %} + + {{ formLabel(element) }} + + {% endif %} + + {{ formElement(element) }} + +
    + + {% endif %} + + {% endif %} + + {% endif %} + +{% endfor %} \ No newline at end of file diff --git a/templates/partial/form-display.html.twig b/templates/partial/form-display.html.twig new file mode 100644 index 0000000..10fc131 --- /dev/null +++ b/templates/partial/form-display.html.twig @@ -0,0 +1,62 @@ +{% if showLabels is not defined %} + {% set showLabels = false %} +{% endif %} + +{% for element in form %} + + {% if element is Captcha %} + +
    + + {% set dummy = element.setAttribute('class', 'form-control') %} + + {{ formLabel(element) }} + {{ formCaptcha(element) }} + +
    + + {% elseif element is Checkbox %} + +
    + +
    + + {% elseif element is Hidden %} + + {{ formElement(element) }} + + {% elseif element is Button and element.getName() != 'submit' %} + + {% set dummy = element.setAttribute('class', 'btn btn-block') %} + {{ formButton(element) }} + + {% elseif element is Submit or element is Button and element.getName() == 'submit' %} + + {% set dummy = element.setAttribute('class', 'btn btn-lg btn-primary btn-block') %} + {{ formSubmit(element) }} + + {% elseif element is Fieldset %} + + {% include '@partial/form-display.html.twig' with {'form' : element, 'showLabels' : showLabels} only %} + + {% else %} + +
    + + {% set dummy = element.setAttribute('class', 'form-control') %} + + {% if showLabels and element.getLabel() is defined and element.getLabel() is not empty %} + + {{ formLabel(element) }} + + {% endif %} + + {{ formElement(element) }} + +
    + + {% endif %} + +{% endfor %} diff --git a/templates/partial/menu.html.twig b/templates/partial/menu.html.twig index 642f7e2..7b1ef2c 100644 --- a/templates/partial/menu.html.twig +++ b/templates/partial/menu.html.twig @@ -18,7 +18,7 @@ {% else %}
  • - + {% if page.hasChildren() and depth == 0 %}