├── .gitignore
├── README.md
├── TODO.md
├── Vagrantfile
├── app
├── assets
│ ├── AnimateAsset.php
│ ├── AppAsset.php
│ ├── BootstrapNotifyAsset.php
│ ├── CheckAsset.php
│ ├── Select2Asset.php
│ ├── TimePickerAsset.php
│ └── TypeAheadAsset.php
├── base
│ ├── Action.php
│ ├── Composer.php
│ ├── ConsoleApplication.php
│ ├── Controller.php
│ ├── Formatter.php
│ ├── MailTrait.php
│ ├── Migration.php
│ ├── ModelUrlTrait.php
│ ├── Module.php
│ ├── ModuleApplicationTrait.php
│ ├── ModuleMigrateException.php
│ ├── RbacMigration.php
│ ├── WebApplication.php
│ ├── actions
│ │ ├── Settings.php
│ │ └── user
│ │ │ ├── Login.php
│ │ │ ├── Logout.php
│ │ │ ├── PasswordRequest.php
│ │ │ ├── PasswordReset.php
│ │ │ ├── Profile.php
│ │ │ └── Register.php
│ ├── behaviors
│ │ ├── LinkedBehavior.php
│ │ ├── SerializableBehavior.php
│ │ └── StatusBehavior.php
│ ├── console
│ │ └── Controller.php
│ ├── gii
│ │ └── ModuleGenerator.php
│ └── grid
│ │ ├── CheckboxColumn.php
│ │ ├── DeleteColumn.php
│ │ └── EditColumn.php
├── commands
│ ├── MigrateController.php
│ ├── ModuleController.php
│ ├── RbacController.php
│ ├── SqlController.php
│ └── UserController.php
├── components
│ ├── Menu.php
│ └── Param.php
├── config
│ ├── common.php
│ ├── console.php
│ └── web.php
├── controllers
│ ├── SiteController.php
│ └── UserController.php
├── forms
│ ├── ContactForm.php
│ └── user
│ │ ├── Login.php
│ │ ├── PasswordRequest.php
│ │ ├── PasswordReset.php
│ │ ├── Profile.php
│ │ └── Register.php
├── helpers
│ ├── Icon.php
│ └── UserHelper.php
├── mail
│ ├── accountCreated-html.php
│ ├── accountCreated-text.php
│ ├── layouts
│ │ ├── html.php
│ │ └── text.php
│ ├── passwordRequest-html.php
│ └── passwordRequest-text.php
├── migrations
│ ├── m140506_102106_rbac_init.php
│ ├── m160314_212231_user.php
│ ├── m160818_075724_config.php
│ ├── m160830_073241_param_default_role.php
│ ├── m160831_094735_module.php
│ ├── m161108_083707_config_perms.php
│ └── m180130_201810_param_site_name.php
├── models
│ ├── Config.php
│ └── User.php
├── views
│ ├── layouts
│ │ ├── content.php
│ │ ├── header.php
│ │ ├── left.php
│ │ ├── main-login.php
│ │ └── main.php
│ ├── site
│ │ ├── about.php
│ │ ├── contact.php
│ │ ├── error.php
│ │ └── index.php
│ ├── templates
│ │ ├── confirmation.php
│ │ ├── gii-module
│ │ │ ├── controller.php
│ │ │ ├── module.php
│ │ │ └── view.php
│ │ └── migration.php
│ └── user
│ │ ├── _create_modal.php
│ │ ├── _profile_account.php
│ │ ├── _profile_admin.php
│ │ ├── index.php
│ │ ├── login.php
│ │ ├── passwordRequest.php
│ │ ├── passwordReset.php
│ │ ├── profile.php
│ │ └── register.php
└── widgets
│ ├── ActiveForm.php
│ ├── Box.php
│ ├── Check.php
│ ├── GridView.php
│ ├── InputClear.php
│ ├── InputGroup.php
│ ├── ItemList.php
│ ├── Modal.php
│ ├── Notify.php
│ ├── Pjax.php
│ ├── ProgressBar.php
│ ├── ProgressBarGroup.php
│ ├── Select2.php
│ ├── Tabs.php
│ ├── TimePicker.php
│ ├── Timeline.php
│ └── TypeAhead.php
├── bin
├── requirements
└── yii
├── composer.json
├── composer.lock
├── config-sample.php
├── docs
└── Param.md
├── modules
├── README.md
└── wiki
│ ├── DiffRendererHtmlInline.php
│ ├── RuleOwnWiki.php
│ ├── WikiModule.php
│ ├── assets
│ ├── DiffAsset.php
│ ├── MarkdownEditorAsset.php
│ └── diff.css
│ ├── controllers
│ └── PageController.php
│ ├── forms
│ ├── DeleteWiki.php
│ └── Editor.php
│ ├── helpers
│ └── DiffHelper.php
│ ├── migrations
│ ├── m160901_090402_wiki.php
│ ├── m160902_065120_wiki_change.php
│ └── m160902_073325_auth.php
│ ├── models
│ ├── History.php
│ └── Wiki.php
│ ├── views
│ └── page
│ │ ├── _editor.php
│ │ ├── _history.php
│ │ ├── create.php
│ │ ├── delete.php
│ │ ├── index.php
│ │ ├── update.php
│ │ └── view.php
│ └── widgets
│ └── MarkdownEditor.php
├── runtime
└── .gitignore
├── screenshot.png
├── tests
├── README.md
├── codeception.yml
└── codeception
│ ├── .gitignore
│ ├── _bootstrap.php
│ ├── _output
│ └── .gitignore
│ ├── _pages
│ ├── AboutPage.php
│ ├── ContactPage.php
│ └── LoginPage.php
│ ├── _support
│ ├── AcceptanceTester.php
│ ├── FunctionalTester.php
│ ├── UnitTester.php
│ └── _generated
│ │ ├── AcceptanceTesterActions.php
│ │ ├── FunctionalTesterActions.php
│ │ └── UnitTesterActions.php
│ ├── acceptance.suite.yml
│ ├── acceptance
│ ├── AboutCept.php
│ ├── ContactCept.php
│ ├── HomeCept.php
│ ├── LoginCept.php
│ └── _bootstrap.php
│ ├── bin
│ ├── _bootstrap.php
│ ├── yii
│ └── yii.bat
│ ├── config
│ ├── acceptance.php
│ ├── functional.php
│ └── unit.php
│ ├── fixtures
│ └── .gitignore
│ ├── functional.suite.yml
│ ├── functional
│ ├── AboutCept.php
│ ├── ContactCept.php
│ ├── HomeCept.php
│ ├── LoginCept.php
│ └── _bootstrap.php
│ ├── templates
│ └── .gitignore
│ ├── unit.suite.yml
│ └── unit
│ ├── _bootstrap.php
│ ├── fixtures
│ ├── .gitkeep
│ └── data
│ │ └── .gitkeep
│ ├── models
│ ├── ContactFormTest.php
│ ├── LoginFormTest.php
│ └── UserTest.php
│ └── templates
│ └── fixtures
│ └── .gitkeep
└── web
├── .htaccess
├── assets
└── .gitignore
├── css
├── admin.css
└── site.css
├── images
└── avatars
│ ├── avatar1.png
│ ├── avatar2.png
│ ├── avatar3.png
│ ├── avatar4.png
│ └── avatar5.png
├── index-test.php
├── index.php
└── js
└── admin.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # phpstorm project files
2 | .idea
3 |
4 | # netbeans project files
5 | nbproject
6 |
7 | # zend studio for eclipse project files
8 | .buildpath
9 | .project
10 | .settings
11 |
12 | # windows thumbnail cache
13 | Thumbs.db
14 |
15 | # composer vendor dir
16 | /vendor
17 |
18 | # composer itself is not needed
19 | composer.phar
20 |
21 | # Mac DS_Store Files
22 | .DS_Store
23 |
24 | # phpunit itself is not needed
25 | phpunit.phar
26 | # local phpunit config
27 | /phpunit.xml
28 |
29 | # Editor backups
30 | *~
31 | .*~
32 |
33 | # local application config
34 | config.php
35 |
36 |
37 | tests/_output/*
38 |
39 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | Things to be done
2 | -----------------
3 |
4 | 1. Fork dmstr/yii2-adminlte-asset and break dependency from cebe/yii2-gravatar
5 | 2. Make use account avatars (adminlte has some pictures).
6 | 3. User profile page. +++
7 | 4. Console command for database backup. +++ (via sql console command)
8 | 5. Session messages (after logout, register and so on). +++
9 | 6. Global translation function t() wrapper for Yii::t(). +++
10 | 7. Wrapper for session->setFlash in base Controller. +++
11 | 8. New "Settings" tab in user profile with preferred language and timezone.
12 | 9. Option for enable/disable social login in user login action.
13 |
14 | Features for 0.2 release:
15 |
16 | 1. Users management page (related with no.3). +++
17 | 2. Configuration editor (avoid of manual editing params in config/web.php). +++
18 | 3. Simple RBAC with "Administrator" and "Registered" roles. +++
19 | 4. Modal with remote content. +++
20 | 5. Modules integration (consider module as subapplication). +++
21 |
22 | Features for 0.3 release:
23 |
24 | 1. Send email after registration:
25 | - email text
26 | - setting for enable/disable sending
27 | - add checkbox in admin user create form for sending email
28 | 2. Localization.
29 | 3. System log page.
30 | 4. User activation by email (levearage of 'Pending' status).
31 | 5. Role editor (allow to add/delete permissions to role).
32 | 6. Modules administation.
33 | 7. Server info (info about server and php) module.
34 |
35 |
--------------------------------------------------------------------------------
/Vagrantfile:
--------------------------------------------------------------------------------
1 | # For a complete reference, please see the online documentation at
2 | # https://docs.vagrantup.com.
3 |
4 | # In case of "rejecting i/o input from offline devices" error in
5 | # guest machine change:
6 | # - kernel type from linux to ubuntu64
7 | # - change cpu count to 1
8 | # - disable I/O APIC feature
9 |
10 | # In case out of memory create swapfile:
11 | # sudo fallocate -l 1G /swapfile
12 | # sudo chmod 600 /swapfile
13 | # sudo mkswap /swapfile
14 | # sudo swapon /swapfile
15 | # echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
16 |
17 | # MySQL root's password is "pw"
18 |
19 | Vagrant.configure("2") do |config|
20 |
21 | config.vm.box = "ubuntu/xenial64"
22 |
23 | config.vm.boot_timeout = 600
24 | config.vm.network "forwarded_port", guest: 80, host: 8080
25 | config.vm.provider "virtualbox" do |vb|
26 | vb.memory = "800"
27 | end
28 |
29 | # Permit write to temporary folders by web server.
30 | #config.vm.synced_folder "web/sites/default/files", "/vagrant/web/sites/default/files", owner: "www-data", group: "www-data"
31 |
32 | config.vm.provision "shell", inline: <<-SHELL
33 | apt-get update
34 | apt-get install -y apache2 libapache2-mod-php php-zip php-curl php-mysql php-xdebug php-gd php-xml git
35 | debconf-set-selections <<< "mysql-server mysql-server/root_password password pw"
36 | debconf-set-selections <<< "mysql-server mysql-server/root_password_again password pw"
37 | apt-get install -y --no-install-recommends mysql-server mysql-client
38 | a2enmod rewrite
39 | if ! [ -L /var/www/html ]; then
40 | rm -rf /var/www/html
41 | ln -sf /vagrant /var/www/html
42 | fi
43 | cd /home/ubuntu || exit
44 | curl -sS https://getcomposer.org/installer | php
45 | mkdir -p .local/bin
46 | mv composer.phar .local/bin/composer
47 | chmod +x .local/bin/composer
48 | chown ubuntu.ubuntu -R .local
49 | ( grep .local/bin .profile >/dev/null ) || \
50 | ( echo 'PATH="$PATH:$HOME/.local/bin"' >> .profile )
51 | echo 'PATH="$HOME/.config/composer/vendor/bin:$PATH"' >> .profile
52 | cd /etc/php/7.0/mods-available || exit
53 | echo 'xdebug.remote_enable=1' >> xdebug.ini
54 | echo 'xdebug.remote_connect_back=1' >> xdebug.ini
55 | echo 'xdebug.remote_port=9000' >> xdebug.ini
56 | echo 'xdebug.idekey=netbeans-xdebug' >> xdebug.ini
57 | phpdismod -s cli xdebug
58 | service apache2 restart
59 | SHELL
60 | end
61 |
--------------------------------------------------------------------------------
/app/assets/AnimateAsset.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @version $Id$
6 | */
7 |
8 | namespace app\assets;
9 |
10 | use yii\web\AssetBundle;
11 |
12 | /**
13 | * AnimateAsset
14 | *
15 | * @author skoro
16 | */
17 | class AnimateAsset extends AssetBundle
18 | {
19 |
20 | public $sourcePath = '@bower/animate.css';
21 |
22 | public $css = [
23 | 'animate.min.css',
24 | ];
25 | }
26 |
--------------------------------------------------------------------------------
/app/assets/AppAsset.php:
--------------------------------------------------------------------------------
1 |
14 | * @since 2.0
15 | */
16 | class AppAsset extends AssetBundle
17 | {
18 | public $basePath = '@webroot';
19 | public $baseUrl = '@web';
20 | public $css = [
21 | 'css/site.css',
22 | 'css/admin.css',
23 | ];
24 | public $js = [
25 | 'js/admin.js',
26 | ];
27 | public $depends = [
28 | 'yii\web\YiiAsset',
29 | 'yii\bootstrap\BootstrapAsset',
30 | ];
31 | }
32 |
--------------------------------------------------------------------------------
/app/assets/BootstrapNotifyAsset.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @version $Id$
6 | */
7 |
8 | namespace app\assets;
9 |
10 | use yii\web\AssetBundle;
11 |
12 | /**
13 | * GrowlAsset
14 | *
15 | * @author skoro
16 | */
17 | class BootstrapNotifyAsset extends AssetBundle
18 | {
19 |
20 | public $sourcePath = '@bower/remarkable-bootstrap-notify';
21 |
22 | public $js = [
23 | 'dist/bootstrap-notify.min.js',
24 | ];
25 |
26 | public $depends = [
27 | 'yii\bootstrap\BootstrapAsset',
28 | ];
29 | }
30 |
--------------------------------------------------------------------------------
/app/assets/CheckAsset.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @version $Id$
6 | */
7 |
8 | namespace app\assets;
9 |
10 | use yii\web\AssetBundle;
11 |
12 | /**
13 | * iCheck check/radion plugin.
14 | *
15 | * @author skoro
16 | */
17 | class CheckAsset extends AssetBundle
18 | {
19 |
20 | public $sourcePath = '@vendor/almasaeed2010/adminlte/plugins/iCheck';
21 |
22 | public $js = [
23 | 'icheck.min.js',
24 | ];
25 |
26 | public $css = [
27 | 'all.css',
28 | ];
29 |
30 | public $depends = [
31 | 'yii\web\JqueryAsset',
32 | ];
33 |
34 | /**
35 | * @var string checkbox style name.
36 | */
37 | public $style;
38 |
39 | /**
40 | * @inheritdoc
41 | */
42 | public function registerAssetFiles($view)
43 | {
44 | $this->css[] = $this->style . '/_all.css';
45 | parent::registerAssetFiles($view);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/assets/Select2Asset.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @version $Id$
6 | */
7 |
8 | namespace app\assets;
9 |
10 | use yii\web\AssetBundle;
11 |
12 | /**
13 | * Select2Asset
14 | *
15 | * @author skoro
16 | */
17 | class Select2Asset extends AssetBundle
18 | {
19 |
20 | public $sourcePath = '@vendor/almasaeed2010/adminlte/bower_components/select2/dist';
21 |
22 | public $css = [
23 | 'css/select2.css',
24 | ];
25 |
26 | public $js = [
27 | 'js/select2.js',
28 | ];
29 |
30 | public $depends = [
31 | 'yii\web\JqueryAsset',
32 | ];
33 |
34 | /**
35 | * Select2 localization language.
36 | * @var string
37 | */
38 | public $language;
39 |
40 | /**
41 | * Register language script.
42 | */
43 | public function registerAssetFiles($view)
44 | {
45 | if ($this->language) {
46 | $this->js[] = "i18n/{$this->language}.js";
47 | }
48 | parent::registerAssetFiles($view);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/assets/TimePickerAsset.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @version $Id$
6 | */
7 |
8 | namespace app\assets;
9 |
10 | use yii\web\AssetBundle;
11 |
12 | /**
13 | * TimePickerAsset
14 | *
15 | * @author skoro
16 | */
17 | class TimePickerAsset extends AssetBundle
18 | {
19 |
20 | public $sourcePath = '@vendor/almasaeed2010/adminlte/plugins/timepicker';
21 |
22 | public $css = [
23 | 'bootstrap-timepicker.min.css',
24 | ];
25 |
26 | public $js = [
27 | 'bootstrap-timepicker.min.js',
28 | ];
29 | }
30 |
--------------------------------------------------------------------------------
/app/assets/TypeAheadAsset.php:
--------------------------------------------------------------------------------
1 |
15 | * @since 2.0
16 | */
17 | class TypeAheadAsset extends AssetBundle
18 | {
19 | public $sourcePath = '@bower/typeahead.js/dist';
20 | public $js = [
21 | 'typeahead.bundle.js',
22 | ];
23 | public $depends = [
24 | 'yii\bootstrap\BootstrapAsset',
25 | 'yii\bootstrap\BootstrapPluginAsset',
26 | ];
27 | }
28 |
--------------------------------------------------------------------------------
/app/base/Action.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.1
6 | */
7 |
8 | namespace app\base;
9 |
10 | /**
11 | * Parent of controller actions.
12 | *
13 | * @author skoro
14 | */
15 | class Action extends \yii\base\Action
16 | {
17 | /**
18 | * @var string view layout
19 | */
20 | public $layout;
21 |
22 | /**
23 | * @var string action view
24 | */
25 | public $view;
26 |
27 | /**
28 | * Render action.
29 | * @param array $params view parameters
30 | * @return string
31 | */
32 | protected function render(array $params = [])
33 | {
34 | if ($this->layout) {
35 | $this->controller->layout = $this->layout;
36 | }
37 | return $this->controller->render($this->view, $params);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/base/ConsoleApplication.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.2
6 | */
7 |
8 | namespace app\base;
9 |
10 | /**
11 | * ConsoleApplication
12 | *
13 | * @author skoro
14 | */
15 | class ConsoleApplication extends \yii\console\Application
16 | {
17 | use ModuleApplicationTrait;
18 | }
19 |
--------------------------------------------------------------------------------
/app/base/Formatter.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.1
6 | */
7 |
8 | namespace app\base;
9 |
10 | use app\models\User;
11 | use app\widgets\ProgressBar;
12 | use Yii;
13 | use yii\helpers\Html;
14 |
15 | /**
16 | * Formatter
17 | *
18 | * @author skoro
19 | */
20 | class Formatter extends \yii\i18n\Formatter
21 | {
22 |
23 | /**
24 | * Markdown parsers.
25 | * @see Formatter::asMarkdown()
26 | */
27 | const MARKDOWN_PARSER_TRADITIONAL = '\cebe\markdown\Markdown';
28 | const MARKDOWN_PARSER_GITHUB = '\cebe\markdown\GithubMarkdown';
29 | const MARKDOWN_PARSER_EXTRA = '\cebe\markdown\MarkdownExtra';
30 |
31 | /**
32 | * Format value as progress bar widget.
33 | * @param integer $value progress value
34 | * @param array $options widget options
35 | * @return string
36 | */
37 | public function asProgressBar($value, $options = [])
38 | {
39 | $options['value'] = $value;
40 | return ProgressBar::widget($options);
41 | }
42 |
43 | /**
44 | * Converts Markdown to html.
45 | * @param string $text markdown source.
46 | * @param string $parserClass markdown parser class.
47 | * @return string
48 | */
49 | public function asMarkdown($text, $parserClass = self::MARKDOWN_PARSER_EXTRA)
50 | {
51 | $parser = new $parserClass();
52 | return $parser->parse($text);
53 | }
54 |
55 | /**
56 | * Output user as link to their profile.
57 | * User must has 'viewAnyUser' permission to link to user profiles otherwise
58 | * outputs as simple text.
59 | * @param User $user
60 | * @param array $options link options
61 | * @return string
62 | */
63 | public function asUserlink($user, array $options = [])
64 | {
65 | if (empty($user)) {
66 | return $this->nullDisplay;
67 | }
68 |
69 | $username = Html::encode($user->name);
70 | if (Yii::$app->user->id == $user->id) {
71 | $link = ['/user/profile'];
72 | } elseif (Yii::$app->user->can('viewAnyUser')) {
73 | $link = ['/user/profile', 'id' => $user->id];
74 | } else {
75 | return $username;
76 | }
77 |
78 | return Html::a($username, $link, $options);
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/app/base/MailTrait.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.3
6 | */
7 |
8 | namespace app\base;
9 |
10 | use app\components\Param;
11 | use Yii;
12 | use yii\helpers\ArrayHelper;
13 |
14 | /**
15 | * Wrapper for mailer compose.
16 | *
17 | * Send email to destination address from site admin email.
18 | *
19 | * @author skoro
20 | */
21 | trait MailTrait
22 | {
23 |
24 | /**
25 | * Send email.
26 | * @param string $view mail template.
27 | * @param string $to destination email address.
28 | * @param array $params view parameters. Special parameter 'subject'
29 | * used for mail subject rest parameters passed to view.
30 | * @return boolean
31 | */
32 | public function mail($view, $to, array $params)
33 | {
34 | $views = [
35 | 'html' => $view . '-html',
36 | 'text' => $view . '-text',
37 | ];
38 |
39 | $subject = ArrayHelper::remove($params, 'subject');
40 |
41 | $compose = Yii::$app->mailer->compose($views, $params);
42 | if (!empty($subject)) {
43 | $compose->setSubject($subject);
44 | }
45 | $compose->setTo($to)
46 | ->setFrom(Param::value('Site.adminEmail'));
47 |
48 | return $compose->send();
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/base/Migration.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.1
6 | */
7 |
8 | namespace app\base;
9 |
10 | /**
11 | * Migration
12 | *
13 | * @author skoro
14 | */
15 | class Migration extends \yii\db\Migration
16 | {
17 | /**
18 | * @var string default database table options.
19 | */
20 | protected $tableOptions = null;
21 |
22 | /**
23 | * Is MS SQL Server database driver used ?
24 | * @return bool
25 | */
26 | protected function isMSSQL()
27 | {
28 | return $this->db->driverName === 'mssql' || $this->db->driverName === 'sqlsrv' || $this->db->driverName === 'dblib';
29 | }
30 |
31 | /**
32 | * Is SQLite database driver used ?
33 | * @return bool
34 | */
35 | protected function isSqlite()
36 | {
37 | return $this->db->driverName === 'sqlite';
38 | }
39 |
40 | /**
41 | * Get specific table options for database driver.
42 | * @return string
43 | */
44 | protected function getTableOptions()
45 | {
46 | if ($this->tableOptions === null) {
47 | switch ($this->db->driverName) {
48 | case 'mysql':
49 | // http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci
50 | $this->tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci';
51 | break;
52 |
53 | default:
54 | $this->tableOptions = '';
55 | }
56 | }
57 |
58 | return $this->tableOptions;
59 | }
60 |
61 | /**
62 | * @inheritdoc
63 | */
64 | public function createTable($table, $columns, $options = null)
65 | {
66 | parent::createTable($table, $columns, $options === null ? $this->getTableOptions() : $options);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/app/base/ModelUrlTrait.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.2
6 | */
7 |
8 | namespace app\base;
9 |
10 | use Closure;
11 |
12 | /**
13 | * This trait is helper for generate model url.
14 | *
15 | * @author skoro
16 | */
17 | trait ModelUrlTrait
18 | {
19 | /**
20 | * @var string|array|Closure delete url.
21 | */
22 | public $url;
23 |
24 | /**
25 | * Returns model's url.
26 | * @param mixed $model
27 | * @param string|array $default default url
28 | * @return array|string
29 | */
30 | protected function getModelUrl($model, $default)
31 | {
32 | if ($this->url instanceof Closure) {
33 | $url = call_user_func($this->url, $model);
34 | }
35 | else {
36 | $url = empty($this->url) ? $default : $this->url;
37 | if (is_array($url) && isset($model->id)) {
38 | $url['id'] = $model->id;
39 | }
40 | }
41 |
42 | return $url;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/base/Module.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.2
6 | */
7 |
8 | namespace app\base;
9 |
10 | use Yii;
11 |
12 | /**
13 | * Application module.
14 | *
15 | * @author skoro
16 | */
17 | class Module extends \yii\base\Module
18 | {
19 | /**
20 | * Module statuses.
21 | */
22 | const STATUS_INSTALLED = 1;
23 | const STATUS_NOTINSTALLED = 0;
24 |
25 | /**
26 | * Events.
27 | */
28 | const EVENT_MODULE_INSTALLED = 'eventModuleInstalled';
29 | const EVENT_MODULE_UNINSTALLED = 'eventModuleUninstalled';
30 |
31 | /**
32 | * @var string required, module name.
33 | */
34 | public $moduleName;
35 |
36 | /**
37 | * @var string
38 | */
39 | public $moduleDescription = '';
40 |
41 | /**
42 | * Emit event when module is installed.
43 | */
44 | public function install()
45 | {
46 | $this->trigger(self::EVENT_MODULE_INSTALLED);
47 | }
48 |
49 | /**
50 | * Emit event when module is uninstalled.
51 | */
52 | public function uninstall()
53 | {
54 | $this->trigger(self::EVENT_MODULE_UNINSTALLED);
55 | }
56 |
57 | /**
58 | * Adds menu entries.
59 | */
60 | public function addMenu($menu, array $items)
61 | {
62 | try {
63 | Yii::$app->menu->insertItems($menu, $items);
64 | } catch (\Exception $e) {
65 | // Menu does not available in console applications.
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/app/base/ModuleMigrateException.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.2
6 | */
7 |
8 | namespace app\base;
9 |
10 | /**
11 | * Exception occurs when module migrations failed (up/down).
12 | * @author skoro
13 | */
14 | class ModuleMigrateException extends \Exception
15 | {
16 | /**
17 | * @var string
18 | */
19 | public $moduleId;
20 |
21 | /**
22 | * @var array
23 | */
24 | public $migrations;
25 |
26 | /**
27 | * @var string migration command output.
28 | */
29 | public $output = '';
30 |
31 | /**
32 | * @param string $moduleId
33 | * @param array $migrations list of failed migrations.
34 | * @param string $output migration command output
35 | */
36 | public function __construct($moduleId, array $migrations, $output = '') {
37 | $this->moduleId = $moduleId;
38 | $this->migrations = $migrations;
39 | $this->output = $output;
40 | parent::__construct('Cannot apply module migrations.');
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/base/WebApplication.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.2
6 | */
7 |
8 | namespace app\base;
9 |
10 | /**
11 | * Web application.
12 | *
13 | * @author skoro
14 | */
15 | class WebApplication extends \yii\web\Application
16 | {
17 | use ModuleApplicationTrait;
18 | }
19 |
--------------------------------------------------------------------------------
/app/base/actions/user/Login.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.1
6 | */
7 |
8 | namespace app\base\actions\user;
9 |
10 | use app\base\Action;
11 | use app\base\Controller;
12 | use app\components\Param;
13 | use Yii;
14 | use yii\web\Response;
15 | use yii\widgets\ActiveForm;
16 |
17 | /**
18 | * User login action.
19 | *
20 | * @todo enable/disable social links.
21 | * @author skoro
22 | */
23 | class Login extends Action
24 | {
25 |
26 | /**
27 | * @var string class name for Login form.
28 | */
29 | public $modelClass = 'app\forms\user\Login';
30 |
31 | /**
32 | * @var string
33 | */
34 | public $view = 'login';
35 |
36 | /**
37 | * @inheritdoc
38 | */
39 | public function run()
40 | {
41 | if (!Yii::$app->user->isGuest) {
42 | return $this->controller->goBack();
43 | }
44 |
45 | $model = new $this->modelClass;
46 | if (Yii::$app->request->isPost) {
47 | if ($model->load(Yii::$app->request->post()) && $model->login()) {
48 | return $this->controller->goBack();
49 | } else {
50 | $this->controller->addFlash(Controller::FLASH_ERROR, Yii::t('app', 'Login to your account failed.'));
51 | $model->password = '';
52 | }
53 | }
54 |
55 | if (!Yii::$app->request->isPjax && Yii::$app->request->isAjax) {
56 | Yii::$app->response->format = Response::FORMAT_JSON;
57 | return ActiveForm::validate($model);
58 | }
59 |
60 | return $this->render([
61 | 'model' => $model,
62 | 'disableUserRegister' => Param::value('User.disableUserRegister'),
63 | ]);
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/app/base/actions/user/Logout.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.1
6 | */
7 |
8 | namespace app\base\actions\user;
9 |
10 | use Yii;
11 | use app\base\Action;
12 |
13 | /**
14 | * User logout action.
15 | *
16 | * @author skoro
17 | */
18 | class Logout extends Action
19 | {
20 |
21 | /**
22 | * @inheritdoc
23 | */
24 | public function run()
25 | {
26 | Yii::$app->user->logout();
27 | return $this->controller->goHome();
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/app/base/actions/user/PasswordRequest.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.1
6 | */
7 |
8 | namespace app\base\actions\user;
9 |
10 | use Yii;
11 | use app\base\Action;
12 | use app\base\Controller;
13 |
14 | /**
15 | * Request user password.
16 | *
17 | * @author skoro
18 | */
19 | class PasswordRequest extends Action
20 | {
21 |
22 | /**
23 | * @var string class name for Password Request form.
24 | */
25 | public $modelClass = 'app\forms\user\PasswordRequest';
26 |
27 | /**
28 | * @var string
29 | */
30 | public $view = 'passwordRequest';
31 |
32 | /**
33 | * @inheritdoc
34 | */
35 | public function run()
36 | {
37 | $model = new $this->modelClass;
38 |
39 | if (Yii::$app->request->isPost) {
40 | if ($model->load(Yii::$app->request->post()) && $model->validate()) {
41 | if ($model->sendEmail()) {
42 | $this->controller->addFlash(Controller::FLASH_INFO, Yii::t('app', 'Check your email for further instructions.'));
43 | return $this->controller->redirect(['user/login']);
44 | } else {
45 | $this->controller->addFlash(Controller::FLASH_ERROR, Yii::t('app', 'Sorry, we are unable to reset password for provided email.'));
46 | }
47 | }
48 | }
49 |
50 | return $this->render([
51 | 'model' => $model,
52 | ]);
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/app/base/actions/user/PasswordReset.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.1
6 | */
7 |
8 | namespace app\base\actions\user;
9 |
10 | use Yii;
11 | use app\base\Action;
12 | use app\base\Controller;
13 |
14 | /**
15 | * User password reset.
16 | *
17 | * @author skoro
18 | */
19 | class PasswordReset extends Action
20 | {
21 | /**
22 | * @var string class name for Login form.
23 | */
24 | public $modelClass = 'app\forms\user\PasswordReset';
25 |
26 | /**
27 | * @var string
28 | */
29 | public $view = 'passwordReset';
30 |
31 | /**
32 | * @inheritdoc
33 | */
34 | public function run($token)
35 | {
36 | try {
37 | $model = new $this->modelClass($token);
38 | }
39 | catch(\yii\base\InvalidParamException $e) {
40 | $this->controller->addFlash(Controller::FLASH_ERROR, $e->getMessage());
41 | return $this->controller->goHome();
42 | }
43 |
44 | if (Yii::$app->request->isPost) {
45 | if ($model->load(Yii::$app->request->post()) && $model->validate()) {
46 | if ($model->resetPassword()) {
47 | $this->controller->addFlash(Controller::FLASH_SUCCESS, Yii::t('app', 'Password has been changed. Now you may login.'));
48 | return $this->controller->redirect(['user/login']);
49 | }
50 | else {
51 | $this->controller->addFlash(Controller::FLASH_ERROR, Yii::t('app', 'Unable to change password.'));
52 | }
53 | }
54 | }
55 |
56 | return $this->render([
57 | 'model' => $model,
58 | ]);
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/app/base/actions/user/Profile.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.1
6 | */
7 |
8 | namespace app\base\actions\user;
9 |
10 | use app\base\Action;
11 | use app\base\Controller;
12 | use app\models\User;
13 | use Yii;
14 | use yii\web\Response;
15 | use yii\widgets\ActiveForm;
16 |
17 | /**
18 | * User profile.
19 | *
20 | * @author skoro
21 | */
22 | class Profile extends Action
23 | {
24 |
25 | /**
26 | * @var string class name for Register form.
27 | */
28 | public $modelClass = 'app\forms\user\Profile';
29 |
30 | /**
31 | * @var string
32 | */
33 | public $view = 'profile';
34 |
35 | /**
36 | * @inheritdoc
37 | */
38 | public function run($id = null, $tab = 'account')
39 | {
40 | if (Yii::$app->user->isGuest) {
41 | return $this->controller->redirect(['user/login']);
42 | }
43 |
44 | if ($id === null) {
45 | $user = Yii::$app->user->getIdentity();
46 | } elseif (Yii::$app->user->can('updateAnyUser')) {
47 | $user = $this->controller->findModel(User::className(), $id);
48 | } else {
49 | throw new \yii\web\ForbiddenHttpException();
50 | }
51 |
52 | $model = new $this->modelClass($user);
53 |
54 | if (Yii::$app->request->isPost) {
55 | if (Yii::$app->user->can('updateAnyUser')) {
56 | $model->setScenario('admin');
57 | }
58 | if ($model->load(Yii::$app->request->post()) && $model->save()) {
59 | $this->controller->addFlash(Controller::FLASH_INFO, Yii::t('app', 'Changes has saved.'));
60 | $model->reset();
61 | }
62 | }
63 |
64 | if (!Yii::$app->request->isPjax && Yii::$app->request->isAjax) {
65 | Yii::$app->response->format = Response::FORMAT_JSON;
66 | return ActiveForm::validate($model);
67 | }
68 |
69 | return $this->render([
70 | 'model' => $model,
71 | 'tab' => $tab,
72 | ]);
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/app/base/actions/user/Register.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.1
6 | */
7 |
8 | namespace app\base\actions\user;
9 |
10 | use app\base\Action;
11 | use app\components\Param;
12 | use Yii;
13 | use yii\helpers\Url;
14 | use yii\web\NotFoundHttpException;
15 | use yii\web\Response;
16 | use yii\widgets\ActiveForm;
17 |
18 | /**
19 | * User register action.
20 | *
21 | * To disable user registration, put to the app's config
22 | * parameter `disableUserRegister => true`.
23 | *
24 | * @author skoro
25 | */
26 | class Register extends Action
27 | {
28 |
29 | /**
30 | * @var string class name for Register form.
31 | */
32 | public $modelClass = 'app\forms\user\Register';
33 |
34 | /**
35 | * @var string
36 | */
37 | public $view = 'register';
38 |
39 | /**
40 | * @inheritdoc
41 | */
42 | public function run()
43 | {
44 | if (Param::value('User.disableUserRegister', false)) {
45 | throw new NotFoundHttpException();
46 | }
47 |
48 | if (!Yii::$app->user->isGuest) {
49 | return $this->controller->goBack();
50 | }
51 |
52 | $model = new $this->modelClass;
53 | if (Yii::$app->request->isPost) {
54 | if ($model->load(Yii::$app->request->post()) && $model->register()) {
55 | $this->controller->addFlash('info',
56 | Yii::t('app', 'Registration successful. Now you can login.', [
57 | // FIXME: use app's login route.
58 | 'login' => Url::to(['user/login'])
59 | ])
60 | );
61 | return $this->controller->goHome();
62 | }
63 | }
64 |
65 | if (!Yii::$app->request->isPjax && Yii::$app->request->isAjax) {
66 | Yii::$app->response->format = Response::FORMAT_JSON;
67 | return ActiveForm::validate($model);
68 | }
69 |
70 | return $this->render([
71 | 'model' => $model,
72 | ]);
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/app/base/behaviors/LinkedBehavior.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.1
6 | */
7 |
8 | namespace app\base\behaviors;
9 |
10 | use Yii;
11 | use yii\base\Behavior;
12 | use yii\db\Query;
13 |
14 | /**
15 | * LinkedBehavior
16 | *
17 | * This behavior links two tables via helper table (many-to-many relation).
18 | *
19 | * @author skoro
20 | */
21 | class LinkedBehavior extends Behavior
22 | {
23 |
24 | /**
25 | * @var string helper table name.
26 | */
27 | protected $_table;
28 |
29 | /**
30 | * @var string linked model attribute in helper table.
31 | */
32 | protected $_linkAttribute;
33 |
34 | /**
35 | * @var string which module to link to owner.
36 | */
37 | protected $_linkModel;
38 |
39 | /**
40 | * @var string owner attribute in helper table.
41 | */
42 | protected $_sourceAttribute;
43 |
44 | /**
45 | * @var string
46 | */
47 | protected $_idAttribute = 'id';
48 |
49 | /**
50 | * Relation to linked models.
51 | * @return \yii\db\ActiveQuery
52 | */
53 | protected function getRelation()
54 | {
55 | return $this->owner->hasMany($this->_linkModel, [$this->_idAttribute => $this->_linkAttribute])
56 | ->viaTable($this->_table, [$this->_sourceAttribute => $this->_idAttribute]);
57 | }
58 |
59 | /**
60 | * Link model with owner.
61 | * @param Model $model
62 | * @return boolean
63 | * @throws InvalidCallException
64 | */
65 | protected function addLinked($model)
66 | {
67 | if ($model->isNewRecord) {
68 | throw new InvalidCallException('Model must be saved before adding.');
69 | }
70 | if ($this->owner->isNewRecord) {
71 | throw new InvalidCallException('Save the model before linking with other.');
72 | }
73 | $params = [
74 | $this->_sourceAttribute => $this->owner->id,
75 | $this->_linkAttribute => $model->id,
76 | ];
77 | $exists = (new Query())->from($this->_table)
78 | ->where($params)
79 | ->exists();
80 | if ($exists) {
81 | return false;
82 | }
83 | return Yii::$app->db->createCommand()
84 | ->insert($this->_table, $params)
85 | ->execute();
86 |
87 | }
88 |
89 | /**
90 | * Break relation between linking and owner.
91 | * @param Model $model linked model
92 | * @return integer
93 | */
94 | protected function removeLinked($model)
95 | {
96 | return Yii::$app->db->createCommand()
97 | ->delete($this->_table, [
98 | $this->_sourceAttribute => $this->owner->id,
99 | $this->_linkAttribute => $model->id,
100 | ])
101 | ->execute();
102 | }
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/app/base/behaviors/SerializableBehavior.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.2
6 | */
7 |
8 | namespace app\base\behaviors;
9 |
10 | use yii\base\Behavior;
11 | use yii\db\ActiveRecord;
12 |
13 | /**
14 | * SerializableBehavior
15 | *
16 | * @author skoro
17 | */
18 | class SerializableBehavior extends Behavior
19 | {
20 |
21 | /**
22 | * @var array list of attribute name to be serialize.
23 | */
24 | public $attributes = [];
25 |
26 | /**
27 | * @inheritdoc
28 | */
29 | public function events()
30 | {
31 | return [
32 | ActiveRecord::EVENT_AFTER_FIND => 'onAfterFind',
33 | ActiveRecord::EVENT_BEFORE_INSERT => 'onBeforeSave',
34 | ActiveRecord::EVENT_BEFORE_UPDATE => 'onBeforeSave',
35 | ActiveRecord::EVENT_AFTER_INSERT => 'onAfterSave',
36 | ActiveRecord::EVENT_AFTER_UPDATE => 'onAfterSave',
37 | ];
38 | }
39 |
40 | public function onAfterFind()
41 | {
42 | $this->unserializeAttributes();
43 | }
44 |
45 | public function onBeforeSave()
46 | {
47 | $this->serializeAttributes();
48 | }
49 |
50 | public function onAfterSave()
51 | {
52 | $this->unserializeAttributes();
53 | }
54 |
55 | protected function serializeAttributes()
56 | {
57 | foreach ($this->attributes as $attribute) {
58 | $this->owner->$attribute = serialize($this->owner->$attribute);
59 | }
60 | }
61 |
62 | protected function unserializeAttributes()
63 | {
64 | foreach ($this->attributes as $attribute) {
65 | $this->owner->$attribute = unserialize($this->owner->$attribute);
66 | $this->owner->setOldAttribute($attribute, $this->owner->$attribute);
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/app/base/behaviors/StatusBehavior.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.1
6 | */
7 |
8 | namespace app\base\behaviors;
9 |
10 | use Yii;
11 | use ReflectionClass;
12 | use yii\base\Behavior;
13 |
14 | /**
15 | * StatusBehavior
16 | *
17 | * This behavior extracts STATUS_ constant from a model and makes appropriate
18 | * labels. For example, model has:
19 | * ```php
20 | * class Order extends Model {
21 | * const STATUS_ACTIVE = 0;
22 | * const STATUS_COMPLETED = 1;
23 | * const STATUS_CANCELLED = 2;
24 | *
25 | * public $status;
26 | * }
27 | * $order = new Order();
28 | * $order->getStatusLabels(); // Will returns: 0 => Active, 1 => Completed, ...
29 | * $order->getStatusLabel(); // Will returns current model status based on
30 | * // 'status' model's attribute.
31 | * ```
32 | *
33 | *
34 | * @author skoro
35 | */
36 | class StatusBehavior extends Behavior
37 | {
38 |
39 | /**
40 | * @var string[] a list of status labels.
41 | */
42 | protected $_statuses;
43 |
44 | /**
45 | * @var string
46 | */
47 | public $statusAttribute = 'status';
48 |
49 | /**
50 | * @inheritdoc
51 | */
52 | public function attach($owner)
53 | {
54 | parent::attach($owner);
55 | $reflection = new ReflectionClass($this->owner);
56 | $consts = $reflection->getConstants();
57 | foreach ($consts as $name => $value) {
58 | if (strpos($name, 'STATUS_') === 0) {
59 | $this->_statuses[$value] = $this->createLabel($name);
60 | }
61 | }
62 | }
63 |
64 | /**
65 | * Returns model's all status labels.
66 | * @return array
67 | */
68 | public function getStatusLabels()
69 | {
70 | return $this->_statuses;
71 | }
72 |
73 | /**
74 | * Returns model's status field label.
75 | * @return string
76 | */
77 | public function getStatusLabel()
78 | {
79 | $status = $this->owner->{$this->statusAttribute};
80 | return $this->_statuses[$status];
81 | }
82 |
83 | /**
84 | * Create label from constant name.
85 | * @param string $const
86 | * @return string
87 | */
88 | protected function createLabel($const)
89 | {
90 | $label = str_replace('STATUS_', '', $const);
91 | $label = ucfirst(strtolower($label));
92 | return Yii::t('app', $label);
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/app/base/console/Controller.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.1
6 | */
7 |
8 | namespace app\base\console;
9 |
10 | use Yii;
11 | use yii\helpers\Console;
12 |
13 | /**
14 | * Controller
15 | *
16 | * Parent for console controllers.
17 | *
18 | * @todo Helper method for output tabular data.
19 | * @author skoro
20 | */
21 | class Controller extends \yii\console\Controller
22 | {
23 |
24 | /**
25 | * Prints translated message.
26 | * @param string $message
27 | * @param array $params
28 | */
29 | public function p($message, array $params = [])
30 | {
31 | $this->stdout(Yii::t('app', $message, $params) . PHP_EOL);
32 | }
33 |
34 | /**
35 | * Prints error message.
36 | * @param string $message
37 | * @param array $params
38 | */
39 | public function err($message, array $params = [])
40 | {
41 | $this->stderr(Yii::t('app', $message, $params) . PHP_EOL, Console::FG_RED);
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/app/base/gii/ModuleGenerator.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.2
6 | */
7 |
8 | namespace app\base\gii;
9 |
10 | /**
11 | * Application module generator.
12 | *
13 | * Parameter moduleID is required only. Template 'app-module' used by default.
14 | * moduleClass parameter generated automatically from moduleID property.
15 | *
16 | * Use on command line:
17 | * ```
18 | * ./bin/yii gii/module --moduleID=my-orders
19 | * ```
20 | *
21 | * Will create module `MyOrdersModule` in `modules\\my_orders` folder.
22 | *
23 | * @author skoro
24 | */
25 | class ModuleGenerator extends \yii\gii\generators\module\Generator
26 | {
27 | /**
28 | * @var string the name of the code template that the user has selected.
29 | * The value of this property is internally managed by this class.
30 | */
31 | public $template = 'app-module';
32 |
33 | /**
34 | * @inheritdoc
35 | */
36 | public function rules()
37 | {
38 | return [
39 | ['moduleID', 'required'],
40 | ['moduleID', 'filter', 'filter' => 'trim'],
41 | ['moduleID', 'match', 'pattern' => '/^[a-zA-Z]+[a-zA-Z-_]+$/', 'message' => 'Only word characters and dashes are allowed.'],
42 | ['moduleID', 'validateModuleID'],
43 |
44 | ['template', 'required', 'message' => 'A code template must be selected.'],
45 | ['template', 'validateTemplate'],
46 | ];
47 | }
48 |
49 | /**
50 | * Not actual validation but class generator for moduleClass property.
51 | */
52 | public function validateModuleID()
53 | {
54 | $id = str_replace('-', '_', strtolower($this->moduleID));
55 | $class = ucwords(str_replace('_', ' ', $id)) . 'Module';
56 | $class = str_replace(' ', '', $class);
57 | $this->moduleClass = 'modules\\' . $id . '\\' . $class;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/app/base/grid/CheckboxColumn.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.3
6 | */
7 |
8 | namespace app\base\grid;
9 |
10 | use app\widgets\Check;
11 | use yii\helpers\Json;
12 |
13 | /**
14 | * Checkbox column with iChecks.
15 | *
16 | * @author skoro
17 | */
18 | class CheckboxColumn extends \yii\grid\CheckboxColumn
19 | {
20 |
21 | /**
22 | * @var string control style.
23 | */
24 | public $style = Check::STYLE_FLAT;
25 |
26 | /**
27 | * @var string style color.
28 | */
29 | public $color = Check::COLOR_GREEN;
30 |
31 | /**
32 | * @inheritdoc
33 | */
34 | public function registerClientScript()
35 | {
36 | $id = $this->grid->options['id'];
37 | $options = Json::encode([
38 | 'name' => $this->name,
39 | 'class' => $this->cssClass,
40 | 'multiple' => $this->multiple,
41 | 'checkAll' => $this->grid->showHeader ? $this->getHeaderCheckBoxName() : null,
42 | 'checkboxClass' => Check::createStyleName(Check::TYPE_CHECKBOX, $this->style, $this->color),
43 | ]);
44 | //$this->grid->getView()->registerJs("jQuery('#$id input[type=checkbox').iCheck($options);");
45 | $this->grid->getView()->registerJs("Admin.Grid.initSelectionColumn('#$id', $options);");
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/base/grid/DeleteColumn.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.1
6 | */
7 |
8 | namespace app\base\grid;
9 |
10 | use app\helpers\Icon;
11 | use Closure;
12 | use Yii;
13 | use yii\grid\Column;
14 | use yii\helpers\Html;
15 | use app\base\ModelUrlTrait;
16 |
17 | /**
18 | * DeleteColumn
19 | *
20 | * @author skoro
21 | */
22 | class DeleteColumn extends Column
23 | {
24 |
25 | use ModelUrlTrait;
26 |
27 | /**
28 | * @var string|Closure
29 | */
30 | public $confirm;
31 |
32 | /**
33 | * @inheritdoc
34 | */
35 | protected function renderDataCellContent($model, $key, $index)
36 | {
37 | if ($this->confirm instanceof Closure) {
38 | $confirm = call_user_func($this->confirm, $model);
39 | }
40 | elseif (is_string($this->confirm)) {
41 | $confirm = $this->confirm;
42 | }
43 | else {
44 | $confirm = Yii::t('app', 'Are you sure ?');
45 | }
46 | $confirm = Html::encode($confirm);
47 |
48 | $url = $this->getModelUrl($model, ['delete']);
49 |
50 | return Html::a(Icon::TRASH, $url, ['class' => 'confirm', 'data-confirm' => $confirm]);
51 | }
52 |
53 | /**
54 | * @inheritdoc
55 | */
56 | protected function getHeaderCellLabel()
57 | {
58 | return Yii::t('app', 'Delete');
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/base/grid/EditColumn.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.1
6 | */
7 |
8 | namespace app\base\grid;
9 |
10 | use app\helpers\Icon;
11 | use Yii;
12 | use yii\grid\Column;
13 | use yii\helpers\Html;
14 | use app\base\ModelUrlTrait;
15 |
16 | /**
17 | * EditColumn
18 | *
19 | * @author skoro
20 | */
21 | class EditColumn extends Column
22 | {
23 |
24 | use ModelUrlTrait;
25 |
26 | /**
27 | * @var array url options.
28 | */
29 | public $options = [];
30 |
31 | /**
32 | * @inheritdoc
33 | */
34 | protected function renderDataCellContent($model, $key, $index)
35 | {
36 | $url = $this->getModelUrl($model, ['update']);
37 | return Html::a(Icon::EDIT, $url, $this->options);
38 | }
39 |
40 | /**
41 | * @inheritdoc
42 | */
43 | protected function getHeaderCellLabel()
44 | {
45 | return Yii::t('app', 'Edit');
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/commands/MigrateController.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.2
6 | */
7 |
8 | namespace app\commands;
9 |
10 | use app\base\ModuleMigrateException;
11 | use Yii;
12 |
13 | /**
14 | * Apply/revert module migration hooks.
15 | *
16 | * @author skoro
17 | */
18 | class MigrateController extends \yii\console\controllers\MigrateController
19 | {
20 |
21 | /**
22 | * Get new migrations for module.
23 | * @param string $moduleId
24 | * @return array migrations list
25 | */
26 | public function getModuleNewMigrations($moduleId)
27 | {
28 | if (!is_dir($dir = $this->getModuleMigrationsDir($moduleId))) {
29 | return [];
30 | }
31 | $this->migrationPath = $dir;
32 | return $this->getNewMigrations();
33 | }
34 |
35 | /**
36 | * Apply module migrations.
37 | * @param string $moduleId
38 | * @throws ModuleMigrateException
39 | */
40 | public function moduleMigrateUp($moduleId)
41 | {
42 | $failed = [];
43 | $migrations = $this->getModuleNewMigrations($moduleId);
44 | ob_start();
45 | foreach ($migrations as $migration) {
46 | if (!$this->migrateUp($migration)) {
47 | $failed[] = $migration;
48 | }
49 | }
50 | $output = ob_get_clean();
51 | if ($failed) {
52 | throw new ModuleMigrateException($moduleId, $failed, $output);
53 | }
54 | }
55 |
56 | /**
57 | * Revert module migrations.
58 | * @param string $moduleId
59 | * @param array $migrations
60 | * @throws ModuleMigrateException
61 | */
62 | public function moduleMigrateDown($moduleId, $migrations)
63 | {
64 | $this->migrationPath = $this->getModuleMigrationsDir($moduleId);
65 | $failed = [];
66 | ob_start();
67 | // Start revert migrations from reverse order.
68 | $migrations = array_reverse($migrations);
69 | foreach ($migrations as $migration) {
70 | if (!$this->migrateDown($migration)) {
71 | $failed[] = $migration;
72 | }
73 | }
74 | $output = ob_get_clean();
75 | if ($failed) {
76 | throw new ModuleMigrateException($moduleId, $failed, $output);
77 | }
78 | }
79 |
80 | /**
81 | *
82 | * @param string $moduleId
83 | * @return string
84 | */
85 | protected function getModuleMigrationsDir($moduleId)
86 | {
87 | return Yii::getAlias('@modules') . DIRECTORY_SEPARATOR . $moduleId .
88 | DIRECTORY_SEPARATOR . 'migrations';
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/app/commands/SqlController.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.2
6 | */
7 |
8 | namespace app\commands;
9 |
10 | use app\base\console\Controller;
11 | use Yii;
12 | use yii\helpers\Console;
13 |
14 | /**
15 | * SqlController
16 | *
17 | * @author skoro
18 | */
19 | class SqlController extends Controller
20 | {
21 |
22 | /**
23 | * Launch database SQL client on application's database.
24 | */
25 | public function actionIndex()
26 | {
27 | $db = Yii::$app->db;
28 | switch ($driver = $db->getDriverName()) {
29 | case 'sqlite':
30 | $file = preg_replace('/^sqlite:/', '', $db->dsn);
31 | $cmd = 'sqlite3 ' . Yii::getAlias($file);
32 | break;
33 |
34 | case 'mysql':
35 | if (!($dsn = $this->parseMysqlDsn())) {
36 | $this->stderr('Cannot parse DSN: ' . $dsn, Console::FG_RED);
37 | return 1;
38 | }
39 | $cmd = sprintf('mysql -u %s -p%s -h %s %s',
40 | $dsn['user'],
41 | $dsn['password'],
42 | $dsn['host'],
43 | $dsn['db']
44 | );
45 | break;
46 |
47 | default:
48 | $this->stderr('Not implemented for driver: ' . $driver, Console::FG_RED);
49 | return 1;
50 | }
51 |
52 | $this->procOpen($cmd);
53 | }
54 |
55 | /**
56 | * Generate dump of application database to stdout.
57 | */
58 | public function actionDump()
59 | {
60 | $db = Yii::$app->db;
61 | if ($db->getDriverName() !== 'mysql') {
62 | $this->stderr('Dump generation implemented only for MySQL yet.', Console::FG_RED);
63 | return 1;
64 | }
65 |
66 | if (!($dsn = $this->parseMysqlDsn())) {
67 | $this->stderr('Cannot parse DSN: ' . $dsn, Console::FG_RED);
68 | return 1;
69 | }
70 |
71 | $cmd = sprintf('mysqldump -u %s -p%s -h %s %s',
72 | $dsn['user'],
73 | $dsn['password'],
74 | $dsn['host'],
75 | $dsn['db']
76 | );
77 |
78 | $this->procOpen($cmd);
79 | }
80 |
81 | /**
82 | * Open process.
83 | * @param string $cmd command line
84 | * @return integer command exit status
85 | */
86 | protected function procOpen($cmd)
87 | {
88 | $process = proc_open($cmd, [0 => STDIN, 1 => STDOUT, 2 => STDERR], $pipes);
89 | $proc_status = proc_get_status($process);
90 | $exit_code = proc_close($process);
91 | return ($proc_status['running'] ? $exit_code : $proc_status['exitcode']);
92 | }
93 |
94 | /**
95 | * Parse MySQL dsn string.
96 | * @return array parsed elements: host, db, user, password.
97 | */
98 | protected function parseMysqlDsn()
99 | {
100 | $dsn = Yii::$app->db->dsn;
101 | if (!preg_match('/^mysql:host=(.*?);dbname=(.*)/', $dsn, $matches)) {
102 | return false;
103 | }
104 | return [
105 | 'host' => $matches[1],
106 | 'db' => $matches[2],
107 | 'user' => Yii::$app->db->username,
108 | 'password' => Yii::$app->db->password,
109 | ];
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/app/config/common.php:
--------------------------------------------------------------------------------
1 | APPROOT_DIR . '/app',
15 | 'vendorPath' => APPROOT_DIR . '/vendor',
16 | 'runtimePath' => APPROOT_DIR . '/runtime',
17 | 'components' => [
18 | 'authManager' => [
19 | 'class' => 'yii\rbac\DbManager',
20 | ],
21 | ],
22 | ];
23 |
24 | if (YII_ENV_DEV) {
25 | // configuration adjustments for 'dev' environment
26 | $config['bootstrap'][] = 'debug';
27 | $config['modules']['debug'] = [
28 | 'class' => 'yii\debug\Module',
29 | ];
30 |
31 | $config['bootstrap'][] = 'gii';
32 | $config['modules']['gii'] = [
33 | 'class' => 'yii\gii\Module',
34 | 'newFileMode' => 0664,
35 | 'newDirMode' => 0775,
36 | 'generators' => [
37 | 'module' => [
38 | 'class' => 'app\base\gii\ModuleGenerator',
39 | 'templates' => [
40 | 'app-module' => '@app/views/templates/gii-module',
41 | ],
42 | ],
43 | ],
44 | ];
45 |
46 | // Link assets instead of copy them (useful for development environment).
47 | $config['components']['assetManager']['linkAssets'] = true;
48 | }
49 |
50 | return $config;
51 | });
52 |
--------------------------------------------------------------------------------
/app/config/console.php:
--------------------------------------------------------------------------------
1 | 'backend-console',
7 | 'bootstrap' => ['log'],
8 | 'controllerNamespace' => 'app\commands',
9 | 'components' => [
10 | 'cache' => [
11 | 'class' => 'yii\caching\FileCache',
12 | ],
13 | 'log' => [
14 | 'targets' => [
15 | [
16 | 'class' => 'yii\log\FileTarget',
17 | 'levels' => ['error', 'warning'],
18 | ],
19 | ],
20 | ],
21 | ],
22 | 'controllerMap' => [
23 | 'migrate' => [
24 | 'class' => 'yii\console\controllers\MigrateController',
25 | 'templateFile' => '@app/views/templates/migration.php',
26 | ],
27 | 'serve' => [
28 | 'class' => 'yii\console\controllers\ServeController',
29 | 'docroot' => APPROOT_DIR . '/web',
30 | ],
31 | # 'fixture' => [ // Fixture generation command line.
32 | # 'class' => 'yii\faker\FixtureController',
33 | # ],
34 | ],
35 | ];
36 |
37 | return yii\helpers\ArrayHelper::merge(require APPROOT_DIR . '/app/config/common.php', $config);
38 |
--------------------------------------------------------------------------------
/app/controllers/SiteController.php:
--------------------------------------------------------------------------------
1 | [
16 | 'class' => 'yii\web\ErrorAction',
17 | ],
18 | 'captcha' => [
19 | 'class' => 'yii\captcha\CaptchaAction',
20 | 'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
21 | ],
22 | 'settings' => [
23 | 'class' => 'app\base\actions\Settings',
24 | ],
25 | ];
26 | }
27 |
28 | public function actionIndex()
29 | {
30 | return $this->render('index');
31 | }
32 |
33 | public function actionContact()
34 | {
35 | $model = new ContactForm();
36 | if ($model->load(Yii::$app->request->post()) && $model->contact(Yii::$app->params['adminEmail'])) {
37 | Yii::$app->session->setFlash('contactFormSubmitted');
38 |
39 | return $this->refresh();
40 | }
41 | return $this->render('contact', [
42 | 'model' => $model,
43 | ]);
44 | }
45 |
46 | public function actionAbout()
47 | {
48 | return $this->render('about');
49 | }
50 |
51 | public function actionModalRemote()
52 | {
53 | return '
Loaded from remote source!
';
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/app/forms/ContactForm.php:
--------------------------------------------------------------------------------
1 | 'Verification Code',
41 | ];
42 | }
43 |
44 | /**
45 | * Sends an email to the specified email address using the information collected by this model.
46 | * @param string $email the target email address
47 | * @return boolean whether the model passes validation
48 | */
49 | public function contact($email)
50 | {
51 | if ($this->validate()) {
52 | Yii::$app->mailer->compose()
53 | ->setTo($email)
54 | ->setFrom([$this->email => $this->name])
55 | ->setSubject($this->subject)
56 | ->setTextBody($this->body)
57 | ->send();
58 |
59 | return true;
60 | }
61 | return false;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/forms/user/Login.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.1
6 | */
7 |
8 | namespace app\forms\user;
9 |
10 | use Yii;
11 | use yii\base\Model;
12 | use app\models\User;
13 |
14 | /**
15 | * Login is the model behind the login form.
16 | */
17 | class Login extends Model
18 | {
19 | public $email;
20 | public $password;
21 | public $rememberMe = true;
22 |
23 | private $_user = false;
24 |
25 |
26 | /**
27 | * @return array the validation rules.
28 | */
29 | public function rules()
30 | {
31 | return [
32 | [['email', 'password'], 'required'],
33 | ['email', 'email'],
34 | ['rememberMe', 'boolean'],
35 | ['password', 'validatePassword'],
36 | ];
37 | }
38 |
39 | /**
40 | * @inheritdoc
41 | */
42 | public function attributeLabels()
43 | {
44 | return [
45 | 'email' => Yii::t('app', 'Email'),
46 | 'password' => Yii::t('app', 'Password'),
47 | 'rememberMe' => Yii::t('app', 'Remember Me'),
48 | ];
49 | }
50 |
51 | /**
52 | * Validates the password.
53 | * This method serves as the inline validation for password.
54 | *
55 | * @param string $attribute the attribute currently being validated
56 | * @param array $params the additional name-value pairs given in the rule
57 | */
58 | public function validatePassword($attribute, $params)
59 | {
60 | if (!$this->hasErrors()) {
61 | $user = $this->getUser();
62 |
63 | if (!$user || !$user->validatePassword($this->password)) {
64 | $this->addError($attribute, 'Incorrect email or password.');
65 | }
66 | }
67 | }
68 |
69 | /**
70 | * Logs in a user using the provided email and password.
71 | * @return boolean whether the user is logged in successfully
72 | */
73 | public function login()
74 | {
75 | if ($this->validate()) {
76 | $user = $this->getUser();
77 | if ($user->status === User::STATUS_ENABLED) {
78 | return Yii::$app->user->login($user, $this->rememberMe ? 3600*24*30 : 0);
79 | }
80 | }
81 | return false;
82 | }
83 |
84 | /**
85 | * Finds user by [[email]]
86 | *
87 | * @return User|null
88 | */
89 | public function getUser()
90 | {
91 | if ($this->_user === false) {
92 | $this->_user = User::findByEmail($this->email);
93 | }
94 |
95 | return $this->_user;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/app/forms/user/PasswordRequest.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.1
6 | */
7 |
8 | namespace app\forms\user;
9 |
10 | use app\models\User;
11 | use app\base\MailTrait;
12 | use Yii;
13 | use yii\base\Model;
14 |
15 | /**
16 | * PasswordRequest
17 | *
18 | * @author skoro
19 | */
20 | class PasswordRequest extends Model
21 | {
22 |
23 | use MailTrait;
24 |
25 | /**
26 | * @var string
27 | */
28 | public $email;
29 |
30 | /**
31 | * @inheritdoc
32 | */
33 | public function rules()
34 | {
35 | return [
36 | ['email', 'required'],
37 | ['email', 'email'],
38 | ['email', 'exist',
39 | 'targetClass' => Yii::$app->user->identityClass,
40 | 'filter' => ['status' => User::STATUS_ENABLED],
41 | 'message' => 'User with this email not found.',
42 | ],
43 | ];
44 | }
45 |
46 | /**
47 | * Send password reset instructions.
48 | * @return boolean
49 | */
50 | public function sendEmail()
51 | {
52 | $user = User::findByEmail($this->email);
53 | if ($user && $user->status === User::STATUS_ENABLED) {
54 | $user->generatePasswordResetToken();
55 | if ($user->save()) {
56 | return $this->mail('passwordRequest', $this->email, [
57 | 'subject' => Yii::t('app', 'Reset password information for {name} at {site}', [
58 | 'name' => $user->name,
59 | 'site' => Yii::$app->name]
60 | ),
61 | 'user' => $user,
62 | ]);
63 | }
64 | }
65 |
66 | return false;
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/app/forms/user/PasswordReset.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.1
6 | */
7 |
8 | namespace app\forms\user;
9 |
10 | use Yii;
11 | use app\models\User;
12 | use yii\base\InvalidParamException;
13 | use yii\base\Model;
14 |
15 | /**
16 | * Password reset form
17 | */
18 | class PasswordReset extends Model
19 | {
20 |
21 | public $password;
22 | public $password_repeat;
23 |
24 | /**
25 | * @var app\models\User
26 | */
27 | private $_user;
28 |
29 |
30 | /**
31 | * Creates a form model given a token.
32 | *
33 | * @param string $token
34 | * @param array $config name-value pairs that will be used to initialize the object properties
35 | * @throws \yii\base\InvalidParamException if token is empty or not valid
36 | */
37 | public function __construct($token, $config = [])
38 | {
39 | if (empty($token) || !is_string($token)) {
40 | throw new InvalidParamException('Password reset token cannot be blank.');
41 | }
42 | $this->_user = User::findByResetToken($token);
43 | if (!$this->_user) {
44 | throw new InvalidParamException('Wrong password reset token.');
45 | }
46 | parent::__construct($config);
47 | }
48 |
49 | /**
50 | * @inheritdoc
51 | */
52 | public function rules()
53 | {
54 | return [
55 | [['password', 'password_repeat'], 'required'],
56 | ['password', 'string', 'min' => 6],
57 | ['password', 'compare'],
58 | ];
59 | }
60 |
61 | /**
62 | * @inheritdoc
63 | */
64 | public function attributeLabels()
65 | {
66 | return [
67 | 'password' => Yii::t('app', 'Password'),
68 | 'password_repeat' => Yii::t('app', 'Confirm password'),
69 | ];
70 | }
71 |
72 | /**
73 | * Resets password.
74 | *
75 | * @return boolean if password was reset.
76 | */
77 | public function resetPassword()
78 | {
79 | $user = $this->_user;
80 | $user->setPassword($this->password);
81 | $user->removeResetToken();
82 |
83 | return $user->save(false);
84 | }
85 |
86 | /**
87 | * @return User
88 | */
89 | public function getUser()
90 | {
91 | return $this->_user;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/app/helpers/Icon.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.1
6 | */
7 |
8 | namespace app\helpers;
9 |
10 | /**
11 | * Icon
12 | *
13 | * @author skoro
14 | */
15 | class Icon
16 | {
17 |
18 | /**
19 | * Well known Bootstrap icons.
20 | */
21 | const OK = '';
22 | const REMOVE = '';
23 | const USER = '';
24 | const STAR = '';
25 | const PLUS = '';
26 | const MINUS = '';
27 | const TRASH = '';
28 | const COG = '';
29 | const EYE_OPEN = '';
30 | const PAPERCLIP = '';
31 | const TASKS = '';
32 | const FILTER = '';
33 | const DASHBOARD = '';
34 | const SORT = '';
35 | const EDIT = '';
36 | const CHECK = '';
37 | const UNCHECKED = '';
38 | const STATS = '';
39 | const FLASH = '';
40 | const SEARCH = '';
41 | const STAR_EMPTY = '';
42 | const ENVELOPE = '';
43 |
44 | /**
45 | * Returns icon html and optional (unencoded) text after icon.
46 | * @param string $ico icon class (for example, 'fa fa-circle').
47 | * @param string $text optional text after icon.
48 | * @return string
49 | */
50 | public static function icon($ico, $text = '')
51 | {
52 | return ' ' . $text;
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/app/helpers/UserHelper.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.2
6 | */
7 |
8 | namespace app\helpers;
9 |
10 | use app\models\User;
11 | use yii\helpers\Html;
12 |
13 | /**
14 | * Some view helpers for User model.
15 | *
16 | * @author skoro
17 | * @since 0.2
18 | */
19 | class UserHelper
20 | {
21 |
22 | /**
23 | * Renders user status label.
24 | * @param User $user
25 | * @param array $options
26 | * @return string
27 | */
28 | public static function status(User $user, array $options = [])
29 | {
30 | $defaults = ['class' => 'label'];
31 | switch ($user->status) {
32 | case User::STATUS_DISABLED:
33 | Html::addCssClass($defaults, 'label-danger');
34 | break;
35 | case User::STATUS_PENDING:
36 | Html::addCssClass($defaults, 'label-warning');
37 | break;
38 | default:
39 | Html::addCssClass($defaults, 'label-success');
40 |
41 | }
42 | $options = array_merge($defaults, $options);
43 | return Html::tag('span', $user->getStatusLabel(), $options);
44 | }
45 |
46 | /**
47 | * Returns a link to user profile page suitable for use in Url::to().
48 | * @param User $user
49 | * @param array $params optional, additional parameters to link.
50 | * @return array
51 | */
52 | public static function getProfileUrl(User $user, array $params = [])
53 | {
54 | return array_merge(['/user/profile', 'id' => $user->id], $params);
55 | }
56 |
57 | /**
58 | *
59 | * @param User $user
60 | * @return string
61 | */
62 | public static function userLink(User $user, array $options = [], array $linkParams = [])
63 | {
64 | return Html::a(Html::encode($user->name), static::getProfileUrl($user, $linkParams), $options);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/app/mail/accountCreated-html.php:
--------------------------------------------------------------------------------
1 | user->loginUrl, true);
12 | ?>
13 |
14 |
Hello = Html::encode($user->name) ?>,
15 |
16 |
A site administrator at = Yii::$app->name ?> has created an account for you.
17 |
18 |
You will be able to log in at = Html::a($loginUrl, $loginUrl) ?> using:
19 |
20 | - Email: = $user->email ?>
21 | - Password: = $register->password ?>
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/mail/accountCreated-text.php:
--------------------------------------------------------------------------------
1 | user->loginUrl, true);
11 | ?>
12 | Hello = $user->name ?>,
13 | A site administrator at = Yii::$app->name ?> has created an account for you.
14 |
15 | You will be able to log in at = $loginUrl ?> using:
16 |
17 | Email: = $user->email ?>
18 |
19 | Password: = $register->password ?>
20 |
--------------------------------------------------------------------------------
/app/mail/layouts/html.php:
--------------------------------------------------------------------------------
1 |
8 | beginPage() ?>
9 |
10 |
11 |
12 |
13 | = Html::encode($this->title) ?>
14 | head() ?>
15 |
16 |
17 | beginBody() ?>
18 | = $content ?>
19 | endBody() ?>
20 |
21 |
22 | endPage() ?>
23 |
--------------------------------------------------------------------------------
/app/mail/layouts/text.php:
--------------------------------------------------------------------------------
1 |
8 | beginPage() ?>
9 | beginBody() ?>
10 | = $content ?>
11 | endBody() ?>
12 | endPage() ?>
13 |
--------------------------------------------------------------------------------
/app/mail/passwordRequest-html.php:
--------------------------------------------------------------------------------
1 | urlManager->createAbsoluteUrl(['user/password-reset', 'token' => $user->reset_token]);
10 | ?>
11 |
12 |
Hello = Html::encode($user->name) ?>,
13 |
14 |
A request to reset the password for your account has been made at = Html::a(Yii::$app->name, Url::home(true)) ?>
15 |
Follow the link below to reset your password:
16 |
17 |
= Html::a(Html::encode($resetLink), $resetLink) ?>
18 |
19 |
--------------------------------------------------------------------------------
/app/mail/passwordRequest-text.php:
--------------------------------------------------------------------------------
1 | urlManager->createAbsoluteUrl(['user/password-reset', 'token' => $user->reset_token]);
7 | ?>
8 | Hello = $user->name ?>,
9 | A request to reset the password for your account has been made at = Yii::$app->name ?>.
10 |
11 | Follow the link below to reset your password:
12 |
13 | = $resetLink ?>
14 |
--------------------------------------------------------------------------------
/app/migrations/m160314_212231_user.php:
--------------------------------------------------------------------------------
1 | createTable(User::tableName(), [
12 | 'id' => $this->primaryKey(),
13 | 'name' => $this->string(64)->notNull()->unique(),
14 | 'email' => $this->string(64)->unique(),
15 | 'password_hash' => $this->string()->notNull(),
16 | 'reset_token' => $this->string()->notNull()->defaultValue(''),
17 | 'activate_token' => $this->string()->notNull()->defaultValue(''),
18 | 'auth_key' => $this->string()->notNull()->unique(),
19 | 'status' => $this->smallInteger()->unsigned()->defaultValue(0),
20 | 'created_at' => $this->integer()->unsigned(),
21 | 'logged_at' => $this->integer()->unsigned(),
22 | ]);
23 |
24 | $this->createIndex('idx_user_reset_token', User::tableName(), 'reset_token');
25 | $this->createIndex('idx_user_activate_token', User::tableName(), 'activate_token');
26 | }
27 |
28 | public function down()
29 | {
30 | $this->dropTable(User::tableName());
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/app/migrations/m160818_075724_config.php:
--------------------------------------------------------------------------------
1 | createTable($this->table, [
13 | 'id' => $this->primaryKey(),
14 | 'name' => $this->string(255)->notNull(),
15 | 'value' => $this->binary(),
16 | 'value_type' => $this->char(8)->notNull(),
17 | 'options' => $this->binary(),
18 | 'title' => $this->string(255)->notNull(),
19 | 'desc' => $this->text(),
20 | 'section' => $this->string(32)->notNull()->defaultValue('global'),
21 | 'required' => $this->boolean()->notNull()->defaultValue(0),
22 | ]);
23 |
24 | $this->createIndex('idx_config_name', $this->table, ['name', 'section'], true);
25 |
26 | echo 'Insert default parameters...'.PHP_EOL;
27 | $this->insertParam([
28 | 'name' => 'passwordResetTokenExpire',
29 | 'title' => 'Password reset token expire',
30 | 'value' => 3600,
31 | 'value_type' => 'integer',
32 | 'section' => 'User',
33 | 'desc' => 'How long (in seconds) password reset token will be actual.',
34 | 'required' => true,
35 | ]);
36 |
37 | $this->insertParam([
38 | 'name' => 'disableUserRegister',
39 | 'title' => 'Disable user registration',
40 | 'value' => false,
41 | 'value_type' => 'switch',
42 | 'section' => 'User',
43 | ]);
44 |
45 | $this->insertParam([
46 | 'name' => 'noAvatarImage',
47 | 'title' => 'Default user avatar image',
48 | 'value' => '@web/images/avatars/avatar2.png',
49 | 'value_type' => 'text',
50 | 'section' => 'User',
51 | 'desc' => 'Default user avatar picture.',
52 | 'required' => true,
53 | ]);
54 |
55 | $this->insertParam([
56 | 'name' => 'adminEmail',
57 | 'title' => 'Site email',
58 | 'value' => 'admin@example.com',
59 | 'value_type' => 'email',
60 | 'section' => 'Site',
61 | 'desc' => 'Email address used for replies.',
62 | 'required' => true,
63 | ]);
64 |
65 | // This is 'select' param demo:
66 | // $this->insertParam([
67 | // 'name' => 'defaultUserRole',
68 | // 'title' => 'Default user role',
69 | // 'value' => 'Registered',
70 | // 'options' => [
71 | // 'Administrator' => 'Administrator',
72 | // 'Registered' => 'Registerd',
73 | // 'Editor' => 'Editor',
74 | // 'Subscriber' => 'Subscriber',
75 | // ],
76 | // 'value_type' => 'select',
77 | // 'section' => 'User',
78 | // 'desc' => 'Assign newly registered users to specified role.',
79 | // ]);
80 | }
81 |
82 | public function down()
83 | {
84 | $this->dropTable($this->table);
85 | }
86 |
87 | protected function insertParam($data)
88 | {
89 | $data['value'] = serialize($data['value']);
90 | if (isset($data['options'])) {
91 | $data['options'] = serialize($data['options']);
92 | }
93 | Yii::$app->db->createCommand()
94 | ->insert($this->table, $data)
95 | ->execute();
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/app/migrations/m160830_073241_param_default_role.php:
--------------------------------------------------------------------------------
1 | authManager->getRoles(), 'name', 'name');
13 | $columns = [
14 | 'section' => 'User',
15 | 'name' => 'defaultRole',
16 | 'title' => 'Default user role',
17 | 'value' => serialize('Registered'),
18 | 'value_type' => 'select',
19 | 'options' => serialize($roles),
20 | 'desc' => 'Assign newly registered users to specified role.',
21 | ];
22 | Yii::$app->db->createCommand()
23 | ->insert($this->table, $columns)
24 | ->execute();
25 | }
26 |
27 | public function down()
28 | {
29 | Yii::$app->db->createCommand()
30 | ->delete($this->table, [
31 | 'section' => 'User',
32 | 'name' => 'defaultRole'
33 | ])
34 | ->execute();
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/app/migrations/m160831_094735_module.php:
--------------------------------------------------------------------------------
1 | createTable($this->table, [
13 | 'module_id' => $this->string(32)->notNull()->unique(),
14 | 'name' => $this->string(255)->notNull()->defaultValue(''),
15 | 'installed' => $this->boolean()->notNull()->defaultValue(false),
16 | 'desc' => $this->text(),
17 | 'data' => $this->text(),
18 | ]);
19 | $this->createIndex('idx_module_status', $this->table, ['installed']);
20 | }
21 |
22 | public function down()
23 | {
24 | $this->dropTable($this->table);
25 | echo "\n\nWARNING!\n";
26 | echo "Module is essential part of application and application cannot work without module table.\n";
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/app/migrations/m161108_083707_config_perms.php:
--------------------------------------------------------------------------------
1 | addColumn(Config::tableName(), 'perms', $this->binary()->defaultValue(
14 | serialize([
15 | 'updateSettings',
16 | ])
17 | ));
18 | }
19 |
20 | public function down()
21 | {
22 | if ($this->isSqlite()) {
23 | echo '!!! SQLite does not support drop columns.'.PHP_EOL;
24 | return;
25 | }
26 | $this->dropColumn(Config::tableName(), 'perms');
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/app/migrations/m180130_201810_param_site_name.php:
--------------------------------------------------------------------------------
1 | name = 'siteName';
13 | $config->title = 'Site name';
14 | $config->value = 'Admin template';
15 | $config->section = 'Site';
16 | $config->value_type = Config::TYPE_TEXT;
17 | $config->required = true;
18 | $config->save();
19 | }
20 |
21 | public function down()
22 | {
23 | Config::deleteAll(['name' => 'siteName']);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/views/layouts/left.php:
--------------------------------------------------------------------------------
1 |
4 |
45 |
--------------------------------------------------------------------------------
/app/views/layouts/main-login.php:
--------------------------------------------------------------------------------
1 |
11 | beginPage() ?>
12 |
13 |
14 |
15 |
16 |
17 | = Html::csrfMetaTags() ?>
18 | = Html::encode($this->title) ?>
19 | head() ?>
20 |
21 |
22 |
23 | beginBody() ?>
24 |
25 | = Alert::widget() ?>
26 |
27 | = $content ?>
28 |
29 | endBody() ?>
30 |
31 |
32 | endPage() ?>
33 |
--------------------------------------------------------------------------------
/app/views/layouts/main.php:
--------------------------------------------------------------------------------
1 | assetManager->getPublishedUrl('@vendor/almasaeed2010/adminlte/dist');
18 | Notify::widget();
19 | ?>
20 | beginPage() ?>
21 |
22 |
23 |
24 |
25 |
26 | = Html::csrfMetaTags() ?>
27 | = Html::encode($this->title) ?>
28 | head() ?>
29 |
30 |
31 | beginBody() ?>
32 |
33 |
34 | = $this->render(
35 | 'header.php',
36 | ['directoryAsset' => $directoryAsset]
37 | ) ?>
38 |
39 | = $this->render(
40 | 'left.php',
41 | ['directoryAsset' => $directoryAsset]
42 | )
43 | ?>
44 |
45 | = $this->render(
46 | 'content.php',
47 | ['content' => $content, 'directoryAsset' => $directoryAsset]
48 | ) ?>
49 |
50 |
51 |
52 | endBody() ?>
53 |
54 |
55 | endPage() ?>
--------------------------------------------------------------------------------
/app/views/site/about.php:
--------------------------------------------------------------------------------
1 | title = 'About';
8 | $this->params['breadcrumbs'][] = $this->title;
9 | ?>
10 |
11 |
= Html::encode($this->title) ?>
12 |
13 |
14 | This is the About page. You may modify the following file to customize its content:
15 |
16 |
17 |
= __FILE__ ?>
18 |
19 |
--------------------------------------------------------------------------------
/app/views/site/contact.php:
--------------------------------------------------------------------------------
1 | title = 'Contact';
12 | $this->params['breadcrumbs'][] = $this->title;
13 | ?>
14 |
69 |
--------------------------------------------------------------------------------
/app/views/site/error.php:
--------------------------------------------------------------------------------
1 | title = $name;
11 | ?>
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
= $name ?>
20 |
21 |
22 | = nl2br(Html::encode($message)) ?>
23 |
24 |
25 |
26 | The above error occurred while the Web server was processing your request.
27 | Please contact us if you think this is a server error. Thank you.
28 | Meanwhile, you may return to dashboard or try using the search
29 | form.
30 |
31 |
32 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/app/views/templates/confirmation.php:
--------------------------------------------------------------------------------
1 | title = $title;
19 | ?>
20 |
21 |
22 |
23 |
= $this->title ?>
24 |
= $encodeMessage ? Html::encode($message) : $message ?>
25 |
26 |
27 |
28 | = Icon::icon($icon) ?>
29 |
30 |
31 |
32 | $actionUrl,
34 | ]) ?>
35 |
36 | $value): ?>
37 |
44 |
45 |
46 | $button,
48 | 'cancel' => [
49 | 'url' => $cancelUrl,
50 | ],
51 | ]) ?>
52 |
--------------------------------------------------------------------------------
/app/views/templates/gii-module/controller.php:
--------------------------------------------------------------------------------
1 |
11 |
12 | namespace = $generator->getControllerNamespace() ?>;
13 |
14 | use app\base\Controller;
15 |
16 | /**
17 | * Default controller for the `= $generator->moduleID ?>` module
18 | */
19 | class DefaultController extends Controller
20 | {
21 | /**
22 | * Renders the index view for the module
23 | * @return string
24 | */
25 | public function actionIndex()
26 | {
27 | return $this->render('index');
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/views/templates/gii-module/module.php:
--------------------------------------------------------------------------------
1 | moduleClass;
10 | $pos = strrpos($className, '\\');
11 | $ns = ltrim(substr($className, 0, $pos), '\\');
12 | $className = substr($className, $pos + 1);
13 |
14 | echo "
16 |
17 | namespace = $ns ?>;
18 |
19 | /**
20 | * = $generator->moduleID ?> module definition class
21 | */
22 | class = $className ?> extends \app\base\Module
23 | {
24 | /**
25 | * @var string required, module name.
26 | */
27 | public $moduleName = '= $generator->moduleID ?>';
28 |
29 | /**
30 | * @var string
31 | */
32 | public $moduleDescription = '';
33 |
34 | /**
35 | * @inheritdoc
36 | */
37 | public $controllerNamespace = '= $generator->getControllerNamespace() ?>';
38 |
39 | /**
40 | * @inheritdoc
41 | */
42 | public function init()
43 | {
44 | parent::init();
45 |
46 | // custom initialization code goes here
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/views/templates/gii-module/view.php:
--------------------------------------------------------------------------------
1 |
5 |
6 |
= "= " ?>$this->context->action->uniqueId ?>
7 |
8 | This is the view content for action "= "= " ?>$this->context->action->id ?>".
9 | The action belongs to the controller "= "= " ?>get_class($this->context) ?>"
10 | in the "= "= " ?>$this->context->module->id ?>" module.
11 |
12 |
13 | You may customize this page by editing the following file:
14 | = "= " ?>__FILE__ ?>
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/views/templates/migration.php:
--------------------------------------------------------------------------------
1 |
10 |
11 | use app\base\Migration;
12 |
13 | class = $className ?> extends Migration
14 | {
15 |
16 | public $table = '{{%table}}';
17 |
18 | public function up()
19 | {
20 |
21 | }
22 |
23 | public function down()
24 | {
25 | echo "= $className ?> cannot be reverted.\n";
26 |
27 | return false;
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/app/views/user/_create_modal.php:
--------------------------------------------------------------------------------
1 |
8 |
9 | true,
11 | 'enableAjaxValidation' => true,
12 | ]) ?>
13 | = $form->field($register, 'name') ?>
14 | = $form->field($register, 'email') ?>
15 | = $form->field($register, 'password')->passwordInput() ?>
16 | = $form->field($register, 'password_repeat')->passwordInput() ?>
17 | = $form->field($register, 'sendmail')
18 | ->widget(Check::className())
19 | ->label(false) ?>
20 | [
22 | 'options' => ['data-dismiss' => 'modal'],
23 | ],
24 | ]) ?>
--------------------------------------------------------------------------------
/app/views/user/_profile_account.php:
--------------------------------------------------------------------------------
1 |
17 | 'user-profile-form',
19 | 'pjax' => true,
20 | 'layout' => 'horizontal',
21 | 'enableClientValidation' => true,
22 | 'fieldConfig' => [
23 | 'horizontalCssClasses' => [
24 | 'label' => 'col-sm-2',
25 | 'wrapper' => 'col-sm-10',
26 | ],
27 | ],
28 | ]) ?>
29 |
30 | = $form->field($model, 'email')->widget(InputGroup::className(), [
31 | 'inputOptions' => [
32 | 'class' => 'form-control',
33 | 'disabled' => 'disabled',
34 | ],
35 | 'button' => true,
36 | 'addon' => Button::widget([
37 | 'label' => Icon::icon('glyphicon glyphicon-copy'),
38 | 'encodeLabel' => false,
39 | 'options' => ['class' => 'btn btn-default btn-flat'],
40 | 'clientEvents' => [
41 | 'click' => new JsExpression("function (ev) {
42 | ev.preventDefault();
43 | $('#{$emailId}').removeAttr('disabled').select();
44 | document.execCommand('copy');
45 | $('#{$emailId}').attr('disabled', 'disabled');
46 | }"),
47 | ],
48 | ]),
49 | ]) ?>
50 | = $form->field($model, 'name') ?>
51 | = $form->field($model, 'password')->passwordInput() ?>
52 | = $form->field($model, 'password_repeat')->passwordInput() ?>
53 |
54 | false,
56 | ]) ?>
57 |
--------------------------------------------------------------------------------
/app/views/user/_profile_admin.php:
--------------------------------------------------------------------------------
1 |
11 | 'user-profile-admin-form',
13 | 'layout' => 'horizontal',
14 | 'fieldConfig' => [
15 | 'horizontalCssClasses' => [
16 | 'label' => 'col-sm-2',
17 | 'wrapper' => 'col-sm-10',
18 | ],
19 | ],
20 | 'action' => ['profile', 'id' => $model->getUser()->id, 'tab' => 'admin'],
21 | ]) ?>
22 |
23 | = $form->field($model, 'roles')->widget(Check::className(), [
24 | 'items' => ArrayHelper::map(Yii::$app->authManager->getRoles(), 'name', 'name'),
25 | 'options' => ['class' => 'checkbox-list-vert'],
26 | ]) ?>
27 |
28 | = $form->field($model, 'status')->widget(Check::className(), [
29 | 'type' => Check::TYPE_RADIO,
30 | 'items' => $model->getUser()->getStatusLabels(),
31 | 'options' => ['class' => 'checkbox-list-vert'],
32 | ]) ?>
33 |
34 | false,
36 | ]) ?>
37 |
--------------------------------------------------------------------------------
/app/views/user/index.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Users');
17 | $this->params['breadcrumbs'][] = $this->title;
18 | ?>
19 |
20 |
23 | true,
25 | ]) ?>
26 |
27 | user->can('createUser')): ?>
28 | '' . Yii::t('app', 'Create a new user') . '',
30 | 'toggleButton' => [
31 | 'label' => Yii::t('app', 'Create'),
32 | 'class' => ['btn btn-flat btn-default'],
33 | ],
34 | ]) ?>
35 | = $this->render('_create_modal', ['register' => $register]) ?>
36 |
37 |
38 |
39 | = GridView::widget([
40 | 'dataProvider' => $userProvider,
41 | 'bulk' => [
42 | 'items' => [
43 | 'enable' => Yii::t('app', 'Enable'),
44 | 'disable' => Yii::t('app', 'Disable'),
45 | 'delete' => Yii::t('app', 'Delete'),
46 | ],
47 | 'pjax' => true,
48 | 'visible' => Yii::$app->user->can('updateAnyUser') || Yii::$app->user->can('deleteAnyUser'),
49 | ],
50 | 'columns' => [
51 | 'id',
52 | [
53 | 'attribute' => 'name',
54 | 'format' => 'raw',
55 | 'value' => function ($model) {
56 | return UserHelper::userLink($model, ['data-pjax' => 0]);
57 | },
58 | ],
59 | 'email',
60 | [
61 | 'header' => Yii::t('app', 'Roles'),
62 | 'format' => 'html',
63 | 'value' => function ($user) {
64 | return Html::ul(ArrayHelper::getColumn($user->getRoles(), 'name'));
65 | },
66 | ],
67 | [
68 | 'attribute' => 'status',
69 | 'format' => 'html',
70 | 'value' => function ($model) {
71 | return UserHelper::status($model);
72 | },
73 | ],
74 | 'created_at:relativeTime',
75 | 'logged_at:relativeTime',
76 | [
77 | 'class' => DeleteColumn::className(),
78 | 'visible' => Yii::$app->user->can('deleteAnyUser'),
79 | ],
80 | ],
81 | ]) ?>
82 |
83 |
84 |
--------------------------------------------------------------------------------
/app/views/user/login.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Sign In');
15 |
16 | $fieldOptions = function ($icon) {
17 | return [
18 | 'options' => ['class' => 'form-group has-feedback'],
19 | 'inputTemplate' => "{input}"
20 | ];
21 | };
22 | ?>
23 |
24 |
25 |
28 |
29 |
30 |
= Yii::t('app', 'Sign in to start your session') ?>
31 |
32 | 'login-form', 'enableClientValidation' => false]); ?>
33 |
34 | = $form
35 | ->field($model, 'email', $fieldOptions('envelope'))
36 | ->label(false)
37 | ->textInput(['placeholder' => $model->getAttributeLabel('email')]) ?>
38 |
39 | = $form
40 | ->field($model, 'password', $fieldOptions('lock'))
41 | ->label(false)
42 | ->passwordInput(['placeholder' => $model->getAttributeLabel('password')]) ?>
43 |
44 |
45 |
46 | = $form->field($model, 'rememberMe')->widget(Check::className())->label(false) ?>
47 |
48 |
49 |
50 | = Html::submitButton(Yii::t('app', 'Sign in'), ['class' => 'btn btn-primary btn-block btn-flat', 'name' => 'login-button']) ?>
51 |
52 |
53 |
54 |
55 |
56 |
57 |
64 |
65 |
66 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/app/views/user/passwordRequest.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Request password');
13 | $fieldOptions = function ($icon) {
14 | return [
15 | 'options' => ['class' => 'form-group has-feedback'],
16 | 'inputTemplate' => "{input}"
17 | ];
18 | };
19 | ?>
20 |
21 |
24 |
25 |
26 |
= Yii::t('app', 'If you have forgotten your password, you can reset it here. When you fill in your registered email address, you will be sent instructions on how to reset your password.') ?>
27 |
28 | 'login-form', 'enableClientValidation' => false]); ?>
29 |
30 | = $form
31 | ->field($model, 'email', $fieldOptions('envelope'))
32 | ->label(false)
33 | ->textInput(['placeholder' => $model->getAttributeLabel('email')]) ?>
34 |
35 |
36 |
37 | = Html::submitButton(Yii::t('app', 'Submit'), ['class' => 'btn btn-primary btn-block btn-flat', 'name' => 'submit-button']) ?>
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/views/user/passwordReset.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Reset password');
13 | $fieldOptions = function ($icon) {
14 | return [
15 | 'options' => ['class' => 'form-group has-feedback'],
16 | 'inputTemplate' => "{input}"
17 | ];
18 | };
19 | ?>
20 |
21 |
24 |
25 |
26 |
= Html::encode($model->getUser()->name) ?>
27 |
= Yii::t('app', 'Please, change your password.') ?>
28 |
29 | 'reset-password-form', 'enableClientValidation' => false]); ?>
30 |
31 | = $form
32 | ->field($model, 'password', $fieldOptions('lock'))
33 | ->label(false)
34 | ->passwordInput(['placeholder' => $model->getAttributeLabel('password')]) ?>
35 |
36 | = $form
37 | ->field($model, 'password_repeat', $fieldOptions('lock'))
38 | ->label(false)
39 | ->passwordInput(['placeholder' => $model->getAttributeLabel('password_repeat')]) ?>
40 |
41 |
42 |
43 | = Html::submitButton(Yii::t('app', 'Submit'), ['class' => 'btn btn-primary btn-block btn-flat', 'name' => 'submit-button']) ?>
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/app/views/user/profile.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'User Profile');
15 | if (Yii::$app->user->can('viewAnyUser')) {
16 | $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Users'), 'url' => ['index']];
17 | }
18 | $this->params['breadcrumbs'][] = $this->title;
19 | ?>
20 |
21 |
22 |
23 |
24 | Box::BOX_PRIMARY,
26 | 'bodyOptions' => ['class' => 'box-profile'],
27 | ]) ?>
28 | = Html::img(Param::value('User.noAvatarImage'), ['class' => 'profile-user-img img-responsive img-circle']) ?>
29 |
30 | = Html::encode($model->name) ?>
31 |
32 |
33 | = Yii::t('app', 'Member since {date}', ['date' => Yii::$app->formatter->asDate(Yii::$app->user->identity->created_at)]) ?>
34 |
35 | = ItemList::widget([
36 | 'items' => [
37 | [
38 | 'title' => 'ID',
39 | 'value' => $model->getUser()->id,
40 | ],
41 | [
42 | 'title' => Yii::t('app', 'Status'),
43 | 'value' => UserHelper::status($model->getUser()),
44 | ],
45 | [
46 | 'title' => Yii::t('app', 'Last login'),
47 | 'value' => Yii::$app->formatter->asRelativeTime($model->getUser()->logged_at),
48 | ],
49 | ],
50 | ]) ?>
51 |
52 |
53 |
54 |
55 |
56 | = Tabs::widget([
57 | 'items' => [
58 | [
59 | 'label' => Yii::t('app', 'Account'),
60 | 'content' => $this->render('_profile_account', ['model' => $model]),
61 | 'active' => $tab == 'account',
62 | ],
63 | [
64 | 'label' => Yii::t('app', 'Administer'),
65 | 'content' => $this->render('_profile_admin', ['model' => $model]),
66 | 'visible' => Yii::$app->user->can('updateAnyUser'),
67 | 'active' => $tab == 'admin',
68 | ],
69 | ],
70 | ]) ?>
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/app/views/user/register.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'User register');
13 | $fieldOptions = function ($icon) {
14 | return [
15 | 'options' => ['class' => 'form-group has-feedback'],
16 | 'inputTemplate' => "{input}"
17 | ];
18 | };
19 | ?>
20 |
21 |
22 |
25 |
26 |
27 |
= Yii::t('app', 'Register a new account') ?>
28 |
29 | 'register-form', 'enableClientValidation' => false]); ?>
30 |
31 | = $form
32 | ->field($model, 'name', $fieldOptions('user'))
33 | ->label(false)
34 | ->textInput(['placeholder' => $model->getAttributeLabel('name')]) ?>
35 |
36 | = $form
37 | ->field($model, 'email', $fieldOptions('envelope'))
38 | ->label(false)
39 | ->textInput(['placeholder' => $model->getAttributeLabel('email')]) ?>
40 |
41 | = $form
42 | ->field($model, 'password', $fieldOptions('lock'))
43 | ->label(false)
44 | ->passwordInput(['placeholder' => $model->getAttributeLabel('password')]) ?>
45 |
46 | = $form
47 | ->field($model, 'password_repeat', $fieldOptions('lock'))
48 | ->label(false)
49 | ->passwordInput(['placeholder' => $model->getAttributeLabel('password_repeat')]) ?>
50 |
51 |
52 |
53 | = Html::submitButton(Yii::t('app', 'Register'), ['class' => 'btn btn-primary btn-block btn-flat', 'name' => 'register-button']) ?>
54 |
55 |
56 |
57 |
58 |
59 |
60 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/app/widgets/ActiveForm.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @version $Id$
6 | */
7 |
8 | namespace app\widgets;
9 |
10 | use Yii;
11 | use yii\helpers\Html;
12 | use yii\helpers\ArrayHelper;
13 |
14 | /**
15 | * ActiveForm
16 | *
17 | * @author skoro
18 | */
19 | class ActiveForm extends \yii\bootstrap\ActiveForm
20 | {
21 |
22 | /**
23 | * @var boolean on/off form's data-pjax attribute.
24 | */
25 | public $pjax = false;
26 |
27 | /**
28 | * @inheritdoc
29 | */
30 | public function init()
31 | {
32 | if ($this->pjax) {
33 | $this->options['data-pjax'] = 1;
34 | }
35 | parent::init();
36 | }
37 |
38 | /**
39 | * End form with action buttons: submit and cancel.
40 | *
41 | * @todo allow to add more than two internal buttons.
42 | * @param array $options button options.
43 | */
44 | public static function endWithActions(array $options = [])
45 | {
46 | $defaults = [
47 | 'options' => ['class' => 'actions'],
48 | 'save' => [
49 | 'label' => Yii::t('app', 'Save'),
50 | 'options' => ['class' => 'btn btn-primary btn-flat'],
51 | ],
52 | 'cancel' => [
53 | 'label' => Yii::t('app', 'Cancel'),
54 | 'options' => ['class' => 'btn btn-warning btn-flat pull-right'],
55 | ],
56 | ];
57 | $options = ArrayHelper::merge($defaults, $options);
58 | $buttons = '';
59 | if (!empty($options['save'])) {
60 | $buttons .= Html::submitButton(
61 | ArrayHelper::getValue($options, 'save.label'),
62 | ArrayHelper::getValue($options, 'save.options', [])
63 | );
64 | }
65 | if (!empty($options['cancel'])) {
66 | if (!empty($options['cancel']['url'])) {
67 | $buttons .= Html::a(
68 | ArrayHelper::getValue($options, 'cancel.label'),
69 | $options['cancel']['url'],
70 | ArrayHelper::getValue($options, 'cancel.options', [])
71 | );
72 | } else {
73 | $buttons .= Html::button(
74 | ArrayHelper::getValue($options, 'cancel.label'),
75 | ArrayHelper::getValue($options, 'cancel.options', [])
76 | );
77 | }
78 | }
79 | echo Html::tag('div', $buttons, $options['options']);
80 | static::end();
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/app/widgets/Check.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @version $Id$
6 | */
7 |
8 | namespace app\widgets;
9 |
10 | use app\assets\CheckAsset;
11 | use yii\base\InvalidValueException;
12 | use yii\bootstrap\InputWidget;
13 | use yii\helpers\Html;
14 |
15 | /**
16 | * iCheck plugin
17 | *
18 | * @link https://github.com/fronteed/icheck
19 | * @author skoro
20 | */
21 | class Check extends InputWidget
22 | {
23 |
24 | /**
25 | * Control type.
26 | */
27 | const TYPE_CHECKBOX = 'checkbox';
28 | const TYPE_RADIO = 'radio';
29 |
30 | /**
31 | * Predefined styles.
32 | */
33 | const STYLE_MINIMAL = 'minimal';
34 | const STYLE_FLAT = 'flat';
35 | const STYLE_LINE = 'line';
36 | const STYLE_SQUARE = 'square';
37 |
38 | /**
39 | * Style colors.
40 | */
41 | const COLOR_RED = 'red';
42 | const COLOR_GREEN = 'green';
43 | const COLOR_BLUE = 'blue';
44 | const COLOR_AERO = 'aero';
45 | const COLOR_GREY = 'grey';
46 | const COLOR_ORANGE = 'orange';
47 | const COLOR_YELLOW = 'yellow';
48 | const COLOR_PINK = 'pink';
49 | const COLOR_PURPLE = 'purple';
50 |
51 | /**
52 | * @var string control type.
53 | */
54 | public $type = self::TYPE_CHECKBOX;
55 |
56 | /**
57 | * @var string control style.
58 | */
59 | public $style = self::STYLE_FLAT;
60 |
61 | /**
62 | * @var string style color.
63 | */
64 | public $color = self::COLOR_GREEN;
65 |
66 | /**
67 | * @var string
68 | */
69 | public $label = '';
70 |
71 | /**
72 | * @var array checkbox list
73 | */
74 | public $items = [];
75 |
76 | /**
77 | * @inheritdoc
78 | */
79 | public function run()
80 | {
81 | $this->registerPlugin('iCheck');
82 |
83 | if ($this->label) {
84 | $this->options['label'] = $this->label;
85 | }
86 |
87 | if ($this->items) {
88 | if (!($this->items = array_filter($this->items))) {
89 | throw new InvalidValueException('Empty items list.');
90 | }
91 | }
92 |
93 | if ($this->hasModel()) {
94 | if ($this->items) {
95 | $method = $this->type === static::TYPE_CHECKBOX ? 'activeCheckboxList' : 'activeRadioList';
96 | $input = Html::$method($this->model, $this->attribute, $this->items, $this->options);
97 | } else {
98 | $input = Html::activeCheckbox($this->model, $this->attribute, $this->options);
99 | }
100 | } elseif ($this->items) {
101 | $method = $this->type === static::TYPE_CHECKBOX ? 'checkboxList' : 'radioList';
102 | $input = Html::$method($this->name, (bool) $this->value, $this->items, $this->options);
103 | } else {
104 | $input = Html::checkbox($this->name, (bool) $this->value, $this->options);
105 | }
106 |
107 | return $input;
108 | }
109 |
110 | /**
111 | * @inheritdoc
112 | */
113 | protected function registerPlugin($name)
114 | {
115 | $asset = CheckAsset::register($this->getView());
116 | $asset->style = $this->style;
117 |
118 | $class = $this->type === static::TYPE_CHECKBOX ? 'checkboxClass' : 'radioClass';
119 | $this->clientOptions[$class] = static::createStyleName($this->type, $this->style, $this->color);
120 |
121 | parent::registerPlugin($name);
122 | }
123 |
124 | /**
125 | * Create compound checkbox style name.
126 | * @param string $type
127 | * @param string $style
128 | * @param string $color
129 | * @return string
130 | */
131 | public static function createStyleName($type, $style, $color)
132 | {
133 | return 'i' . $type . '_' . $style . '-' . $color;
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/app/widgets/InputClear.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @version $Id$
6 | * @since 0.1
7 | */
8 |
9 | namespace app\widgets;
10 |
11 | use app\helpers\Icon;
12 | use yii\bootstrap\Button;
13 | use yii\web\JsExpression;
14 |
15 | /**
16 | * InputClear
17 | *
18 | * Adds to input button by pressing which input is cleared.
19 | *
20 | * @author skoro
21 | */
22 | class InputClear extends InputGroup
23 | {
24 |
25 | /**
26 | * @var string
27 | */
28 | public $icon = 'glyphicon glyphicon-erase';
29 |
30 | /**
31 | * @inheritdoc
32 | */
33 | public $button = true;
34 |
35 | /**
36 | * @var array
37 | */
38 | public $buttonOptions = ['class' => 'btn btn-default btn-flat'];
39 |
40 | /**
41 | * @inheritdoc
42 | */
43 | public function run()
44 | {
45 | $this->addon = Button::widget([
46 | 'label' => Icon::icon($this->icon),
47 | 'options' => $this->buttonOptions,
48 | 'encodeLabel' => false,
49 | 'clientEvents' => [
50 | 'click' => new JsExpression("
51 | function (ev) {
52 | ev.preventDefault();
53 | jQuery('#{$this->inputOptions['id']}').val('');
54 | }
55 | "),
56 | ],
57 | ]);
58 | return parent::run();
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/app/widgets/InputGroup.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @version $Id$
6 | * @since 0.1
7 | */
8 |
9 | namespace app\widgets;
10 |
11 | use yii\bootstrap\InputWidget;
12 | use yii\helpers\Html;
13 |
14 | /**
15 | * InputGroup
16 | *
17 | * For example, put button labeled Go! to end of text input:
18 | * ```php
19 | * echo InputGroup::widget([
20 | * 'model' => $model,
21 | * 'attribute' => 'name',
22 | * 'addon' => Html::a('Go!', ['go'], ['class' => 'btn btn-default']),
23 | * 'button' => true,
24 | * ]);
25 | * ```
26 | *
27 | * @link http://getbootstrap.com/components/#input-groups
28 | * @author skoro
29 | */
30 | class InputGroup extends InputWidget
31 | {
32 |
33 | const SIZE_LARGE = 'input-group-lg';
34 | const SIZE_SMALL = 'input-group-sm';
35 |
36 | /**
37 | * @var string input group size.
38 | */
39 | public $size = '';
40 |
41 | /**
42 | * @var boolean put addon on left or right.
43 | */
44 | public $left = false;
45 |
46 | /**
47 | * @var string addon content.
48 | */
49 | public $addon = '';
50 |
51 | /**
52 | * @var boolean treat addon as a button.
53 | */
54 | public $button = false;
55 |
56 | /**
57 | * @var array
58 | */
59 | public $addonOptions = [];
60 |
61 | /**
62 | * @var array
63 | */
64 | public $inputOptions = ['class' => 'form-control'];
65 |
66 | /**
67 | * @inheritdoc
68 | */
69 | public function init()
70 | {
71 | parent::init();
72 | $this->inputOptions['id'] = $this->options['id'];
73 | $this->options['id'] = $this->getId();
74 | }
75 |
76 | /**
77 | * @inheritdoc
78 | */
79 | public function run()
80 | {
81 | if ($this->hasModel()) {
82 | $input = Html::activeTextInput($this->model, $this->attribute, $this->inputOptions);
83 | } else {
84 | $input = Html::textInput($this->name, $this->value, $this->inputOptions);
85 | }
86 |
87 | $addon = $this->renderAddon();
88 |
89 | Html::addCssClass($this->options, 'input-group');
90 | Html::addCssClass($this->options, $this->size);
91 |
92 | $content = $this->left ? $addon . $input : $input . $addon;
93 |
94 | return Html::tag('div', $content, $this->options);
95 | }
96 |
97 | /**
98 | * Renders addon container.
99 | * @return string
100 | */
101 | protected function renderAddon()
102 | {
103 | if ($this->button) {
104 | $tag = 'div';
105 | Html::addCssClass($this->addonOptions, 'input-group-btn');
106 | }
107 | else {
108 | $tag = 'span';
109 | Html::addCssClass($this->addonOptions, 'input-group-addon');
110 | }
111 | return Html::tag($tag, $this->addon, $this->addonOptions);
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/app/widgets/ItemList.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @version $Id$
6 | */
7 |
8 | namespace app\widgets;
9 |
10 | use yii\bootstrap\Widget;
11 | use yii\helpers\Html;
12 | use yii\helpers\ArrayHelper;
13 |
14 | /**
15 | * AdminLTE items list widget.
16 | *
17 | * @author skoro
18 | */
19 | class ItemList extends Widget
20 | {
21 |
22 | /**
23 | * @var array
24 | */
25 | public $items = [];
26 |
27 | /**
28 | * @inheritdoc
29 | */
30 | public function run()
31 | {
32 | $items = array_map(function ($item) {
33 | $options = ArrayHelper::getValue($item, 'options', []);
34 | Html::addCssClass($options, 'list-group-item');
35 | $valueOptions = ArrayHelper::getValue($item, 'valueOptions', []);
36 | Html::addCssClass($valueOptions, 'pull-right');
37 | $content = $item['title'] . Html::tag('span', $item['value'], $valueOptions);
38 | return Html::tag('li', $content, $options);
39 | }, $this->items);
40 |
41 | $options = $this->options;
42 | Html::addCssClass($options, 'list-group list-group-unbordered');
43 |
44 | return Html::tag('ul', implode("\n", $items), $options);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/widgets/Modal.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @version $Id$
6 | */
7 |
8 | namespace app\widgets;
9 |
10 | use yii\helpers\Url;
11 |
12 | /**
13 | * Modal widget.
14 | *
15 | * ~~~php
16 | * Modal::begin([
17 | * 'header' => 'Hello world
',
18 | * 'toggleButton' => ['label' => 'click me'],
19 | * 'remote' => ['demo'],
20 | * ]);
21 | *
22 | * echo 'Say hello...';
23 | *
24 | * Modal::end();
25 | *
26 | * // In controller:
27 | * public function actionDemo() {
28 | * return 'Put content to modal.';
29 | * }
30 | * ~~~
31 | *
32 | * @author skoro
33 | */
34 | class Modal extends \yii\bootstrap\Modal
35 | {
36 |
37 | /**
38 | * @var array|string fetch content from remote source.
39 | */
40 | public $remote;
41 |
42 | /**
43 | * @inheritdoc
44 | */
45 | public function run()
46 | {
47 | parent::run();
48 | if ($this->remote) {
49 | $id = $this->options['id'];
50 | $url = Url::to($this->remote);
51 | $this->getView()->registerJs("Admin.Modal.remote('#$id', '$url');");
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/widgets/Notify.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @version $Id$
6 | */
7 |
8 | namespace app\widgets;
9 |
10 | use app\assets\AnimateAsset;
11 | use app\assets\BootstrapNotifyAsset;
12 | use Yii;
13 | use yii\base\Widget;
14 | use yii\helpers\Json;
15 |
16 | /**
17 | * Bootstrap notify widget.
18 | *
19 | * @link http://bootstrap-notify.remabledesigns.com
20 | * @author skoro
21 | */
22 | class Notify extends Widget
23 | {
24 |
25 | /**
26 | * @var array
27 | */
28 | public $animate = [
29 | 'enter' => 'animated fadeInRight',
30 | 'exit' => 'animated fadeOutRight',
31 | ];
32 |
33 | /**
34 | * @inheritdoc
35 | */
36 | public function run()
37 | {
38 | $session = Yii::$app->getSession();
39 | $flashes = $session->getAllFlashes(true);
40 |
41 | if (empty($flashes)) {
42 | return;
43 | }
44 |
45 | $view = $this->getView();
46 | BootstrapNotifyAsset::register($view);
47 | if ($this->animate) {
48 | // FIXME: in pjax responses animate.css does not included in body.
49 | AnimateAsset::register($view);
50 | }
51 |
52 | foreach ($flashes as $type => $messages) {
53 | $settings = [];
54 | switch ($type) {
55 | case 'success': case 'info':
56 | case 'warning':
57 | case 'danger': case 'error':
58 | $settings['type'] = $type;
59 | break;
60 | }
61 |
62 | if ($this->animate) {
63 | $settings['animate'] = $this->animate;
64 | }
65 |
66 | foreach ($messages as $message) {
67 | $settings = Json::encode($settings);
68 | $view->registerJs("\$.notify('$message', $settings);");
69 | }
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/app/widgets/Pjax.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @version $Id$
6 | */
7 |
8 | namespace app\widgets;
9 |
10 | /**
11 | * Pjax
12 | *
13 | * Modified Pjax widget with support of session notifications.
14 | * @author skoro
15 | */
16 | class Pjax extends \yii\widgets\Pjax
17 | {
18 |
19 | /**
20 | * @var array
21 | */
22 | public $notifyOptions = [];
23 |
24 | /**
25 | * @var boolean enable hacks when pjax container in modal.
26 | */
27 | public $modal = false;
28 |
29 | /**
30 | * @inheritdoc
31 | */
32 | public function run()
33 | {
34 | if ($this->requiresPjax()) {
35 | echo Notify::widget($this->notifyOptions);
36 | }
37 | $id = $this->options['id'];
38 | if ($this->modal) {
39 | $this->getView()->registerJs("Admin.Modal.pjax('#$id');");
40 | }
41 | parent::run();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/widgets/ProgressBar.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @version $Id$
6 | */
7 |
8 | namespace app\widgets;
9 |
10 | use yii\base\Widget;
11 | use yii\helpers\Html;
12 |
13 | /**
14 | * ProgressBar
15 | *
16 | * ```php
17 | * echo ProgressBar::widget([
18 | * 'total' => 1200,
19 | * 'value' => 250,
20 | * ]);
21 | * ```
22 | *
23 | * @author skoro
24 | */
25 | class ProgressBar extends Widget
26 | {
27 |
28 | /**
29 | * Progress bar styles.
30 | */
31 | const STYLE_DANGER = 'danger';
32 | const STYLE_INFO = 'info';
33 | const STYLE_PRIMARY = 'primary';
34 | const STYLE_SUCCESS = 'success';
35 | const STYLE_WARNING = 'warning';
36 |
37 | /**
38 | * Progress sizes.
39 | */
40 | const SIZE_XS = 'progress-xs';
41 | const SIZE_XXS = 'progress-xxs';
42 | const SIZE_SM = 'progress-sm';
43 |
44 | /**
45 | * @var integer max progress
46 | */
47 | public $total = 100;
48 |
49 | /**
50 | * @var integer current value of progress.
51 | */
52 | public $value;
53 |
54 | /**
55 | * @var string progress bar style.
56 | */
57 | public $style = self::STYLE_PRIMARY;
58 |
59 | /**
60 | * @var array
61 | */
62 | public $options = [];
63 |
64 | /**
65 | * @var string progress bar height.
66 | */
67 | public $size = '';
68 |
69 | /**
70 | * @var boolean render a vertical progress bar.
71 | */
72 | public $vertical = false;
73 |
74 | /**
75 | * @inheritdoc
76 | */
77 | public function run()
78 | {
79 | $options = $this->options;
80 | Html::addCssClass($options, 'progress');
81 |
82 | if ($this->size) {
83 | Html::addCssClass($options, $this->size);
84 | }
85 |
86 | if ($this->vertical) {
87 | Html::addCssClass($options, 'vertical');
88 | }
89 |
90 | $bar = $this->renderBar();
91 |
92 | return Html::tag('div', $bar, $options);
93 | }
94 |
95 | /**
96 | * Renders and calculate percent value.
97 | * @return string
98 | */
99 | protected function renderBar()
100 | {
101 | $options = [
102 | 'class' => 'progress-bar',
103 | 'role' => 'progressbar',
104 | 'aria-valuenow' => $this->value,
105 | 'aria-valuemin' => 0,
106 | 'aria-valuemax' => $this->total,
107 | ];
108 | if ($this->style) {
109 | Html::addCssClass($options, 'progress-bar-' . $this->style);
110 | }
111 |
112 | if ($this->value === 0 || $this->value < 0) {
113 | $value = 0;
114 | }
115 | elseif ($this->value > 0 && $this->total > 0) {
116 | $value = (int)(($this->value * 100) / $this->total);
117 | }
118 | else {
119 | $value = $this->total == 0 ? 100 :
120 | ($this->total > 100 ? 100 : $this->total);
121 | }
122 |
123 | $percent = $value . '%';
124 | if ($this->vertical) {
125 | Html::addCssStyle($options, ['height' => $percent]);
126 | } else {
127 | Html::addCssStyle($options, ['width' => $percent]);
128 | }
129 |
130 | $content = Html::tag('span', $percent, ['class' => 'sr-only']);
131 | return Html::tag('div', $content, $options);
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/app/widgets/ProgressBarGroup.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @version $Id$
6 | */
7 |
8 | namespace app\widgets;
9 |
10 | use yii\bootstrap\Widget;
11 | use yii\helpers\Html;
12 |
13 | /**
14 | * Extension to ProgressBar widget which add label and number.
15 | *
16 | * @author skoro
17 | */
18 | class ProgressBarGroup extends Widget
19 | {
20 | /**
21 | * @var string progress bar label.
22 | */
23 | public $label = '';
24 |
25 | /**
26 | * @var boolean
27 | */
28 | public $encodeLabel = true;
29 |
30 | /**
31 | * @var array
32 | */
33 | public $options = [];
34 |
35 | /**
36 | * @var array
37 | */
38 | public $labelOptions = [];
39 |
40 | /**
41 | * @var array
42 | */
43 | public $numberOptions = [];
44 |
45 | /**
46 | * @var integer current value of progress.
47 | */
48 | public $value;
49 |
50 | /**
51 | * @var integer max progress
52 | */
53 | public $total = 100;
54 |
55 | /**
56 | * @var array progress bar options.
57 | */
58 | public $progress = [];
59 |
60 | /**
61 | * @inheritdoc
62 | */
63 | public function run()
64 | {
65 | $options = $this->options;
66 | Html::addCssClass($options, 'progress-group');
67 |
68 | $label = $this->renderLabel();
69 | $number = $this->renderNumber();
70 |
71 | $this->progress['value'] = $this->value;
72 | $this->progress['total'] = $this->total;
73 | $progress = ProgressBar::widget($this->progress);
74 |
75 | return Html::tag('div', $label . "\n" . $number . "\n" . $progress, $options);
76 | }
77 |
78 | /**
79 | * Renders progress bar label.
80 | * @return string
81 | */
82 | protected function renderLabel()
83 | {
84 | $options = $this->labelOptions;
85 | Html::addCssClass($options, 'progress-text');
86 | $label = $this->encodeLabel ? Html::encode($this->label) : $this->label;
87 | return Html::tag('span', $label, $options);
88 | }
89 |
90 | /**
91 | * Renders progress number.
92 | * @return string
93 | */
94 | protected function renderNumber()
95 | {
96 | $options = $this->numberOptions;
97 | Html::addCssClass($options, 'progress-number');
98 | $number = '' . $this->value . '/' . $this->total;
99 | return Html::tag('span', $number, $options);
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/app/widgets/TimePicker.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @version $Id$
6 | */
7 |
8 | namespace app\widgets;
9 |
10 | use yii\helpers\Html;
11 | use yii\bootstrap\InputWidget;
12 | use app\assets\TimePickerAsset;
13 |
14 | /**
15 | * AdminLTE TimePicker widget.
16 | *
17 | * @author skoro
18 | */
19 | class TimePicker extends InputWidget
20 | {
21 |
22 | const MODE_24H = '24h';
23 | const MODE_12H = '12h';
24 |
25 | /**
26 | * @var string
27 | */
28 | public $mode = self::MODE_24H;
29 |
30 | /**
31 | * @var array
32 | */
33 | public $containerOptions = [];
34 |
35 | /**
36 | * @inheritdoc
37 | */
38 | public function run()
39 | {
40 | $this->registerScript();
41 |
42 | $options = $this->options;
43 | Html::addCssClass($options, 'form-control');
44 |
45 | if ($this->hasModel()) {
46 | $input = Html::activeTextInput($this->model, $this->attribute, $options);
47 | } else {
48 | $input = Html::textInput($this->name, $this->value, $options);
49 | }
50 |
51 | $input .= '';
52 |
53 | $containerOptions = $this->containerOptions;
54 | $containerOptions['id'] = $this->getId();
55 | Html::addCssClass($containerOptions, 'input-group bootstrap-timepicker timepicker');
56 |
57 | return Html::tag('div', $input, $containerOptions);
58 | }
59 |
60 | /**
61 | * Include assets and enable plugin.
62 | */
63 | protected function registerScript()
64 | {
65 | if ($this->mode === static::MODE_24H) {
66 | $this->clientOptions['showMeridian'] = false;
67 | }
68 |
69 | if ($this->hasModel() || $this->value) {
70 | $this->clientOptions['defaultTime'] = false;
71 | }
72 |
73 | TimePickerAsset::register($this->getView());
74 |
75 | $this->registerPlugin('timepicker');
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/app/widgets/TypeAhead.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @version $Id$
6 | */
7 |
8 | namespace app\widgets;
9 |
10 | use yii\bootstrap\InputWidget;
11 | use yii\gii\TypeAheadAsset;
12 | use yii\helpers\Html;
13 | use yii\helpers\Json;
14 | use yii\helpers\Url;
15 | use yii\web\JsExpression;
16 | use yii\web\View;
17 |
18 | /**
19 | * Bootstrap TypeAhead widget.
20 | *
21 | * In view:
22 | * ```php
23 | * echo TypeAhead::widget([
24 | * 'model' => $model,
25 | * 'attribute' => $name,
26 | * 'remote' => ['autocomplete', 'add' => 'that is additional parameter passed to action'],
27 | * ]);
28 | * ```
29 | *
30 | * In controller:
31 | * ```php
32 | * public function actionAutocomplete($add)
33 | * {
34 | * Yii::$app->response->format = Response::FORMAT_JSON;
35 | * return [
36 | * ['value' => 'Apple'],
37 | * ['value' => 'Orange'],
38 | * ['value' => 'Cherry'],
39 | * ];
40 | * }
41 | * ```
42 | *
43 | * @author skoro
44 | */
45 | class TypeAhead extends InputWidget
46 | {
47 |
48 | /**
49 | * @var string|array fetch item from the remote source.
50 | */
51 | public $remote;
52 |
53 | /**
54 | * @var array
55 | */
56 | public $options = ['class' => 'form-control'];
57 |
58 | /**
59 | * @inheritdoc
60 | */
61 | public function run()
62 | {
63 | $this->registerPlugin('typeahead');
64 | if ($this->hasModel()) {
65 | return Html::activeTextInput($this->model, $this->attribute, $this->options);
66 | } else {
67 | return Html::textInput($this->name, $this->value, $this->options);
68 | }
69 | }
70 |
71 | protected function getDataSource()
72 | {
73 | $name = 'ds-' . $this->options['id'];
74 | $source = 'ds_' . str_replace('-', '_', $this->options['id']);
75 | $this->remote['q'] = '__QUERY';
76 | $url = Url::to($this->remote);
77 |
78 | $js = <<getView()->registerJs($js, View::POS_READY);
89 |
90 | return [
91 | 'name' => $name,
92 | 'display' => 'value',
93 | 'source' => new JsExpression($source),
94 | ];
95 | }
96 |
97 | /**
98 | * @inheritdoc
99 | */
100 | protected function registerPlugin($name)
101 | {
102 | $view = $this->getView();
103 |
104 | TypeAheadAsset::register($view);
105 |
106 | $id = $this->options['id'];
107 |
108 | if ($this->clientOptions !== false) {
109 | $options = empty($this->clientOptions) ? 'null' : Json::htmlEncode($this->clientOptions);
110 | $dataSource = Json::htmlEncode($this->getDataSource());
111 | $js = "jQuery('#$id').$name($options, $dataSource);";
112 | $view->registerJs($js);
113 | }
114 |
115 | $this->registerClientEvents();
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/bin/yii:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | [
26 | 'db' => $local['components']['db']
27 | ]] : []
28 | );
29 |
30 | $application = new app\base\ConsoleApplication($config);
31 | $exitCode = $application->run();
32 | exit($exitCode);
33 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "skoro/yii2-admin-template",
3 | "description": "Yii 2 project template for backend projects",
4 | "keywords": ["yii2", "framework", "project template", "admin", "backend"],
5 | "license": "MIT",
6 | "type": "project",
7 | "authors": [
8 | {
9 | "name": "Alexei Skorobogatko",
10 | "email": "skorobogatko.oleksii@gmail.com",
11 | "role": "Developer"
12 | }
13 | ],
14 | "require": {
15 | "php": ">=5.4.0",
16 | "yiisoft/yii2": "^2.0",
17 | "yiisoft/yii2-bootstrap": "*",
18 | "yiisoft/yii2-swiftmailer": "*",
19 | "dmstr/yii2-adminlte-asset": "2.*",
20 | "bower-asset/remarkable-bootstrap-notify": "^3.1",
21 | "bower-asset/animate.css": "^3.5",
22 | "bower-asset/bootstrap-markdown": "^2.10"
23 | },
24 | "require-dev": {
25 | "yiisoft/yii2-gii": "^2.0",
26 | "yiisoft/yii2-debug": "^2.0",
27 | "yiisoft/yii2-codeception": "*",
28 | "yiisoft/yii2-faker": "*"
29 | },
30 | "minimum-stability": "stable",
31 | "autoload": {
32 | "psr-4": {
33 | "app\\": "app/",
34 | "modules\\": "modules/"
35 | }
36 | },
37 | "scripts": {
38 | "post-create-project-cmd": "app\\base\\Composer::postCreateProjectCmd"
39 | },
40 | "extra": {
41 | "asset-installer-paths": {
42 | "npm-asset-library": "vendor/npm",
43 | "bower-asset-library": "vendor/bower"
44 | },
45 | "app\\base\\Composer::postCreateProjectCmd": {
46 | "createLocalConfig": ["config-sample.php", "config.php"],
47 | "generateCookieValidationKey": "config.php",
48 | "setPermissions": [
49 | {
50 | "runtime": "0777",
51 | "runtime/cache": "0777",
52 | "runtime/logs": "0777",
53 | "web/assets": "0777"
54 | }
55 | ],
56 | "setDatabaseConfiguration": "config.php"
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/config-sample.php:
--------------------------------------------------------------------------------
1 | [
15 | 'request' => [
16 | // !!! insert a secret key in the following (if it is empty) -
17 | // this is required by cookie validation
18 | 'cookieValidationKey' => '',
19 | ],
20 | // Database configuration.
21 | 'db' => [
22 | 'class' => 'yii\db\Connection',
23 | 'dsn' => 'mysql:host=localhost;dbname=yii2basic',
24 | // SQLite3 example:
25 | // 'dsn' => 'sqlite:@runtime/data/db.sq3',
26 | 'username' => 'root',
27 | 'password' => '',
28 | 'charset' => 'utf8',
29 | ],
30 | ],
31 | // Configure your modules here:
32 | 'modules' => [
33 | // 'debug' => [
34 | // 'allowedIPs' => ['192.168.1.*'],
35 | // ],
36 | // 'gii' => [
37 | // 'allowedIPs' => ['192.168.1.*'],
38 | // ],
39 | ],
40 | 'params' => [
41 | // Application parameters.
42 | ],
43 | ];
44 |
--------------------------------------------------------------------------------
/docs/Param.md:
--------------------------------------------------------------------------------
1 | Управление параметрами
2 | ======================
3 |
4 | Получить значение параметра:
5 |
6 | ```php
7 | $value = Param::value($key, 'default value');
8 | ```
9 |
10 | где, `$key` ключ параметра который состоит из: названия секции и названия
11 | параметра разделенных точкой. Например, `Site.adminEmail`.
12 |
13 | Поиск значения параметра происходит по такой схеме:
14 | - сначала в параметрах приложения `Yii::$app->params` (они задаются в файле
15 | `app/config/web.php` в массиве с индексом `params`)
16 | - среди закешированных (параметры которые уже были запрошены ранее)
17 | - и, наконец, в таблице `config`, после чего параметр кешируется
18 |
19 | Описание полей таблицы config
20 | =============================
21 |
22 | Каждый параметр это запись в таблице `config` с такими полями:
23 |
24 | - **name** краткое название параметра (например, adminEmail как в примере выше)
25 | - **value** начальное значение параметра (сериализуется функцией `serialize()`)
26 | - **value_type** тип параметра (см. ниже доступные типы)
27 | - **options** если тип параметра `TYPE_SELECT` то содержит список доступных
28 | элементов для выпадающего списка (сериализуется функцией `serialize()`)
29 | - **title** заголовок параметра на странице настроек
30 | - **desc** описание параметра на странице настроек
31 | - **section** название секции (вкладки) к которой относится параметр
32 | - **required** признак что параметр является обязательным
33 | - **perms** список прав доступа к параметру (сериализуется функцией `serialize()`)
34 |
35 | Следует заметить, что связка **name** и **section** должна быть уникальной.
36 | Не может быть два одинаковых параметра в одной секции.
37 |
38 | Типы параметров
39 | ===============
40 |
41 | Тип параметра - это виджет который будет выводится на странице настроек **Settings**.
42 | - `TYPE_INT` поле ввода целого числа
43 | - `TYPE_NUM` поле ввода десятичного числа
44 | - `TYPE_EMAIL` поле ввода email адреса
45 | - `TYPE_URL` поле ввода ссылки
46 | - `TYPE_SWITCH` переключатель (чекбокс)
47 | - `TYPE_TEXT` текстовое поле ввода
48 | - `TYPE_EDITOR` редактор
49 | - `TYPE_SELECT` выпадающий список, варианты выбора содержатся в поле `options`.
50 | - `TYPE_PASSWORD` поле ввода пароля
51 |
52 | Страница настроек Settings
53 | ==========================
54 |
55 | Все параметры группируются по вкладкам (поле `section`). За работу с параметрами
56 | отвечает класс `app\base\actions\Settings` в котором происходит рендеринг и
57 | обработка страницы настроек.
58 |
59 | Права доступа
60 | =============
61 |
62 | Поле `perms` содержит список прав доступа к параметру. Если у пользователя
63 | нет соответствующих прав, то такой параметр будет скрыт на странице
64 | настройек **Settings** для изменения.
65 |
66 | Право `updateSettings` доступно для администратора, модули могут добавлять
67 | свои права.
68 |
69 | Добавление нового параметра
70 | ===========================
71 |
72 | Добавление параметра - создание новой модели `Config`.
73 |
74 | ```php
75 | $test = new Config();
76 | $test->name = 'key';
77 | $test->title = 'Some key';
78 | $test->section = 'MyModule';
79 | $test->value_type = Config::TYPE_TEXT;
80 | $test->perms = ['MyModuleAdminSettings'];
81 | $test->save();
82 | ```
83 |
84 | Добавляется параметр `MyModule.key` который будет отображен на вкладке `MyModule`
85 | и который будет доступен пользователям с правом `MyModuleAdminSettings` и для
86 | остальных скрыт.
87 |
88 | Добавление параметра в миграциях возможно аналогичным созданием модели `Config`:
89 | ```
90 | public function up() {
91 | $config = new \app\models\Config();
92 | $config->name = 'fetchTime';
93 | $config->title = 'Remote fetch time';
94 | $config->section = 'Site';
95 | $config->value_type = \app\models\Config::TYPE_TEXT;
96 | $config->save();
97 | }
98 | public function down() {
99 | \app\models\Config::deleteAll([
100 | 'name' => 'fetchTime',
101 | 'section' => 'Site',
102 | ]);
103 | }
104 | ```
105 |
--------------------------------------------------------------------------------
/modules/README.md:
--------------------------------------------------------------------------------
1 | What is modules
2 | ---------------
3 |
4 | Modules extend your site functionality beyond core.
5 |
6 | Place custom modules in this directory. It's better to use modules to
7 | extend your backend than direct change core files (files in `app` directory).
8 |
9 | How to develop modules
10 | ----------------------
11 |
12 | Use console gii command:
13 |
14 | ```
15 | ./bin/yii gii/module --moduleID=pages
16 | ```
17 |
18 | This command will create `pages` directory under your project root `modules`
19 | directory.
20 | Inside `pages` directory is base module skeleton. Main module class
21 | is PagesModule. Please take attention to following properties in that class:
22 |
23 | ```
24 | public $moduleName = '';
25 | public $moduleDescription = '';
26 | ```
27 |
28 | These properties give information for others users/developers what module do.
29 |
30 | To create migration for `pages` module execute following command:
31 |
32 | ```
33 | ./bin/yii migrate/create --migrationPath=@modules/pages/migrations schema
34 | ```
35 |
36 | Module management
37 | -----------------
38 |
39 | Yii's command line tool provide `module` command with followin subcommands:
40 | - index (by default)
41 | - install
42 | - uninstall
43 | - info
44 |
45 | Command `module/index` or just `module` will show list of all available
46 | modules (installed and not installed).
47 |
48 | `module/install` will install a module. This command requires module id
49 | argument. You can get module id from output from the `module` command.
50 | `module/uninstall` will uninstall a module and requires module id also.
51 |
52 | `info` command provides module information such: readable name, description,
53 | module status (installed/not installed) and contained migrations.
54 |
--------------------------------------------------------------------------------
/modules/wiki/RuleOwnWiki.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.2
6 | */
7 |
8 | namespace modules\wiki;
9 |
10 | /**
11 | * RuleOwnWiki
12 | *
13 | * @author skoro
14 | */
15 | class RuleOwnWiki extends \yii\rbac\Rule
16 | {
17 | public $name = 'isOwnWiki';
18 |
19 | /**
20 | * @param string|integer $user the user ID.
21 | * @param Item $item the role or permission that this rule is associated with
22 | * @param array $params parameters passed to ManagerInterface::checkAccess().
23 | * @return boolean a value indicating whether the rule permits the role or permission it is associated with.
24 | */
25 | public function execute($user, $item, $params)
26 | {
27 | return isset($params['wiki']) ? $params['wiki']->user_id == $user : false;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/modules/wiki/WikiModule.php:
--------------------------------------------------------------------------------
1 | addMenu('main-nav', [
33 | ['label' => 'Wiki', 'icon' => 'wikipedia-w', 'url' => '#', 'roles' => ['viewWiki'], 'items' => [
34 | ['label' => 'Pages list', 'icon' => 'file-text', 'roles' => ['viewWiki'], 'url' => ['/wiki/page/index']],
35 | ]],
36 | ]);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/modules/wiki/assets/DiffAsset.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @version $Id$
6 | * @since 0.2
7 | */
8 |
9 | namespace modules\wiki\assets;
10 |
11 | /**
12 | * DiffAsset
13 | *
14 | * @author skoro
15 | */
16 | class DiffAsset extends \yii\web\AssetBundle
17 | {
18 | public $sourcePath = '@modules/wiki/assets';
19 |
20 | public $css = [
21 | 'diff.css',
22 | ];
23 | }
24 |
--------------------------------------------------------------------------------
/modules/wiki/assets/MarkdownEditorAsset.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @version $Id$
6 | */
7 |
8 | namespace modules\wiki\assets;
9 |
10 | /**
11 | * MarkdownEditorAsset
12 | *
13 | * @author skoro
14 | */
15 | class MarkdownEditorAsset extends \yii\web\AssetBundle
16 | {
17 | public $sourcePath = '@bower/bootstrap-markdown';
18 |
19 | public $css = [
20 | 'css/bootstrap-markdown.min.css',
21 | ];
22 |
23 | public $js = [
24 | 'js/bootstrap-markdown.js',
25 | ];
26 |
27 | public $depends = [
28 | 'yii\bootstrap\BootstrapAsset',
29 | ];
30 |
31 | /**
32 | * Editor language.
33 | * @var string
34 | */
35 | public $language;
36 |
37 | /**
38 | * Register language script.
39 | */
40 | public function registerAssetFiles($view)
41 | {
42 | if ($this->language) {
43 | $this->js[] = 'locale/bootstrap-markdown.' . $this->language . '.js';
44 | }
45 | parent::registerAssetFiles($view);
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/modules/wiki/assets/diff.css:
--------------------------------------------------------------------------------
1 | .Differences {
2 | width: 100%;
3 | border-collapse: collapse;
4 | border-spacing: 0;
5 | empty-cells: show;
6 | }
7 |
8 | .Differences thead {
9 | display: none;
10 | }
11 |
12 | .Differences tbody th {
13 | text-align: right;
14 | background: #FAFAFA;
15 | padding: 1px 2px;
16 | border-right: 1px solid #eee;
17 | vertical-align: top;
18 | font-size: 13px;
19 | font-family: Monaco, Menlo, Consolas, 'Courier New', monospace;
20 | font-weight: normal;
21 | color: #999;
22 | width: 5px;
23 | }
24 |
25 | .Differences td {
26 | padding: 1px 2px;
27 | font-size: 13px;
28 | font-family: Monaco, Menlo, Consolas, 'Courier New', monospace;
29 | }
30 |
31 | .DifferencesSideBySide .ChangeInsert td.Left {
32 | background: #dfd;
33 | }
34 |
35 | .DifferencesSideBySide .ChangeInsert td.Right {
36 | background: #cfc;
37 | }
38 |
39 | .DifferencesSideBySide .ChangeDelete td.Left {
40 | background: #f88;
41 | }
42 |
43 | .DifferencesSideBySide .ChangeDelete td.Right {
44 | background: #faa;
45 | }
46 |
47 | .DifferencesSideBySide .ChangeReplace .Left {
48 | background: #fe9;
49 | }
50 |
51 | .DifferencesSideBySide .ChangeReplace .Right {
52 | background: #fd8;
53 | }
54 |
55 | .Differences ins, .Differences del {
56 | text-decoration: none;
57 | }
58 |
59 | .DifferencesSideBySide .ChangeReplace ins, .DifferencesSideBySide .ChangeReplace del {
60 | background: #fc0;
61 | }
62 |
63 | .Differences .Skipped {
64 | background: #f7f7f7;
65 | }
66 |
67 | .DifferencesInline .ChangeReplace .Left,
68 | .DifferencesInline .ChangeDelete .Left {
69 | background: #fdd;
70 | }
71 |
72 | .DifferencesInline .ChangeReplace .Right,
73 | .DifferencesInline .ChangeInsert .Right {
74 | background: #dfd;
75 | }
76 |
77 | .DifferencesInline .ChangeReplace ins {
78 | background: #9e9;
79 | }
80 |
81 | .DifferencesInline .ChangeReplace del {
82 | background: #e99;
83 | }
84 |
85 | .DifferencesInline th[data-line-number]:before {
86 | content: attr(data-line-number);
87 | }
88 |
--------------------------------------------------------------------------------
/modules/wiki/forms/Editor.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.2
6 | */
7 |
8 | namespace modules\wiki\forms;
9 |
10 | use modules\wiki\models\History;
11 | use modules\wiki\models\Wiki;
12 | use Yii;
13 | use yii\base\Model;
14 |
15 | /**
16 | * Editor
17 | *
18 | * @author skoro
19 | */
20 | class Editor extends Model
21 | {
22 |
23 | const EVENT_BEFORE_UPDATE = 'beforeWikiUpdate';
24 | const EVENT_AFTER_UPDATE = 'afterWikiUpdate';
25 |
26 | /**
27 | * @var string
28 | */
29 | public $title;
30 |
31 | /**
32 | * @var string
33 | */
34 | public $slug;
35 |
36 | /**
37 | * @var string
38 | */
39 | public $content;
40 |
41 | /**
42 | * @var string
43 | */
44 | public $summary;
45 |
46 | /**
47 | * @var Wiki
48 | */
49 | protected $_wiki;
50 |
51 | /**
52 | * @param Wiki $wiki
53 | * @param array $config
54 | */
55 | public function __construct(Wiki $wiki, $config = array())
56 | {
57 | $this->_wiki = $wiki;
58 | $this->title = $wiki->title;
59 | $this->slug = $wiki->slug;
60 |
61 | $this->content = $this->getHistoryContent();
62 |
63 | parent::__construct($config);
64 | }
65 |
66 |
67 | /**
68 | * @inheritdoc
69 | */
70 | public function rules()
71 | {
72 | return [
73 | ['title', 'required'],
74 | ['title', 'string', 'max' => 255],
75 | ['title', 'filter', 'filter' => 'strip_tags'],
76 | ['title', 'filter', 'filter' => 'trim'],
77 |
78 | ['slug', 'string', 'max' => 255],
79 |
80 | ['summary', 'string', 'max' => 255],
81 | ['summary', 'default', 'value' => ''],
82 |
83 | ['content', 'string'],
84 | ];
85 | }
86 |
87 | /**
88 | * Validate and save wiki page.
89 | * @return Wiki|false
90 | */
91 | public function save()
92 | {
93 | $this->trigger(self::EVENT_BEFORE_UPDATE);
94 |
95 | if (!$this->validate()) {
96 | return false;
97 | }
98 |
99 | $transaction = Yii::$app->db->beginTransaction();
100 |
101 | try {
102 | $isNew = $this->isNew(); // Preserve New status for later checking.
103 | $this->_wiki->title = $this->title;
104 | $this->_wiki->slug = $this->slug;
105 | if (!$this->_wiki->save()) {
106 | throw new \yii\db\Exception('Cannot save Wiki model.');
107 | }
108 |
109 | // Don't save wiki if content not modified.
110 | if ($isNew || $this->content != $this->getHistoryContent()) {
111 | $history = new History();
112 | $history->wiki_id = $this->_wiki->id;
113 | $history->content = $this->content;
114 | $history->host_ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '';
115 | $history->summary = $this->summary;
116 | if (!$history->save()) {
117 | throw new \yii\db\Exception('Cannot save History model.');
118 | }
119 | }
120 |
121 | $transaction->commit();
122 | } catch (\Exception $e) {
123 | $transaction->rollBack();
124 | return false;
125 | }
126 |
127 | $this->trigger(self::EVENT_AFTER_UPDATE);
128 |
129 | return $this->_wiki;
130 | }
131 |
132 | /**
133 | * @return boolean
134 | */
135 | public function isNew()
136 | {
137 | return $this->_wiki->isNewRecord;
138 | }
139 |
140 | /**
141 | * Returns wiki instance.
142 | * @return Wiki
143 | */
144 | public function getWiki()
145 | {
146 | return $this->_wiki;
147 | }
148 |
149 | /**
150 | * Returns recent actual page content.
151 | * @return string
152 | */
153 | public function getHistoryContent()
154 | {
155 | $history = $this->_wiki->historyLatest;
156 | return $history ? $history->content : '';
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/modules/wiki/helpers/DiffHelper.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.2
6 | */
7 |
8 | namespace modules\wiki\helpers;
9 |
10 | use Diff;
11 | use modules\wiki\DiffRendererHtmlInline;
12 | use modules\wiki\models\History;
13 |
14 | /**
15 | * Page differences helper.
16 | *
17 | * @author skoro
18 | */
19 | class DiffHelper
20 | {
21 |
22 | /**
23 | *
24 | * @param History $history
25 | * @param array $diffOptions
26 | * @return string
27 | */
28 | public static function diff(History $history, $diffOptions = [])
29 | {
30 | if (!($previous = $history->previous)) {
31 | return '';
32 | }
33 |
34 | $content = explode("\n", $history->content);
35 | $prevContent = explode("\n", $previous->content);
36 | $diff = new Diff($content, $prevContent, $diffOptions);
37 | $renderer = new DiffRendererHtmlInline();
38 | return $diff->render($renderer);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/modules/wiki/migrations/m160901_090402_wiki.php:
--------------------------------------------------------------------------------
1 | createTable($this->table, [
13 | 'id' => $this->primaryKey(),
14 | 'user_id' => $this->integer(),
15 | 'title' => $this->string(255)->notNull(),
16 | 'slug' => $this->string(255)->notNull()->defaultValue(''),
17 | 'parent_id' => $this->integer(),
18 | 'created_at' => $this->integer(),
19 | ]);
20 | $this->createIndex('idx_wiki_slug', $this->table, ['slug']);
21 | $this->createIndex('idx_wiki_parent', $this->table, ['parent_id']);
22 | }
23 |
24 | public function down()
25 | {
26 | $this->dropTable($this->table);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/modules/wiki/migrations/m160902_065120_wiki_change.php:
--------------------------------------------------------------------------------
1 | createTable($this->table, [
13 | 'id' => $this->primaryKey(),
14 | 'wiki_id' => $this->integer()->notNull(),
15 | 'user_id' => $this->integer(),
16 | 'content' => $this->text()->defaultValue(''),
17 | 'rev' => $this->integer()->notNull()->defaultValue(0),
18 | 'created_at' => $this->integer(),
19 | 'summary' => $this->string(255)->notNull()->defaultValue(''),
20 | 'host_ip' => $this->char(15)->notNull()->defaultValue(''),
21 | ]);
22 |
23 | if (!$this->isSqlite()) {
24 | $this->addForeignKey('fk_wiki_id', $this->table, 'wiki_id', '{{%wiki}}', 'id', 'CASCADE');
25 | }
26 | $this->createIndex('idx_wiki_history_rev', $this->table, ['wiki_id', 'rev']);
27 | }
28 |
29 | public function down()
30 | {
31 | $this->dropTable($this->table);
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/modules/wiki/migrations/m160902_073325_auth.php:
--------------------------------------------------------------------------------
1 | [
9 | 'createWiki' => 'Create a wiki',
10 | 'updateOwnWiki' => [
11 | 'description' => 'Update only own wiki',
12 | 'rule' => 'modules\wiki\RuleOwnWiki',
13 | 'child' => 'updateWiki',
14 | ],
15 | 'updateWiki' => 'Update any wiki page',
16 | 'viewWiki' => 'View wiki',
17 | 'viewWikiHistory' => 'View wiki changes history',
18 | 'deleteOwnWiki' => [
19 | 'description' => 'Delete only own wiki',
20 | 'rule' => 'modules\wiki\RuleOwnWiki',
21 | 'child' => 'deleteWiki',
22 | ],
23 | 'deleteWiki' => 'Delete any wiki page',
24 | ],
25 | 'roles' => [
26 | 'WikiEditor' => ['createWiki', 'viewWiki', 'viewWikiHistory', 'updateOwnWiki', 'deleteOwnWiki'],
27 | 'WikiAdmin' => ['WikiEditor', 'updateWiki', 'deleteWiki'],
28 | ],
29 | ];
30 | }
31 |
--------------------------------------------------------------------------------
/modules/wiki/views/page/_editor.php:
--------------------------------------------------------------------------------
1 | getWiki();
13 | if ($wiki->id) {
14 | $cancelUrl = ['page/view', 'id' => $wiki->id];
15 | } elseif ($wiki->parent_id) {
16 | $cancelUrl = ['page/view', 'id' => $wiki->parent_id];
17 | } else {
18 | $cancelUrl = ['page/index'];
19 | }
20 | ?>
21 |
22 |
23 |
24 | = $form->field($editor, 'title') ?>
25 | = $form->field($editor, 'content')->widget(MarkdownEditor::className(), [
26 | 'previewUrl' => ['page/markdown-preview'],
27 | ]) ?>
28 | = $form->field($editor, 'summary')->textInput([
29 | 'placeholder' => Yii::t('app', 'What did you change ?'),
30 | ]) ?>
31 |
32 | [
34 | 'url' => $cancelUrl,
35 | ],
36 | ]) ?>
37 |
--------------------------------------------------------------------------------
/modules/wiki/views/page/_history.php:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 | = Timeline::widget([
16 | 'dataProvider' => $historyProvider,
17 | 'dateValue' => function ($model) {
18 | return Yii::$app->formatter->asDate($model->created_at);
19 | },
20 | 'timeView' => function ($model) {
21 | return Yii::$app->formatter->asRelativeTime($model->created_at);
22 | },
23 | 'itemHeaderView' => function ($model) {
24 | return Yii::$app->formatter->asUserlink($model->user) . ' ' . Html::tag('span', Html::encode($model->summary), ['class' => 'text-muted summary-change']);
25 | },
26 | 'itemView' => function ($model) {
27 | return Html::tag('pre', DiffHelper::diff($model));
28 | },
29 | 'itemFooterView' => function ($model) {
30 | return Html::a(Yii::t('app', 'Edit'), ['page/update', 'id' => $model->wiki_id, 'rev' => $model->id], ['class' => 'btn btn-default btn-flat btn-xs', 'data-pjax' => 0]);
31 | },
32 | ]) ?>
33 |
34 |
--------------------------------------------------------------------------------
/modules/wiki/views/page/create.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Create a new page');
9 | $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Wiki'), 'url' => ['page/index']];
10 | $this->params['breadcrumbs'][] = $this->title;
11 | ?>
12 |
13 | $this->title,
15 | ]) ?>
16 | = $this->render('_editor', ['editor' => $editor]) ?>
17 |
18 |
--------------------------------------------------------------------------------
/modules/wiki/views/page/delete.php:
--------------------------------------------------------------------------------
1 | title = $delete->getWiki()->title;
13 | ?>
14 |
15 |
= Yii::t('app', 'Are you sure to delete page ?') ?>
16 |
= Yii::t('app', 'This operation cannot be undo.') ?>
17 |
18 |
19 |
20 |
21 | isChildrenExists()): ?>
22 | = Yii::t('app', 'These pages also affected: ') ?>
23 |
24 | getChildren() as $child): ?>
25 | - = Html::a(e($child->title), ['page/view', 'id' => $child->id]) ?>
26 |
27 |
28 | = Yii::t('app', 'Please select what to do with these pages: ') ?>
29 | = $form->field($delete, 'mode')->widget(Check::className(), [
30 | 'type' => Check::TYPE_RADIO,
31 | 'options' => ['class' => 'checkbox-list-vert'],
32 | 'items' => $delete->getChoices(),
33 | ]) ?>
34 | = $form->field($delete, 'parentId')->widget(Select2::className(), [
35 | 'hideSearch' => false,
36 | 'remote' => ['wiki-suggest', 'ign' => $delete->getWiki()->id],
37 | ])->label(false) ?>
38 |
39 |
40 |
41 | [
43 | 'label' => Yii::t('app', 'DELETE'),
44 | 'options' => ['class' => 'btn btn-flat bg-red'],
45 | ],
46 | 'cancel' => [
47 | 'url' => ['page/view', 'id' => $delete->getWiki()->id],
48 | ],
49 | ]) ?>
50 |
--------------------------------------------------------------------------------
/modules/wiki/views/page/index.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Wiki');
11 | $this->params['breadcrumbs'][] = $this->title;
12 | ?>
13 |
14 |
15 |
16 |
17 | user->can('createWiki')): ?>
18 | = Html::a(Yii::t('app', 'Create'), ['page/create'], ['class' => 'btn btn-flat btn-default']) ?>
19 |
20 |
21 |
22 |
23 | = Yii::t('app', 'Page list is empty.') ?>
24 |
25 |
26 |
27 | -
28 | = Html::a(Icon::icon($wiki->getChildren()->count() ? 'fa fa-book' : 'fa fa-file-text') . Html::encode($wiki->title), ['page/view', 'id' => $wiki->id]) ?>
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/modules/wiki/views/page/update.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('app', 'Update page');
10 | $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Wiki'), 'url' => ['page/index']];
11 | $this->params['breadcrumbs'][] = $this->title;
12 | ?>
13 |
14 | = Tabs::widget([
15 | 'items' => [
16 | [
17 | 'label' => Yii::t('app', 'Editor'),
18 | 'content' => $this->render('_editor', ['editor' => $editor]),
19 | ],
20 | [
21 | 'label' => Yii::t('app', 'History'),
22 | 'content' => $this->render('_history', [
23 | 'historyProvider' => $historyProvider,
24 | ]),
25 | 'visible' => Yii::$app->user->can('viewWikiHistory'),
26 | ],
27 | ],
28 | ]) ?>
29 |
--------------------------------------------------------------------------------
/modules/wiki/views/page/view.php:
--------------------------------------------------------------------------------
1 | title = $wiki->title;
12 | $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Wiki'), 'url' => ['page/index']];
13 | $this->params['breadcrumbs'][] = $this->title;
14 | ?>
15 |
16 |
17 |
18 | = Html::a(Icon::icon('fa fa-home'), ['page/index'], ['class' => 'btn btn-flat btn-default']) ?>
19 | parent_id)): ?>
20 | = Html::a(Icon::icon('fa fa-chevron-left'), ['page/view', 'id' => $wiki->parent_id], ['class' => 'btn btn-flat btn-default']) ?>
21 |
22 | getChildren()->count()): ?>
23 | = ButtonDropdown::widget([
24 | 'label' => Yii::t('app', 'Pages'),
25 | 'tagName' => 'a',
26 | 'options' => ['class' => ['btn btn-flat btn-default']],
27 | 'dropdown' => [
28 | 'items' => array_map(function ($child) {
29 | return [
30 | 'label' => $child->title,
31 | 'url' => ['page/view', 'id' => $child->id],
32 | ];
33 | }, $wiki->children),
34 | ],
35 | ]) ?>
36 |
37 | = ButtonDropdown::widget([
38 | 'label' => Yii::t('app', 'Actions'),
39 | 'tagName' => 'a',
40 | 'options' => ['class' => ['btn btn-flat btn-default']],
41 | 'dropdown' => [
42 | 'items' => [
43 | [
44 | 'label' => Yii::t('app', 'Edit'),
45 | 'url' => ['page/update', 'id' => $wiki->id],
46 | 'visible' => Yii::$app->user->can('updateWiki', ['wiki' => $wiki]),
47 | ],
48 | [
49 | 'label' => Yii::t('app', 'Create child page'),
50 | 'url' => ['page/create', 'id' => $wiki->id],
51 | 'visible' => Yii::$app->user->can('createWiki'),
52 | ],
53 | [
54 | 'label' => Yii::t('app', 'View raw'),
55 | 'url' => ['page/raw', 'id' => $wiki->id],
56 | 'visible' => Yii::$app->user->can('viewWiki'),
57 | ],
58 | [
59 | 'label' => Yii::t('app', 'Delete'),
60 | 'url' => ['page/delete', 'id' => $wiki->id],
61 | 'visible' => Yii::$app->user->can('deleteWiki', ['wiki' => $wiki]),
62 | ],
63 | ],
64 | ],
65 | ]) ?>
66 |
67 |
68 |
69 | = Yii::t('app', 'Added by {creator}, last edit by {editor} {time}', [
70 | 'creator' => Yii::$app->formatter->asUserlink($wiki->user),
71 | 'editor' => Yii::$app->formatter->asUserlink($wiki->historyLatest->user),
72 | 'time' => Yii::$app->formatter->asRelativeTime($wiki->historyLatest->created_at),
73 | ]) ?>
74 |
75 |
76 | = Yii::$app->formatter->asMarkdown($wiki->historyLatest->content) ?>
77 |
78 |
79 | getChildren()->count()): ?>
80 | Yii::t('app', 'Child pages'),
82 | ]) ?>
83 |
84 | children as $child): ?>
85 | - = Html::a(Icon::icon('fa fa-file-text', Html::encode($child->title)), ['page/view', 'id' => $child->id]) ?>
86 |
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/modules/wiki/widgets/MarkdownEditor.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @since 0.2
6 | */
7 |
8 | namespace modules\wiki\widgets;
9 |
10 | use yii\widgets\InputWidget;
11 | use yii\helpers\Html;
12 | use yii\helpers\Json;
13 | use yii\helpers\Url;
14 | use modules\wiki\assets\MarkdownEditorAsset;
15 |
16 | /**
17 | * Bootstrap markdown editor.
18 | *
19 | * @link http://www.codingdrama.com/bootstrap-markdown/
20 | * @author skoro
21 | */
22 | class MarkdownEditor extends InputWidget
23 | {
24 |
25 | const RESIZE_NONE = 'none';
26 | const RESIZE_BOTH = 'both';
27 | const RESIZE_VERTICAL = 'vertical';
28 | const RESIZE_HORIZONTAL = 'horizontal';
29 |
30 | /**
31 | * @var array
32 | */
33 | public $clientOptions = [];
34 |
35 | /**
36 | * @var string
37 | */
38 | public $language;
39 |
40 | /**
41 | * @var boolean wrap editor by container with 'form-group' class.
42 | */
43 | public $formGroup = true;
44 |
45 | /**
46 | * @var integer|false editor height.
47 | */
48 | public $rows = 10;
49 |
50 | /**
51 | * @var string editor resize mode.
52 | */
53 | public $resize = self::RESIZE_VERTICAL;
54 |
55 | /**
56 | * @var string|array|false
57 | */
58 | public $previewUrl = false;
59 |
60 | /**
61 | * @inheritdoc
62 | */
63 | public function run()
64 | {
65 | $this->registerClientScript();
66 |
67 | if ($this->rows !== false) {
68 | $this->options['rows'] = $this->rows;
69 | }
70 |
71 | if ($this->hasModel()) {
72 | $editor = Html::activeTextarea($this->model, $this->attribute, $this->options);
73 | } else {
74 | $editor = Html::textarea($this->name, $this->value, $this->options);
75 | }
76 |
77 | if ($this->formGroup) {
78 | $editor = Html::tag('div', $editor, ['class' => 'form-group']);
79 | }
80 |
81 | return $editor;
82 | }
83 |
84 | /**
85 | * Register plugin script.
86 | */
87 | protected function registerClientScript()
88 | {
89 | $view = $this->getView();
90 | $id = $this->options['id'];
91 | if ($this->language) {
92 | $this->clientOptions['language'] = $this->language;
93 | }
94 | if ($this->resize !== self::RESIZE_NONE) {
95 | $this->clientOptions['resize'] = $this->resize;
96 | }
97 | if ($this->previewUrl !== false) {
98 | // $this->clientOptions['previewUrl'] = Url::to($this->previewUrl);
99 | $previewUrl = Url::to($this->previewUrl);
100 | $this->clientOptions['onPreview'] = new \yii\web\JsExpression("function (e) {
101 | var content = e.getContent();
102 | if (content.trim().length === 0) { return ''; }
103 | $.ajax({
104 | url: '{$previewUrl}',
105 | method: 'POST',
106 | cache: false,
107 | data: {content: content},
108 | async: false,
109 | success: function (html) { content = html; }
110 | });
111 | return content;
112 | }");
113 | }
114 | $clientOptions = Json::encode($this->clientOptions);
115 |
116 |
117 | $view->registerJs("jQuery('#$id').markdown($clientOptions);");
118 | $asset = MarkdownEditorAsset::register($view);
119 | if ($this->language) {
120 | $asset->language = $this->language;
121 | }
122 | }
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/runtime/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skoro/yii2-admin-template/832cf0f365d78b96879ddddfb3e17d9f239cf698/screenshot.png
--------------------------------------------------------------------------------
/tests/codeception.yml:
--------------------------------------------------------------------------------
1 | actor: Tester
2 | #coverage:
3 | # #c3_url: http://localhost:8080/index-test.php/
4 | # enabled: true
5 | # #remote: true
6 | # #remote_config: '../tests/codeception.yml'
7 | # white_list:
8 | # include:
9 | # - ../models/*
10 | # - ../controllers/*
11 | # - ../commands/*
12 | # - ../mail/*
13 | # blacklist:
14 | # include:
15 | # - ../assets/*
16 | # - ../config/*
17 | # - ../runtime/*
18 | # - ../vendor/*
19 | # - ../views/*
20 | # - ../web/*
21 | # - ../tests/*
22 | paths:
23 | tests: codeception
24 | log: codeception/_output
25 | data: codeception/_data
26 | helpers: codeception/_support
27 | settings:
28 | bootstrap: _bootstrap.php
29 | suite_class: \PHPUnit_Framework_TestSuite
30 | memory_limit: 1024M
31 | log: true
32 | colors: true
33 | config:
34 | # the entry script URL (with host info) for functional and acceptance tests
35 | # PLEASE ADJUST IT TO THE ACTUAL ENTRY SCRIPT URL
36 | test_entry_url: http://localhost:8080/index-test.php
--------------------------------------------------------------------------------
/tests/codeception/.gitignore:
--------------------------------------------------------------------------------
1 | # these files are auto generated by codeception build
2 | /unit/UnitTester.php
3 | /functional/FunctionalTester.php
4 | /acceptance/AcceptanceTester.php
5 |
--------------------------------------------------------------------------------
/tests/codeception/_bootstrap.php:
--------------------------------------------------------------------------------
1 | $value) {
21 | $inputType = $field === 'body' ? 'textarea' : 'input';
22 | $this->actor->fillField($inputType . '[name="ContactForm[' . $field . ']"]', $value);
23 | }
24 | $this->actor->click('contact-button');
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/tests/codeception/_pages/LoginPage.php:
--------------------------------------------------------------------------------
1 | actor->fillField('input[name="Login[email]"]', $username);
22 | $this->actor->fillField('input[name="Login[password]"]', $password);
23 | $this->actor->click('login-button');
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tests/codeception/_support/AcceptanceTester.php:
--------------------------------------------------------------------------------
1 | wantTo('ensure that about works');
9 | AboutPage::openBy($I);
10 | $I->see('About', 'h1');
11 |
--------------------------------------------------------------------------------
/tests/codeception/acceptance/ContactCept.php:
--------------------------------------------------------------------------------
1 | wantTo('ensure that contact works');
9 |
10 | $contactPage = ContactPage::openBy($I);
11 |
12 | $I->see('Contact', 'h1');
13 |
14 | $I->amGoingTo('submit contact form with no data');
15 | $contactPage->submit([]);
16 | if (method_exists($I, 'wait')) {
17 | $I->wait(3); // only for selenium
18 | }
19 | $I->expectTo('see validations errors');
20 | $I->see('Contact', 'h1');
21 | $I->see('Name cannot be blank');
22 | $I->see('Email cannot be blank');
23 | $I->see('Subject cannot be blank');
24 | $I->see('Body cannot be blank');
25 | $I->see('The verification code is incorrect');
26 |
27 | $I->amGoingTo('submit contact form with not correct email');
28 | $contactPage->submit([
29 | 'name' => 'tester',
30 | 'email' => 'tester.email',
31 | 'subject' => 'test subject',
32 | 'body' => 'test content',
33 | 'verifyCode' => 'testme',
34 | ]);
35 | if (method_exists($I, 'wait')) {
36 | $I->wait(3); // only for selenium
37 | }
38 | $I->expectTo('see that email address is wrong');
39 | $I->dontSee('Name cannot be blank', '.help-inline');
40 | $I->see('Email is not a valid email address.');
41 | $I->dontSee('Subject cannot be blank', '.help-inline');
42 | $I->dontSee('Body cannot be blank', '.help-inline');
43 | $I->dontSee('The verification code is incorrect', '.help-inline');
44 |
45 | $I->amGoingTo('submit contact form with correct data');
46 | $contactPage->submit([
47 | 'name' => 'tester',
48 | 'email' => 'tester@example.com',
49 | 'subject' => 'test subject',
50 | 'body' => 'test content',
51 | 'verifyCode' => 'testme',
52 | ]);
53 | if (method_exists($I, 'wait')) {
54 | $I->wait(3); // only for selenium
55 | }
56 | $I->dontSeeElement('#contact-form');
57 | $I->see('Thank you for contacting us. We will respond to you as soon as possible.');
58 |
--------------------------------------------------------------------------------
/tests/codeception/acceptance/HomeCept.php:
--------------------------------------------------------------------------------
1 | wantTo('ensure that home page works');
7 | $I->amOnPage(Yii::$app->homeUrl);
8 | $I->see('Congratulations!');
9 | $I->seeLink('Login');
10 | $I->seeLink('Register');
11 |
--------------------------------------------------------------------------------
/tests/codeception/acceptance/LoginCept.php:
--------------------------------------------------------------------------------
1 | wantTo('ensure that login works');
9 |
10 | $loginPage = LoginPage::openBy($I);
11 |
12 | $I->see('Sign in to start your session');
13 |
14 | $I->amGoingTo('try to login with empty credentials');
15 | $loginPage->login('', '');
16 | if (method_exists($I, 'wait')) {
17 | $I->wait(3); // only for selenium
18 | }
19 | $I->expectTo('see validations errors');
20 | $I->see('Email cannot be blank.');
21 | $I->see('Password cannot be blank.');
22 |
23 | $I->amGoingTo('try to login with wrong credentials');
24 | $loginPage->login('admin', 'wrong');
25 | if (method_exists($I, 'wait')) {
26 | $I->wait(3); // only for selenium
27 | }
28 | $I->expectTo('see validations errors');
29 | $I->see('Email is not a valid email address.');
30 |
31 | $I->amGoingTo('try to login with correct credentials');
32 | $loginPage->login('login', 'password');
33 | if (method_exists($I, 'wait')) {
34 | $I->wait(3); // only for selenium
35 | }
36 | $I->expectTo('see user info');
37 | $I->seeLink('Logout');
38 |
--------------------------------------------------------------------------------
/tests/codeception/acceptance/_bootstrap.php:
--------------------------------------------------------------------------------
1 | [
19 | 'db' => $config['components']['db']
20 | ]] : [],
21 | require(__DIR__ . '/../config/config.php')
22 | );
23 |
24 | $application = new yii\console\Application($config);
25 | $exitCode = $application->run();
26 | exit($exitCode);
27 |
--------------------------------------------------------------------------------
/tests/codeception/bin/yii.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | rem -------------------------------------------------------------
4 | rem Yii command line bootstrap script for Windows.
5 | rem
6 | rem @author Qiang Xue
7 | rem @link http://www.yiiframework.com/
8 | rem @copyright Copyright (c) 2008 Yii Software LLC
9 | rem @license http://www.yiiframework.com/license/
10 | rem -------------------------------------------------------------
11 |
12 | @setlocal
13 |
14 | set YII_PATH=%~dp0
15 |
16 | if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe
17 |
18 | "%PHP_COMMAND%" "%YII_PATH%yii" %*
19 |
20 | @endlocal
21 |
--------------------------------------------------------------------------------
/tests/codeception/config/acceptance.php:
--------------------------------------------------------------------------------
1 | [
14 | 'request' => [
15 | // it's not recommended to run functional tests with CSRF validation enabled
16 | 'enableCsrfValidation' => false,
17 | // but if you absolutely need it set cookie domain to localhost
18 | /*
19 | 'csrfCookie' => [
20 | 'domain' => 'localhost',
21 | ],
22 | */
23 | ],
24 | ],
25 | ]
26 | );
27 |
--------------------------------------------------------------------------------
/tests/codeception/config/unit.php:
--------------------------------------------------------------------------------
1 | wantTo('ensure that about works');
9 | AboutPage::openBy($I);
10 | $I->see('About', 'h1');
11 |
--------------------------------------------------------------------------------
/tests/codeception/functional/ContactCept.php:
--------------------------------------------------------------------------------
1 | wantTo('ensure that contact works');
9 |
10 | $contactPage = ContactPage::openBy($I);
11 |
12 | $I->see('Contact', 'h1');
13 |
14 | $I->amGoingTo('submit contact form with no data');
15 | $contactPage->submit([]);
16 | $I->expectTo('see validations errors');
17 | $I->see('Contact', 'h1');
18 | $I->see('Name cannot be blank');
19 | $I->see('Email cannot be blank');
20 | $I->see('Subject cannot be blank');
21 | $I->see('Body cannot be blank');
22 | $I->see('The verification code is incorrect');
23 |
24 | $I->amGoingTo('submit contact form with not correct email');
25 | $contactPage->submit([
26 | 'name' => 'tester',
27 | 'email' => 'tester.email',
28 | 'subject' => 'test subject',
29 | 'body' => 'test content',
30 | 'verifyCode' => 'testme',
31 | ]);
32 | $I->expectTo('see that email address is wrong');
33 | $I->dontSee('Name cannot be blank', '.help-inline');
34 | $I->see('Email is not a valid email address.');
35 | $I->dontSee('Subject cannot be blank', '.help-inline');
36 | $I->dontSee('Body cannot be blank', '.help-inline');
37 | $I->dontSee('The verification code is incorrect', '.help-inline');
38 |
39 | $I->amGoingTo('submit contact form with correct data');
40 | $contactPage->submit([
41 | 'name' => 'tester',
42 | 'email' => 'tester@example.com',
43 | 'subject' => 'test subject',
44 | 'body' => 'test content',
45 | 'verifyCode' => 'testme',
46 | ]);
47 | $I->dontSeeElement('#contact-form');
48 | $I->see('Thank you for contacting us. We will respond to you as soon as possible.');
49 |
--------------------------------------------------------------------------------
/tests/codeception/functional/HomeCept.php:
--------------------------------------------------------------------------------
1 | wantTo('ensure that home page works');
7 | $I->amOnPage(Yii::$app->homeUrl);
8 | $I->see('My Company');
9 | $I->seeLink('About');
10 | $I->click('About');
11 | $I->see('This is the About page.');
12 |
--------------------------------------------------------------------------------
/tests/codeception/functional/LoginCept.php:
--------------------------------------------------------------------------------
1 | wantTo('ensure that login works');
9 |
10 | $loginPage = LoginPage::openBy($I);
11 |
12 | $I->see('Login', 'h1');
13 |
14 | $I->amGoingTo('try to login with empty credentials');
15 | $loginPage->login('', '');
16 | $I->expectTo('see validations errors');
17 | $I->see('Username cannot be blank.');
18 | $I->see('Password cannot be blank.');
19 |
20 | $I->amGoingTo('try to login with wrong credentials');
21 | $loginPage->login('admin', 'wrong');
22 | $I->expectTo('see validations errors');
23 | $I->see('Incorrect username or password.');
24 |
25 | $I->amGoingTo('try to login with correct credentials');
26 | $loginPage->login('admin', 'admin');
27 | $I->expectTo('see user info');
28 | $I->see('Logout (admin)');
29 |
--------------------------------------------------------------------------------
/tests/codeception/functional/_bootstrap.php:
--------------------------------------------------------------------------------
1 | mailer->fileTransportCallback = function ($mailer, $message) {
17 | return 'testing_message.eml';
18 | };
19 | }
20 |
21 | protected function tearDown()
22 | {
23 | unlink($this->getMessageFile());
24 | parent::tearDown();
25 | }
26 |
27 | public function testContact()
28 | {
29 | $model = $this->getMock('app\models\ContactForm', ['validate']);
30 | $model->expects($this->once())->method('validate')->will($this->returnValue(true));
31 |
32 | $model->attributes = [
33 | 'name' => 'Tester',
34 | 'email' => 'tester@example.com',
35 | 'subject' => 'very important letter subject',
36 | 'body' => 'body of current message',
37 | ];
38 |
39 | $model->contact('admin@example.com');
40 |
41 | $this->specify('email should be send', function () {
42 | expect('email file should exist', file_exists($this->getMessageFile()))->true();
43 | });
44 |
45 | $this->specify('message should contain correct data', function () use ($model) {
46 | $emailMessage = file_get_contents($this->getMessageFile());
47 |
48 | expect('email should contain user name', $emailMessage)->contains($model->name);
49 | expect('email should contain sender email', $emailMessage)->contains($model->email);
50 | expect('email should contain subject', $emailMessage)->contains($model->subject);
51 | expect('email should contain body', $emailMessage)->contains($model->body);
52 | });
53 | }
54 |
55 | private function getMessageFile()
56 | {
57 | return Yii::getAlias(Yii::$app->mailer->fileTransportPath) . '/testing_message.eml';
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/tests/codeception/unit/models/LoginFormTest.php:
--------------------------------------------------------------------------------
1 | user->logout();
17 | parent::tearDown();
18 | }
19 |
20 | public function testLoginNoUser()
21 | {
22 | $model = new LoginForm([
23 | 'username' => 'not_existing_username',
24 | 'password' => 'not_existing_password',
25 | ]);
26 |
27 | $this->specify('user should not be able to login, when there is no identity', function () use ($model) {
28 | expect('model should not login user', $model->login())->false();
29 | expect('user should not be logged in', Yii::$app->user->isGuest)->true();
30 | });
31 | }
32 |
33 | public function testLoginWrongPassword()
34 | {
35 | $model = new LoginForm([
36 | 'username' => 'demo',
37 | 'password' => 'wrong_password',
38 | ]);
39 |
40 | $this->specify('user should not be able to login with wrong password', function () use ($model) {
41 | expect('model should not login user', $model->login())->false();
42 | expect('error message should be set', $model->errors)->hasKey('password');
43 | expect('user should not be logged in', Yii::$app->user->isGuest)->true();
44 | });
45 | }
46 |
47 | public function testLoginCorrect()
48 | {
49 | $model = new LoginForm([
50 | 'username' => 'demo',
51 | 'password' => 'demo',
52 | ]);
53 |
54 | $this->specify('user should be able to login with correct credentials', function () use ($model) {
55 | expect('model should login user', $model->login())->true();
56 | expect('error message should not be set', $model->errors)->hasntKey('password');
57 | expect('user should be logged in', Yii::$app->user->isGuest)->false();
58 | });
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/tests/codeception/unit/models/UserTest.php:
--------------------------------------------------------------------------------
1 | loadFixtures(['user']);
14 | }
15 |
16 | // TODO add test methods here
17 | }
18 |
--------------------------------------------------------------------------------
/tests/codeception/unit/templates/fixtures/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skoro/yii2-admin-template/832cf0f365d78b96879ddddfb3e17d9f239cf698/tests/codeception/unit/templates/fixtures/.gitkeep
--------------------------------------------------------------------------------
/web/.htaccess:
--------------------------------------------------------------------------------
1 | # use mod_rewrite for pretty URL support
2 |
3 | RewriteEngine on
4 |
5 | # If a directory or a file exists, use the request directly
6 | RewriteCond %{REQUEST_FILENAME} !-f
7 | RewriteCond %{REQUEST_FILENAME} !-d
8 |
9 | # Otherwise forward the request to index.php
10 | RewriteRule . index.php
11 |
--------------------------------------------------------------------------------
/web/assets/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/web/css/admin.css:
--------------------------------------------------------------------------------
1 | /*
2 | Document : admin
3 | Created on : Mar 16, 2016, 10:21:04 AM
4 | Author : skoro
5 | Description:
6 | Backend styles.
7 | */
8 |
9 | .navbar-nav > .user-menu > .dropdown-menu {
10 | padding: 0 0 0;
11 | }
12 |
13 | .login-box-body .password-request {
14 | text-align: left;
15 | padding: 0 0 15px 0;
16 | }
17 |
18 | .login-page .alert, .register-page .alert {
19 | margin: 10px;
20 | }
21 |
22 | .register-box .account-links {
23 | margin-top: 10px;
24 | }
25 |
26 | .checkbox-list-vert label {
27 | display: block;
28 | }
29 |
30 | ul.pagination {
31 | margin: 0;
32 | }
33 |
--------------------------------------------------------------------------------
/web/css/site.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | height: 100%;
4 | }
5 |
6 | .wrap {
7 | min-height: 100%;
8 | height: auto;
9 | margin: 0 auto -60px;
10 | padding: 0 0 60px;
11 | }
12 |
13 | .wrap > .container {
14 | padding: 70px 15px 20px;
15 | }
16 |
17 | .footer {
18 | height: 60px;
19 | background-color: #f5f5f5;
20 | border-top: 1px solid #ddd;
21 | padding-top: 20px;
22 | }
23 |
24 | .jumbotron {
25 | text-align: center;
26 | background-color: transparent;
27 | }
28 |
29 | .jumbotron .btn {
30 | font-size: 21px;
31 | padding: 14px 24px;
32 | }
33 |
34 | .not-set {
35 | color: #c55;
36 | font-style: italic;
37 | }
38 |
39 | /* add sorting icons to gridview sort links */
40 | a.asc:after, a.desc:after {
41 | position: relative;
42 | top: 1px;
43 | display: inline-block;
44 | font-family: 'Glyphicons Halflings';
45 | font-style: normal;
46 | font-weight: normal;
47 | line-height: 1;
48 | padding-left: 5px;
49 | }
50 |
51 | a.asc:after {
52 | content: /*"\e113"*/ "\e151";
53 | }
54 |
55 | a.desc:after {
56 | content: /*"\e114"*/ "\e152";
57 | }
58 |
59 | .sort-numerical a.asc:after {
60 | content: "\e153";
61 | }
62 |
63 | .sort-numerical a.desc:after {
64 | content: "\e154";
65 | }
66 |
67 | .sort-ordinal a.asc:after {
68 | content: "\e155";
69 | }
70 |
71 | .sort-ordinal a.desc:after {
72 | content: "\e156";
73 | }
74 |
75 | .grid-view th {
76 | white-space: nowrap;
77 | }
78 |
79 | .hint-block {
80 | display: block;
81 | margin-top: 5px;
82 | color: #999;
83 | }
84 |
85 | .error-summary {
86 | color: #a94442;
87 | background: #fdf7f7;
88 | border-left: 3px solid #eed3d7;
89 | padding: 10px 20px;
90 | margin: 0 0 15px 0;
91 | }
92 |
--------------------------------------------------------------------------------
/web/images/avatars/avatar1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skoro/yii2-admin-template/832cf0f365d78b96879ddddfb3e17d9f239cf698/web/images/avatars/avatar1.png
--------------------------------------------------------------------------------
/web/images/avatars/avatar2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skoro/yii2-admin-template/832cf0f365d78b96879ddddfb3e17d9f239cf698/web/images/avatars/avatar2.png
--------------------------------------------------------------------------------
/web/images/avatars/avatar3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skoro/yii2-admin-template/832cf0f365d78b96879ddddfb3e17d9f239cf698/web/images/avatars/avatar3.png
--------------------------------------------------------------------------------
/web/images/avatars/avatar4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skoro/yii2-admin-template/832cf0f365d78b96879ddddfb3e17d9f239cf698/web/images/avatars/avatar4.png
--------------------------------------------------------------------------------
/web/images/avatars/avatar5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skoro/yii2-admin-template/832cf0f365d78b96879ddddfb3e17d9f239cf698/web/images/avatars/avatar5.png
--------------------------------------------------------------------------------
/web/index-test.php:
--------------------------------------------------------------------------------
1 | run();
17 |
--------------------------------------------------------------------------------
/web/index.php:
--------------------------------------------------------------------------------
1 |
4 | * @copyright 2016
5 | * @version $Id$
6 | */
7 |
8 | define('APPROOT_DIR', dirname(__DIR__));
9 |
10 | $localConfig = [];
11 | if (file_exists(APPROOT_DIR . '/config.php')) {
12 | $localConfig = require(APPROOT_DIR . '/config.php');
13 | }
14 | else {
15 | die('config.php is missing.');
16 | }
17 |
18 | require(APPROOT_DIR . '/vendor/autoload.php');
19 | require(APPROOT_DIR . '/vendor/yiisoft/yii2/Yii.php');
20 |
21 | $config = yii\helpers\ArrayHelper::merge(
22 | require(APPROOT_DIR . '/app/config/web.php'),
23 | $localConfig
24 | );
25 |
26 | $app = new app\base\WebApplication($config);
27 | $app->run();
28 |
--------------------------------------------------------------------------------