├── .gitignore
├── .php_cs
├── .travis.yml
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── Vagrantfile
├── bin
├── .gitignore
├── clear-cache
├── include
│ └── functions.php
├── init
└── requirements
├── codeception.yml
├── composer.json
├── config
├── .gitignore
├── application-test.config.php
├── application.config.php
├── autoload
│ ├── .gitignore
│ └── global.php
├── constants.php
└── modules.config.php
├── data
├── images
│ └── logo.png
├── migrations
│ └── .gitkeep
└── runtime
│ └── .gitignore
├── doc
└── screenshot.png
├── env
├── config.php
└── dev
│ ├── bin
│ ├── console
│ └── console-test
│ ├── config
│ └── autoload
│ │ └── local.php
│ └── public
│ ├── index-test.php
│ └── index.php
├── module
├── Cli
│ ├── config
│ │ └── module.config.php
│ └── src
│ │ ├── Manager
│ │ ├── CommandManager.php
│ │ ├── CommandManagerFactory.php
│ │ └── Provider
│ │ │ └── CommandManagerProviderInterface.php
│ │ ├── Module.php
│ │ └── Options
│ │ ├── ModuleOptions.php
│ │ └── ModuleOptionsFactory.php
├── Core
│ ├── assets
│ │ ├── css
│ │ │ ├── 404.sass
│ │ │ ├── base.sass
│ │ │ └── style.sass
│ │ ├── img
│ │ │ ├── 404.png
│ │ │ └── logo.png
│ │ └── js
│ │ │ └── navbar.js
│ ├── config
│ │ ├── assets.config.php
│ │ ├── doctrine.config.php
│ │ ├── module.config.php
│ │ ├── navigation.config.php
│ │ ├── rbac.config.php
│ │ ├── router.config.php
│ │ └── twig.config.php
│ ├── language
│ │ ├── en.php
│ │ └── ru.php
│ ├── logs
│ │ └── .gitignore
│ ├── src
│ │ ├── Controller
│ │ │ ├── ActionControllerAbstract.php
│ │ │ └── Plugin
│ │ │ │ ├── TranslatePlugin.php
│ │ │ │ └── TranslatePluginFactory.php
│ │ ├── Module.php
│ │ └── View
│ │ │ └── Helper
│ │ │ ├── AlertMessageHelper.php
│ │ │ ├── LocaleHelper.php
│ │ │ └── LocaleHelperFactory.php
│ └── view
│ │ ├── error
│ │ ├── 404.twig
│ │ └── index.twig
│ │ ├── layout
│ │ ├── breadcrumbs.twig
│ │ ├── layout.twig
│ │ ├── navigation.twig
│ │ └── paginator.twig
│ │ └── macro
│ │ ├── component.twig
│ │ ├── form.twig
│ │ ├── icon.twig
│ │ └── table.twig
├── DataProvider
│ ├── config
│ │ └── module.config.php
│ └── src
│ │ ├── DataProviderInterface.php
│ │ ├── Module.php
│ │ └── QueryDataProvider.php
├── ExAssetic
│ ├── config
│ │ ├── cli.config.php
│ │ ├── module.config.php
│ │ └── rbac.config.php
│ └── src
│ │ ├── Cache
│ │ └── CacheBusterFactory.php
│ │ ├── Command
│ │ ├── BuildCommandFactory.php
│ │ └── SetupCommandFactory.php
│ │ ├── Filter
│ │ ├── Sass
│ │ │ └── SassFilterFactory.php
│ │ ├── UglifyJs2FilterFactory.php
│ │ └── Yui
│ │ │ └── CssCompressorFilterFactory.php
│ │ ├── Module.php
│ │ └── Options
│ │ ├── ModuleOptions.php
│ │ └── ModuleOptionsFactory.php
├── ExCodeception
│ └── src
│ │ ├── BaseFixture.php
│ │ ├── Connector
│ │ └── ZF3.php
│ │ └── Module
│ │ ├── Fixture.php
│ │ └── ZF3.php
├── ExDebugBar
│ ├── assets
│ │ └── .gitkeep
│ ├── config
│ │ ├── assets.config.php
│ │ └── module.config.php
│ └── src
│ │ └── Module.php
├── ExDoctrine
│ ├── config
│ │ ├── module.config.php
│ │ └── rbac.config.php
│ └── src
│ │ ├── Hydrator
│ │ └── ObjectHydratorFactory.php
│ │ ├── Module.php
│ │ ├── Repository
│ │ └── RepositoryInvokableFactory.php
│ │ └── Type
│ │ └── DateTimeType.php
├── ExPaginator
│ └── src
│ │ └── Adapter
│ │ └── DoctrineAdapter.php
├── ExRbac
│ └── src
│ │ └── Module.php
├── ExTwig
│ ├── config
│ │ └── module.config.php
│ └── src
│ │ ├── Extension
│ │ └── ClassExtension.php
│ │ └── Module.php
├── ExValidate
│ ├── config
│ │ └── module.config.php
│ ├── language
│ │ ├── en.php
│ │ └── ru.php
│ └── src
│ │ ├── Delegator
│ │ └── TranslatorDelegatorFactory.php
│ │ └── Module.php
├── Mail
│ ├── config
│ │ └── module.config.php
│ └── src
│ │ ├── Hydrator
│ │ └── ModelHydrator.php
│ │ ├── Message
│ │ ├── MessageBuilder.php
│ │ └── MessageBuilderFactory.php
│ │ ├── Module.php
│ │ ├── Options
│ │ ├── ModuleOptions.php
│ │ └── ModuleOptionsFactory.php
│ │ └── Service
│ │ ├── MailService.php
│ │ └── MailServiceFactory.php
└── User
│ ├── codeception.yml
│ ├── config
│ ├── doctrine.config.php
│ ├── mapping
│ │ └── User.Entity.User.dcm.xml
│ ├── module.config.php
│ ├── navigation.config.php
│ ├── rbac.config.php
│ └── router.config.php
│ ├── language
│ ├── en.php
│ └── ru.php
│ ├── src
│ ├── Adapter
│ │ ├── AuthAdapter.php
│ │ └── AuthAdapterFactory.php
│ ├── Auth
│ │ └── Result.php
│ ├── Controller
│ │ ├── AccessController.php
│ │ ├── AccessControllerFactory.php
│ │ ├── AuthController.php
│ │ ├── AuthControllerFactory.php
│ │ ├── ConfirmEmailController.php
│ │ ├── ConfirmEmailControllerFactory.php
│ │ ├── IndexController.php
│ │ ├── SignupController.php
│ │ └── SignupControllerFactory.php
│ ├── Entity
│ │ └── User.php
│ ├── Form
│ │ ├── AgainConfirmForm.php
│ │ ├── ForgotPassForm.php
│ │ ├── RestorePassForm.php
│ │ ├── SignInForm.php
│ │ ├── SignUpForm.php
│ │ ├── SignUpFormFactory.php
│ │ └── UserSearchForm.php
│ ├── Module.php
│ ├── Repository
│ │ └── UserRepository.php
│ ├── Search
│ │ └── UserSearch.php
│ └── Service
│ │ ├── AccessService.php
│ │ ├── AccessServiceFactory.php
│ │ ├── AuthServiceFactory.php
│ │ ├── ConfirmEmailService.php
│ │ ├── SignUpService.php
│ │ └── SignUpServiceFactory.php
│ ├── test
│ ├── _bootstrap.php
│ ├── _output
│ │ └── .gitignore
│ ├── _support
│ │ ├── FunctionalTester.php
│ │ ├── Helper
│ │ │ ├── Functional.php
│ │ │ └── Unit.php
│ │ ├── UnitTester.php
│ │ └── _generated
│ │ │ └── .gitignore
│ ├── functional.suite.yml
│ ├── functional
│ │ ├── SignInCest.php
│ │ └── _bootstrap.php
│ ├── unit.suite.yml
│ └── unit
│ │ └── _bootstrap.php
│ └── view
│ ├── mail
│ ├── confirm-request.twig
│ └── restore-pass.twig
│ └── user
│ ├── access
│ ├── forgot-pass.twig
│ └── restore-pass.twig
│ ├── auth
│ └── signin.twig
│ ├── confirm-email
│ └── again.twig
│ ├── index
│ ├── index.twig
│ └── row.twig
│ └── signup
│ └── signup.twig
├── package.json
├── public
├── .gitignore
├── assets
│ └── .gitignore
└── favicon.ico
├── test
├── _data
│ └── user.php
└── _fixture
│ └── UserFixture.php
├── travis
├── before-install.sh
└── before-script.sh
└── workenv
├── config
├── .gitignore
└── vagrant-local.yml.dist
├── nginx
├── log
│ └── .gitignore
└── site.conf
└── provision
├── always-as-root.sh
├── once-as-root.sh
└── once-as-vagrant.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.swp
3 | /vendor
4 | /.vagrant
5 | /nbproject
6 | /*.sublime-project
7 | /*.sublime-workspace
8 | /.idea
9 | /.php_cs.cache
10 | /_codeceptOutput
11 | /yarn-error.log
12 | /node_modules
13 |
14 | # delete before to create own project
15 | /yarn.lock
16 | /composer.lock
17 |
--------------------------------------------------------------------------------
/.php_cs:
--------------------------------------------------------------------------------
1 | in(__DIR__ . '/bin')
8 | ->in(__DIR__ . '/config')
9 | ->in(__DIR__ . '/env')
10 | ->in(__DIR__ . '/module')
11 | ->in(__DIR__ . '/public');
12 | return Config::create()->setRules([
13 | '@PSR2' => true,
14 | 'array_syntax' => [
15 | 'syntax' => 'short',
16 | ],
17 | ])
18 | ->setFinder($finder);
19 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 | php:
3 | - '7.1'
4 | - '7.2'
5 |
6 | dist: trusty
7 |
8 | services:
9 | - postgresql
10 |
11 | addons:
12 | postgresql: "9.6"
13 |
14 | before_install: sudo bash ./travis/before-install.sh
15 |
16 | before_script: bash ./travis/before-script.sh
17 |
18 | script:
19 | # run tests
20 | - composer test:build
21 | - composer test:run-with-cov
22 |
23 | after_script:
24 | - vendor/bin/php-coveralls -v --coverage_clover=_codeceptOutput/coverage.xml --json_path=data/runtime/coveralls-upload.json
25 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | zf-app-blank
2 | ============
3 |
4 | 1.6.5
5 | -----
6 |
7 | - Enh: Disabled generate proxies for not debug mode.
8 |
9 | 1.6.4 [2018-01-14]
10 | ------------------
11 |
12 | - Add: Integration with Travis CI.
13 | - Add: Integration with Coveralls.
14 |
15 | 1.6.3 [2018-01-13]
16 | ------------------
17 |
18 | - Fix: Link in README.
19 | - Enh: Upgrade `widmogrod/zf2-assetic-module` to 2.4.
20 | - Enh: Small refactoring of `ExAssetic`.
21 | - Rem: Mailgun config from the global config file.
22 |
23 | 1.6.2 [2018-01-12]
24 | ------------------
25 |
26 | - Enh: Add CLI and Web configuration of XDebug.
27 | - Fix: Small fixes.
28 |
29 | 1.6.1 [2018-01-11]
30 | ------------------
31 |
32 | - Fix: Requirements checker.
33 | - Fix: Email templates.
34 | - Fix: User demo list.
35 |
36 | 1.6.0 [2018-01-11]
37 | ------------------
38 |
39 | - Fix: Symlinks.
40 | - Rem: Less.
41 | - Add: Sass.
42 | - Rem: Bootstrap 3.
43 | - Add: Bootstrap 4.
44 | - Enh: 2 intent instead of 4 for Twig, HTML, CSS, JS files.
45 | - Enh: Refactoring.
46 |
47 |
48 | 1.5.1 [2018-01-10]
49 | ------------------
50 |
51 | - Enh: Upgrade packages.
52 | - Enh: Refactoring composer.json.
53 | - Rem: Assets packagist repository.
54 | - Add: Yarn package manager.
55 | - Add: `Bupy7\Zf\TimeZone` module.
56 | - Enh: Replaced `Application` name module to `Core`.
57 | - Enh: Small refactoring.
58 | - Add: List users.
59 |
60 | 1.5.0 [2017-09-13]
61 | ------------------
62 |
63 | - Enh: Rename Composer command `fix` to `cs:fix`.
64 | - Add: Composer command `cs:check`.
65 | - Add: Composer command `test:run-with-cov`.
66 | - Fix: Package versions.
67 | - Add: #15.
68 | - Fix: #14.
69 | - Fix: #13.
70 | - Enh: #12.
71 | - Fix: #10.
72 | - Add: Authentication user on test.
73 | - Add: Autoload fixtures on each test.
74 | - Add: Signin, signup and login action tests.
75 |
76 | 1.4.0-beta.1 [2017-08-15]
77 | -------------------------
78 |
79 | - Enh: Moved tests to each module.
80 | - Fix (#8): Doctrine proxy config.
81 | - Add (#9): CLI.
82 | - Fix: Small bugs.
83 | - Rem: DI.
84 | - Rem: Local constants.
85 | - Enh: Small changes.
86 | - Add: Clear chache Bash command.
87 | - Add: Error logs to `module/Application/logs`.
88 |
89 | 1.3.0 [2017-07-23]
90 | ------------------
91 |
92 | - Fix (#4): Revert local constants.
93 | - Rem: JS framework integration.
94 | - Fix: Small bugs.
95 |
96 | 1.2.0 [2017-06-16]
97 | -----------------
98 | - Add: Mailgun.
99 | - Add: Confirm email action.
100 | - Add: Recovery password action.
101 | - Add: Asset packagist.
102 | - Add: Codeception.
103 | - Add: XDebug.
104 | - Add: DI.
105 | - Add: Markdown in flash messages.
106 | - Enh: Composer packages.
107 | - Enh: PHP to 7.1.
108 | - Enh: Vagrant configuration.
109 | - Enh: Form.
110 | - Rep: Backbone validation package.
111 | - Rem: Ajax Email validation.
112 | - Rem: jQuery dependency.
113 | - Rem: Bower.
114 |
115 | 1.1.2 [2017-05-26]
116 | ------------------
117 |
118 | - Fix: Provision.
119 |
120 | 1.1.1 [2017-03-30]
121 | ------------------
122 |
123 | - Enh #3: Update the requirements script.
124 |
125 | 1.1.0 [2017-03-29]
126 | ------------------
127 |
128 | - Fix: Added PHPCSFixer package to composer.json.
129 | - Enh: Rewrite Vagrant configuration.
130 | - New: Added MySQL.
131 |
132 | 1.0.0 [2016-11-15]
133 | ------------
134 |
135 | - First release.
136 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016, Vasilij Belosludcev
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
5 | following conditions are met:
6 |
7 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
8 | disclaimer.
9 |
10 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
11 | disclaimer in the documentation and/or other materials provided with the distribution.
12 |
13 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products
14 | derived from this software without specific prior written permission.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
17 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
22 | USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | zf-app-blank
2 | ============
3 |
4 | [](https://packagist.org/packages/bupy7/zf-app-blank)
5 | [](https://packagist.org/packages/bupy7/zf-app-blank)
6 | [](https://packagist.org/packages/bupy7/zf-app-blank)
7 | [](https://travis-ci.org/bupy7/zf-app-blank)
8 | [](https://coveralls.io/github/bupy7/zf-app-blank?branch=master)
9 |
10 | Autoloading standart [PSR-4](http://www.php-fig.org/psr/psr-4/). Coding standart [PSR-2](http://www.php-fig.org/psr/psr-2/).
11 |
12 | 
13 |
14 | TODO
15 | ----
16 |
17 | - Refactoring code to PHP 7.1.
18 | - Add PHP tests.
19 |
20 | Features
21 | --------
22 |
23 | - PHP 7.1
24 | - [Zend Framework 3](https://github.com/zendframework/zendframework)
25 | - [Twitter Bootstrap 4](http://getbootstrap.com/)
26 | - [Doctrine ORM 2](http://www.doctrine-project.org/)
27 | - [Debug Bar](https://github.com/bupy7/zf-php-debug-bar)
28 | - [Twig](http://twig.sensiolabs.org/)
29 | - [Assetic Management](https://github.com/kriswallsmith/assetic)
30 | - [RBAC](https://github.com/ZF-Commons/zfc-rbac)
31 | - [Flexible Form Builder](https://github.com/bupy7/zf-form)
32 | - [Support Vagrant](https://www.vagrantup.com/)
33 | - [Support Composer](https://getcomposer.org/)
34 | - [Support Yarn](https://yarnpkg.com/)
35 | - [Database is PostgreSQL](https://www.postgresql.org/)
36 | - [Database is MySQL](https://www.mysql.com/)
37 | - [YUI Comressor](https://github.com/yui/yuicompressor)
38 | - [UglifyJS2](https://github.com/mishoo/UglifyJS2)
39 | - [PHP Coding Standarts Fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer)
40 | - [XDebug](https://xdebug.org/)
41 | - [Mailgun](https://www.mailgun.com/)
42 | - Multilanguage (English and Russian).
43 | - Simple example application:
44 | - Sign in
45 | - Sign up
46 | - Log out
47 | - Confirm Email address
48 | - Recovery password
49 | - [Symfony CLI](https://github.com/symfony/console)
50 | - [Codeception](http://codeception.com/)
51 | - [Sass](http://sass-lang.com/)
52 |
53 | Installation
54 | ------------
55 |
56 | - Download and unpack the repository.
57 |
58 | - [Install Vagrant](https://www.vagrantup.com/docs/installation/)
59 |
60 | - Install plugins for Vagrant:
61 |
62 | ```
63 | $ vagrant plugin install vagrant-vbguest
64 | $ vagrant plugin install vagrant-hostmanager
65 | ```
66 |
67 | - Run install the work environment:
68 |
69 | ```
70 | $ vagrant up
71 | ```
72 |
73 | - Paste GitHub token in `/workenv/config/vagrant-local.yml`
74 |
75 | - Run again:
76 |
77 | ```
78 | $ vagrant up
79 | ```
80 |
81 | - Configure Mailgun in `/config/autoload/local.php`:
82 |
83 | You should [create Mailgun account](https://www.mailgun.com/) if you didn't do it before.
84 | Also, create [Postbin](http://bin.mailgun.net/).
85 |
86 | ```php
87 | 'mailgun' => [
88 | 'key' => 'key-somekey',
89 | 'endpoint' => 'http://bin.mailgun.net/somekey',
90 | ],
91 | 'mail' => [
92 | 'domain' => 'somesudomain.mailgun.org',
93 | ],
94 | ```
95 |
96 | - Create scheme:
97 |
98 | ```
99 | $ vagrant ssh -c 'php bin/console orm:schema-tool:create'
100 | ```
101 |
102 | - Done.
103 |
104 | Testing
105 | -------
106 |
107 | Run tests:
108 |
109 | ```
110 | $ vagrant ssh -c 'composer test:build'
111 | $ vagrant ssh -c 'composer test:run'
112 | ```
113 |
114 | License
115 | -------
116 |
117 | zf-app-blank is released under the BSD 3-Clause License.
118 |
--------------------------------------------------------------------------------
/Vagrantfile:
--------------------------------------------------------------------------------
1 | # Required plugins
2 | # -----------------
3 | # vagrant plugin install vagrant-vbguest
4 | # vagrant plugin install vagrant-hostmanager
5 |
6 | require 'yaml'
7 | require 'fileutils'
8 |
9 | domains = {
10 | main: 'zf-app-blank.local',
11 | }
12 |
13 | config = {
14 | local: './workenv/config/vagrant-local.yml',
15 | dist: './workenv/config/vagrant-local.yml.dist'
16 | }
17 |
18 | # copy config from example if local config not exists
19 | FileUtils.cp config[:dist], config[:local] unless File.exist?(config[:local])
20 | # read config
21 | options = YAML.load_file config[:local]
22 |
23 | # check github token
24 | if options['github_token'].nil? || options['github_token'].to_s.length != 40
25 | puts "You must place REAL GitHub token into configuration:\n/workenv/config/vagrant-local.yml"
26 | exit
27 | end
28 |
29 | # vagrant configurate
30 | Vagrant.configure(2) do |config|
31 | # select the box
32 | config.vm.box = 'debian/jessie64'
33 |
34 | # should we ask about box updates?
35 | config.vm.box_check_update = options['box_check_update']
36 |
37 | config.vm.provider 'virtualbox' do |vb|
38 | # machine cpus count
39 | vb.cpus = options['server_cpus']
40 | # machine memory size
41 | vb.memory = options['server_memory']
42 | # machine name (for VirtualBox UI)
43 | vb.name = options['server_name']
44 | end
45 |
46 | # machine name (for vagrant console)
47 | config.vm.define options['server_name']
48 |
49 | # machine name (for guest machine console)
50 | config.vm.hostname = options['server_name']
51 |
52 | # network settings
53 | config.vm.network 'private_network', ip: options['ip']
54 |
55 | # sync: folder of project (host machine) -> folder '/vagrant' (guest machine)
56 | config.vm.synced_folder ".", "/vagrant", type: "nfs"
57 |
58 | # hosts settings (host machine)
59 | config.vm.provision :hostmanager
60 | config.hostmanager.enabled = true
61 | config.hostmanager.manage_host = true
62 | config.hostmanager.ignore_private_ip = false
63 | config.hostmanager.include_offline = true
64 | config.hostmanager.aliases = domains.values
65 |
66 | # provisioners
67 | config.vm.provision 'shell' do |s|
68 | s.path = './workenv/provision/once-as-root.sh'
69 | s.args = [
70 | options['server_time_zone'],
71 | options['mysql_db'],
72 | options['mysql_user'],
73 | options['mysql_pass'],
74 | options['php_time_zone'],
75 | options['php_memory_limit'],
76 | options['php_execution_time'],
77 | options['php_input_time'],
78 | options['server_locale'],
79 | options['pgsql_db'],
80 | options['pgsql_user'],
81 | options['pgsql_pass'],
82 | options['pgsql_locale'],
83 | options['db_type'],
84 | options['xdebug_idekey'],
85 | options['ip']
86 | ]
87 | end
88 | config.vm.provision 'shell' do |s|
89 | s.path = './workenv/provision/once-as-vagrant.sh'
90 | s.args = [options['github_token']]
91 | s.privileged = false
92 | end
93 | config.vm.provision 'shell', path: './workenv/provision/always-as-root.sh',
94 | run: 'always', args: [options['db_type']]
95 |
96 | # post-install message (vagrant console)
97 | config.vm.post_up_message = "Main URL: http://#{domains[:main]}"
98 | end
99 |
--------------------------------------------------------------------------------
/bin/.gitignore:
--------------------------------------------------------------------------------
1 | /console*
2 |
--------------------------------------------------------------------------------
/bin/clear-cache:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ROOT_PATH=$(cd $(dirname $0) && pwd);
4 |
5 | if [ -f $ROOT_PATH/../data/runtime/module-classmap-cache.module_map.php ]; then
6 | echo '>>> Removing "module-classmap-cache.module_map.php"'
7 | rm $ROOT_PATH/../data/runtime/module-classmap-cache.module_map.php
8 | fi
9 | if [ -f $ROOT_PATH/../data/runtime/module-config-cache.app_config.php ]; then
10 | echo '>>> Removing "module-config-cache.app_config.php"'
11 | rm $ROOT_PATH/../data/runtime/module-config-cache.app_config.php
12 | fi
13 | if [ -d $ROOT_PATH/../data/runtime/DoctrineORMModule ]; then
14 | echo '>>> Removing "DoctrineORMModule"'
15 | rm -rf $ROOT_PATH/../data/runtime/DoctrineORMModule
16 | fi
17 | echo 'OK'
18 |
--------------------------------------------------------------------------------
/bin/include/functions.php:
--------------------------------------------------------------------------------
1 | 31,
16 | 'green' => 32,
17 | 'yellow' => 33,
18 | 'blue' => 34,
19 | 'magenta' => 35,
20 | 'cyan' => 36,
21 | 'white' => 37,
22 | ];
23 | return $styles[$name];
24 | }
25 |
--------------------------------------------------------------------------------
/bin/requirements:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | 'php-bcmath',
15 | 'condition' => extension_loaded('bcmath'),
16 | ],
17 | [
18 | 'name' => 'php-curl',
19 | 'condition' => extension_loaded('curl'),
20 | ],
21 | [
22 | 'name' => 'php-intl',
23 | 'condition' => extension_loaded('intl'),
24 | ],
25 | [
26 | 'name' => 'php-json',
27 | 'condition' => extension_loaded('json'),
28 | ],
29 | [
30 | 'name' => 'php-mbstring',
31 | 'condition' => extension_loaded('mbstring'),
32 | ],
33 | [
34 | 'name' => 'php-pdo-(pgsql|mysql)',
35 | 'condition' => extension_loaded('pdo_pgsql') || extension_loaded('pdo_mysql'),
36 | ],
37 | [
38 | 'name' => 'php-mcrypt',
39 | 'condition' => extension_loaded('mcrypt'),
40 | ],
41 | [
42 | 'name' => 'php-bz2',
43 | 'condition' => extension_loaded('bz2'),
44 | ],
45 | [
46 | 'name' => 'php-zip',
47 | 'condition' => extension_loaded('zip'),
48 | ],
49 | [
50 | 'name' => 'php-xml',
51 | 'condition' => extension_loaded('xml'),
52 | ],
53 | [
54 | 'name' => 'sass',
55 | 'condition' => shellCommandExists('sass'),
56 | ],
57 | [
58 | 'name' => 'yui-compressor',
59 | 'condition' => shellCommandExists('yuicompressor'),
60 | ],
61 | [
62 | 'name' => 'uglify-js2',
63 | 'condition' => shellCommandExists('uglifyjs'),
64 | ],
65 | [
66 | 'name' => 'java',
67 | 'condition' => shellCommandExists('java'),
68 | ],
69 | [
70 | 'name' => 'ruby',
71 | 'condition' => shellCommandExists('ruby'),
72 | ],
73 | ];
74 |
75 | $errors = 0;
76 | foreach ($checkList as $checkItem) {
77 | $errors += !$checkItem['condition'];
78 | $result = $checkItem['condition'] ? colorStr('OK', 'green') : colorStr('FAIL', 'red');
79 | echo sprintf("%s: %s\n", $result, $checkItem['name']);
80 | }
81 | echo "---\n";
82 | if ($errors) {
83 | echo colorStr(sprintf("%d Error(s)\n", $errors), 'red');
84 | } else {
85 | echo colorStr("OK\n", 'green');
86 | }
87 |
88 | function shellCommandExists($cmd)
89 | {
90 | $exec = shell_exec(sprintf('which %s', $cmd));
91 | return !empty($exec);
92 | }
93 |
--------------------------------------------------------------------------------
/codeception.yml:
--------------------------------------------------------------------------------
1 | include:
2 | - module/User
3 | paths:
4 | output: _codeceptOutput
5 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bupy7/zf-app-blank",
3 | "description": "A blank application Zend Framework 3.",
4 | "version": "1.6.4",
5 | "type": "project",
6 | "license": "BSD-3-Clause",
7 | "keywoards": [
8 | "zend",
9 | "zf",
10 | "zf3",
11 | "framework",
12 | "skeleton",
13 | "blank",
14 | "application",
15 | "app"
16 | ],
17 | "authors": [
18 | {
19 | "name": "Vasily Belosludcev",
20 | "email": "bupy765@gmail.com"
21 | }
22 | ],
23 | "require": {
24 | "php": "^7.1",
25 | "zendframework/zend-mvc": "~3.1.0",
26 | "zendframework/zend-mvc-i18n": "~1.0.0",
27 | "zendframework/zend-navigation": "~2.8.2",
28 | "zendframework/zend-validator": "~2.9.1",
29 | "zendframework/zend-crypt": "~3.2.0",
30 | "zendframework/zend-authentication": "~2.5.3",
31 | "zendframework/zend-mvc-plugin-flashmessenger": "~1.0.0",
32 | "zendframework/zend-mvc-plugin-identity": "~1.0.0",
33 | "zendframework/zend-mvc-plugin-prg": "~1.0.0",
34 | "zendframework/zend-mvc-plugin-fileprg": "~1.0.0",
35 | "zendframework/zend-json": "~3.0.0",
36 | "widmogrod/zf2-assetic-module": "~2.4",
37 | "zf-commons/zfc-rbac": "~2.6.3",
38 | "doctrine/doctrine-orm-module": "~1.1.0",
39 | "bupy7/zf-mailgun": "~1.0.0",
40 | "cebe/markdown": "~1.1.1",
41 | "bupy7/zf-form": "~1.2.0",
42 | "kokspflanze/zfc-twig": "~2.1",
43 | "symfony/console": "~3.3",
44 | "zendframework/zend-log": "~2.9",
45 | "doctrine/migrations": "~1.6",
46 | "bupy7/zf-time-zone": "~1.0"
47 | },
48 | "require-dev": {
49 | "friendsofphp/php-cs-fixer": "~2.6",
50 | "codeception/codeception": "~2.3",
51 | "doctrine/data-fixtures": "~1.3",
52 | "bupy7/zf-php-debug-bar": "~0.13",
53 | "php-coveralls/php-coveralls": "~2.0"
54 | },
55 | "autoload": {
56 | "psr-4": {
57 | "Core\\": "module/Core/src",
58 | "ExAssetic\\": "module/ExAssetic/src",
59 | "ExDebugBar\\": "module/ExDebugBar/src",
60 | "ExDoctrine\\": "module/ExDoctrine/src",
61 | "ExRbac\\": "module/ExRbac/src",
62 | "ExTwig\\": "module/ExTwig/src",
63 | "ExValidate\\": "module/ExValidate/src",
64 | "ExCodeception\\": "module/ExCodeception/src",
65 | "User\\": "module/User/src",
66 | "Mail\\": "module/Mail/src",
67 | "Cli\\": "module/Cli/src",
68 | "DataProvider\\": "module/DataProvider/src",
69 | "ExPaginator\\": "module/ExPaginator/src"
70 | }
71 | },
72 | "autoload-dev": {
73 | "psr-4": {
74 | "User\\Test\\": "module/User/test"
75 | }
76 | },
77 | "scripts": {
78 | "cs:fix": "./vendor/bin/php-cs-fixer fix",
79 | "cs:check": "./vendor/bin/php-cs-fixer fix --dry-run --diff",
80 | "test:run": "./vendor/bin/codecept run --ansi",
81 | "test:run-with-cov": "./vendor/bin/codecept run --ansi --coverage-html --coverage-xml",
82 | "test:build": [
83 | "@db:test:drop",
84 | "@db:test:create",
85 | "@codecept:build"
86 | ],
87 | "db:test:drop": "bin/console-test orm:schema-tool:drop --force",
88 | "db:test:create": "bin/console-test orm:schema-tool:create",
89 | "db:test:update": "bin/console-test orm:schema-tool:update --force",
90 | "codecept:build": "./vendor/bin/codecept build --ansi"
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/config/.gitignore:
--------------------------------------------------------------------------------
1 | /constants-local.php
2 |
--------------------------------------------------------------------------------
/config/application-test.config.php:
--------------------------------------------------------------------------------
1 | require __DIR__ . '/modules.config.php',
9 | 'module_listener_options' => [
10 | 'config_glob_paths' => [
11 | 'config/autoload/{{,*.}global,{,*.}local}.php',
12 | ],
13 | 'config_cache_enabled' => APP_ENV_PROD,
14 | 'config_cache_key' => 'app_config',
15 | 'module_map_cache_enabled' => APP_ENV_PROD,
16 | 'module_map_cache_key' => 'module_map',
17 | 'cache_dir' => 'data/runtime',
18 | 'check_dependencies' => APP_ENV_DEV || APP_ENV_TEST,
19 | ],
20 | ];
21 |
--------------------------------------------------------------------------------
/config/autoload/.gitignore:
--------------------------------------------------------------------------------
1 | # ignore the local configuration files
2 | local.php
3 |
--------------------------------------------------------------------------------
/config/autoload/global.php:
--------------------------------------------------------------------------------
1 | [
10 | 'display_exceptions' => APP_DEBUG,
11 | 'display_not_found_reason' => APP_DEBUG,
12 | ],
13 | // The ZfcTwig module configurations.
14 | 'zfctwig' => [
15 | 'environment_options' => [
16 | 'debug' => APP_DEBUG,
17 | ],
18 | ],
19 | // The ZfSnapPhpDebugBar moduleconfigurations.
20 | 'php-debug-bar' => [
21 | 'enabled' => APP_ENV_DEV,
22 | ],
23 | // Doctrine
24 | 'doctrine' => [
25 | 'configuration' => [
26 | 'orm_default' => [
27 | 'generate_proxies' => APP_DEBUG,
28 | ],
29 | ],
30 | ],
31 | ];
32 |
--------------------------------------------------------------------------------
/config/constants.php:
--------------------------------------------------------------------------------
1 | [
10 | * 'path' => 'directory storing the local files',
11 | * 'skipFiles' => [
12 | * // list of files that should only copied once and skipped if they already exist
13 | * ],
14 | * 'setWritable' => [
15 | * // list of directories that should be set writable
16 | * ],
17 | * 'setExecutable' => [
18 | * // list of files that should be set executable
19 | * ],
20 | * 'relativeSymlinks' => 'Whether need to create relative symlinks (true/false)? By default: false.',
21 | * 'createSymlink' => [
22 | * // list of symlinks to be created. Keys are symlinks, and values are the targets.
23 | * ],
24 | * ],
25 | * ];
26 | */
27 | return [
28 | 'dev' => [
29 | 'path' => 'dev',
30 | 'setWritable' => [
31 | 'public/assets',
32 | 'data/cache',
33 | ],
34 | 'setExecutable' => [
35 | 'bin/console',
36 | 'bin/console-test',
37 | ],
38 | 'relativeSymlinks' => true,
39 | 'createSymlinks' => [],
40 | ],
41 | ];
42 |
--------------------------------------------------------------------------------
/env/dev/bin/console:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | name, $composerConfig->version);
24 |
25 | /** @var \Zend\ServiceManager\ServiceManager $sm */
26 | $sm = $app->getServiceManager();
27 |
28 | // adding commands
29 | foreach ($sm->get('Cli\Options\ModuleOptions')->getCommands() as $command) {
30 | $cli->add($sm->get('CommandManager')->get($command));
31 | }
32 |
33 | // adding doctrine commands
34 | /** @var Cli $doctrineCli */
35 | $doctrineCli = $sm->get('doctrine.cli');
36 | $cli->setHelperSet($doctrineCli->getHelperSet());
37 | foreach ($doctrineCli->all() as $command) {
38 | $cli->add($command);
39 | }
40 |
41 | // run cli
42 | $cli->run();
43 |
--------------------------------------------------------------------------------
/env/dev/bin/console-test:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | name, $composerConfig->version);
24 |
25 | /** @var \Zend\ServiceManager\ServiceManager $sm */
26 | $sm = $app->getServiceManager();
27 |
28 | // adding commands
29 | foreach ($sm->get('Cli\Options\ModuleOptions')->getCommands() as $command) {
30 | $cli->add($sm->get('CommandManager')->get($command));
31 | }
32 |
33 | // adding doctrine commands
34 | /** @var Cli $doctrineCli */
35 | $doctrineCli = $sm->get('doctrine.cli');
36 | $cli->setHelperSet($doctrineCli->getHelperSet());
37 | foreach ($doctrineCli->all() as $command) {
38 | $cli->add($command);
39 | }
40 |
41 | // run cli
42 | $cli->run();
43 |
--------------------------------------------------------------------------------
/env/dev/config/autoload/local.php:
--------------------------------------------------------------------------------
1 | [
12 | 'connection' => [
13 | 'orm_default' => [
14 | // 'driverClass' => PgSqlDriver::class,
15 | 'driverClass' => MySqlDriver::class,
16 | 'params' => [
17 | 'host' => '127.0.0.1',
18 | // 'port' => 5432,
19 | 'port' => 3306,
20 | 'user' => 'zf_app_blank',
21 | 'password' => '1234',
22 | 'dbname' => 'zf_app_blank' . (APP_ENV_TEST ? '_test' : ''),
23 | ],
24 | ],
25 | ],
26 | ],
27 | 'ex_assetic' => [
28 | 'node_bin' => '/usr/bin/node',
29 | 'yui_path' => '/usr/lib/node_modules/yuicompressor/build/yuicompressor-2.4.8.jar',
30 | 'java_path' => '/usr/bin/java',
31 | 'uglify_js2_path' => '/usr/bin/uglifyjs',
32 | 'sass_path' => '/usr/local/bin/sass',
33 | ],
34 | 'mailgun' => [
35 | 'debug' => true,
36 | 'key' => 'key-example', // example key-dfdfh87d765s45ra7s65a4sasdas76253e
37 | 'endpoint' => 'endpoint-example', // example http://bin.mailgun.net/sd7gs4wsd
38 | ],
39 | 'mail' => [
40 | 'domain' => 'example.mailgun.org', // example mail.my-site.com
41 | ],
42 | ];
43 |
--------------------------------------------------------------------------------
/env/dev/public/index-test.php:
--------------------------------------------------------------------------------
1 | run();
21 |
--------------------------------------------------------------------------------
/env/dev/public/index.php:
--------------------------------------------------------------------------------
1 | run();
21 |
--------------------------------------------------------------------------------
/module/Cli/config/module.config.php:
--------------------------------------------------------------------------------
1 | [
7 | 'commands' => [],
8 | ],
9 | 'command_manager' => [],
10 | 'service_manager' => [
11 | 'factories' => [
12 | Manager\CommandManager::class => Manager\CommandManagerFactory::class,
13 | Options\ModuleOptions::class => Options\ModuleOptionsFactory::class,
14 | ],
15 | 'aliases' => [
16 | 'CommandManager' => Manager\CommandManager::class,
17 | ],
18 | ],
19 | ];
20 |
--------------------------------------------------------------------------------
/module/Cli/src/Manager/CommandManager.php:
--------------------------------------------------------------------------------
1 | addInitializer([$this, 'injectEventManager']);
18 | parent::__construct($configOrContainerInstance, $config);
19 | }
20 |
21 | public function injectEventManager(ContainerInterface $container, Command $command)
22 | {
23 | if (!$command instanceof EventManagerAwareInterface) {
24 | return;
25 | }
26 | $events = $command->getEventManager();
27 | if (!$events || !$events->getSharedManager() instanceof SharedEventManagerInterface) {
28 | $command->setEventManager($container->get('EventManager'));
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/module/Cli/src/Manager/CommandManagerFactory.php:
--------------------------------------------------------------------------------
1 | getEvent();
19 | $container = $event->getParam('ServiceManager');
20 | /** @var \Zend\ModuleManager\Listener\ServiceListener $serviceListener */
21 | $serviceListener = $container->get('ServiceListener');
22 | $serviceListener->addServiceManager(
23 | 'CommandManager',
24 | 'command_manager',
25 | 'Cli\Manager\Provider\CommandManagerProviderInterface',
26 | 'getCommandManagerConfig'
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/module/Cli/src/Options/ModuleOptions.php:
--------------------------------------------------------------------------------
1 | commands = $commands;
17 | return $this;
18 | }
19 |
20 | public function getCommands(): array
21 | {
22 | return $this->commands;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/module/Cli/src/Options/ModuleOptionsFactory.php:
--------------------------------------------------------------------------------
1 | get('config')['cli']);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/module/Core/assets/css/404.sass:
--------------------------------------------------------------------------------
1 | @import url("//fonts.googleapis.com/css?family=Comfortaa&subset=latin,cyrillic")
2 |
3 | .page-404
4 | h1
5 | font-family: 'Comfortaa', cursive
6 |
7 |
--------------------------------------------------------------------------------
/module/Core/assets/css/base.sass:
--------------------------------------------------------------------------------
1 | html,
2 | body
3 | height: 100%
4 |
5 | .wrap
6 | height: auto
7 | margin: 0 auto -60px
8 | min-height: 100%
9 | padding-bottom: 60px
10 |
11 | > .container
12 | padding: 15px 20px
13 |
14 | footer
15 | height: 60px
16 | background-color: #f5f5f5
17 | border-top: 1px solid #ddd
18 | padding-top: 20px
19 |
20 | .navbar-brand
21 | img
22 | margin-top: -3px
23 | margin-right: 5px
24 |
25 | .form-group
26 | &.required label::after
27 | content: " *"
28 | color: $danger
29 |
30 | .help-block
31 | font-size: $small-font-size
32 |
--------------------------------------------------------------------------------
/module/Core/assets/css/style.sass:
--------------------------------------------------------------------------------
1 | // libraries
2 | @import "../../../../node_modules/bootstrap/scss/bootstrap"
3 | @import "../../../../node_modules/font-awesome/scss/font-awesome"
4 |
5 | // core styles
6 | @import "404"
7 | @import "base"
8 |
--------------------------------------------------------------------------------
/module/Core/assets/img/404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bupy7/zf-app-blank/6ffb30bcfca4c0ab135c6bdd5469fbaac273a63b/module/Core/assets/img/404.png
--------------------------------------------------------------------------------
/module/Core/assets/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bupy7/zf-app-blank/6ffb30bcfca4c0ab135c6bdd5469fbaac273a63b/module/Core/assets/img/logo.png
--------------------------------------------------------------------------------
/module/Core/assets/js/navbar.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var mainNavbar = document.querySelector('#main-navbar');
3 |
4 | new Collapse(mainNavbar.querySelector('.navbar-toggler'));
5 | })();
6 |
--------------------------------------------------------------------------------
/module/Core/config/assets.config.php:
--------------------------------------------------------------------------------
1 | [
13 | 'acceptableErrors' => [
14 | GuardInterface::GUARD_UNAUTHORIZED
15 | ],
16 | 'debug' => APP_DEBUG,
17 | 'buildOnRequest' => APP_ENV_DEV,
18 | 'webPath' => getcwd() . '/public/assets',
19 | 'basePath' => 'assets',
20 | 'default' => [
21 | 'assets' => [
22 | '@library_js',
23 |
24 | '@app_css',
25 | '@app_js',
26 | ],
27 | 'options' => [
28 | 'mixin' => true,
29 | ],
30 | ],
31 | 'modules' => [
32 | 'Core' => [
33 | 'root_path' => __DIR__ . '/../assets',
34 | 'collections' => [
35 | 'app_css' => [
36 | 'assets' => [
37 | 'css/style.sass',
38 | ],
39 | 'filters' => [
40 | 'SassFilter',
41 | '?CssCompressorFilter',
42 | ],
43 | 'options' => [
44 | 'output' => 'css/app.min.css',
45 | ],
46 | ],
47 | 'app_js' => [
48 | 'assets' => [
49 | 'js/navbar.js',
50 | ],
51 | 'filters' => [
52 | '?UglifyJs2Filter',
53 | ],
54 | 'options' => [
55 | 'output' => 'js/app.min.js',
56 | ],
57 | ],
58 | 'library_js' => [
59 | 'assets' => [
60 | $npmPath . '/bootstrap.native/dist/bootstrap-native-v4.js',
61 | ],
62 | 'filters' => [
63 | '?UglifyJs2Filter',
64 | ],
65 | 'options' => [
66 | 'output' => 'js/library.min.js',
67 | ],
68 | ],
69 | 'app_images' => [
70 | 'assets' => [
71 | 'img/*',
72 | ],
73 | 'options' => [
74 | 'move_raw' => true,
75 | ],
76 | ],
77 | 'font_awesome_fonts' => [
78 | 'assets' => [
79 | $npmPath . '/font-awesome/fonts/*',
80 | ],
81 | 'options' => [
82 | 'move_raw' => true,
83 | 'targetPath' => 'fonts',
84 | 'disable_source_path' => true,
85 | ],
86 | ],
87 | ],
88 | ],
89 | ],
90 | ],
91 | ];
92 |
--------------------------------------------------------------------------------
/module/Core/config/doctrine.config.php:
--------------------------------------------------------------------------------
1 | [
9 | 'migrations_configuration' => [
10 | 'orm_default' => [
11 | 'directory' => getcwd() . '/data/migrations',
12 | 'name' => 'Doctrine Database Migrations',
13 | 'namespace' => 'DoctrineORMModule\Migrations',
14 | 'table' => 'migration',
15 | 'column' => 'version',
16 | ],
17 | ],
18 | 'configuration' => [
19 | 'orm_default' => [
20 | 'proxy_dir' => 'data/runtime/DoctrineORMModule/Proxy'
21 | ],
22 | ],
23 | ],
24 | ];
25 |
--------------------------------------------------------------------------------
/module/Core/config/module.config.php:
--------------------------------------------------------------------------------
1 | [
17 | 'time_zone' => 'Europe/London',
18 | ],
19 | 'service_manager' => [
20 | 'abstract_factories' => [
21 | StorageCacheAbstractServiceFactory::class,
22 | LoggerAbstractServiceFactory::class,
23 | NavigationAbstractServiceFactory::class,
24 | ],
25 | 'factories' => [
26 | 'translator' => TranslatorFactory::class,
27 | ],
28 | ],
29 | 'translator' => [
30 | 'locale' => 'en',
31 | 'translation_file_patterns' => [
32 | [
33 | 'type' => 'phparray',
34 | 'base_dir' => __DIR__ . '/../language',
35 | 'pattern' => '%s.php',
36 | 'text_domain' => 'Core',
37 | ],
38 | ],
39 | ],
40 | 'controller_plugins' => [
41 | 'factories' => [
42 | 'translate' => TranslatePluginFactory::class,
43 | ],
44 | ],
45 | 'view_manager' => [
46 | 'doctype' => 'HTML5',
47 | 'not_found_template' => 'error/404',
48 | 'exception_template' => 'error/index',
49 | 'template_path_stack' => [
50 | __DIR__ . '/../view',
51 | ],
52 | 'strategies' => [
53 | 'ViewJsonStrategy',
54 | ],
55 | ],
56 | 'view_helpers' => [
57 | 'factories' => [
58 | 'locale' => LocaleHelperFactory::class,
59 | ],
60 | 'invokables' => [
61 | 'alertMessage' => AlertMessageHelper::class,
62 | ],
63 | ],
64 | ];
65 |
--------------------------------------------------------------------------------
/module/Core/config/navigation.config.php:
--------------------------------------------------------------------------------
1 | [
9 | // for guest
10 | 'guestright' => [],
11 | // for auth
12 | 'authright' => [],
13 | // default
14 | 'default' => [],
15 | ],
16 | ];
17 |
--------------------------------------------------------------------------------
/module/Core/config/rbac.config.php:
--------------------------------------------------------------------------------
1 | [
13 | /**
14 | * Key that is used to fetch the identity provider
15 | *
16 | * Please note that when an identity is found, it MUST implements the ZfcRbac\Identity\IdentityProviderInterface
17 | * interface, otherwise it will throw an exception.
18 | */
19 | // 'identity_provider' => 'ZfcRbac\Identity\AuthenticationIdentityProvider',
20 |
21 | /**
22 | * Set the guest role
23 | *
24 | * This role is used by the authorization service when the authentication service returns no identity
25 | */
26 | 'guest_role' => 'guest',
27 | /**
28 | * Set the guards
29 | *
30 | * You must comply with the various options of guards. The format must be of the following format:
31 | *
32 | * 'guards' => [
33 | * 'ZfcRbac\Guard\RouteGuard' => [
34 | * // options
35 | * ]
36 | * ]
37 | */
38 | 'guards' => [
39 | RoutePermissionsGuard::class => [],
40 | ],
41 | /**
42 | * As soon as one rule for either route or controller is specified, a guard will be automatically
43 | * created and will start to hook into the MVC loop.
44 | *
45 | * If the protection policy is set to DENY, then any route/controller will be denied by
46 | * default UNLESS it is explicitly added as a rule. On the other hand, if it is set to ALLOW, then
47 | * not specified route/controller will be implicitly approved.
48 | *
49 | * DENY is the most secure way, but it is more work for the developer
50 | */
51 | 'protection_policy' => GuardInterface::POLICY_DENY,
52 | /**
53 | * Configuration for role provider
54 | *
55 | * It must be an array that contains configuration for the role provider. The provider config
56 | * must follow the following format:
57 | *
58 | * 'ZfcRbac\Role\InMemoryRoleProvider' => [
59 | * 'role1' => [
60 | * 'children' => ['children1', 'children2'], // OPTIONAL
61 | * 'permissions' => ['edit', 'read'] // OPTIONAL
62 | * ]
63 | * ]
64 | *
65 | * Supported options depend of the role provider, so please refer to the official documentation
66 | */
67 | 'role_provider' => [
68 | InMemoryRoleProvider::class => [
69 | 'registered' => [
70 | 'children' => ['guest'],
71 | ],
72 | 'guest' => [],
73 | ],
74 | ],
75 | /**
76 | * Configure the unauthorized strategy. It is used to render a template whenever a user is unauthorized
77 | */
78 | 'unauthorized_strategy' => [
79 | /**
80 | * Set the template name to render
81 | */
82 | //'template' => 'error/403'
83 | ],
84 | /**
85 | * Configure the redirect strategy. It is used to redirect the user to another route when a user is
86 | * unauthorized
87 | */
88 | 'redirect_strategy' => [
89 | /**
90 | * Enable redirection when the user is connected
91 | */
92 | 'redirect_when_connected' => true,
93 | /**
94 | * Set the route to redirect when user is connected (of course, it must exist!)
95 | */
96 | 'redirect_to_route_connected' => 'home',
97 | /**
98 | * Set the route to redirect when user is disconnected (of course, it must exist!)
99 | */
100 | 'redirect_to_route_disconnected' => 'signin',
101 | /**
102 | * If a user is unauthorized and redirected to another route (login, for instance), should we
103 | * append the previous URI (the one that was unauthorized) in the query params?
104 | */
105 | 'append_previous_uri' => false,
106 | /**
107 | * If append_previous_uri option is set to true, this option set the query key to use when
108 | * the previous uri is appended
109 | */
110 | 'previous_uri_query_key' => 'redirectTo'
111 | ],
112 | /**
113 | * Various plugin managers for guards and role providers. Each of them must follow a common
114 | * plugin manager config format, and can be used to create your custom objects
115 | */
116 | // 'guard_manager' => [],
117 | // 'role_provider_manager' => []
118 | ]
119 | ];
120 |
--------------------------------------------------------------------------------
/module/Core/config/router.config.php:
--------------------------------------------------------------------------------
1 | [
9 | 'routes' => [
10 | 'home' => [
11 | 'type' => 'literal',
12 | 'options' => [
13 | 'route' => '/',
14 | ],
15 | ],
16 | ],
17 | ],
18 | ];
19 |
--------------------------------------------------------------------------------
/module/Core/config/twig.config.php:
--------------------------------------------------------------------------------
1 | [
13 | 'environment_options' => [
14 | 'autoescape' => false,
15 | 'strict_variables' => true,
16 | ],
17 | 'extensions' => [
18 | 'debug' => Twig_Extension_Debug::class,
19 | ],
20 | 'helper_manager' => [
21 | 'invokables' => [
22 | 'partial' => Partial::class,
23 | 'paginationControl' => \Zend\View\Helper\PaginationControl::class,
24 | ],
25 | 'factories' => [
26 | FormBuilderHelper::class => FormBuilderHelperFactory::class,
27 | ],
28 | 'shared' => [
29 | 'formBuilder' => false,
30 | FormBuilderHelper::class => false,
31 | ],
32 | 'aliases' => [
33 | 'formBuilder' => FormBuilderHelper::class,
34 | ],
35 | ],
36 | ],
37 | ];
38 |
--------------------------------------------------------------------------------
/module/Core/language/en.php:
--------------------------------------------------------------------------------
1 | 'All right reserved',
5 |
6 | 'TITLE_HOMEPAGE' => 'App-Blank',
7 | 'TITLE_404' => '404 - Not found',
8 | 'TITLE_FATAL_ERROR' => 'Fatal error',
9 |
10 | 'DESC_404' => 'The requested page could not be found',
11 | 'EXCEPTION_USER_MESSAGE' => 'For more information contact to the site administrator.',
12 |
13 | 'LABEL_NOTHING_FOUND' => 'Nothing found',
14 | 'LABEL_PAGINATION_INFO' => 'Records %d-%d of %d',
15 | 'LABEL_ERROR_FILE' => 'File',
16 | 'LABEL_ERROR_MESSAGE' => 'Message',
17 | 'LABEL_ERROR_STACK_TRACE' => 'Call stack',
18 | 'LABEL_ERROR_EXCEPTION' => 'Exception',
19 | ];
20 |
--------------------------------------------------------------------------------
/module/Core/language/ru.php:
--------------------------------------------------------------------------------
1 | 'Все права защищены',
5 |
6 | 'TITLE_HOMEPAGE' => 'App-Blank',
7 | 'TITLE_404' => '404 - Ничего не найдено',
8 | 'TITLE_FATAL_ERROR' => 'Произошла непоправимая ошибка',
9 |
10 | 'DESC_404' => 'Запрашиваемая страница не найдена',
11 |
12 | 'LABEL_ERROR_FILE' => 'Файл',
13 | 'LABEL_ERROR_MESSAGE' => 'Сообщение',
14 | 'LABEL_ERROR_STACK_TRACE' => 'Стек вызовов',
15 | 'LABEL_ERROR_EXCEPTION' => 'Исключение',
16 | 'LABEL_NOTHING_FOUND' => 'Ничего не найдено',
17 | 'LABEL_PAGINATION_INFO' => 'Показаны записи %d-%d из %d',
18 |
19 | 'EXCEPTION_USER_MESSAGE' => 'Обратитесь к администратору сайта за дополнительной информацией.',
20 | ];
21 |
--------------------------------------------------------------------------------
/module/Core/logs/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !/.gitignore
3 |
--------------------------------------------------------------------------------
/module/Core/src/Controller/ActionControllerAbstract.php:
--------------------------------------------------------------------------------
1 | getResponse()->setStatusCode($code);
20 | return new JsonModel($data);
21 | }
22 |
23 | public function asView(array $data = [], array $options = []): ViewModel
24 | {
25 | return new ViewModel(count($data) > 0 ? $data : null, count($options) > 0 ? $options : null);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/module/Core/src/Controller/Plugin/TranslatePlugin.php:
--------------------------------------------------------------------------------
1 | translator = $translator;
25 | }
26 |
27 | /**
28 | * Translate a message.
29 | *
30 | * @param string $message
31 | * @param string $textDomain
32 | * @param string $locale
33 | * @return string
34 | */
35 | public function __invoke($message, $textDomain = null, $locale = null)
36 | {
37 | if ($textDomain === null) {
38 | $textDomain = $this->translatorTextDomain;
39 | }
40 | return $this->translator->translate($message, $textDomain, $locale);
41 | }
42 |
43 | /**
44 | * @param string $textDomain
45 | */
46 | public function setTranslatorTextDomain($textDomain)
47 | {
48 | $this->translatorTextDomain = $textDomain;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/module/Core/src/Controller/Plugin/TranslatePluginFactory.php:
--------------------------------------------------------------------------------
1 | get('translator'));
16 | return $translatePlg;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/module/Core/src/Module.php:
--------------------------------------------------------------------------------
1 | getApplication()->getEventManager()->getSharedManager();
33 | $logger = $this->getLogger();
34 | $sharedManager->attach('Zend\Mvc\Application', 'dispatch.error', function ($e) use ($logger) {
35 | if ($e->getParam('exception')) {
36 | $logger->crit($e->getParam('exception'));
37 | }
38 | });
39 | Logger::registerFatalErrorShutdownFunction($logger);
40 | }
41 |
42 | protected function getLogger(): Logger
43 | {
44 | $logger = new Logger;
45 | $writer = new Stream(self::ERROR_LOG_FILE);
46 | return $logger->addWriter($writer);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/module/Core/src/View/Helper/AlertMessageHelper.php:
--------------------------------------------------------------------------------
1 | setFlashMessenger();
45 | return $this->getFlashMessages();
46 | }
47 |
48 | /**
49 | * Constructor method.
50 | */
51 | public function __construct()
52 | {
53 | $this->setFlashMessenger();
54 | $this->markdown = new GithubMarkdown();
55 | }
56 |
57 | /**
58 | * Add a new message
59 | *
60 | * @param string $message
61 | * @param bool $dismissable
62 | */
63 | public function addMessage($message, $dismissable = true)
64 | {
65 | $this->addMessageByType(self::MESSAGE_TYPE_DEFAULT, $message, $dismissable);
66 | }
67 |
68 | /**
69 | * Add a new success message
70 | *
71 | * @param string $successMessage
72 | * @param bool $dismissable
73 | */
74 | public function addSuccessMessage($successMessage, $dismissable = true)
75 | {
76 | $this->addMessageByType(self::MESSAGE_TYPE_SUCCESS, $successMessage, $dismissable);
77 | }
78 |
79 | /**
80 | * Add a new info message
81 | *
82 | * @param string $infoMessage
83 | * @param bool $dismissable
84 | */
85 | public function addInfoMessage($infoMessage, $dismissable = true)
86 | {
87 | $this->addMessageByType(self::MESSAGE_TYPE_INFO, $infoMessage, $dismissable);
88 | }
89 |
90 | /**
91 | * Add a new warning message
92 | *
93 | * @param string $warningMessage
94 | * @param bool $dismissable
95 | */
96 | public function addWarningMessage($warningMessage, $dismissable = false)
97 | {
98 | $this->addMessageByType(self::MESSAGE_TYPE_WARNING, $warningMessage, $dismissable);
99 | }
100 |
101 | /**
102 | * Add a new error message
103 | *
104 | * @param string $errorMessage
105 | * @param bool $dismissable
106 | */
107 | public function addErrorMessage($errorMessage, $dismissable = false)
108 | {
109 | $this->addMessageByType(self::MESSAGE_TYPE_ERROR, $errorMessage, $dismissable);
110 | }
111 |
112 | /**
113 | * Set a instance of the FlashMessenger.
114 | */
115 | public function setFlashMessenger()
116 | {
117 | if ($this->flashMessenger === null) {
118 | $this->flashMessenger = new FlashMessenger();
119 | }
120 | }
121 |
122 | /**
123 | * Add a message by type
124 | *
125 | * @param string $type
126 | * @param string $message
127 | * @param bool $dismissable
128 | */
129 | protected function addMessageByType($type, $message, $dismissable = true)
130 | {
131 | self::$flashMessages[$type][] = [
132 | 'message' => $this->markdown->parseParagraph($message),
133 | 'dismissable' => $dismissable,
134 | ];
135 | }
136 |
137 | /**
138 | * Get all flash messages from the plugin
139 | */
140 | protected function getFlashMessages()
141 | {
142 | if ($this->flashMessenger->hasMessages()) {
143 | foreach ($this->flashMessenger->getMessages() as $message) {
144 | $this->addMessageByType(self::MESSAGE_TYPE_DEFAULT, $message);
145 | }
146 | }
147 | if ($this->flashMessenger->hasSuccessMessages()) {
148 | foreach ($this->flashMessenger->getSuccessMessages() as $successMessages) {
149 | $this->addMessageByType(self::MESSAGE_TYPE_SUCCESS, $successMessages);
150 | }
151 | }
152 | if ($this->flashMessenger->hasInfoMessages()) {
153 | foreach ($this->flashMessenger->getInfoMessages() as $infoMessages) {
154 | $this->addMessageByType(self::MESSAGE_TYPE_INFO, $infoMessages);
155 | }
156 | }
157 | if ($this->flashMessenger->hasWarningMessages()) {
158 | foreach ($this->flashMessenger->getWarningMessages() as $warningMessages) {
159 | $this->addMessageByType(self::MESSAGE_TYPE_WARNING, $warningMessages);
160 | }
161 | }
162 | if ($this->flashMessenger->hasErrorMessages()) {
163 | foreach ($this->flashMessenger->getErrorMessages() as $errorMessages) {
164 | $this->addMessageByType(self::MESSAGE_TYPE_ERROR, $errorMessages);
165 | }
166 | }
167 | return self::$flashMessages;
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/module/Core/src/View/Helper/LocaleHelper.php:
--------------------------------------------------------------------------------
1 | _locale = $locale;
23 | }
24 |
25 | /**
26 | * @return string
27 | */
28 | public function __invoke()
29 | {
30 | return $this->_locale;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/module/Core/src/View/Helper/LocaleHelperFactory.php:
--------------------------------------------------------------------------------
1 | get('config')['translator']['locale']);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/module/Core/view/error/404.twig:
--------------------------------------------------------------------------------
1 | {% extends 'layout/layout.twig' %}
2 |
3 | {% do headTitle(translate('TITLE_404', 'Core')) %}
4 |
5 | {% block content %}
6 |
7 |
8 |
{{ translate('DESC_404', 'Core') }}
9 |
10 |
11 |
 }})
12 |
13 |
14 | {% endblock content %}
15 |
--------------------------------------------------------------------------------
/module/Core/view/error/index.twig:
--------------------------------------------------------------------------------
1 | {% extends 'layout/layout.twig' %}
2 |
3 | {% do headTitle(translate('TITLE_FATAL_ERROR', 'Core')) %}
4 |
5 | {% block content %}
6 |
14 | {% if display_exceptions %}
15 | {{ translate('LABEL_ERROR_EXCEPTION', 'Core') }}: {{ get_class(exception) }}
16 | {{ translate('LABEL_ERROR_FILE', 'Core') }}:
17 | {{ exception.getFile() }}:{{ exception.getLine() }}
18 | {% if exception.getMessage %}
19 | {{ translate('LABEL_ERROR_MESSAGE', 'Core') }}:
20 | {{ exception.getMessage()|e }}
21 | {% endif %}
22 | {{ translate('LABEL_ERROR_STACK_TRACE', 'Core') }}:
23 | {{ exception.getTraceAsString()|e }}
24 | {% endif %}
25 | {% if not display_exceptions %}
26 | {{ translate('EXCEPTION_USER_MESSAGE', 'Core') }}
27 | {% endif %}
28 | {% endblock content %}
29 |
--------------------------------------------------------------------------------
/module/Core/view/layout/breadcrumbs.twig:
--------------------------------------------------------------------------------
1 | {% if pages is not empty %}
2 |
3 | -
4 |
5 |
6 |
7 |
8 | {% for page in pages %}
9 | {% if page.isActive() %}
10 | -
11 | {{ translate(
12 | page.getLabel(),
13 | page.get('text_domain') ?: navigation(container).getTranslatorTextDomain()
14 | ) }}
15 |
16 | {% else %}
17 | -
18 |
19 | {{ translate(
20 | page.getLabel(),
21 | page.get('text_domain') ?: navigation(container).getTranslatorTextDomain()
22 | ) }}
23 |
24 |
25 | {% endif %}
26 | {% endfor %}
27 |
28 | {% endif %}
29 |
--------------------------------------------------------------------------------
/module/Core/view/layout/layout.twig:
--------------------------------------------------------------------------------
1 | {% import 'macro/component.twig' as component %}
2 |
3 | {% set identity = identity() %}
4 |
5 | {{ doctype() }}
6 |
7 |
8 |
9 |
10 | {{ headTitle(translate('TITLE_HOMEPAGE', 'Core')).setSeparator(' - ') }}
11 |
12 |
13 |
14 |
15 | {% block meta %}{% endblock meta %}
16 |
17 | {{ headMeta() }}
18 |
19 |
20 |
21 | {{ headLink() }}
22 |
23 | {% block link %}{% endblock link %}
24 |
25 | {{ headScript() }}
26 |
27 | {% block script %}{% endblock script %}
28 |
29 |
30 |
31 |
63 |
64 |
65 | {{ component.alert(alertMessage()) }}
66 | {{ navigation('Zend\\Navigation\\Default')
67 | .breadcrumbs()
68 | .setPartial('layout/breadcrumbs.twig') }}
69 | {% block content %}{% endblock content %}
70 |
71 |
72 |
73 |
82 |
83 | {{ inlineScript() }}
84 |
85 | {% block inline %}{% endblock inline %}
86 |
87 |
88 |
--------------------------------------------------------------------------------
/module/Core/view/layout/navigation.twig:
--------------------------------------------------------------------------------
1 |
70 |
--------------------------------------------------------------------------------
/module/Core/view/layout/paginator.twig:
--------------------------------------------------------------------------------
1 | {% if pageCount > 1 %}
2 | {% set query = query | default({}) %}
3 |
38 | {% endif %}
39 |
--------------------------------------------------------------------------------
/module/Core/view/macro/component.twig:
--------------------------------------------------------------------------------
1 | {% macro alert(items) %}
2 | {% set templates = {
3 | 'default': '%s%s
',
4 | 'success': '%s%s
',
5 | 'info': '%s%s
',
6 | 'warning': '%s%s
',
7 | 'error': '%s%s
'
8 | } %}
9 | {% for type, messages in items %}
10 | {% for message in messages %}
11 | {% set dismissable = {class: '', button: ''} %}
12 | {% if message.dismissable %}
13 | {% set dismissable = {
14 | class: ' alert-dismisable',
15 | button: ''
16 | } %}
17 | {% endif %}
18 | {{ templates[type]|format(dismissable.class, dismissable.button, message.message) }}
19 | {% endfor %}
20 | {% endfor %}
21 | {% endmacro %}
22 |
23 | {% macro paginator(paginator, queryParams, part, displayTotal) %}
24 | {% set displayTotal = displayTotal is null ? true : displayTotal %}
25 |
26 |
27 |
28 | {{ paginationControl(
29 | paginator,
30 | 'sliding',
31 | ['layout', part | default(''), 'paginator.twig'] | join('/'),
32 | {
33 | query: queryParams | default({})
34 | }
35 | ) }}
36 |
37 | {% if displayTotal %}
38 |
39 |
40 | {% set inc = 0 %}
41 | {% if paginator.getTotalItemCount() > 0 %}
42 | {% set inc = 1 %}
43 | {% endif %}
44 |
45 | {% set start = (paginator.getCurrentPageNumber() - 1) * paginator.getItemCountPerPage() + inc %}
46 | {% if paginator.getItemCountPerPage() != paginator.getCurrentItemCount() %}
47 | {% set diff = (paginator.getItemCountPerPage() - paginator.getCurrentItemCount()) | abs %}
48 | {% set end = paginator.getCurrentPageNumber() * paginator.getItemCountPerPage() - diff %}
49 | {% else %}
50 | {% set end = paginator.getCurrentPageNumber() * paginator.getItemCountPerPage() %}
51 | {% endif %}
52 |
53 | {% set total = paginator.getTotalItemCount() %}
54 |
55 | {{ translate('LABEL_PAGINATION_INFO', 'Core') | format(start, end, total) }}
56 |
57 |
58 | {% endif %}
59 |
60 | {% endmacro %}
61 |
62 |
--------------------------------------------------------------------------------
/module/Core/view/macro/form.twig:
--------------------------------------------------------------------------------
1 | {% macro hasError(formBuilder, name) %}
2 | {{ formBuilder.hasError(name) ? ' is-invalid' : '' }}
3 | {% endmacro %}
4 |
--------------------------------------------------------------------------------
/module/Core/view/macro/icon.twig:
--------------------------------------------------------------------------------
1 | {% macro fa(name) %}
2 |
3 | {% endmacro %}
4 |
--------------------------------------------------------------------------------
/module/Core/view/macro/table.twig:
--------------------------------------------------------------------------------
1 | {% macro emptyRow(colspan) %}
2 |
3 | {{ translate('LABEL_NOTHING_FOUND', 'Core') }} |
4 |
5 | {% endmacro %}
6 |
--------------------------------------------------------------------------------
/module/DataProvider/config/module.config.php:
--------------------------------------------------------------------------------
1 | [
7 | 'factories' => [
8 | QueryDataProvider::class => \Zend\ServiceManager\AbstractFactory\ReflectionBasedAbstractFactory::class,
9 | ],
10 | 'shared' => [
11 | QueryDataProvider::class => false,
12 | ],
13 | ],
14 | ];
15 |
--------------------------------------------------------------------------------
/module/DataProvider/src/DataProviderInterface.php:
--------------------------------------------------------------------------------
1 | pageRange = $pageRange;
72 | return $this;
73 | }
74 |
75 | public function setPage(int $page): DataProviderInterface
76 | {
77 | if ($page <= 0) {
78 | $page = self::PAGE_DEFAULT;
79 | }
80 | $this->page = $page;
81 | return $this;
82 | }
83 |
84 | public function setLimit(int $limit): DataProviderInterface
85 | {
86 | $this->limit = $limit;
87 | return $this;
88 | }
89 |
90 | public function getPaginator(): Paginator
91 | {
92 | if ($this->paginator === null) {
93 | $this->paginator = new Paginator(new DoctrineAdapter($this->getCollection(), $this->getCount()));
94 | $this->paginator->setItemCountPerPage($this->limit)
95 | ->setCurrentPageNumber($this->getValidPage())
96 | ->setPageRange($this->pageRange);
97 | }
98 | return $this->paginator;
99 | }
100 |
101 | public function __construct(EntityManager $entityManager)
102 | {
103 | $this->entityManager = $entityManager;
104 | }
105 |
106 | public function getQueryBuilder(): QueryBuilder
107 | {
108 | if ($this->queryBuilder === null) {
109 | $this->queryBuilder = $this->entityManager->createQueryBuilder();
110 | }
111 | return $this->queryBuilder;
112 | }
113 |
114 | public function getCountBuilder(): QueryBuilder
115 | {
116 | if ($this->countBuilder === null) {
117 | $this->countBuilder = clone $this->getQueryBuilder();
118 | $this->countBuilder->select(sprintf('COUNT(%s)', self::MAIN_ENTITY_ALIAS));
119 | }
120 | return $this->countBuilder;
121 | }
122 |
123 | public function asObject(): QueryDataProvider
124 | {
125 | $this->hydrationMode = AbstractQuery::HYDRATE_OBJECT;
126 | return $this;
127 | }
128 |
129 | public function asArray(): QueryDataProvider
130 | {
131 | $this->hydrationMode = AbstractQuery::HYDRATE_ARRAY;
132 | return $this;
133 | }
134 |
135 | public function setQueryParams(array $queryParams): QueryDataProvider
136 | {
137 | $this->queryParams = $queryParams;
138 | return $this;
139 | }
140 |
141 | public function getQueryParams(): array
142 | {
143 | return $this->queryParams;
144 | }
145 |
146 | /**
147 | * @return array|Collection
148 | */
149 | protected function getCollection()
150 | {
151 | if ($this->collection === null) {
152 | $qb = clone $this->getQueryBuilder();
153 | $qb->setFirstResult($this->getFirstResult())
154 | ->setMaxResults($this->limit);
155 | $this->collection = $qb->getQuery()->getResult($this->hydrationMode);
156 | }
157 | return $this->collection;
158 | }
159 |
160 | protected function getCount(): int
161 | {
162 | if ($this->count === null) {
163 | $this->count = (int)$this->getCountBuilder()->getQuery()->getSingleScalarResult();
164 | }
165 | return $this->count;
166 | }
167 |
168 | protected function getFirstResult(): int
169 | {
170 | $result = ($this->page - 1) * $this->limit;
171 | if ($this->getCount() > $result) {
172 | return $result;
173 | }
174 | return self::FIRST_RESULT_DEFAULT * $this->limit;
175 | }
176 |
177 | protected function getValidPage(): int
178 | {
179 | return $this->page > ceil($this->getCount() / $this->limit) ? self::PAGE_DEFAULT : $this->page;
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/module/ExAssetic/config/cli.config.php:
--------------------------------------------------------------------------------
1 | [
7 | 'commands' => [
8 | 'ExAssetic\Command\BuildCommand',
9 | 'ExAssetic\Command\SetupCommand',
10 | ],
11 | ],
12 | 'command_manager' => [
13 | 'factories' => [
14 | 'ExAssetic\Command\BuildCommand' => Command\BuildCommandFactory::class,
15 | 'ExAssetic\Command\SetupCommand' => Command\SetupCommandFactory::class,
16 | ],
17 | ],
18 | ];
19 |
--------------------------------------------------------------------------------
/module/ExAssetic/config/module.config.php:
--------------------------------------------------------------------------------
1 | [],
11 | 'service_manager' => [
12 | 'factories' => [
13 | \Assetic\Filter\Sass\SassFilter::class => Filter\Sass\SassFilterFactory::class,
14 | \Assetic\Filter\Yui\CssCompressorFilter::class => Filter\Yui\CssCompressorFilterFactory::class,
15 | \Assetic\Filter\UglifyJs2Filter::class => Filter\UglifyJs2FilterFactory::class,
16 |
17 | Options\ModuleOptions::class => Options\ModuleOptionsFactory::class,
18 |
19 | 'AsseticCacheBuster' => Cache\CacheBusterFactory::class,
20 | ],
21 | 'aliases' => [
22 | 'CssCompressorFilter' => \Assetic\Filter\Yui\CssCompressorFilter::class,
23 | 'UglifyJs2Filter' => \Assetic\Filter\UglifyJs2Filter::class,
24 | 'SassFilter' => \Assetic\Filter\Sass\SassFilter::class,
25 | ],
26 | ],
27 | ];
28 |
--------------------------------------------------------------------------------
/module/ExAssetic/config/rbac.config.php:
--------------------------------------------------------------------------------
1 | [
12 | 'guards' => [
13 | RoutePermissionsGuard::class => [
14 | 'assetic*' => ['assetic'],
15 | ],
16 | ],
17 | 'role_provider' => [
18 | InMemoryRoleProvider::class => [
19 | 'guest' => [
20 | 'permissions' => [
21 | 'assetic',
22 | ],
23 | ],
24 | ],
25 | ],
26 | ],
27 | ];
28 |
--------------------------------------------------------------------------------
/module/ExAssetic/src/Cache/CacheBusterFactory.php:
--------------------------------------------------------------------------------
1 | get('AsseticService'));
14 | return $cmd->setName('assetic:build');
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/module/ExAssetic/src/Command/SetupCommandFactory.php:
--------------------------------------------------------------------------------
1 | get('AsseticService'));
14 | return $cmd->setName('assetic:setup');
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/module/ExAssetic/src/Filter/Sass/SassFilterFactory.php:
--------------------------------------------------------------------------------
1 | get('ExAssetic\Options\ModuleOptions');
14 | return new SassFilter($options->sassPath);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/module/ExAssetic/src/Filter/UglifyJs2FilterFactory.php:
--------------------------------------------------------------------------------
1 | get('ExAssetic\Options\ModuleOptions');
17 | return new UglifyJs2Filter($options->uglifyJs2Path, $options->nodeBin);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/module/ExAssetic/src/Filter/Yui/CssCompressorFilterFactory.php:
--------------------------------------------------------------------------------
1 | get('ExAssetic\Options\ModuleOptions');
17 | return new CssCompressorFilter($options->yuiPath, $options->javaPath);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/module/ExAssetic/src/Module.php:
--------------------------------------------------------------------------------
1 | nodeBin = $nodeBin;
33 | return $this;
34 | }
35 |
36 | public function getNodeBin(): string
37 | {
38 | return $this->nodeBin;
39 | }
40 |
41 | public function setYuiPath(string $yuiPath): ModuleOptions
42 | {
43 | $this->yuiPath = $yuiPath;
44 | return $this;
45 | }
46 |
47 | public function getYuiPath(): string
48 | {
49 | return $this->yuiPath;
50 | }
51 |
52 | public function setJavaPath(string $javaPath): ModuleOptions
53 | {
54 | $this->javaPath = $javaPath;
55 | return $this;
56 | }
57 |
58 | public function getJavaPath(): string
59 | {
60 | return $this->javaPath;
61 | }
62 |
63 | public function setUglifyJs2Path(string $uglifyJs2Path): ModuleOptions
64 | {
65 | $this->uglifyJs2Path = $uglifyJs2Path;
66 | return $this;
67 | }
68 |
69 | public function getUglifyJs2Path(): string
70 | {
71 | return $this->uglifyJs2Path;
72 | }
73 |
74 | public function setSassPath(string $sassPath): ModuleOptions
75 | {
76 | $this->sassPath = $sassPath;
77 | return $this;
78 | }
79 |
80 | public function getSassPath(): string
81 | {
82 | return $this->sassPath;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/module/ExAssetic/src/Options/ModuleOptionsFactory.php:
--------------------------------------------------------------------------------
1 | get('config')['ex_assetic']);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/module/ExCodeception/src/BaseFixture.php:
--------------------------------------------------------------------------------
1 | dataFile) {
33 | $this->items = require $this->dataFile;
34 | }
35 |
36 | foreach ($this->items as $item) {
37 | /** @var ClassMetadataInfo $metadata */
38 | $metadata = $manager->getClassMetadata($this->entityClass);
39 | $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO);
40 |
41 | $entity = new $this->entityClass;
42 |
43 | foreach ($item as $name => $value) {
44 | $setValueMethodName = $this->createSetValueMethodName($name);
45 |
46 | if (method_exists($this, $setValueMethodName)) {
47 | $this->$setValueMethodName($entity, $item, $name);
48 | } else {
49 | $entity->$setValueMethodName($value);
50 | }
51 |
52 | if ($this->reference) {
53 | $this->setReference($this->reference . $item['id'], $entity);
54 | }
55 | }
56 |
57 | $manager->persist($entity);
58 | }
59 | $manager->flush();
60 | }
61 |
62 | public function getOrder(): int
63 | {
64 | return 0;
65 | }
66 |
67 | protected function createSetValueMethodName(string $name): string
68 | {
69 | $ucfirstName = ucfirst($name);
70 | $setValueMethodName = 'set' . $ucfirstName;
71 |
72 | return $setValueMethodName;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/module/ExCodeception/src/Module/Fixture.php:
--------------------------------------------------------------------------------
1 | config['fixturePaths'];
27 |
28 | foreach ($fixturePaths as $path) {
29 | $fullPath = $this->createFullPath($path);
30 | if (is_dir($fullPath) === true) {
31 | $loader->loadFromDirectory($fullPath);
32 | }
33 | }
34 |
35 | $this->pushFixtures($loader->getFixtures());
36 | }
37 |
38 | public function _getEntityManager(): EntityManagerInterface
39 | {
40 | return $this->getModule('\ExCodeception\Module\ZF3')->_getEntityManager();
41 | }
42 |
43 | /**
44 | * @param \Doctrine\Common\DataFixtures\FixtureInterface[] $fixtures
45 | * @return void
46 | */
47 | public function pushFixtures(array $fixtures): void
48 | {
49 | $purger = new ORMPurger();
50 | $executor = new ORMExecutor($this->_getEntityManager(), $purger);
51 | $executor->execute($fixtures);
52 | }
53 |
54 | /**
55 | * @param string $path
56 | * @return string
57 | */
58 | public function createFullPath(string $path): string
59 | {
60 | return Configuration::testsDir() . $path;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/module/ExDebugBar/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/module/ExDebugBar/config/assets.config.php:
--------------------------------------------------------------------------------
1 | [
11 | 'routes' => [
12 | '.*' => [
13 | '@ex_debugbar_css',
14 | '@ex_debugbar_js',
15 | ],
16 | ],
17 | 'modules' => [
18 | 'ExDebugBar' => [
19 | 'root_path' => __DIR__ . '/../assets',
20 | 'collections' => [
21 | 'ex_debugbar_js' => [
22 | 'assets' => [
23 | getcwd() . '/node_modules/jquery/dist/jquery.min.js',
24 | $debugBarRes . '/vendor/highlightjs/highlight.pack.js',
25 | $debugBarRes . '/debugbar.js',
26 | $debugBarRes . '/widgets.js',
27 | $debugBarRes . '/openhandler.js',
28 | $debugBarRes . '/widgets/sqlqueries/widget.js',
29 | ],
30 | 'options' => [
31 | 'output' => 'js/ex-debugbar.js',
32 | ],
33 | ],
34 | 'ex_debugbar_css' => [
35 | 'assets' => [
36 | $debugBarRes . '/vendor/font-awesome/css/font-awesome.min.css',
37 | $debugBarRes . '/vendor/highlightjs/styles/github.css',
38 | $debugBarRes . '/debugbar.css',
39 | $debugBarRes . '/widgets.css',
40 | $debugBarRes . '/openhandler.css',
41 | $debugBarRes . '/widgets/sqlqueries/widget.css',
42 | getcwd() . '/vendor/bupy7/zf-php-debug-bar/assets/zf-snap-php-debug-bar.css',
43 | ],
44 | 'options' => [
45 | 'output' => 'css/ex-debugbar.css',
46 | ],
47 | ],
48 | ],
49 | ],
50 | ],
51 | ],
52 | ];
53 |
--------------------------------------------------------------------------------
/module/ExDebugBar/config/module.config.php:
--------------------------------------------------------------------------------
1 | [
12 | 'auto-append-assets' => false,
13 | 'collectors' => [
14 | DoctrineCollector::class,
15 | ],
16 | ],
17 | 'service_manager' => [
18 | 'delegators' => [
19 | 'doctrine.configuration.orm_default' => [
20 | DoctrineConfigurationDelegatorFactory::class,
21 | ],
22 | ],
23 | ],
24 | ];
25 |
--------------------------------------------------------------------------------
/module/ExDebugBar/src/Module.php:
--------------------------------------------------------------------------------
1 | [
11 | 'factories' => [
12 | 'ExDoctrine\Hydrator\ObjectHydrator' => ObjectHydratorFactory::class,
13 | ],
14 | ],
15 | ];
16 |
--------------------------------------------------------------------------------
/module/ExDoctrine/config/rbac.config.php:
--------------------------------------------------------------------------------
1 | [
12 | 'guards' => [
13 | RoutePermissionsGuard::class => [
14 | 'doctrine_cli' => ['doctrine'],
15 | ],
16 | ],
17 | 'role_provider' => [
18 | InMemoryRoleProvider::class => [
19 | 'guest' => [
20 | 'permissions' => [
21 | 'doctrine'
22 | ],
23 | ],
24 | ],
25 | ],
26 | ],
27 | ];
28 |
--------------------------------------------------------------------------------
/module/ExDoctrine/src/Hydrator/ObjectHydratorFactory.php:
--------------------------------------------------------------------------------
1 | get('Doctrine\ORM\EntityManager'));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/module/ExDoctrine/src/Module.php:
--------------------------------------------------------------------------------
1 | get('Doctrine\ORM\EntityManager');
18 | $entityName = preg_replace(['/\\\\Repository/', '/Repository$/'], ['\\Entity', ''], $requestedName);
19 | return $em->getRepository($entityName);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/module/ExDoctrine/src/Type/DateTimeType.php:
--------------------------------------------------------------------------------
1 | setTimezone($this->getDbTz());
17 | }
18 | return parent::convertToDatabaseValue($value, $platform);
19 | }
20 |
21 | public function convertToPHPValue($value, AbstractPlatform $platform)
22 | {
23 | if ($value === null || $value instanceof DateTime) {
24 | return $value;
25 | }
26 | $dt = DateTime::createFromFormat($platform->getDateTimeFormatString(), $value, $this->getDbTz());
27 | if (!$dt) {
28 | throw ConversionException::conversionFailedFormat(
29 | $value,
30 | $this->getName(),
31 | $platform->getDateTimeFormatString()
32 | );
33 | }
34 | $dt->setTimezone($this->getAppTz());
35 | return $dt;
36 | }
37 |
38 | /**
39 | * @var DateTimeZone
40 | */
41 | private static $dbTz;
42 |
43 | protected function getDbTz(): DateTimeZone
44 | {
45 | if (self::$dbTz === null) {
46 | self::$dbTz = new DateTimeZone('UTC');
47 | }
48 | return self::$dbTz;
49 | }
50 |
51 | /**
52 | * @var DateTimeZone
53 | */
54 | private static $appTz;
55 |
56 | protected function getAppTz(): DateTimeZone
57 | {
58 | if (self::$appTz === null) {
59 | self::$appTz = new DateTimeZone(date_default_timezone_get());
60 | }
61 | return self::$appTz;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/module/ExPaginator/src/Adapter/DoctrineAdapter.php:
--------------------------------------------------------------------------------
1 | count = $collection->count();
28 | } elseif (is_array($collection)) {
29 | $this->count = $count;
30 | } else {
31 | throw new InvalidArgumentException('Iterator must implement ' . Collection::class . ' or array');
32 | }
33 | $this->collection = $collection;
34 | }
35 |
36 | /**
37 | * Returns an iterator of items for a page, or an empty array.
38 | * @param int $offset Page offset
39 | * @param int $itemCountPerPage Number of items per page
40 | * @return Collection|array
41 | */
42 | public function getItems($offset, $itemCountPerPage)
43 | {
44 | if ($this->count == 0) {
45 | return [];
46 | }
47 | return $this->collection;
48 | }
49 |
50 | /**
51 | * Returns the total number of rows in the collection.
52 | */
53 | public function count(): int
54 | {
55 | return $this->count;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/module/ExRbac/src/Module.php:
--------------------------------------------------------------------------------
1 | getApplication();
20 | $services = $app->getServiceManager();
21 |
22 | // redirect to 403 Forbidden
23 | if ($e->getRequest() instanceof HttpRequest) {
24 | $services->get('ZfcRbac\View\Strategy\RedirectStrategy')->attach($app->getEventManager());
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/module/ExTwig/config/module.config.php:
--------------------------------------------------------------------------------
1 | [
7 | 'extensions' => [
8 | 'class' => Extension\ClassExtension::class,
9 | ],
10 | ],
11 | ];
12 |
--------------------------------------------------------------------------------
/module/ExTwig/src/Extension/ClassExtension.php:
--------------------------------------------------------------------------------
1 | new Twig_SimpleFunction('get_class', [$this, 'getClassName']),
26 | ];
27 | }
28 |
29 | /**
30 | * {@inheritDoc}
31 | */
32 | public function getTests()
33 | {
34 | return [
35 | 'instanceof' => new Twig_SimpleTest('instanceof', [$this, 'isInstanceOf']),
36 | ];
37 | }
38 |
39 | /**
40 | * @param object $object
41 | * @return string
42 | */
43 | public function getClassName($object)
44 | {
45 | return get_class($object);
46 | }
47 |
48 | /**
49 | * @param object|string $actualClass
50 | * @param object|string $expectClass
51 | * @return boolean
52 | */
53 | public function isInstanceOf($actualClass, $expectClass)
54 | {
55 | return $actualClass instanceof $expectClass;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/module/ExTwig/src/Module.php:
--------------------------------------------------------------------------------
1 | [
11 | 'delegators' => [
12 | 'translator' => [
13 | TranslatorDelegatorFactory::class,
14 | ],
15 | ],
16 | ],
17 | ];
18 |
--------------------------------------------------------------------------------
/module/ExValidate/src/Delegator/TranslatorDelegatorFactory.php:
--------------------------------------------------------------------------------
1 | addTranslationFilePattern('phparray', __DIR__ . '/../../language', '%s.php', 'ExValidate');
20 | return $translator;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/module/ExValidate/src/Module.php:
--------------------------------------------------------------------------------
1 | getApplication()->getServiceManager()->get('translator');
22 | AbstractValidator::setDefaultTranslator($translator, 'ExValidate');
23 | }
24 |
25 | /**
26 | * {@inheritDoc}
27 | */
28 | public function getConfig()
29 | {
30 | return require __DIR__ . '/../config/module.config.php';
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/module/Mail/config/module.config.php:
--------------------------------------------------------------------------------
1 | [
7 | 'supportEmail' => 'no-reply@zf-app-blank.com',
8 | ],
9 | 'mailgun' => [
10 | 'hydrator' => Hydrator\ModelHydrator::class,
11 | ],
12 | 'service_manager' => [
13 | 'factories' => [
14 | Service\MailService::class => Service\MailServiceFactory::class,
15 | Options\ModuleOptions::class => Options\ModuleOptionsFactory::class,
16 | Message\MessageBuilder::class => Message\MessageBuilderFactory::class,
17 | ],
18 | 'shared' => [
19 | Message\MessageBuilder::class => false,
20 | ],
21 | ],
22 | ];
23 |
--------------------------------------------------------------------------------
/module/Mail/src/Hydrator/ModelHydrator.php:
--------------------------------------------------------------------------------
1 | getBody()->__toString();
20 | $data = json_decode($body, true);
21 | if (JSON_ERROR_NONE !== json_last_error()) {
22 | throw new HydrationException(sprintf('Error (%d) when trying to json_decode response', json_last_error()));
23 | }
24 | if (is_subclass_of($class, ApiResponse::class)) {
25 | $object = call_user_func($class.'::create', $data);
26 | } else {
27 | $object = new $class($data);
28 | }
29 | return $object;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/module/Mail/src/Message/MessageBuilder.php:
--------------------------------------------------------------------------------
1 | translator = $translator;
24 | $this->viewRenderer = $viewRenderer;
25 | }
26 |
27 | public function setRenderHtmlBody(string $template, array $variables): string
28 | {
29 | $viewModel = new ViewModel($variables);
30 | $viewModel->setTemplate($template);
31 | return parent::setHtmlBody($this->viewRenderer->render($viewModel));
32 | }
33 |
34 | public function setTranslateSubject(string $message, string $domain): string
35 | {
36 | return parent::setSubject($this->translator->translate($message, $domain));
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/module/Mail/src/Message/MessageBuilderFactory.php:
--------------------------------------------------------------------------------
1 | get('translator'), $container->get('ZfcTwigRenderer'));
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/module/Mail/src/Module.php:
--------------------------------------------------------------------------------
1 | supportEmail = $supportEmail;
25 | return $this;
26 | }
27 |
28 | /**
29 | * @return string
30 | */
31 | public function getSupportEmail()
32 | {
33 | return $this->supportEmail;
34 | }
35 |
36 | /**
37 | * @param string $domain
38 | * @return static
39 | */
40 | public function setDomain($domain)
41 | {
42 | $this->domain = $domain;
43 | return $this;
44 | }
45 |
46 | /**
47 | * @return string
48 | */
49 | public function getDomain()
50 | {
51 | return $this->domain;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/module/Mail/src/Options/ModuleOptionsFactory.php:
--------------------------------------------------------------------------------
1 | get('config')['mail']);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/module/Mail/src/Service/MailService.php:
--------------------------------------------------------------------------------
1 | client = $client;
31 | $this->supportEmail = $supportEmail;
32 | $this->domain = $domain;
33 | $this->messageBuilder = $messageBuilder;
34 | }
35 |
36 | public function send(MessageBuilder $message): ApiResponse
37 | {
38 | $message->setFromAddress($this->supportEmail);
39 | return $this->client->messages()->send($this->domain, $message->getMessage());
40 | }
41 |
42 | public function createMessageBuilder(): MessageBuilder
43 | {
44 | return clone $this->messageBuilder;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/module/Mail/src/Service/MailServiceFactory.php:
--------------------------------------------------------------------------------
1 | get('Mail\Options\ModuleOptions');
13 | return new MailService(
14 | $container->get('Bupy7\Mailgun\Service\MailgunService'),
15 | $options->getDomain(),
16 | $options->getSupportEmail(),
17 | $container->get('Mail\Message\MessageBuilder')
18 | );
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/module/User/codeception.yml:
--------------------------------------------------------------------------------
1 | namespace: User\Test
2 | paths:
3 | tests: test
4 | output: test/_output
5 | data: test/_data
6 | support: test/_support
7 | envs: test/_envs
8 | actor_suffix: Tester
9 | extensions:
10 | enabled:
11 | - Codeception\Extension\RunFailed
12 | settings:
13 | bootstrap: _bootstrap.php
14 | colors: true
15 | memory_limit: 1024M
16 | coverage:
17 | enabled: true
18 | include:
19 | - src/*
20 |
--------------------------------------------------------------------------------
/module/User/config/doctrine.config.php:
--------------------------------------------------------------------------------
1 | [
13 | 'authentication' => [
14 | 'orm_default' => [
15 | 'object_manager' => EntityManager::class,
16 | 'identity_class' => User::class,
17 | 'identity_property' => 'email',
18 | 'credential_property' => 'password',
19 | ],
20 | ],
21 | 'driver' => [
22 | 'user_entity' => [
23 | 'class' => 'Doctrine\ORM\Mapping\Driver\XmlDriver',
24 | 'paths' => __DIR__ . '/mapping',
25 | ],
26 | 'orm_default' => [
27 | 'drivers' => [
28 | 'User\Entity' => 'user_entity',
29 | ],
30 | ],
31 | ],
32 | 'fixtures' => [
33 | 'User' => __DIR__ . '/../test/fixture',
34 | ],
35 | ],
36 | 'doctrine_factories' => [
37 | 'authenticationadapter' => AuthAdapterFactory::class,
38 | ],
39 | ];
40 |
--------------------------------------------------------------------------------
/module/User/config/mapping/User.Entity.User.dcm.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/module/User/config/module.config.php:
--------------------------------------------------------------------------------
1 | [
11 | 'translation_file_patterns' => [
12 | [
13 | 'type' => 'phparray',
14 | 'base_dir' => __DIR__ . '/../language',
15 | 'pattern' => '%s.php',
16 | 'text_domain' => __NAMESPACE__,
17 | ],
18 | ],
19 | ],
20 | 'view_manager' => [
21 | 'template_path_stack' => [
22 | __DIR__ . '/../view',
23 | ],
24 | 'template_map' => [
25 | 'user/mail/confirm-request' => __DIR__ . '/../view/mail/confirm-request.twig',
26 | 'user/mail/restore-pass' => __DIR__ . '/../view/mail/restore-pass.twig'
27 | ],
28 | ],
29 | 'controllers' => [
30 | 'factories' => [
31 | Controller\AuthController::class => Controller\AuthControllerFactory::class,
32 | Controller\SignupController::class => Controller\SignupControllerFactory::class,
33 | Controller\ConfirmEmailController::class => Controller\ConfirmEmailControllerFactory::class,
34 | Controller\AccessController::class => Controller\AccessControllerFactory::class,
35 | Controller\IndexController::class => LazyControllerAbstractFactory::class,
36 | ],
37 | ],
38 | 'service_manager' => [
39 | 'factories' => [
40 | Form\SignUpForm::class => Form\SignUpFormFactory::class,
41 |
42 | \Zend\Authentication\AuthenticationService::class => Service\AuthServiceFactory::class,
43 | Service\SignUpService::class => Service\SignUpServiceFactory::class,
44 | Service\ConfirmEmailService::class => ReflectionBasedAbstractFactory::class,
45 | Service\AccessService::class => Service\AccessServiceFactory::class,
46 |
47 | \Zend\Crypt\Password\BcryptSha::class => \Zend\ServiceManager\Factory\InvokableFactory::class,
48 |
49 | Search\UserSearch::class => ReflectionBasedAbstractFactory::class,
50 |
51 | Repository\UserRepository::class => RepositoryInvokableFactory::class,
52 | ],
53 | 'aliases' => [
54 | 'User\Crypt\PasswordCrypt' => \Zend\Crypt\Password\BcryptSha::class,
55 | ],
56 | ],
57 | ];
58 |
--------------------------------------------------------------------------------
/module/User/config/navigation.config.php:
--------------------------------------------------------------------------------
1 | [
9 | 'guestright' => [
10 | [
11 | 'label' => 'MENU_SIGNIN',
12 | 'route' => 'signin',
13 | 'icon' => 'fa fa-sign-in',
14 | 'text_domain' => 'User',
15 | ],
16 | [
17 | 'label' => 'MENU_SIGNUP',
18 | 'route' => 'signup',
19 | 'icon' => 'fa fa-user-plus',
20 | 'text_domain' => 'User',
21 | ],
22 | ],
23 | 'authright' => [
24 | [
25 | 'label' => 'MENU_LOGOUT',
26 | 'route' => 'logout',
27 | 'icon' => 'fa fa-sign-out',
28 | 'text_domain' => 'User',
29 | ],
30 | ],
31 | ],
32 | ];
33 |
--------------------------------------------------------------------------------
/module/User/config/rbac.config.php:
--------------------------------------------------------------------------------
1 | [
8 | 'role_provider' => [
9 | InMemoryRoleProvider::class => [
10 | 'registered' => [
11 | 'permissions' => [
12 | 'user/index/index',
13 | ],
14 | ],
15 | 'guest' => [
16 | 'permissions' => [
17 | 'user/auth/signin',
18 | 'user/auth/logout',
19 | 'user/signup/signup',
20 | 'user/confirm-email/confirm',
21 | 'user/confirm-again/again',
22 | 'user/access/forgot-pass',
23 | 'user/access/restore-pass',
24 | ],
25 | ],
26 | ],
27 | ],
28 | 'guards' => [
29 | RoutePermissionsGuard::class => [
30 | 'home' => 'user/index/index',
31 |
32 | 'signin' => 'user/auth/signin',
33 | 'logout' => 'user/auth/logout',
34 |
35 | 'signup' => 'user/signup/signup',
36 |
37 | 'confirm-email' => 'user/confirm-email/confirm',
38 |
39 | 'confirm-again' => 'user/confirm-again/again',
40 |
41 | 'forgot-pass' => 'user/access/forgot-pass',
42 | 'restore-pass' => 'user/access/restore-pass',
43 | ],
44 | ],
45 | ],
46 | ];
47 |
--------------------------------------------------------------------------------
/module/User/config/router.config.php:
--------------------------------------------------------------------------------
1 | [
13 | 'routes' => [
14 | 'home' => [
15 | 'type' => Literal::class,
16 | 'options' => [
17 | 'route' => '/',
18 | 'defaults' => [
19 | 'controller' => IndexController::class,
20 | 'action' => 'index',
21 | ],
22 | ],
23 | ],
24 | 'signin' => [
25 | 'type' => Literal::class,
26 | 'options' => [
27 | 'route' => '/signin',
28 | 'defaults' => [
29 | 'controller' => AuthController::class,
30 | 'action' => 'signin',
31 | ],
32 | ],
33 | ],
34 | 'logout' => [
35 | 'type' => Literal::class,
36 | 'options' => [
37 | 'route' => '/logout',
38 | 'defaults' => [
39 | 'controller' => AuthController::class,
40 | 'action' => 'logout',
41 | ],
42 | ],
43 | ],
44 | 'signup' => [
45 | 'type' => Literal::class,
46 | 'options' => [
47 | 'route' => '/signup',
48 | 'defaults' => [
49 | 'controller' => SignupController::class,
50 | 'action' => 'signup',
51 | ],
52 | ],
53 | ],
54 | 'confirm-email' => [
55 | 'type' => Segment::class,
56 | 'options' => [
57 | 'route' => '/confirm-email/:e/:k',
58 | 'defaults' => [
59 | 'controller' => ConfirmEmailController::class,
60 | 'action' => 'confirm',
61 | ],
62 | ],
63 | ],
64 | 'confirm-again' => [
65 | 'type' => Literal::class,
66 | 'options' => [
67 | 'route' => '/confirm-again',
68 | 'defaults' => [
69 | 'controller' => ConfirmEmailController::class,
70 | 'action' => 'again',
71 | ],
72 | ],
73 | ],
74 | 'forgot-pass' => [
75 | 'type' => Literal::class,
76 | 'options' => [
77 | 'route' => '/forgot-pass',
78 | 'defaults' => [
79 | 'controller' => AccessController::class,
80 | 'action' => 'forgot-pass',
81 | ],
82 | ],
83 | ],
84 | 'restore-pass' => [
85 | 'type' => Segment::class,
86 | 'options' => [
87 | 'route' => '/restore-pass/:e/:k',
88 | 'defaults' => [
89 | 'controller' => AccessController::class,
90 | 'action' => 'restore-pass',
91 | ],
92 | ],
93 | ],
94 | ],
95 | ],
96 | ];
97 |
--------------------------------------------------------------------------------
/module/User/language/en.php:
--------------------------------------------------------------------------------
1 | 'Username',
5 | 'LABEL_EMAIL' => 'Email',
6 | 'LABEL_PASSWORD' => 'Password',
7 | 'LABEL_PERSON' => 'Name',
8 | 'LABEL_SIGNIN' => 'Sign in',
9 | 'LABEL_SIGNUP' => 'Sign up',
10 | 'LABEL_SEND_CONFIRM_KEY' => 'Send',
11 | 'LABEL_FORGOT_PASS' => 'Did you forget password?',
12 | 'LABEL_SEND_RESTORE_KEY' => 'Send',
13 | 'LABEL_RESTORE_PASS' => 'Save',
14 | 'LABEL_NEW_PASSWORD' => 'New password',
15 | 'LABEL_YES' => 'Yes',
16 | 'LABEL_NO' => 'No',
17 |
18 | 'PAGE_TITLE_SIGNUP' => 'Signup',
19 | 'PAGE_TITLE_SIGNIN' => 'Signin',
20 | 'PAGE_CONFIRM_AGAIN' => 'Email validation',
21 | 'PAGE_FORGOT_PASS' => 'Password recovery',
22 | 'PAGE_TITLE_USER_LIST' => 'Users',
23 |
24 | 'SUCCESS_SIGNUP' => 'You have registered successfully! Check your mail inbox in order to verify his '
25 | . 'Email address.',
26 | 'SUCCESS_SIGNIN' => 'You have logged successfully!',
27 | 'SUCCESS_CONFIRM_EMAIL' => 'You have confirmed successfully his Email address.',
28 | 'SUCCESS_SENT_CONFIRM_KEY_AGAIN' => 'Key had been sent successfully to your Email address.',
29 | 'SUCCESS_SENT_RESTORE_KEY' => 'Key had been sent successfully to your Email address.',
30 | 'SUCCESS_RESTORE_PASS' => 'Password had been recovered successfully!',
31 |
32 | 'FAILED_SIGNIN' => 'Invalid login/password.',
33 | 'FAILED_SENT_CONFIRM_KEY_AGAIN' => 'The user not found either Email had been confirmed.',
34 | 'FAILED_SIGNIN_DIDNT_CONFIRM' => 'The user didn\'t still confirmed his Email. Send [confirm key again](%s) '
35 | . 'if you didn\'t receive it.',
36 | 'FAILED_SEND_RESTORE_KEY' => 'The user not found.',
37 | 'FAILED_RESTORE_PASS' => 'The user not found either the waiting time recovery password had expired.',
38 |
39 | 'MENU_SIGNIN' => 'Sign in',
40 | 'MENU_SIGNUP' => 'Sign up',
41 | 'MENU_LOGOUT' => 'Log out',
42 |
43 | 'EMAIL_SUBJECT_CONFIRM_EMAIL' => 'Confirm Email address',
44 | 'EMAIL_BODY_CONFIRM_EMAIL' => 'For confirm Email address you should follow by link: %s',
45 | 'EMAIL_HELLO' => 'Hello, %s!',
46 | 'EMAIL_WITH_THE_RESPECT' => 'With respect, %s.',
47 | 'EMAIL_CONFIRM_EMAIL' => 'Confirm',
48 | 'EMAIL_SUBJECT_RESTORE_PASS' => 'Recovering password',
49 | 'EMAIL_RESTORE_PASS' => 'Recovery password',
50 | 'EMAIL_BODY_RESTORE_PASS' => 'For recovery password you should follow by link: %s',
51 |
52 | 'WARNING_CONFIRM_EMAIL_NOT_FOUND' => 'The user not found either Email has been confirmed.',
53 |
54 | 'COLUMN_USERNAME' => 'Username',
55 | 'COLUMN_EMAIL' => 'Email',
56 | 'COLUMN_ROLE' => 'Role',
57 | 'COLUMN_EMAIL_CONFIRM' => 'Confirmed Email',
58 | 'COLUMN_CREATED_AT' => 'Date/time',
59 | ];
60 |
--------------------------------------------------------------------------------
/module/User/language/ru.php:
--------------------------------------------------------------------------------
1 | 'Имя пользователя',
5 | 'LABEL_EMAIL' => 'Email',
6 | 'LABEL_PASSWORD' => 'Пароль',
7 | 'LABEL_PERSON' => 'Имя',
8 | 'LABEL_SIGNIN' => 'Войти',
9 | 'LABEL_SIGNUP' => 'Зарегистрироваться',
10 | 'LABEL_SEND_CONFIRM_KEY' => 'Отправить',
11 | 'LABEL_FORGOT_PASS' => 'Забыли пароль?',
12 | 'LABEL_SEND_RESTORE_KEY' => 'Отправить',
13 | 'LABEL_RESTORE_PASS' => 'Сохранить',
14 | 'LABEL_NEW_PASSWORD' => 'Новый пароль',
15 | 'LABEL_YES' => 'Да',
16 | 'LABEL_NO' => 'Нет',
17 |
18 | 'PAGE_TITLE_SIGNUP' => 'Регистрация',
19 | 'PAGE_TITLE_SIGNIN' => 'Форма входа',
20 | 'PAGE_CONFIRM_AGAIN' => 'Подтверждение Email адреса',
21 | 'PAGE_FORGOT_PASS' => 'Восстановление пароля',
22 | 'PAGE_TITLE_USER_LIST' => 'Пользователи',
23 |
24 | 'SUCCESS_SIGNUP' => 'Регистрация завершилась успешно! Проверьте Ваш почтовый ящик, чтобы подтвердить Email.',
25 | 'SUCCESS_SIGNIN' => 'Вы успешно вошли в систему!',
26 | 'SUCCESS_CONFIRM_EMAIL' => 'Вы успешно подтвердили Email адрес.',
27 | 'SUCCESS_SENT_CONFIRM_KEY_AGAIN' => 'Ключ для подтверждения Email успешно отправлен.',
28 | 'SUCCESS_SENT_RESTORE_KEY' => 'Ключ для восстановления пароля отправлен Вам на Email адрес.',
29 | 'SUCCESS_RESTORE_PASS' => 'Пароль был успешно восстановлен!',
30 |
31 | 'FAILED_SIGNIN' => 'Неверный логин/пароль.',
32 | 'FAILED_SENT_CONFIRM_KEY_AGAIN' => 'Пользователь не найден или Email уже был подтвержден.',
33 | 'FAILED_SIGNIN_DIDNT_CONFIRM' => 'Пользователь еще не подтвердил свой Email адрес. Если вы не получали письмо с '
34 | . 'ключом подтверждения, то отправьте его [повторно](%s).',
35 | 'FAILED_SEND_RESTORE_KEY' => 'Пользователь с таким Email не найден.',
36 | 'FAILED_RESTORE_PASS' => 'Пользователь не найден или время ожидания сброса пароля истекло.',
37 |
38 | 'MENU_SIGNIN' => 'Войти',
39 | 'MENU_SIGNUP' => 'Регистрация',
40 | 'MENU_LOGOUT' => 'Выйти',
41 |
42 | 'EMAIL_SUBJECT_CONFIRM_EMAIL' => 'Подтверждение Email адреса',
43 | 'EMAIL_BODY_CONFIRM_EMAIL' => 'Для подтверждения Email адреса перейдите по следующей ссылке: %s',
44 | 'EMAIL_HELLO' => 'Здравствуйте, %s!',
45 | 'EMAIL_WITH_THE_RESPECT' => 'С ув., %s.',
46 | 'EMAIL_CONFIRM_EMAIL' => 'Подвердить',
47 | 'EMAIL_SUBJECT_RESTORE_PASS' => 'Восстановление пароля',
48 | 'EMAIL_RESTORE_PASS' => 'Восстановить пароль',
49 | 'EMAIL_BODY_RESTORE_PASS' => 'Для восстановления пароля перейдите по следующей ссылке: %s.',
50 |
51 | 'WARNING_CONFIRM_EMAIL_NOT_FOUND' => 'Пользователь не найден или Email уже был подтвержден.',
52 |
53 | 'COLUMN_USERNAME' => 'Имя',
54 | 'COLUMN_EMAIL' => 'Email',
55 | 'COLUMN_ROLE' => 'Роль',
56 | 'COLUMN_EMAIL_CONFIRM' => 'Подтвржд. Email',
57 | 'COLUMN_CREATED_AT' => 'Дата/время',
58 | ];
59 |
--------------------------------------------------------------------------------
/module/User/src/Adapter/AuthAdapter.php:
--------------------------------------------------------------------------------
1 | setup();
14 | $options = $this->options;
15 | $identity = $options->getObjectRepository()
16 | ->findOneBy([$options->getIdentityProperty() => $this->identity]);
17 | if (!$identity) {
18 | $this->authenticationResultInfo['code'] = Result::FAILURE_IDENTITY_NOT_FOUND;
19 | $this->authenticationResultInfo['messages'][] = 'A record with the supplied identity could not be found.';
20 | return $this->createAuthenticationResult();
21 | }
22 | if (!$identity->getEmailConfirm()) {
23 | return new Result(Result::FAILURE_DIDNT_CONFIRM, $this->identity);
24 | }
25 | $authResult = $this->validateIdentity($identity);
26 | return $authResult;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/module/User/src/Adapter/AuthAdapterFactory.php:
--------------------------------------------------------------------------------
1 | getOptions($container, 'authentication');
15 | if (is_string($objectManager = $options->getObjectManager())) {
16 | $options->setObjectManager($container->get($objectManager));
17 | }
18 | return new AuthAdapter($options);
19 | }
20 |
21 | public function createService(ServiceLocatorInterface $serviceLocator): AuthAdapter
22 | {
23 | return $this($serviceLocator, ObjectRepository::class);
24 | }
25 |
26 | public function getOptionsClass(): string
27 | {
28 | return 'DoctrineModule\Options\Authentication';
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/module/User/src/Auth/Result.php:
--------------------------------------------------------------------------------
1 | accessService = $accessService;
23 | }
24 |
25 | public function forgotPassAction()
26 | {
27 | $form = new ForgotPassForm;
28 | if ($this->getRequest()->isPost()) {
29 | if ($this->accessService->forgotPass($this->getRequest()->getPost()->toArray(), $form)) {
30 | $this->flashMessenger()->addSuccessMessage($this->translate('SUCCESS_SENT_RESTORE_KEY', 'User'));
31 | return $this->redirect()->refresh();
32 | } else {
33 | $this->flashMessenger()->addWarningMessage($this->translate('FAILED_SEND_RESTORE_KEY', 'User'));
34 | }
35 | }
36 | return new ViewModel(['forgotPassForm' => $form]);
37 | }
38 |
39 | public function restorePassAction()
40 | {
41 | $email = $this->params()->fromRoute('e');
42 | $key = $this->params()->fromRoute('k');
43 | $entity = $this->accessService->findForRestore($email, $key);
44 | if (!$entity) {
45 | $this->flashMessenger()->addWarningMessage($this->translate('FAILED_RESTORE_PASS', 'User'));
46 | return $this->redirect()->toRoute(self::ROUTE_TO_SIGNIN);
47 | }
48 | $form = new RestorePassForm;
49 | if ($this->getRequest()->isPost()) {
50 | if ($this->accessService->restorePass($this->getRequest()->getPost()->toArray(), $entity, $form)) {
51 | $this->flashMessenger()->addSuccessMessage($this->translate('SUCCESS_RESTORE_PASS', 'User'));
52 | return $this->redirect()->toRoute(self::ROUTE_TO_SIGNIN);
53 | }
54 | }
55 | return new ViewModel([
56 | 'restorePassForm' => $form,
57 | 'email' => $email,
58 | 'key' => $key,
59 | ]);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/module/User/src/Controller/AccessControllerFactory.php:
--------------------------------------------------------------------------------
1 | get('User\Service\AccessService'));
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/module/User/src/Controller/AuthController.php:
--------------------------------------------------------------------------------
1 | authService = $authService;
30 | $this->signInForm = $signInForm;
31 | }
32 |
33 | /**
34 | * @return mixed
35 | */
36 | public function signinAction()
37 | {
38 | if ($this->authService->getIdentity()) {
39 | return $this->redirect()->toRoute(self::ROUTE_TO_HOME);
40 | }
41 | $signInForm = $this->signInForm;
42 | if ($this->getRequest()->isPost()) {
43 | $signInForm->setValues($this->getRequest()->getPost());
44 | if ($signInForm->isValid()) {
45 | $auth = $this->authService;
46 | $auth->getAdapter()
47 | ->setIdentity($signInForm->email)
48 | ->setCredential($signInForm->password);
49 | $result = $auth->authenticate();
50 | if ($result->isValid()) {
51 | $this->flashMessenger()->addSuccessMessage($this->translate('SUCCESS_SIGNIN', 'User'));
52 | return $this->redirect()->toRoute(self::ROUTE_TO_HOME);
53 | } elseif ($result->getCode() == Result::FAILURE_DIDNT_CONFIRM) {
54 | $message = $this->translate('FAILED_SIGNIN_DIDNT_CONFIRM', 'User');
55 | $message = sprintf($message, $this->url()->fromRoute(self::ROUTE_TO_CONFIRM_AGAIN));
56 | $this->flashMessenger()->addWarningMessage($message);
57 | return $this->redirect()->toRoute(self::ROUTE_TO_HOME);
58 | }
59 | }
60 | $this->flashMessenger()->addErrorMessage($this->translate('FAILED_SIGNIN', 'User'));
61 | }
62 | return new ViewModel([
63 | 'signInForm' => $signInForm,
64 | ]);
65 | }
66 |
67 | public function logoutAction(): Response
68 | {
69 | $this->authService->clearIdentity();
70 | return $this->redirect()->toRoute(self::ROUTE_TO_SIGNIN);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/module/User/src/Controller/AuthControllerFactory.php:
--------------------------------------------------------------------------------
1 | get('Zend\Authentication\AuthenticationService');
17 | return new AuthController($authService, new SignInForm);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/module/User/src/Controller/ConfirmEmailController.php:
--------------------------------------------------------------------------------
1 | confirmEmailService = $confirmEmailService;
27 | $this->userRepository = $userRepository;
28 | }
29 |
30 | public function confirmAction()
31 | {
32 | $email = $this->params()->fromRoute('e');
33 | $key = $this->params()->fromRoute('k');
34 | $userEntity = $this->userRepository->findNotConfirm($email, $key);
35 | if ($userEntity === null) {
36 | $this->flashMessenger()
37 | ->addWarningMessage($this->translate('WARNING_CONFIRM_EMAIL_NOT_FOUND', 'User'));
38 | } else {
39 | if ($this->confirmEmailService->confirm($userEntity)) {
40 | $this->flashMessenger()
41 | ->addSuccessMessage($this->translate('SUCCESS_CONFIRM_EMAIL', 'User'));
42 | }
43 | }
44 | return $this->redirect()->toRoute(self::ROUTE_TO_SIGNIN);
45 | }
46 |
47 | public function againAction()
48 | {
49 | $form = new AgainConfirmForm;
50 | if ($this->getRequest()->isPost()) {
51 | $result = $this->confirmEmailService->again($this->getRequest()->getPost()->toArray(), $form);
52 | if ($result) {
53 | $this->flashMessenger()->addSuccessMessage($this->translate('SUCCESS_SENT_CONFIRM_KEY_AGAIN', 'User'));
54 | return $this->redirect()->toRoute(self::ROUTE_TO_SIGNIN);
55 | } else {
56 | $this->flashMessenger()->addWarningMessage($this->translate('FAILED_SENT_CONFIRM_KEY_AGAIN', 'User'));
57 | }
58 | }
59 | return new ViewModel([
60 | 'againForm' => $form,
61 | ]);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/module/User/src/Controller/ConfirmEmailControllerFactory.php:
--------------------------------------------------------------------------------
1 | get('User\Service\ConfirmEmailService'),
17 | $container->get('Doctrine\ORM\EntityManager')->getRepository('User\Entity\User')
18 | );
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/module/User/src/Controller/IndexController.php:
--------------------------------------------------------------------------------
1 | userSearch = $userSearch;
20 | }
21 |
22 | public function indexAction(): ViewModel
23 | {
24 | $searchForm = new UserSearchForm();
25 | $dataProvider = $this->userSearch->search((array)$this->params()->fromQuery(), $searchForm);
26 |
27 | return $this->asView([
28 | 'dataProvider' => $dataProvider,
29 | ]);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/module/User/src/Controller/SignupController.php:
--------------------------------------------------------------------------------
1 | authService = $authService;
54 | $this->signUpForm = $signUpForm;
55 | $this->userEntity = $userEntity;
56 | $this->entityHydrator = $entityHydrator;
57 | $this->signUpService = $signUpService;
58 | $this->userRepository = $userRepository;
59 | }
60 |
61 | public function signupAction()
62 | {
63 | if ($this->authService->getIdentity()) {
64 | return $this->redirect()->toRoute(self::ROUTE_TO_HOME);
65 | }
66 | $signUpForm = $this->signUpForm;
67 | if ($this->getRequest()->isPost()) {
68 | $signUpForm->setValues($this->getRequest()->getPost());
69 | if ($signUpForm->isValid()) {
70 | $values = $signUpForm->getValues();
71 | $userEntity = $this->entityHydrator->hydrate($values, clone $this->userEntity);
72 | if ($this->signUpService->signup($userEntity)) {
73 | $this->flashMessenger()
74 | ->addSuccessMessage($this->translate('SUCCESS_SIGNUP', 'User'));
75 | return $this->redirect()->toRoute(self::ROUTE_TO_SIGNIN);
76 | }
77 | }
78 | }
79 | return new ViewModel([
80 | 'signUpForm' => $signUpForm,
81 | ]);
82 | }
83 |
84 | public function confirmEmailAction(): Response
85 | {
86 | $email = $this->params()->fromRoute('e');
87 | $key = $this->params()->fromRoute('k');
88 | $userEntity = $this->userRepository->findNotConfirm($email, $key);
89 | if ($userEntity === null) {
90 | $this->flashMessenger()
91 | ->addWarningMessage($this->translate('WARNING_CONFIRM_EMAIL_NOT_FOUND', 'User'));
92 | } else {
93 | if ($this->signUpService->confirmEmail($userEntity)) {
94 | $this->flashMessenger()
95 | ->addSuccessMessage($this->translate('SUCCESS_CONFIRM_EMAIL', 'User'));
96 | }
97 | }
98 | return $this->redirect()->toRoute(self::ROUTE_TO_SIGNIN);
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/module/User/src/Controller/SignupControllerFactory.php:
--------------------------------------------------------------------------------
1 | get('Zend\Authentication\AuthenticationService');
13 | $signUpForm = $container->get('User\Form\SignUpForm');
14 | $userRepository = $container->get('Doctrine\ORM\EntityManager')->getRepository('User\Entity\User');
15 | $userEntityName = $userRepository->getClassName();
16 | $entityHydrator = $container->get('ExDoctrine\Hydrator\ObjectHydrator');
17 | $signUpService = $container->get('User\Service\SignUpService');
18 | return new SignupController(
19 | $authService,
20 | $signUpForm,
21 | new $userEntityName,
22 | $entityHydrator,
23 | $signUpService,
24 | $userRepository
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/module/User/src/Entity/User.php:
--------------------------------------------------------------------------------
1 | 'guest',
14 | self::ROLE_REGISTERED => 'registered',
15 | ];
16 |
17 | /**
18 | * @var integer
19 | */
20 | private $id;
21 | /**
22 | * @var string
23 | */
24 | private $person;
25 | /**
26 | * @var string
27 | */
28 | private $email;
29 | /**
30 | * @var string
31 | */
32 | private $password;
33 | /**
34 | * @var int
35 | */
36 | private $roleId;
37 | /**
38 | * @var boolean
39 | */
40 | private $emailConfirm = false;
41 | /**
42 | * @var string
43 | */
44 | private $confirmKey;
45 | /**
46 | * @var DateTime
47 | */
48 | private $createdAt;
49 | /**
50 | * @var string|null
51 | */
52 | private $restoreKey;
53 | /**
54 | * @var DateTime|null
55 | */
56 | private $restoreKeyExpire;
57 |
58 | /**
59 | * @return integer|null
60 | */
61 | public function getId(): ?int
62 | {
63 | return $this->id;
64 | }
65 |
66 | public function setId(int $id): User
67 | {
68 | $this->id = $id;
69 | return $this;
70 | }
71 |
72 | public function getPerson(): string
73 | {
74 | return $this->person;
75 | }
76 |
77 | public function setPerson(string $person): User
78 | {
79 | $this->person = $person;
80 | return $this;
81 | }
82 |
83 | public function getEmail(): string
84 | {
85 | return $this->email;
86 | }
87 |
88 | public function setEmail(string $email): User
89 | {
90 | $this->email = $email;
91 | return $this;
92 | }
93 |
94 | public function getPassword(): string
95 | {
96 | return $this->password;
97 | }
98 |
99 | public function setPassword(string $password): User
100 | {
101 | $this->password = $password;
102 | return $this;
103 | }
104 |
105 | /**
106 | * @return int|null
107 | */
108 | public function getRoleId()
109 | {
110 | return $this->roleId;
111 | }
112 |
113 | public function setRoleId(int $role): User
114 | {
115 | $this->roleId = $role;
116 | return $this;
117 | }
118 |
119 | public function getRoles(): array
120 | {
121 | return (array)(self::ROLE_MAP[$this->getRoleId()] ?? []);
122 | }
123 |
124 | public function getEmailConfirm(): bool
125 | {
126 | return $this->emailConfirm;
127 | }
128 |
129 | public function setEmailConfirm(bool $emailConfirm): User
130 | {
131 | $this->emailConfirm = $emailConfirm;
132 | return $this;
133 | }
134 |
135 | public function getConfirmKey(): string
136 | {
137 | return $this->confirmKey;
138 | }
139 |
140 | public function setConfirmKey(string $confirmKey): User
141 | {
142 | $this->confirmKey = $confirmKey;
143 | return $this;
144 | }
145 |
146 | public function setCreatedAt(DateTime $createdAt): User
147 | {
148 | $this->createdAt = $createdAt;
149 | return $this;
150 | }
151 |
152 | public function getCreatedAt(): DateTime
153 | {
154 | return $this->createdAt;
155 | }
156 |
157 | public function setRestoreKey(?string $restoreKey): User
158 | {
159 | $this->restoreKey = $restoreKey;
160 | return $this;
161 | }
162 |
163 | public function getRestoreKey(): ?string
164 | {
165 | return $this->restoreKey;
166 | }
167 |
168 | public function setRestoreKeyExpire(?DateTime $restoreKeyExpire): User
169 | {
170 | $this->restoreKeyExpire = $restoreKeyExpire;
171 | return $this;
172 | }
173 |
174 | public function getRestoreKeyExpire(): ?DateTime
175 | {
176 | return $this->restoreKeyExpire;
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/module/User/src/Form/AgainConfirmForm.php:
--------------------------------------------------------------------------------
1 | 'email',
19 | 'required' => true,
20 | 'validators' => [
21 | [
22 | 'name' => 'EmailAddress',
23 | ],
24 | ],
25 | ],
26 | ];
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/module/User/src/Form/ForgotPassForm.php:
--------------------------------------------------------------------------------
1 | 'email',
19 | 'required' => true,
20 | 'validators' => [
21 | [
22 | 'name' => 'EmailAddress',
23 | ],
24 | ],
25 | ],
26 | ];
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/module/User/src/Form/RestorePassForm.php:
--------------------------------------------------------------------------------
1 | 'password',
19 | 'required' => true,
20 | 'validators' => [
21 | [
22 | 'name' => 'StringLength',
23 | 'options' => [
24 | 'min' => 4,
25 | ],
26 | ]
27 | ],
28 | ],
29 | ];
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/module/User/src/Form/SignInForm.php:
--------------------------------------------------------------------------------
1 | 'email',
23 | 'required' => true,
24 | 'validators' => [
25 | [
26 | 'name' => 'EmailAddress',
27 | ],
28 | ],
29 | ],
30 | [
31 | 'name' => 'password',
32 | 'required' => true,
33 | ],
34 | ];
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/module/User/src/Form/SignUpForm.php:
--------------------------------------------------------------------------------
1 | userRepository = $userRepository;
35 | }
36 |
37 | /**
38 | * {@inheritDoc}
39 | */
40 | protected function inputs()
41 | {
42 | return [
43 | [
44 | 'name' => 'person',
45 | 'required' => true,
46 | 'validators' => [
47 | [
48 | 'name' => 'StringLength',
49 | 'options' => [
50 | 'max' => 255,
51 | ],
52 | ],
53 | ],
54 | ],
55 | [
56 | 'name' => 'email',
57 | 'required' => true,
58 | 'validators' => [
59 | [
60 | 'name' => 'EmailAddress',
61 | 'break_chain_on_failure' => true,
62 | ],
63 | [
64 | 'name' => NoObjectExists::class,
65 | 'options' => [
66 | 'object_repository' => $this->userRepository,
67 | 'fields' => 'email',
68 | ],
69 | ],
70 | ],
71 | ],
72 | [
73 | 'name' => 'password',
74 | 'required' => true,
75 | 'validators' => [
76 | [
77 | 'name' => 'StringLength',
78 | 'options' => [
79 | 'min' => 4,
80 | ],
81 | ]
82 | ],
83 | ],
84 | ];
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/module/User/src/Form/SignUpFormFactory.php:
--------------------------------------------------------------------------------
1 | get('Doctrine\ORM\EntityManager');
16 | return new SignUpForm($entityManager->getRepository('User\Entity\User'));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/module/User/src/Form/UserSearchForm.php:
--------------------------------------------------------------------------------
1 | '_page',
19 | 'allow_empty' => true,
20 | 'validators' => [
21 | [
22 | 'name' => 'Digits',
23 | ],
24 | ],
25 | ],
26 | ];
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/module/User/src/Module.php:
--------------------------------------------------------------------------------
1 | getApplication()->getServiceManager();
25 |
26 | // confirm email request
27 | $confirmEmailService = $sm->get('User\Service\ConfirmEmailService');
28 | $signUpEventManager = $sm->get('User\Service\SignUpService')->getEventManager();
29 | $signUpEventManager->attach('signup', function (EventInterface $event) use ($confirmEmailService) {
30 | $confirmEmailService->request($event->getParam('userEntity'));
31 | });
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/module/User/src/Repository/UserRepository.php:
--------------------------------------------------------------------------------
1 | findOneBy(['email' => $email, 'confirmKey' => $confirmKey]);
16 | if ($entity === null || $entity->getEmailConfirm()) {
17 | return null;
18 | }
19 | return $entity;
20 | }
21 |
22 | public function findForRestore(string $email, string $restoreKey): ?User
23 | {
24 | return $this->findOneBy(['email' => $email, 'restoreKey' => $restoreKey]);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/module/User/src/Search/UserSearch.php:
--------------------------------------------------------------------------------
1 | dataProvider = $dataProvider;
25 | $this->entityManager = $entityManager;
26 | }
27 |
28 | public function search(array $data, UserSearchForm $form): QueryDataProvider
29 | {
30 | // query
31 | $this->dataProvider->getQueryBuilder()->select('t')
32 | ->from('User\Entity\User', QueryDataProvider::MAIN_ENTITY_ALIAS)
33 | ->orderBy('t.id', 'DESC');
34 |
35 | // limit
36 | $this->dataProvider->setLimit(self::RESULT_LIMIT_DEFAULT);
37 |
38 | // validation
39 | $form->setValues($data);
40 | if ($form->isValid()) {
41 | $this->dataProvider->setPage($form->_page);
42 | $this->dataProvider->setQueryParams($form->getValues());
43 | }
44 |
45 | // counter
46 | $this->dataProvider->getCountBuilder()->resetDQLPart('orderBy');
47 |
48 | return $this->dataProvider;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/module/User/src/Service/AccessService.php:
--------------------------------------------------------------------------------
1 | mailService = $mailService;
46 | $this->userRepository = $userRepository;
47 | $this->passwordHashService = $passwordHashService;
48 | $this->entityManager = $entityManager;
49 | }
50 |
51 | public function forgotPass(array $data, ForgotPassForm $form): bool
52 | {
53 | $form->setValues($data);
54 | if (!$form->isValid()) {
55 | return false;
56 | }
57 |
58 | $entity = $this->userRepository->findOneByEmail($form->email);
59 | if (!$entity) {
60 | return false;
61 | }
62 |
63 | // generate restore key
64 | $restoreKeyExpire = new DateTime;
65 | $restoreKeyExpire->add(new DateInterval(sprintf('PT%dS', self::RESTORE_KEY_DURATION)));
66 | $entity->setRestoreKey(Rand::getString(self::RESTORE_KEY_LENGTH, self::RESTORE_KEY_DICT))
67 | ->setRestoreKeyExpire($restoreKeyExpire);
68 |
69 | $this->entityManager->persist($entity);
70 | $this->entityManager->flush();
71 |
72 | // send email
73 | $message = $this->mailService->createMessageBuilder();
74 | $message->addToRecipient($entity->getEmail(), ['full_name' => $entity->getPerson()]);
75 | $message->setTranslateSubject('EMAIL_SUBJECT_RESTORE_PASS', 'User');
76 | $message->setRenderHtmlBody('user/mail/restore-pass', ['userEntity' => $entity]);
77 | $this->mailService->send($message);
78 |
79 | return true;
80 | }
81 |
82 | public function findForRestore(string $email, string $key): ?User
83 | {
84 | $entity = $this->userRepository->findForRestore($email, $key);
85 | if (!$entity || $entity->getRestoreKeyExpire() < new DateTime) {
86 | return null;
87 | }
88 | return $entity;
89 | }
90 |
91 | public function restorePass(array $data, User $entity, RestorePassForm $form): bool
92 | {
93 | $form->setValues($data);
94 | if (!$form->isValid()) {
95 | return false;
96 | }
97 | $hashPassword = $this->passwordHashService->create($form->password);
98 | $entity->setPassword($hashPassword)
99 | ->setRestoreKeyExpire(new DateTime);
100 |
101 | $this->entityManager->persist($entity);
102 | $this->entityManager->flush();
103 |
104 | return true;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/module/User/src/Service/AccessServiceFactory.php:
--------------------------------------------------------------------------------
1 | get('Mail\Service\MailService'),
14 | $container->get('Doctrine\ORM\EntityManager')->getRepository('User\Entity\User'),
15 | $container->get('User\Crypt\PasswordCrypt'),
16 | $container->get('Doctrine\ORM\EntityManager')
17 | );
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/module/User/src/Service/AuthServiceFactory.php:
--------------------------------------------------------------------------------
1 | get('doctrine.authenticationservice.orm_default');
21 | $passHashService = $container->get('User\Crypt\PasswordCrypt');
22 | $authService
23 | ->getAdapter()
24 | ->getOptions()
25 | ->setCredentialCallable(function (User $user, $password) use ($passHashService) {
26 | return $passHashService->verify($password, $user->getPassword());
27 | });
28 | return $authService;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/module/User/src/Service/ConfirmEmailService.php:
--------------------------------------------------------------------------------
1 | mailService = $mailService;
32 | $this->userRepository = $userRepository;
33 | $this->entityManager = $entityManager;
34 | }
35 |
36 | public function request(User $user): bool
37 | {
38 | $message = $this->mailService->createMessageBuilder();
39 | $message->addToRecipient($user->getEmail(), [
40 | 'full_name' => $user->getPerson(),
41 | ]);
42 | $message->setTranslateSubject('EMAIL_SUBJECT_CONFIRM_EMAIL', 'User');
43 | $message->setRenderHtmlBody('user/mail/confirm-request', ['userEntity' => $user]);
44 | $this->mailService->send($message);
45 | return true;
46 | }
47 |
48 | public function confirm(User $user): bool
49 | {
50 | $user->setEmailConfirm(true);
51 |
52 | $this->entityManager->persist($user);
53 | $this->entityManager->flush();
54 |
55 | return true;
56 | }
57 |
58 | public function again(array $data, AgainConfirmForm $form): bool
59 | {
60 | $form->setValues($data);
61 | if (!$form->isValid()) {
62 | return false;
63 | }
64 |
65 | $user = $this->userRepository->findOneByEmail($form->email);
66 | if ($user === null || $user->getEmailConfirm()) {
67 | return false;
68 | }
69 |
70 | return $this->request($user);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/module/User/src/Service/SignUpService.php:
--------------------------------------------------------------------------------
1 | setIdentifiers([__CLASS__, get_called_class()]);
41 | $this->eventManager = $eventManager;
42 | return $this;
43 | }
44 |
45 | public function getEventManager(): EventManagerInterface
46 | {
47 | if ($this->eventManager === null) {
48 | $this->setEventManager(new EventManager());
49 | }
50 | return $this->eventManager;
51 | }
52 |
53 | public function __construct(
54 | UserRepository $userRepository,
55 | PasswordInterface $passwordHashService,
56 | EntityManager $entityManager
57 | ) {
58 | $this->userRepository = $userRepository;
59 | $this->passwordHashService = $passwordHashService;
60 | $this->entityManager = $entityManager;
61 | }
62 |
63 | public function signup(User $userEntity): bool
64 | {
65 | if ($userEntity->getRoleId() === null) {
66 | $userEntity->setRoleId(self::DEFAULT_ROLE);
67 | }
68 | $hashPassword = $this->passwordHashService->create($userEntity->getPassword());
69 | $userEntity->setPassword($hashPassword)
70 | ->setConfirmKey(Rand::getString(self::LENGTH_CONFIRM_KEY, self::CONFIRM_KEY_DICT))
71 | ->setCreatedAt(new DateTime);
72 |
73 | $this->entityManager->persist($userEntity);
74 | $this->entityManager->flush();
75 |
76 | if ($userEntity->getId() === null) {
77 | return false;
78 | }
79 | $this->getEventManager()->trigger(__FUNCTION__, $this, ['userEntity' => $userEntity]);
80 |
81 | return true;
82 | }
83 |
84 | public function confirmEmail(User $userEntity): bool
85 | {
86 | $userEntity->setEmailConfirm(true);
87 |
88 | $this->entityManager->persist($userEntity);
89 | $this->entityManager->flush();
90 |
91 | return true;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/module/User/src/Service/SignUpServiceFactory.php:
--------------------------------------------------------------------------------
1 | get('User\Repository\UserRepository'),
14 | $container->get('User\Crypt\PasswordCrypt'),
15 | $container->get('Doctrine\ORM\EntityManager')
16 | );
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/module/User/test/_bootstrap.php:
--------------------------------------------------------------------------------
1 | amOnRoute('signin');
12 | $I->seeResponseCodeIs(200);
13 |
14 | $I->submitForm('form', [
15 | 'email' => 'vasily@zf-app-blank.com',
16 | 'password' => '1234',
17 | ]);
18 | $I->assertTrue($I->grabService('Zend\Authentication\AuthenticationService')->hasIdentity());
19 | }
20 |
21 | public function logout(FunctionalTester $I)
22 | {
23 | $I->amAuthenticated('vasily@zf-app-blank.com', '1234');
24 |
25 | $I->amOnRoute('home');
26 | $I->seeResponseCodeIs(200);
27 |
28 | $I->seeLink('Log out', '/logout');
29 | $I->click('Log out');
30 | // cannot logout because of Codeception test release
31 | // we can check only status code after logout
32 | $I->seeResponseCodeIs(200);
33 | }
34 |
35 | public function signup(FunctionalTester $I)
36 | {
37 | $I->amOnRoute('signup');
38 |
39 | /** @var \Bupy7\Mailgun\Options\ModuleOptions $mailConfig */
40 | $mailConfig = $I->grabService('Bupy7\Mailgun\Options\ModuleOptions');
41 | if (empty($mailConfig->getEndpoint())) {
42 | throw new Exception('You should setup "endpoint" for Mailgun in your local config file.');
43 | }
44 | $mailConfig->setDebug(true);
45 |
46 | $I->submitForm('form', [
47 | 'person' => 'Test User',
48 | 'email' => 'test@zf-app-blank.com',
49 | 'password' => '1234',
50 | ]);
51 | $I->seeResponseCodeIs(200);
52 |
53 | $I->canSeeInRepository('User\Entity\User', ['email' => 'test@zf-app-blank.com']);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/module/User/test/functional/_bootstrap.php:
--------------------------------------------------------------------------------
1 | {{ translate('EMAIL_HELLO', 'User')|format(userEntity.getPerson() | e) }}
3 | {% set link %}
4 | {{ translate('EMAIL_CONFIRM_EMAIL', 'User') }}
10 | {% endset %}
11 | {{ translate('EMAIL_BODY_CONFIRM_EMAIL', 'User') | format(link) }}
12 | {% endblock content %}
13 |
--------------------------------------------------------------------------------
/module/User/view/mail/restore-pass.twig:
--------------------------------------------------------------------------------
1 | {% block content %}
2 | {{ translate('EMAIL_HELLO', 'User')|format(userEntity.getPerson() | e) }}
3 | {% set link %}
4 | {{ translate('EMAIL_RESTORE_PASS', 'User') }}
10 | {% endset %}
11 | {{ translate('EMAIL_BODY_RESTORE_PASS', 'User') | format(link) }}
12 | {% endblock content %}
13 |
--------------------------------------------------------------------------------
/module/User/view/user/access/forgot-pass.twig:
--------------------------------------------------------------------------------
1 | {% import 'macro/icon.twig' as icon %}
2 | {% import 'macro/form.twig' as form %}
3 |
4 | {% extends 'layout/layout.twig' %}
5 |
6 | {% set title = translate('PAGE_FORGOT_PASS', 'User') %}
7 |
8 | {% do headTitle(title) %}
9 |
10 | {% block content %}
11 | {{ title | e }}
12 |
13 | {% set formBuilder = formBuilder(forgotPassForm) %}
14 | {{ formBuilder
15 | .open()
16 | .action(url('forgot-pass'))
17 | }}
18 |
35 |
36 |
44 | {{ formBuilder.close() }}
45 | {% endblock content %}
46 |
--------------------------------------------------------------------------------
/module/User/view/user/access/restore-pass.twig:
--------------------------------------------------------------------------------
1 | {% import 'macro/icon.twig' as icon %}
2 | {% import 'macro/form.twig' as form %}
3 |
4 | {% extends 'layout/layout.twig' %}
5 |
6 | {% set title = translate('PAGE_FORGOT_PASS', 'User') %}
7 |
8 | {% do headTitle(title) %}
9 |
10 | {% block content %}
11 | {{ title | e }}
12 |
13 | {% set formBuilder = formBuilder(restorePassForm) %}
14 | {{ formBuilder
15 | .open()
16 | .action(url('restore-pass', {e: email, k: key}))
17 | }}
18 |
35 |
44 | {{ formBuilder.close() }}
45 | {% endblock content %}
46 |
--------------------------------------------------------------------------------
/module/User/view/user/auth/signin.twig:
--------------------------------------------------------------------------------
1 | {% import 'macro/icon.twig' as icon %}
2 | {% import 'macro/form.twig' as form %}
3 |
4 | {% extends 'layout/layout.twig' %}
5 |
6 | {% set title = translate('PAGE_TITLE_SIGNIN', 'User') %}
7 |
8 | {% do headTitle(title) %}
9 |
10 | {% block content %}
11 | {{ title | e }}
12 |
13 | {% set formBuilder = formBuilder(signInForm) %}
14 | {{ formBuilder
15 | .open()
16 | .action(url('signin'))
17 | }}
18 |
35 |
36 |
53 |
54 |
66 | {{ formBuilder.close() }}
67 | {% endblock content %}
--------------------------------------------------------------------------------
/module/User/view/user/confirm-email/again.twig:
--------------------------------------------------------------------------------
1 | {% import 'macro/icon.twig' as icon %}
2 | {% import 'macro/form.twig' as form %}
3 |
4 | {% extends 'layout/layout.twig' %}
5 |
6 | {% set title = translate('PAGE_CONFIRM_AGAIN', 'User') %}
7 |
8 | {% do headTitle(title) %}
9 |
10 | {% block content %}
11 | {{ title | e }}
12 |
13 | {% set formBuilder = formBuilder(againForm) %}
14 | {{ formBuilder
15 | .open()
16 | .action(url('confirm-again'))
17 | }}
18 |
35 |
36 |
45 | {{ formBuilder.close() }}
46 | {% endblock content %}
47 |
--------------------------------------------------------------------------------
/module/User/view/user/index/index.twig:
--------------------------------------------------------------------------------
1 | {% import 'macro/component.twig' as component %}
2 | {% import 'macro/table.twig' as table %}
3 |
4 | {% extends 'layout/layout.twig' %}
5 |
6 | {% block content %}
7 | {{ translate('PAGE_TITLE_USER_LIST', 'User') | e }}
8 |
9 |
10 |
11 | # |
12 | {{ translate('COLUMN_USERNAME', 'User') }} |
13 | {{ translate('COLUMN_EMAIL', 'User') }} |
14 | {{ translate('COLUMN_EMAIL_CONFIRM', 'User') }} |
15 | {{ translate('COLUMN_CREATED_AT', 'User') }} |
16 |
17 |
18 | {% set paginator = dataProvider.getPaginator() %}
19 | {% set index = 1 %}
20 | {% if paginator | length > 0 %}
21 | {% for item in paginator %}
22 | {% include 'user/index/row.twig' %}
23 | {% set index = index + 1 %}
24 | {% endfor %}
25 | {% else %}
26 | {{ table.emptyRow(6) }}
27 | {% endif %}
28 |
29 |
30 |
31 | {{ component.paginator(dataProvider.getPaginator(), dataProvider.getQueryParams()) }}
32 | {% endblock content %}
33 |
--------------------------------------------------------------------------------
/module/User/view/user/index/row.twig:
--------------------------------------------------------------------------------
1 |
2 | {{ index }} |
3 | {{ item.getPerson() | e }} |
4 | {{ item.getEmail() | e }} |
5 | {{ item.getEmailConfirm() ? translate('LABEL_YES', 'User') : translate('LABEL_NO', 'User') }} |
6 | {{ item.getCreatedAt() | date('Y-m-d H:i') }} |
7 |
8 |
--------------------------------------------------------------------------------
/module/User/view/user/signup/signup.twig:
--------------------------------------------------------------------------------
1 | {% import 'macro/icon.twig' as icon %}
2 | {% import 'macro/form.twig' as form %}
3 |
4 | {% extends 'layout/layout.twig' %}
5 |
6 | {% set title = translate('PAGE_TITLE_SIGNUP', 'User') %}
7 |
8 | {% do headTitle(title) %}
9 |
10 | {% block content %}
11 | {{ title | e }}
12 |
13 | {% set formBuilder = formBuilder(signUpForm) %}
14 | {{ formBuilder
15 | .open()
16 | .action(url('signup'))
17 | }}
18 |
38 |
39 |
57 |
58 |
75 |
76 |
85 | {{ formBuilder.close() }}
86 | {% endblock content %}
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "zf-app-blank",
3 | "version": "1.6.4",
4 | "description": "A blank application Zend Framework 3.",
5 | "repository": "git@github.com:bupy7/zf-app-blank.git",
6 | "author": "Vasily Belosloodcev",
7 | "license": "BSD-3-Clause",
8 | "dependencies": {
9 | "bootstrap": "^4.0.0-beta.3",
10 | "bootstrap.native": "^2.0.21",
11 | "font-awesome": "~4.7"
12 | },
13 | "devDependencies": {
14 | "jquery": "^3.2"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/public/.gitignore:
--------------------------------------------------------------------------------
1 | /index*.php
2 |
--------------------------------------------------------------------------------
/public/assets/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !/.gitignore
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bupy7/zf-app-blank/6ffb30bcfca4c0ab135c6bdd5469fbaac273a63b/public/favicon.ico
--------------------------------------------------------------------------------
/test/_data/user.php:
--------------------------------------------------------------------------------
1 | 1,
6 | 'person' => 'Vasily',
7 | 'email' => 'vasily@zf-app-blank.com',
8 | 'password' => '$2y$10$tdYs2O82NjKo7w98Gs9yKOjiF43hv31LfehjjJFSysIL1qXC9IDpi', // 1234
9 | 'roleId' => 20,
10 | 'emailConfirm' => true,
11 | 'confirmKey' => 'ub_e-bbf6jbwx0qrp4',
12 | 'restoreKey' => null,
13 | 'restoreKeyExpire' => null,
14 | 'createdAt' => new DateTime(),
15 | ],
16 | ];
17 |
--------------------------------------------------------------------------------
/test/_fixture/UserFixture.php:
--------------------------------------------------------------------------------
1 |
3 |
4 | # https://wiki.debian.org/Locale
5 | server_locale: 'en_US.UTF-8 UTF-8'
6 |
7 | # https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
8 | server_time_zone: Europe/London
9 |
10 | # are we need check box updates for every 'vagrant up'?
11 | box_check_update: false
12 |
13 | # virtual machine name
14 | server_name: zf-app-blank
15 |
16 | # virtual machine IP
17 | ip: 192.168.20.22
18 |
19 | # virtual machine CPU cores number
20 | server_cpus: 1
21 |
22 | # virtual machine RAM
23 | server_memory: 512
24 |
25 | # http://php.net/manual/ru/ini.core.php#ini.memory-limit
26 | php_memory_limit: 256M
27 |
28 | # http://php.net/manual/ru/info.configuration.php#ini.max-execution-time
29 | php_execution_time: 60
30 |
31 | # http://php.net/manual/ru/info.configuration.php#ini.max-input-time
32 | php_input_time: 120
33 |
34 | # http://php.net/manual/ru/timezones.php
35 | php_time_zone: UTC
36 |
37 | # https://xdebug.org/docs/all_settings#idekey
38 | xdebug_idekey: vagrant-xdebug
39 |
40 | # https://dev.mysql.com/doc/refman/5.7/en/adding-users.html
41 | # https://dev.mysql.com/doc/refman/5.7/en/creating-database.html
42 | mysql_db: zf_app_blank
43 | mysql_user: zf_app_blank
44 | mysql_pass: 1234
45 |
46 | # https://www.postgresql.org/docs/9.6/static/sql-createdatabase.html
47 | # https://www.postgresql.org/docs/9.6/static/sql-createuser.html
48 | # https://www.postgresql.org/docs/9.6/static/locale.html
49 | pgsql_db: zf_app_blank
50 | pgsql_user: zf_app_blank
51 | pgsql_pass: 1234
52 | pgsql_locale: 'en_US.utf8'
53 |
54 | # using database (mysql or pgsql)
55 | db_type: mysql
56 |
--------------------------------------------------------------------------------
/workenv/nginx/log/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/workenv/nginx/site.conf:
--------------------------------------------------------------------------------
1 | server {
2 | server_name zf-app-blank.local;
3 | root /var/www/html/public;
4 | index index.php index.html index.htm;
5 |
6 | access_log /vagrant/workenv/nginx/log/access.log;
7 | error_log /vagrant/workenv/nginx/log/error.log;
8 |
9 | location / {
10 | try_files $uri $uri/ /index.php$is_args$args;
11 | location ~* ^.+\.(jpeg|jpg|png|gif|bmp|ico|svg|css|js)$ {
12 | expires max;
13 | }
14 | location ~ [^/]\.php(/|$) {
15 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
16 | if (!-f $document_root$fastcgi_script_name) {
17 | return 404;
18 | }
19 | fastcgi_pass unix:/var/run/php/php7.1-fpm.sock;
20 | fastcgi_index index.php;
21 | include /etc/nginx/fastcgi_params;
22 | }
23 | }
24 |
25 | location ~* "/\." {
26 | deny all;
27 | return 404;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/workenv/provision/always-as-root.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # import script args
4 | # ------------------
5 | DB_TYPE=$(echo "$1")
6 |
7 | # restart services
8 | # ----------------
9 | service php7.1-fpm restart
10 | service nginx restart
11 | case $DB_TYPE in
12 | mysql)
13 | service mysql restart
14 | ;;
15 |
16 | pgsql)
17 | service postgresql restart
18 | ;;
19 | esac
20 |
--------------------------------------------------------------------------------
/workenv/provision/once-as-vagrant.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # import script args
4 | # ------------------
5 | GITHUB_TOKEN=$(echo "$1")
6 |
7 | # common
8 | # ------
9 | cd /vagrant
10 |
11 | # composer
12 | # --------
13 | composer config --global github-oauth.github.com ${GITHUB_TOKEN}
14 | composer install
15 |
16 | # yarn
17 | # ----
18 | yarn install
19 |
20 | # init project
21 | # ------------
22 | php bin/init --env=dev --overwrite=y
23 |
--------------------------------------------------------------------------------