Jiangmenger
12 months ago
commit
2ea7e9a718
76 changed files with 3102 additions and 0 deletions
-
3.bowerrc
-
36.gitignore
-
29LICENSE.md
-
233README.md
-
92Vagrantfile
-
31assets/AppAsset.php
-
27codeception.yml
-
34commands/HelloController.php
-
90commands/PhonebookController.php
-
32commands/PostController.php
-
75composer.json
-
33config/__autocomplete.php
-
56config/console.php
-
14config/db.php
-
7config/params.php
-
46config/test.php
-
6config/test_db.php
-
74config/web.php
-
128controllers/SiteController.php
-
42data/contract.json
-
9docker-compose.yml
-
22mail/layouts/html.php
-
13mail/layouts/text.php
-
65models/ContactForm.php
-
81models/LoginForm.php
-
104models/User.php
-
162requirements.php
-
2runtime/.gitignore
-
6tests/_bootstrap.php
-
1tests/_data/.gitkeep
-
2tests/_output/.gitignore
-
26tests/_support/AcceptanceTester.php
-
23tests/_support/FunctionalTester.php
-
26tests/_support/UnitTester.php
-
10tests/acceptance.suite.yml.example
-
12tests/acceptance/AboutCest.php
-
34tests/acceptance/ContactCest.php
-
18tests/acceptance/HomeCest.php
-
21tests/acceptance/LoginCest.php
-
1tests/acceptance/_bootstrap.php
-
29tests/bin/yii
-
20tests/bin/yii.bat
-
14tests/functional.suite.yml
-
57tests/functional/ContactFormCest.php
-
59tests/functional/LoginFormCest.php
-
1tests/functional/_bootstrap.php
-
11tests/unit.suite.yml
-
3tests/unit/_bootstrap.php
-
41tests/unit/models/ContactFormTest.php
-
51tests/unit/models/LoginFormTest.php
-
44tests/unit/models/UserTest.php
-
261tests/unit/widgets/AlertTest.php
-
2vagrant/config/.gitignore
-
22vagrant/config/vagrant-local.example.yml
-
38vagrant/nginx/app.conf
-
3vagrant/nginx/log/.gitignore
-
18vagrant/provision/always-as-root.sh
-
79vagrant/provision/once-as-root.sh
-
31vagrant/provision/once-as-vagrant.sh
-
50vagrant/provision/provision.awk
-
83views/layouts/main.php
-
18views/site/about.php
-
68views/site/contact.php
-
27views/site/error.php
-
53views/site/index.php
-
55views/site/login.php
-
4web/.htaccess
-
2web/assets/.gitignore
-
88web/css/site.css
-
BINweb/favicon.ico
-
16web/index-test.php
-
12web/index.php
-
2web/robots.txt
-
73widgets/Alert.php
-
21yii
-
20yii.bat
@ -0,0 +1,3 @@ |
|||
{ |
|||
"directory" : "vendor/bower-asset" |
|||
} |
@ -0,0 +1,36 @@ |
|||
# phpstorm project files |
|||
.idea |
|||
|
|||
# netbeans project files |
|||
nbproject |
|||
|
|||
# zend studio for eclipse project files |
|||
.buildpath |
|||
.project |
|||
.settings |
|||
|
|||
# windows thumbnail cache |
|||
Thumbs.db |
|||
|
|||
# composer vendor dir |
|||
/vendor |
|||
|
|||
# composer itself is not needed |
|||
composer.phar |
|||
|
|||
# composer lock |
|||
composer.lock |
|||
|
|||
# Mac DS_Store Files |
|||
.DS_Store |
|||
|
|||
# phpunit itself is not needed |
|||
phpunit.phar |
|||
# local phpunit config |
|||
/phpunit.xml |
|||
|
|||
tests/_output/* |
|||
tests/_support/_generated |
|||
|
|||
#vagrant folder |
|||
/.vagrant |
@ -0,0 +1,29 @@ |
|||
Copyright © 2008 by Yii Software LLC (http://www.yiisoft.com) |
|||
All rights reserved. |
|||
|
|||
Redistribution and use in source and binary forms, with or without |
|||
modification, are permitted provided that the following conditions |
|||
are met: |
|||
|
|||
* Redistributions of source code must retain the above copyright |
|||
notice, this list of conditions and the following disclaimer. |
|||
* Redistributions in binary form must reproduce the above copyright |
|||
notice, this list of conditions and the following disclaimer in |
|||
the documentation and/or other materials provided with the |
|||
distribution. |
|||
* Neither the name of Yii Software LLC nor the names of its |
|||
contributors may be used to endorse or promote products derived |
|||
from this software without specific prior written permission. |
|||
|
|||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
|||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
|||
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
|||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
|||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
|||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|||
POSSIBILITY OF SUCH DAMAGE. |
@ -0,0 +1,233 @@ |
|||
<p align="center"> |
|||
<a href="https://github.com/yiisoft" target="_blank"> |
|||
<img src="https://avatars0.githubusercontent.com/u/993323" height="100px"> |
|||
</a> |
|||
<h1 align="center">Yii 2 Basic Project Template</h1> |
|||
<br> |
|||
</p> |
|||
|
|||
Yii 2 Basic Project Template is a skeleton [Yii 2](https://www.yiiframework.com/) application best for |
|||
rapidly creating small projects. |
|||
|
|||
The template contains the basic features including user login/logout and a contact page. |
|||
It includes all commonly used configurations that would allow you to focus on adding new |
|||
features to your application. |
|||
|
|||
[![Latest Stable Version](https://img.shields.io/packagist/v/yiisoft/yii2-app-basic.svg)](https://packagist.org/packages/yiisoft/yii2-app-basic) |
|||
[![Total Downloads](https://img.shields.io/packagist/dt/yiisoft/yii2-app-basic.svg)](https://packagist.org/packages/yiisoft/yii2-app-basic) |
|||
[![build](https://github.com/yiisoft/yii2-app-basic/workflows/build/badge.svg)](https://github.com/yiisoft/yii2-app-basic/actions?query=workflow%3Abuild) |
|||
|
|||
DIRECTORY STRUCTURE |
|||
------------------- |
|||
|
|||
assets/ contains assets definition |
|||
commands/ contains console commands (controllers) |
|||
config/ contains application configurations |
|||
controllers/ contains Web controller classes |
|||
mail/ contains view files for e-mails |
|||
models/ contains model classes |
|||
runtime/ contains files generated during runtime |
|||
tests/ contains various tests for the basic application |
|||
vendor/ contains dependent 3rd-party packages |
|||
views/ contains view files for the Web application |
|||
web/ contains the entry script and Web resources |
|||
|
|||
|
|||
|
|||
REQUIREMENTS |
|||
------------ |
|||
|
|||
The minimum requirement by this project template that your Web server supports PHP 7.4. |
|||
|
|||
|
|||
INSTALLATION |
|||
------------ |
|||
|
|||
### Install via Composer |
|||
|
|||
If you do not have [Composer](https://getcomposer.org/), you may install it by following the instructions |
|||
at [getcomposer.org](https://getcomposer.org/doc/00-intro.md#installation-nix). |
|||
|
|||
You can then install this project template using the following command: |
|||
|
|||
~~~ |
|||
composer create-project --prefer-dist yiisoft/yii2-app-basic basic |
|||
~~~ |
|||
|
|||
Now you should be able to access the application through the following URL, assuming `basic` is the directory |
|||
directly under the Web root. |
|||
|
|||
~~~ |
|||
http://localhost/basic/web/ |
|||
~~~ |
|||
|
|||
### Install from an Archive File |
|||
|
|||
Extract the archive file downloaded from [yiiframework.com](https://www.yiiframework.com/download/) to |
|||
a directory named `basic` that is directly under the Web root. |
|||
|
|||
Set cookie validation key in `config/web.php` file to some random secret string: |
|||
|
|||
```php |
|||
'request' => [ |
|||
// !!! insert a secret key in the following (if it is empty) - this is required by cookie validation |
|||
'cookieValidationKey' => '<secret random string goes here>', |
|||
], |
|||
``` |
|||
|
|||
You can then access the application through the following URL: |
|||
|
|||
~~~ |
|||
http://localhost/basic/web/ |
|||
~~~ |
|||
|
|||
|
|||
### Install with Docker |
|||
|
|||
Update your vendor packages |
|||
|
|||
docker-compose run --rm php composer update --prefer-dist |
|||
|
|||
Run the installation triggers (creating cookie validation code) |
|||
|
|||
docker-compose run --rm php composer install |
|||
|
|||
Start the container |
|||
|
|||
docker-compose up -d |
|||
|
|||
You can then access the application through the following URL: |
|||
|
|||
http://127.0.0.1:8000 |
|||
|
|||
**NOTES:** |
|||
- Minimum required Docker engine version `17.04` for development (see [Performance tuning for volume mounts](https://docs.docker.com/docker-for-mac/osxfs-caching/)) |
|||
- The default configuration uses a host-volume in your home directory `.docker-composer` for composer caches |
|||
|
|||
|
|||
CONFIGURATION |
|||
------------- |
|||
|
|||
### Database |
|||
|
|||
Edit the file `config/db.php` with real data, for example: |
|||
|
|||
```php |
|||
return [ |
|||
'class' => 'yii\db\Connection', |
|||
'dsn' => 'mysql:host=localhost;dbname=yii2basic', |
|||
'username' => 'root', |
|||
'password' => '1234', |
|||
'charset' => 'utf8', |
|||
]; |
|||
``` |
|||
|
|||
**NOTES:** |
|||
- Yii won't create the database for you, this has to be done manually before you can access it. |
|||
- Check and edit the other files in the `config/` directory to customize your application as required. |
|||
- Refer to the README in the `tests` directory for information specific to basic application tests. |
|||
|
|||
|
|||
TESTING |
|||
------- |
|||
|
|||
Tests are located in `tests` directory. They are developed with [Codeception PHP Testing Framework](https://codeception.com/). |
|||
By default, there are 3 test suites: |
|||
|
|||
- `unit` |
|||
- `functional` |
|||
- `acceptance` |
|||
|
|||
Tests can be executed by running |
|||
|
|||
``` |
|||
vendor/bin/codecept run |
|||
``` |
|||
|
|||
The command above will execute unit and functional tests. Unit tests are testing the system components, while functional |
|||
tests are for testing user interaction. Acceptance tests are disabled by default as they require additional setup since |
|||
they perform testing in real browser. |
|||
|
|||
|
|||
### Running acceptance tests |
|||
|
|||
To execute acceptance tests do the following: |
|||
|
|||
1. Rename `tests/acceptance.suite.yml.example` to `tests/acceptance.suite.yml` to enable suite configuration |
|||
|
|||
2. Replace `codeception/base` package in `composer.json` with `codeception/codeception` to install full-featured |
|||
version of Codeception |
|||
|
|||
3. Update dependencies with Composer |
|||
|
|||
``` |
|||
composer update |
|||
``` |
|||
|
|||
4. Download [Selenium Server](https://www.seleniumhq.org/download/) and launch it: |
|||
|
|||
``` |
|||
java -jar ~/selenium-server-standalone-x.xx.x.jar |
|||
``` |
|||
|
|||
In case of using Selenium Server 3.0 with Firefox browser since v48 or Google Chrome since v53 you must download [GeckoDriver](https://github.com/mozilla/geckodriver/releases) or [ChromeDriver](https://sites.google.com/a/chromium.org/chromedriver/downloads) and launch Selenium with it: |
|||
|
|||
``` |
|||
# for Firefox |
|||
java -jar -Dwebdriver.gecko.driver=~/geckodriver ~/selenium-server-standalone-3.xx.x.jar |
|||
|
|||
# for Google Chrome |
|||
java -jar -Dwebdriver.chrome.driver=~/chromedriver ~/selenium-server-standalone-3.xx.x.jar |
|||
``` |
|||
|
|||
As an alternative way you can use already configured Docker container with older versions of Selenium and Firefox: |
|||
|
|||
``` |
|||
docker run --net=host selenium/standalone-firefox:2.53.0 |
|||
``` |
|||
|
|||
5. (Optional) Create `yii2basic_test` database and update it by applying migrations if you have them. |
|||
|
|||
``` |
|||
tests/bin/yii migrate |
|||
``` |
|||
|
|||
The database configuration can be found at `config/test_db.php`. |
|||
|
|||
|
|||
6. Start web server: |
|||
|
|||
``` |
|||
tests/bin/yii serve |
|||
``` |
|||
|
|||
7. Now you can run all available tests |
|||
|
|||
``` |
|||
# run all available tests |
|||
vendor/bin/codecept run |
|||
|
|||
# run acceptance tests |
|||
vendor/bin/codecept run acceptance |
|||
|
|||
# run only unit and functional tests |
|||
vendor/bin/codecept run unit,functional |
|||
``` |
|||
|
|||
### Code coverage support |
|||
|
|||
By default, code coverage is disabled in `codeception.yml` configuration file, you should uncomment needed rows to be able |
|||
to collect code coverage. You can run your tests and collect coverage with the following command: |
|||
|
|||
``` |
|||
#collect coverage for all tests |
|||
vendor/bin/codecept run --coverage --coverage-html --coverage-xml |
|||
|
|||
#collect coverage only for unit tests |
|||
vendor/bin/codecept run unit --coverage --coverage-html --coverage-xml |
|||
|
|||
#collect coverage for unit and functional tests |
|||
vendor/bin/codecept run functional,unit --coverage --coverage-html --coverage-xml |
|||
``` |
|||
|
|||
You can see code coverage output under the `tests/_output` directory. |
@ -0,0 +1,92 @@ |
|||
require 'yaml' |
|||
require 'fileutils' |
|||
|
|||
required_plugins_installed = nil |
|||
required_plugins = %w( vagrant-hostmanager vagrant-vbguest ) |
|||
required_plugins.each do |plugin| |
|||
unless Vagrant.has_plugin? plugin |
|||
system "vagrant plugin install #{plugin}" |
|||
required_plugins_installed = true |
|||
end |
|||
end |
|||
|
|||
# IF plugin[s] was just installed - restart required |
|||
if required_plugins_installed |
|||
# Get CLI command[s] and call again |
|||
system 'vagrant' + ARGV.to_s.gsub(/\[\"|\", \"|\"\]/, ' ') |
|||
exit |
|||
end |
|||
|
|||
domains = { |
|||
app: 'yii2basic.test' |
|||
} |
|||
|
|||
vagrantfile_dir_path = File.dirname(__FILE__) |
|||
|
|||
config = { |
|||
local: vagrantfile_dir_path + '/vagrant/config/vagrant-local.yml', |
|||
example: vagrantfile_dir_path + '/vagrant/config/vagrant-local.example.yml' |
|||
} |
|||
|
|||
# copy config from example if local config not exists |
|||
FileUtils.cp config[:example], config[:local] unless File.exist?(config[:local]) |
|||
# read config |
|||
options = YAML.load_file config[:local] |
|||
|
|||
# check github token |
|||
if options['github_token'].nil? || options['github_token'].to_s.length != 40 |
|||
puts "You must place REAL GitHub token into configuration:\n/yii2-app-basic/vagrant/config/vagrant-local.yml" |
|||
exit |
|||
end |
|||
|
|||
# vagrant configurate |
|||
Vagrant.configure(2) do |config| |
|||
# select the box |
|||
config.vm.box = 'bento/ubuntu-18.04' |
|||
|
|||
# should we ask about box updates? |
|||
config.vm.box_check_update = options['box_check_update'] |
|||
|
|||
config.vm.provider 'virtualbox' do |vb| |
|||
# machine cpus count |
|||
vb.cpus = options['cpus'] |
|||
# machine memory size |
|||
vb.memory = options['memory'] |
|||
# machine name (for VirtualBox UI) |
|||
vb.name = options['machine_name'] |
|||
end |
|||
|
|||
# machine name (for vagrant console) |
|||
config.vm.define options['machine_name'] |
|||
|
|||
# machine name (for guest machine console) |
|||
config.vm.hostname = options['machine_name'] |
|||
|
|||
# network settings |
|||
config.vm.network 'private_network', ip: options['ip'] |
|||
|
|||
# sync: folder 'yii2-app-advanced' (host machine) -> folder '/app' (guest machine) |
|||
config.vm.synced_folder './', '/app', owner: 'vagrant', group: 'vagrant' |
|||
|
|||
# disable folder '/vagrant' (guest machine) |
|||
config.vm.synced_folder '.', '/vagrant', disabled: true |
|||
|
|||
# hosts settings (host machine) |
|||
config.vm.provision :hostmanager |
|||
config.hostmanager.enabled = true |
|||
config.hostmanager.manage_host = true |
|||
config.hostmanager.ignore_private_ip = false |
|||
config.hostmanager.include_offline = true |
|||
config.hostmanager.aliases = domains.values |
|||
|
|||
# quick fix for failed guest additions installations |
|||
# config.vbguest.auto_update = false |
|||
|
|||
# provisioners |
|||
config.vm.provision 'shell', path: './vagrant/provision/once-as-root.sh', args: [options['timezone'], options['ip']] |
|||
config.vm.provision 'shell', path: './vagrant/provision/once-as-vagrant.sh', args: [options['github_token']], privileged: false |
|||
config.vm.provision 'shell', path: './vagrant/provision/always-as-root.sh', run: 'always' |
|||
|
|||
# post-install message (vagrant console) |
|||
config.vm.post_up_message = "App URL: http://#{domains[:app]}" |
|||
end |
@ -0,0 +1,31 @@ |
|||
<?php |
|||
/** |
|||
* @link https://www.yiiframework.com/ |
|||
* @copyright Copyright (c) 2008 Yii Software LLC |
|||
* @license https://www.yiiframework.com/license/ |
|||
*/ |
|||
|
|||
namespace app\assets; |
|||
|
|||
use yii\web\AssetBundle; |
|||
|
|||
/** |
|||
* Main application asset bundle. |
|||
* |
|||
* @author Qiang Xue <qiang.xue@gmail.com> |
|||
* @since 2.0 |
|||
*/ |
|||
class AppAsset extends AssetBundle |
|||
{ |
|||
public $basePath = '@webroot'; |
|||
public $baseUrl = '@web'; |
|||
public $css = [ |
|||
'css/site.css', |
|||
]; |
|||
public $js = [ |
|||
]; |
|||
public $depends = [ |
|||
'yii\web\YiiAsset', |
|||
'yii\bootstrap5\BootstrapAsset' |
|||
]; |
|||
} |
@ -0,0 +1,27 @@ |
|||
actor: Tester |
|||
bootstrap: _bootstrap.php |
|||
paths: |
|||
tests: tests |
|||
output: tests/_output |
|||
data: tests/_data |
|||
helpers: tests/_support |
|||
settings: |
|||
memory_limit: 1024M |
|||
colors: true |
|||
modules: |
|||
config: |
|||
Yii2: |
|||
configFile: 'config/test.php' |
|||
|
|||
# To enable code coverage: |
|||
#coverage: |
|||
# #c3_url: http://localhost:8080/index-test.php/ |
|||
# enabled: true |
|||
# #remote: true |
|||
# #remote_config: '../codeception.yml' |
|||
# whitelist: |
|||
# include: |
|||
# - models/* |
|||
# - controllers/* |
|||
# - commands/* |
|||
# - mail/* |
@ -0,0 +1,34 @@ |
|||
<?php |
|||
/** |
|||
* @link https://www.yiiframework.com/ |
|||
* @copyright Copyright (c) 2008 Yii Software LLC |
|||
* @license https://www.yiiframework.com/license/ |
|||
*/ |
|||
|
|||
namespace app\commands; |
|||
|
|||
use yii\console\Controller; |
|||
use yii\console\ExitCode; |
|||
|
|||
/** |
|||
* This command echoes the first argument that you have entered. |
|||
* |
|||
* This command is provided as an example for you to learn how to create console commands. |
|||
* |
|||
* @author Qiang Xue <qiang.xue@gmail.com> |
|||
* @since 2.0 |
|||
*/ |
|||
class HelloController extends Controller |
|||
{ |
|||
/** |
|||
* This command echoes what you have entered as the message. |
|||
* @param string $message the message to be echoed. |
|||
* @return int Exit code |
|||
*/ |
|||
public function actionIndex($message = 'hello world') |
|||
{ |
|||
echo $message . "\n"; |
|||
|
|||
return ExitCode::OK; |
|||
} |
|||
} |
@ -0,0 +1,90 @@ |
|||
<?php |
|||
namespace app\commands; |
|||
|
|||
use yii\console\Controller; |
|||
|
|||
class PhonebookController extends Controller |
|||
{ |
|||
// 读取和保存文件
|
|||
private function loadContacts() |
|||
{ |
|||
$file = '/opt/phonebook/data/contract.json'; |
|||
if (!file_exists($file)) { |
|||
return []; |
|||
} |
|||
|
|||
return json_decode(file_get_contents($file), true); |
|||
} |
|||
|
|||
private function saveContacts($contacts) |
|||
{ |
|||
file_put_contents('/opt/phonebook/data/contract.json', json_encode($contacts, JSON_PRETTY_PRINT)); |
|||
} |
|||
|
|||
|
|||
//增添联系人 (actionAdd)
|
|||
public function actionAdd($name, $phone) |
|||
{ |
|||
$contacts = $this->loadContacts(); |
|||
$contacts[] = ['id' => count($contacts) + 1, 'name' => $name, 'phone' => $phone]; |
|||
$this->saveContacts($contacts); |
|||
|
|||
echo "Contact added successfully.\n"; |
|||
} |
|||
//搜索联系人 (actionSearch)
|
|||
public function actionSearch($name) |
|||
{ |
|||
$contacts = $this->loadContacts(); |
|||
$found = array_filter($contacts, function ($contact) use ($name) { |
|||
return strpos(strtolower($contact['name']), strtolower($name)) !== false; |
|||
}); |
|||
|
|||
if (empty($found)) { |
|||
echo "No contacts found.\n"; |
|||
return; |
|||
} |
|||
|
|||
foreach ($found as $contact) { |
|||
echo "ID: {$contact['id']}, Name: {$contact['name']}, Phone: {$contact['phone']}\n"; |
|||
} |
|||
} |
|||
|
|||
//删除联系人 (actionDelete)
|
|||
public function actionDelete($id) |
|||
{ |
|||
$contacts = $this->loadContacts(); |
|||
foreach ($contacts as $i => $contact) { |
|||
if ($contact['id'] == $id) { |
|||
array_splice($contacts, $i, 1); |
|||
$this->saveContacts($contacts); |
|||
echo "Contact deleted successfully.\n"; |
|||
return; |
|||
} |
|||
} |
|||
|
|||
echo "Contact not found.\n"; |
|||
} |
|||
//修改联系人 (actionUpdate)
|
|||
public function actionUpdate($id, $name, $phone) |
|||
{ |
|||
$contacts = $this->loadContacts(); |
|||
foreach ($contacts as &$contact) { |
|||
if ($contact['id'] == $id) { |
|||
$contact['name'] = $name; |
|||
$contact['phone'] = $phone; |
|||
$this->saveContacts($contacts); |
|||
echo "Contact updated successfully.\n"; |
|||
return; |
|||
} |
|||
} |
|||
|
|||
echo "Contact not found.\n"; |
|||
} |
|||
|
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
?>
|
@ -0,0 +1,32 @@ |
|||
<?php |
|||
|
|||
namespace app\controllers; |
|||
|
|||
use Yii; |
|||
use app\models\Post; |
|||
use yii\web\Controller; |
|||
use yii\web\NotFoundHttpException; |
|||
|
|||
class PostController extends Controller{ |
|||
public function actionView($id){ |
|||
$model = Post::findOne($id); |
|||
if( $model === null){ |
|||
throw new NotFoundHttpException; |
|||
} |
|||
|
|||
return $this->render('view', [ |
|||
'model' => $model, |
|||
]); |
|||
} |
|||
|
|||
public function actionCreate(){ |
|||
$model = new Post; |
|||
if($model->load(Yii::$app->request->post())&& $model->save()){ |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
?>
|
@ -0,0 +1,75 @@ |
|||
{ |
|||
"name": "yiisoft/yii2-app-basic", |
|||
"description": "Yii 2 Basic Project Template", |
|||
"keywords": ["yii2", "framework", "basic", "project template"], |
|||
"homepage": "https://www.yiiframework.com/", |
|||
"type": "project", |
|||
"license": "BSD-3-Clause", |
|||
"support": { |
|||
"issues": "https://github.com/yiisoft/yii2/issues?state=open", |
|||
"forum": "https://www.yiiframework.com/forum/", |
|||
"wiki": "https://www.yiiframework.com/wiki/", |
|||
"irc": "ircs://irc.libera.chat:6697/yii", |
|||
"source": "https://github.com/yiisoft/yii2" |
|||
}, |
|||
"minimum-stability": "stable", |
|||
"require": { |
|||
"php": ">=7.4.0", |
|||
"yiisoft/yii2": "~2.0.45", |
|||
"yiisoft/yii2-bootstrap5": "~2.0.2", |
|||
"yiisoft/yii2-symfonymailer": "~2.0.3" |
|||
}, |
|||
"require-dev": { |
|||
"yiisoft/yii2-debug": "~2.1.0", |
|||
"yiisoft/yii2-gii": "~2.2.0", |
|||
"yiisoft/yii2-faker": "~2.0.0", |
|||
"phpunit/phpunit": "~9.5.0", |
|||
"codeception/codeception": "^5.0.0 || ^4.0", |
|||
"codeception/lib-innerbrowser": "^4.0 || ^3.0 || ^1.1", |
|||
"codeception/module-asserts": "^3.0 || ^1.1", |
|||
"codeception/module-yii2": "^1.1", |
|||
"codeception/module-filesystem": "^3.0 || ^2.0 || ^1.1", |
|||
"codeception/verify": "^3.0 || ^2.2", |
|||
"symfony/browser-kit": "^6.0 || >=2.7 <=4.2.4" |
|||
}, |
|||
"config": { |
|||
"allow-plugins": { |
|||
"yiisoft/yii2-composer" : true |
|||
}, |
|||
"process-timeout": 1800, |
|||
"fxp-asset": { |
|||
"enabled": false |
|||
} |
|||
}, |
|||
"scripts": { |
|||
"post-install-cmd": [ |
|||
"yii\\composer\\Installer::postInstall" |
|||
], |
|||
"post-create-project-cmd": [ |
|||
"yii\\composer\\Installer::postCreateProject", |
|||
"yii\\composer\\Installer::postInstall" |
|||
] |
|||
}, |
|||
"extra": { |
|||
"yii\\composer\\Installer::postCreateProject": { |
|||
"setPermission": [ |
|||
{ |
|||
"runtime": "0777", |
|||
"web/assets": "0777", |
|||
"yii": "0755" |
|||
} |
|||
] |
|||
}, |
|||
"yii\\composer\\Installer::postInstall": { |
|||
"generateCookieValidationKey": [ |
|||
"config/web.php" |
|||
] |
|||
} |
|||
}, |
|||
"repositories": [ |
|||
{ |
|||
"type": "composer", |
|||
"url": "https://asset-packagist.org" |
|||
} |
|||
] |
|||
} |
@ -0,0 +1,33 @@ |
|||
<?php |
|||
|
|||
/** |
|||
* This class only exists here for IDE (PHPStorm/Netbeans/...) autocompletion. |
|||
* This file is never included anywhere. |
|||
* Adjust this file to match classes configured in your application config, to enable IDE autocompletion for custom components. |
|||
* Example: A property phpdoc can be added in `__Application` class as `@property \vendor\package\Rollbar|__Rollbar $rollbar` and adding a class in this file |
|||
* ```php |
|||
* // @property of \vendor\package\Rollbar goes here
|
|||
* class __Rollbar { |
|||
* } |
|||
* ``` |
|||
*/ |
|||
class Yii { |
|||
/** |
|||
* @var \yii\web\Application|\yii\console\Application|__Application |
|||
*/ |
|||
public static $app; |
|||
} |
|||
|
|||
/** |
|||
* @property yii\rbac\DbManager $authManager |
|||
* @property \yii\web\User|__WebUser $user |
|||
* |
|||
*/ |
|||
class __Application { |
|||
} |
|||
|
|||
/** |
|||
* @property app\models\User $identity |
|||
*/ |
|||
class __WebUser { |
|||
} |
@ -0,0 +1,56 @@ |
|||
<?php |
|||
|
|||
$params = require __DIR__ . '/params.php'; |
|||
$db = require __DIR__ . '/db.php'; |
|||
|
|||
$config = [ |
|||
'id' => 'basic-console', |
|||
'basePath' => dirname(__DIR__), |
|||
'bootstrap' => ['log'], |
|||
'controllerNamespace' => 'app\commands', |
|||
'aliases' => [ |
|||
'@bower' => '@vendor/bower-asset', |
|||
'@npm' => '@vendor/npm-asset', |
|||
'@tests' => '@app/tests', |
|||
], |
|||
'components' => [ |
|||
'cache' => [ |
|||
'class' => 'yii\caching\FileCache', |
|||
], |
|||
'log' => [ |
|||
'targets' => [ |
|||
[ |
|||
'class' => 'yii\log\FileTarget', |
|||
'levels' => ['error', 'warning'], |
|||
], |
|||
], |
|||
], |
|||
'db' => $db, |
|||
], |
|||
'params' => $params, |
|||
/* |
|||
'controllerMap' => [ |
|||
'fixture' => [ // Fixture generation command line.
|
|||
'class' => 'yii\faker\FixtureController', |
|||
], |
|||
], |
|||
*/ |
|||
]; |
|||
|
|||
if (YII_ENV_DEV) { |
|||
// configuration adjustments for 'dev' environment
|
|||
$config['bootstrap'][] = 'gii'; |
|||
$config['modules']['gii'] = [ |
|||
'class' => 'yii\gii\Module', |
|||
]; |
|||
// configuration adjustments for 'dev' environment
|
|||
// requires version `2.1.21` of yii2-debug module
|
|||
$config['bootstrap'][] = 'debug'; |
|||
$config['modules']['debug'] = [ |
|||
'class' => 'yii\debug\Module', |
|||
// uncomment the following to add your IP if you are not connecting from localhost.
|
|||
//'allowedIPs' => ['127.0.0.1', '::1'],
|
|||
]; |
|||
} |
|||
|
|||
return $config; |
@ -0,0 +1,14 @@ |
|||
<?php |
|||
|
|||
return [ |
|||
'class' => 'yii\db\Connection', |
|||
'dsn' => 'mysql:host=localhost;dbname=yii2basic', |
|||
'username' => 'root', |
|||
'password' => 'deep123', |
|||
'charset' => 'utf8', |
|||
|
|||
// Schema cache options (for production environment)
|
|||
//'enableSchemaCache' => true,
|
|||
//'schemaCacheDuration' => 60,
|
|||
//'schemaCache' => 'cache',
|
|||
]; |
@ -0,0 +1,7 @@ |
|||
<?php |
|||
|
|||
return [ |
|||
'adminEmail' => 'admin@example.com', |
|||
'senderEmail' => 'noreply@example.com', |
|||
'senderName' => 'Example.com mailer', |
|||
]; |
@ -0,0 +1,46 @@ |
|||
<?php |
|||
$params = require __DIR__ . '/params.php'; |
|||
$db = require __DIR__ . '/test_db.php'; |
|||
|
|||
/** |
|||
* Application configuration shared by all test types |
|||
*/ |
|||
return [ |
|||
'id' => 'basic-tests', |
|||
'basePath' => dirname(__DIR__), |
|||
'aliases' => [ |
|||
'@bower' => '@vendor/bower-asset', |
|||
'@npm' => '@vendor/npm-asset', |
|||
], |
|||
'language' => 'en-US', |
|||
'components' => [ |
|||
'db' => $db, |
|||
'mailer' => [ |
|||
'class' => \yii\symfonymailer\Mailer::class, |
|||
'viewPath' => '@app/mail', |
|||
// send all mails to a file by default.
|
|||
'useFileTransport' => true, |
|||
'messageClass' => 'yii\symfonymailer\Message' |
|||
], |
|||
'assetManager' => [ |
|||
'basePath' => __DIR__ . '/../web/assets', |
|||
], |
|||
'urlManager' => [ |
|||
'showScriptName' => true, |
|||
], |
|||
'user' => [ |
|||
'identityClass' => 'app\models\User', |
|||
], |
|||
'request' => [ |
|||
'cookieValidationKey' => 'test', |
|||
'enableCsrfValidation' => false, |
|||
// but if you absolutely need it set cookie domain to localhost
|
|||
/* |
|||
'csrfCookie' => [ |
|||
'domain' => 'localhost', |
|||
], |
|||
*/ |
|||
], |
|||
], |
|||
'params' => $params, |
|||
]; |
@ -0,0 +1,6 @@ |
|||
<?php |
|||
$db = require __DIR__ . '/db.php'; |
|||
// test database! Important not to run tests on production or development databases
|
|||
$db['dsn'] = 'mysql:host=localhost;dbname=yii2basic_test'; |
|||
|
|||
return $db; |
@ -0,0 +1,74 @@ |
|||
<?php |
|||
|
|||
$params = require __DIR__ . '/params.php'; |
|||
$db = require __DIR__ . '/db.php'; |
|||
|
|||
$config = [ |
|||
'id' => 'basic', |
|||
'basePath' => dirname(__DIR__), |
|||
'bootstrap' => ['log'], |
|||
'aliases' => [ |
|||
'@bower' => '@vendor/bower-asset', |
|||
'@npm' => '@vendor/npm-asset', |
|||
], |
|||
'components' => [ |
|||
'request' => [ |
|||
// !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
|
|||
'cookieValidationKey' => 'hovIi5cjCDE92evC_zPUv3332pNJ7sIC', |
|||
], |
|||
'cache' => [ |
|||
'class' => 'yii\caching\FileCache', |
|||
], |
|||
'user' => [ |
|||
'identityClass' => 'app\models\User', |
|||
'enableAutoLogin' => true, |
|||
], |
|||
'errorHandler' => [ |
|||
'errorAction' => 'site/error', |
|||
], |
|||
'mailer' => [ |
|||
'class' => \yii\symfonymailer\Mailer::class, |
|||
'viewPath' => '@app/mail', |
|||
// send all mails to a file by default.
|
|||
'useFileTransport' => true, |
|||
], |
|||
'log' => [ |
|||
'traceLevel' => YII_DEBUG ? 3 : 0, |
|||
'targets' => [ |
|||
[ |
|||
'class' => 'yii\log\FileTarget', |
|||
'levels' => ['error', 'warning'], |
|||
], |
|||
], |
|||
], |
|||
'db' => $db, |
|||
/* |
|||
'urlManager' => [ |
|||
'enablePrettyUrl' => true, |
|||
'showScriptName' => false, |
|||
'rules' => [ |
|||
], |
|||
], |
|||
*/ |
|||
], |
|||
'params' => $params, |
|||
]; |
|||
|
|||
if (YII_ENV_DEV) { |
|||
// configuration adjustments for 'dev' environment
|
|||
$config['bootstrap'][] = 'debug'; |
|||
$config['modules']['debug'] = [ |
|||
'class' => 'yii\debug\Module', |
|||
// uncomment the following to add your IP if you are not connecting from localhost.
|
|||
//'allowedIPs' => ['127.0.0.1', '::1'],
|
|||
]; |
|||
|
|||
$config['bootstrap'][] = 'gii'; |
|||
$config['modules']['gii'] = [ |
|||
'class' => 'yii\gii\Module', |
|||
// uncomment the following to add your IP if you are not connecting from localhost.
|
|||
//'allowedIPs' => ['127.0.0.1', '::1'],
|
|||
]; |
|||
} |
|||
|
|||
return $config; |
@ -0,0 +1,128 @@ |
|||
<?php |
|||
|
|||
namespace app\controllers; |
|||
|
|||
use Yii; |
|||
use yii\filters\AccessControl; |
|||
use yii\web\Controller; |
|||
use yii\web\Response; |
|||
use yii\filters\VerbFilter; |
|||
use app\models\LoginForm; |
|||
use app\models\ContactForm; |
|||
|
|||
class SiteController extends Controller |
|||
{ |
|||
/** |
|||
* {@inheritdoc} |
|||
*/ |
|||
public function behaviors() |
|||
{ |
|||
return [ |
|||
'access' => [ |
|||
'class' => AccessControl::class, |
|||
'only' => ['logout'], |
|||
'rules' => [ |
|||
[ |
|||
'actions' => ['logout'], |
|||
'allow' => true, |
|||
'roles' => ['@'], |
|||
], |
|||
], |
|||
], |
|||
'verbs' => [ |
|||
'class' => VerbFilter::class, |
|||
'actions' => [ |
|||
'logout' => ['post'], |
|||
], |
|||
], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* {@inheritdoc} |
|||
*/ |
|||
public function actions() |
|||
{ |
|||
return [ |
|||
'error' => [ |
|||
'class' => 'yii\web\ErrorAction', |
|||
], |
|||
'captcha' => [ |
|||
'class' => 'yii\captcha\CaptchaAction', |
|||
'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null, |
|||
], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* Displays homepage. |
|||
* |
|||
* @return string |
|||
*/ |
|||
public function actionIndex() |
|||
{ |
|||
return $this->render('index'); |
|||
} |
|||
|
|||
/** |
|||
* Login action. |
|||
* |
|||
* @return Response|string |
|||
*/ |
|||
public function actionLogin() |
|||
{ |
|||
if (!Yii::$app->user->isGuest) { |
|||
return $this->goHome(); |
|||
} |
|||
|
|||
$model = new LoginForm(); |
|||
if ($model->load(Yii::$app->request->post()) && $model->login()) { |
|||
return $this->goBack(); |
|||
} |
|||
|
|||
$model->password = ''; |
|||
return $this->render('login', [ |
|||
'model' => $model, |
|||
]); |
|||
} |
|||
|
|||
/** |
|||
* Logout action. |
|||
* |
|||
* @return Response |
|||
*/ |
|||
public function actionLogout() |
|||
{ |
|||
Yii::$app->user->logout(); |
|||
|
|||
return $this->goHome(); |
|||
} |
|||
|
|||
/** |
|||
* Displays contact page. |
|||
* |
|||
* @return Response|string |
|||
*/ |
|||
public function actionContact() |
|||
{ |
|||
$model = new ContactForm(); |
|||
if ($model->load(Yii::$app->request->post()) && $model->contact(Yii::$app->params['adminEmail'])) { |
|||
Yii::$app->session->setFlash('contactFormSubmitted'); |
|||
|
|||
return $this->refresh(); |
|||
} |
|||
return $this->render('contact', [ |
|||
'model' => $model, |
|||
]); |
|||
} |
|||
|
|||
/** |
|||
* Displays about page. |
|||
* |
|||
* @return string |
|||
*/ |
|||
public function actionAbout() |
|||
{ |
|||
return $this->render('about'); |
|||
} |
|||
} |
@ -0,0 +1,42 @@ |
|||
[ |
|||
{ |
|||
"id": 1, |
|||
"name": "jiang", |
|||
"phone": "19866827739" |
|||
}, |
|||
{ |
|||
"id": 2, |
|||
"name": "zhang", |
|||
"phone": "001" |
|||
}, |
|||
{ |
|||
"id": 3, |
|||
"name": "chen", |
|||
"phone": "002" |
|||
}, |
|||
{ |
|||
"id": 4, |
|||
"name": "chen", |
|||
"phone": "002" |
|||
}, |
|||
{ |
|||
"id": 5, |
|||
"name": "li", |
|||
"phone": "003" |
|||
}, |
|||
{ |
|||
"id": 6, |
|||
"name": "guan", |
|||
"phone": "004" |
|||
}, |
|||
{ |
|||
"id": 7, |
|||
"name": "huang", |
|||
"phone": "005" |
|||
}, |
|||
{ |
|||
"id": 8, |
|||
"name": "zeng", |
|||
"phone": "006" |
|||
} |
|||
] |
@ -0,0 +1,9 @@ |
|||
version: '2' |
|||
services: |
|||
php: |
|||
image: yiisoftware/yii2-php:7.4-apache |
|||
volumes: |
|||
- ~/.composer-docker/cache:/root/.composer/cache:delegated |
|||
- ./:/app:delegated |
|||
ports: |
|||
- '8000:80' |
@ -0,0 +1,22 @@ |
|||
<?php |
|||
use yii\helpers\Html; |
|||
|
|||
/** @var \yii\web\View $this view component instance */ |
|||
/** @var \yii\mail\MessageInterface $message the message being composed */ |
|||
/** @var string $content main view render result */ |
|||
?>
|
|||
<?php $this->beginPage() ?>
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
|||
<html xmlns="http://www.w3.org/1999/xhtml"> |
|||
<head> |
|||
<meta http-equiv="Content-Type" content="text/html; charset=<?= Yii::$app->charset ?>" /> |
|||
<title><?= Html::encode($this->title) ?></title>
|
|||
<?php $this->head() ?>
|
|||
</head> |
|||
<body> |
|||
<?php $this->beginBody() ?>
|
|||
<?= $content ?>
|
|||
<?php $this->endBody() ?>
|
|||
</body> |
|||
</html> |
|||
<?php $this->endPage() ?>
|
@ -0,0 +1,13 @@ |
|||
<?php |
|||
|
|||
/** |
|||
* @var yii\web\View $this view component instance |
|||
* @var yii\mail\BaseMessage $message the message being composed |
|||
* @var string $content main view render result |
|||
*/ |
|||
|
|||
$this->beginPage(); |
|||
$this->beginBody(); |
|||
echo $content; |
|||
$this->endBody(); |
|||
$this->endPage(); |
@ -0,0 +1,65 @@ |
|||
<?php |
|||
|
|||
namespace app\models; |
|||
|
|||
use Yii; |
|||
use yii\base\Model; |
|||
|
|||
/** |
|||
* ContactForm is the model behind the contact form. |
|||
*/ |
|||
class ContactForm extends Model |
|||
{ |
|||
public $name; |
|||
public $email; |
|||
public $subject; |
|||
public $body; |
|||
public $verifyCode; |
|||
|
|||
|
|||
/** |
|||
* @return array the validation rules. |
|||
*/ |
|||
public function rules() |
|||
{ |
|||
return [ |
|||
// name, email, subject and body are required
|
|||
[['name', 'email', 'subject', 'body'], 'required'], |
|||
// email has to be a valid email address
|
|||
['email', 'email'], |
|||
// verifyCode needs to be entered correctly
|
|||
['verifyCode', 'captcha'], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @return array customized attribute labels |
|||
*/ |
|||
public function attributeLabels() |
|||
{ |
|||
return [ |
|||
'verifyCode' => 'Verification Code', |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* Sends an email to the specified email address using the information collected by this model. |
|||
* @param string $email the target email address |
|||
* @return bool whether the model passes validation |
|||
*/ |
|||
public function contact($email) |
|||
{ |
|||
if ($this->validate()) { |
|||
Yii::$app->mailer->compose() |
|||
->setTo($email) |
|||
->setFrom([Yii::$app->params['senderEmail'] => Yii::$app->params['senderName']]) |
|||
->setReplyTo([$this->email => $this->name]) |
|||
->setSubject($this->subject) |
|||
->setTextBody($this->body) |
|||
->send(); |
|||
|
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
} |
@ -0,0 +1,81 @@ |
|||
<?php |
|||
|
|||
namespace app\models; |
|||
|
|||
use Yii; |
|||
use yii\base\Model; |
|||
|
|||
/** |
|||
* LoginForm is the model behind the login form. |
|||
* |
|||
* @property-read User|null $user |
|||
* |
|||
*/ |
|||
class LoginForm extends Model |
|||
{ |
|||
public $username; |
|||
public $password; |
|||
public $rememberMe = true; |
|||
|
|||
private $_user = false; |
|||
|
|||
|
|||
/** |
|||
* @return array the validation rules. |
|||
*/ |
|||
public function rules() |
|||
{ |
|||
return [ |
|||
// username and password are both required
|
|||
[['username', 'password'], 'required'], |
|||
// rememberMe must be a boolean value
|
|||
['rememberMe', 'boolean'], |
|||
// password is validated by validatePassword()
|
|||
['password', 'validatePassword'], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* Validates the password. |
|||
* This method serves as the inline validation for password. |
|||
* |
|||
* @param string $attribute the attribute currently being validated |
|||
* @param array $params the additional name-value pairs given in the rule |
|||
*/ |
|||
public function validatePassword($attribute, $params) |
|||
{ |
|||
if (!$this->hasErrors()) { |
|||
$user = $this->getUser(); |
|||
|
|||
if (!$user || !$user->validatePassword($this->password)) { |
|||
$this->addError($attribute, 'Incorrect username or password.'); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Logs in a user using the provided username and password. |
|||
* @return bool whether the user is logged in successfully |
|||
*/ |
|||
public function login() |
|||
{ |
|||
if ($this->validate()) { |
|||
return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0); |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* Finds user by [[username]] |
|||
* |
|||
* @return User|null |
|||
*/ |
|||
public function getUser() |
|||
{ |
|||
if ($this->_user === false) { |
|||
$this->_user = User::findByUsername($this->username); |
|||
} |
|||
|
|||
return $this->_user; |
|||
} |
|||
} |
@ -0,0 +1,104 @@ |
|||
<?php |
|||
|
|||
namespace app\models; |
|||
|
|||
class User extends \yii\base\BaseObject implements \yii\web\IdentityInterface |
|||
{ |
|||
public $id; |
|||
public $username; |
|||
public $password; |
|||
public $authKey; |
|||
public $accessToken; |
|||
|
|||
private static $users = [ |
|||
'100' => [ |
|||
'id' => '100', |
|||
'username' => 'admin', |
|||
'password' => 'admin', |
|||
'authKey' => 'test100key', |
|||
'accessToken' => '100-token', |
|||
], |
|||
'101' => [ |
|||
'id' => '101', |
|||
'username' => 'demo', |
|||
'password' => 'demo', |
|||
'authKey' => 'test101key', |
|||
'accessToken' => '101-token', |
|||
], |
|||
]; |
|||
|
|||
|
|||
/** |
|||
* {@inheritdoc} |
|||
*/ |
|||
public static function findIdentity($id) |
|||
{ |
|||
return isset(self::$users[$id]) ? new static(self::$users[$id]) : null; |
|||
} |
|||
|
|||
/** |
|||
* {@inheritdoc} |
|||
*/ |
|||
public static function findIdentityByAccessToken($token, $type = null) |
|||
{ |
|||
foreach (self::$users as $user) { |
|||
if ($user['accessToken'] === $token) { |
|||
return new static($user); |
|||
} |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* Finds user by username |
|||
* |
|||
* @param string $username |
|||
* @return static|null |
|||
*/ |
|||
public static function findByUsername($username) |
|||
{ |
|||
foreach (self::$users as $user) { |
|||
if (strcasecmp($user['username'], $username) === 0) { |
|||
return new static($user); |
|||
} |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* {@inheritdoc} |
|||
*/ |
|||
public function getId() |
|||
{ |
|||
return $this->id; |
|||
} |
|||
|
|||
/** |
|||
* {@inheritdoc} |
|||
*/ |
|||
public function getAuthKey() |
|||
{ |
|||
return $this->authKey; |
|||
} |
|||
|
|||
/** |
|||
* {@inheritdoc} |
|||
*/ |
|||
public function validateAuthKey($authKey) |
|||
{ |
|||
return $this->authKey === $authKey; |
|||
} |
|||
|
|||
/** |
|||
* Validates password |
|||
* |
|||
* @param string $password password to validate |
|||
* @return bool if password provided is valid for current user |
|||
*/ |
|||
public function validatePassword($password) |
|||
{ |
|||
return $this->password === $password; |
|||
} |
|||
} |
@ -0,0 +1,162 @@ |
|||
<?php |
|||
/** |
|||
* Application requirement checker script. |
|||
* |
|||
* In order to run this script use the following console command: |
|||
* php requirements.php |
|||
* |
|||
* In order to run this script from the web, you should copy it to the web root. |
|||
* If you are using Linux you can create a hard link instead, using the following command: |
|||
* ln ../requirements.php requirements.php |
|||
*/ |
|||
|
|||
// you may need to adjust this path to the correct Yii framework path
|
|||
// uncomment and adjust the following line if Yii is not located at the default path
|
|||
//$frameworkPath = dirname(__FILE__) . '/vendor/yiisoft/yii2';
|
|||
|
|||
|
|||
if (!isset($frameworkPath)) { |
|||
$searchPaths = array( |
|||
dirname(__FILE__) . '/vendor/yiisoft/yii2', |
|||
dirname(__FILE__) . '/../vendor/yiisoft/yii2', |
|||
); |
|||
foreach ($searchPaths as $path) { |
|||
if (is_dir($path)) { |
|||
$frameworkPath = $path; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (!isset($frameworkPath) || !is_dir($frameworkPath)) { |
|||
$message = "<h1>Error</h1>\n\n" |
|||
. "<p><strong>The path to yii framework seems to be incorrect.</strong></p>\n" |
|||
. '<p>You need to install Yii framework via composer or adjust the framework path in file <abbr title="' . __FILE__ . '">' . basename(__FILE__) . "</abbr>.</p>\n" |
|||
. '<p>Please refer to the <abbr title="' . dirname(__FILE__) . "/README.md\">README</abbr> on how to install Yii.</p>\n"; |
|||
|
|||
if (!empty($_SERVER['argv'])) { |
|||
// do not print HTML when used in console mode
|
|||
echo strip_tags($message); |
|||
} else { |
|||
echo $message; |
|||
} |
|||
exit(1); |
|||
} |
|||
|
|||
require_once($frameworkPath . '/requirements/YiiRequirementChecker.php'); |
|||
$requirementsChecker = new YiiRequirementChecker(); |
|||
|
|||
$gdMemo = $imagickMemo = 'Either GD PHP extension with FreeType support or ImageMagick PHP extension with PNG support is required for image CAPTCHA.'; |
|||
$gdOK = $imagickOK = false; |
|||
|
|||
if (extension_loaded('imagick')) { |
|||
$imagick = new Imagick(); |
|||
$imagickFormats = $imagick->queryFormats('PNG'); |
|||
if (in_array('PNG', $imagickFormats)) { |
|||
$imagickOK = true; |
|||
} else { |
|||
$imagickMemo = 'Imagick extension should be installed with PNG support in order to be used for image CAPTCHA.'; |
|||
} |
|||
} |
|||
|
|||
if (extension_loaded('gd')) { |
|||
$gdInfo = gd_info(); |
|||
if (!empty($gdInfo['FreeType Support'])) { |
|||
$gdOK = true; |
|||
} else { |
|||
$gdMemo = 'GD extension should be installed with FreeType support in order to be used for image CAPTCHA.'; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Adjust requirements according to your application specifics. |
|||
*/ |
|||
$requirements = array( |
|||
// Database :
|
|||
array( |
|||
'name' => 'PDO extension', |
|||
'mandatory' => true, |
|||
'condition' => extension_loaded('pdo'), |
|||
'by' => 'All DB-related classes', |
|||
), |
|||
array( |
|||
'name' => 'PDO SQLite extension', |
|||
'mandatory' => false, |
|||
'condition' => extension_loaded('pdo_sqlite'), |
|||
'by' => 'All DB-related classes', |
|||
'memo' => 'Required for SQLite database.', |
|||
), |
|||
array( |
|||
'name' => 'PDO MySQL extension', |
|||
'mandatory' => false, |
|||
'condition' => extension_loaded('pdo_mysql'), |
|||
'by' => 'All DB-related classes', |
|||
'memo' => 'Required for MySQL database.', |
|||
), |
|||
array( |
|||
'name' => 'PDO PostgreSQL extension', |
|||
'mandatory' => false, |
|||
'condition' => extension_loaded('pdo_pgsql'), |
|||
'by' => 'All DB-related classes', |
|||
'memo' => 'Required for PostgreSQL database.', |
|||
), |
|||
// Cache :
|
|||
array( |
|||
'name' => 'Memcache extension', |
|||
'mandatory' => false, |
|||
'condition' => extension_loaded('memcache') || extension_loaded('memcached'), |
|||
'by' => '<a href="https://www.yiiframework.com/doc-2.0/yii-caching-memcache.html">MemCache</a>', |
|||
'memo' => extension_loaded('memcached') ? 'To use memcached set <a href="https://www.yiiframework.com/doc-2.0/yii-caching-memcache.html#$useMemcached-detail">MemCache::useMemcached</a> to <code>true</code>.' : '' |
|||
), |
|||
// CAPTCHA:
|
|||
array( |
|||
'name' => 'GD PHP extension with FreeType support', |
|||
'mandatory' => false, |
|||
'condition' => $gdOK, |
|||
'by' => '<a href="https://www.yiiframework.com/doc-2.0/yii-captcha-captcha.html">Captcha</a>', |
|||
'memo' => $gdMemo, |
|||
), |
|||
array( |
|||
'name' => 'ImageMagick PHP extension with PNG support', |
|||
'mandatory' => false, |
|||
'condition' => $imagickOK, |
|||
'by' => '<a href="https://www.yiiframework.com/doc-2.0/yii-captcha-captcha.html">Captcha</a>', |
|||
'memo' => $imagickMemo, |
|||
), |
|||
// PHP ini :
|
|||
'phpExposePhp' => array( |
|||
'name' => 'Expose PHP', |
|||
'mandatory' => false, |
|||
'condition' => $requirementsChecker->checkPhpIniOff("expose_php"), |
|||
'by' => 'Security reasons', |
|||
'memo' => '"expose_php" should be disabled at php.ini', |
|||
), |
|||
'phpAllowUrlInclude' => array( |
|||
'name' => 'PHP allow url include', |
|||
'mandatory' => false, |
|||
'condition' => $requirementsChecker->checkPhpIniOff("allow_url_include"), |
|||
'by' => 'Security reasons', |
|||
'memo' => '"allow_url_include" should be disabled at php.ini', |
|||
), |
|||
'phpSmtp' => array( |
|||
'name' => 'PHP mail SMTP', |
|||
'mandatory' => false, |
|||
'condition' => strlen(ini_get('SMTP')) > 0, |
|||
'by' => 'Email sending', |
|||
'memo' => 'PHP mail SMTP server required', |
|||
), |
|||
); |
|||
|
|||
// OPcache check
|
|||
if (!version_compare(phpversion(), '5.5', '>=')) { |
|||
$requirements[] = array( |
|||
'name' => 'APC extension', |
|||
'mandatory' => false, |
|||
'condition' => extension_loaded('apc'), |
|||
'by' => '<a href="https://www.yiiframework.com/doc-2.0/yii-caching-apccache.html">ApcCache</a>', |
|||
); |
|||
} |
|||
|
|||
$result = $requirementsChecker->checkYii()->check($requirements)->getResult(); |
|||
$requirementsChecker->render(); |
|||
exit($result['summary']['errors'] === 0 ? 0 : 1); |
@ -0,0 +1,2 @@ |
|||
* |
|||
!.gitignore |
@ -0,0 +1,6 @@ |
|||
<?php |
|||
define('YII_ENV', 'test'); |
|||
defined('YII_DEBUG') or define('YII_DEBUG', true); |
|||
|
|||
require_once __DIR__ . '/../vendor/yiisoft/yii2/Yii.php'; |
|||
require __DIR__ .'/../vendor/autoload.php'; |
@ -0,0 +1 @@ |
|||
|
@ -0,0 +1,2 @@ |
|||
* |
|||
!.gitignore |
@ -0,0 +1,26 @@ |
|||
<?php |
|||
|
|||
|
|||
/** |
|||
* Inherited Methods |
|||
* @method void wantToTest($text) |
|||
* @method void wantTo($text) |
|||
* @method void execute($callable) |
|||
* @method void expectTo($prediction) |
|||
* @method void expect($prediction) |
|||
* @method void amGoingTo($argumentation) |
|||
* @method void am($role) |
|||
* @method void lookForwardTo($achieveValue) |
|||
* @method void comment($description) |
|||
* @method \Codeception\Lib\Friend haveFriend($name, $actorClass = NULL) |
|||
* |
|||
* @SuppressWarnings(PHPMD) |
|||
*/ |
|||
class AcceptanceTester extends \Codeception\Actor |
|||
{ |
|||
use _generated\AcceptanceTesterActions; |
|||
|
|||
/** |
|||
* Define custom actions here |
|||
*/ |
|||
} |
@ -0,0 +1,23 @@ |
|||
<?php |
|||
|
|||
|
|||
/** |
|||
* Inherited Methods |
|||
* @method void wantToTest($text) |
|||
* @method void wantTo($text) |
|||
* @method void execute($callable) |
|||
* @method void expectTo($prediction) |
|||
* @method void expect($prediction) |
|||
* @method void amGoingTo($argumentation) |
|||
* @method void am($role) |
|||
* @method void lookForwardTo($achieveValue) |
|||
* @method void comment($description) |
|||
* @method \Codeception\Lib\Friend haveFriend($name, $actorClass = NULL) |
|||
* |
|||
* @SuppressWarnings(PHPMD) |
|||
*/ |
|||
class FunctionalTester extends \Codeception\Actor |
|||
{ |
|||
use _generated\FunctionalTesterActions; |
|||
|
|||
} |
@ -0,0 +1,26 @@ |
|||
<?php |
|||
|
|||
|
|||
/** |
|||
* Inherited Methods |
|||
* @method void wantToTest($text) |
|||
* @method void wantTo($text) |
|||
* @method void execute($callable) |
|||
* @method void expectTo($prediction) |
|||
* @method void expect($prediction) |
|||
* @method void amGoingTo($argumentation) |
|||
* @method void am($role) |
|||
* @method void lookForwardTo($achieveValue) |
|||
* @method void comment($description) |
|||
* @method \Codeception\Lib\Friend haveFriend($name, $actorClass = NULL) |
|||
* |
|||
* @SuppressWarnings(PHPMD) |
|||
*/ |
|||
class UnitTester extends \Codeception\Actor |
|||
{ |
|||
use _generated\UnitTesterActions; |
|||
|
|||
/** |
|||
* Define custom actions here |
|||
*/ |
|||
} |
@ -0,0 +1,10 @@ |
|||
actor: AcceptanceTester |
|||
modules: |
|||
enabled: |
|||
- WebDriver: |
|||
url: http://127.0.0.1:8080/ |
|||
browser: firefox |
|||
- Yii2: |
|||
part: orm |
|||
entryScript: index-test.php |
|||
cleanup: false |
@ -0,0 +1,12 @@ |
|||
<?php |
|||
|
|||
use yii\helpers\Url; |
|||
|
|||
class AboutCest |
|||
{ |
|||
public function ensureThatAboutWorks(AcceptanceTester $I) |
|||
{ |
|||
$I->amOnPage(Url::toRoute('/site/about')); |
|||
$I->see('About', 'h1'); |
|||
} |
|||
} |
@ -0,0 +1,34 @@ |
|||
<?php |
|||
|
|||
use yii\helpers\Url; |
|||
|
|||
class ContactCest |
|||
{ |
|||
public function _before(\AcceptanceTester $I) |
|||
{ |
|||
$I->amOnPage(Url::toRoute('/site/contact')); |
|||
} |
|||
|
|||
public function contactPageWorks(AcceptanceTester $I) |
|||
{ |
|||
$I->wantTo('ensure that contact page works'); |
|||
$I->see('Contact', 'h1'); |
|||
} |
|||
|
|||
public function contactFormCanBeSubmitted(AcceptanceTester $I) |
|||
{ |
|||
$I->amGoingTo('submit contact form with correct data'); |
|||
$I->fillField('#contactform-name', 'tester'); |
|||
$I->fillField('#contactform-email', 'tester@example.com'); |
|||
$I->fillField('#contactform-subject', 'test subject'); |
|||
$I->fillField('#contactform-body', 'test content'); |
|||
$I->fillField('#contactform-verifycode', 'testme'); |
|||
|
|||
$I->click('contact-button'); |
|||
|
|||
$I->wait(2); // wait for button to be clicked
|
|||
|
|||
$I->dontSeeElement('#contact-form'); |
|||
$I->see('Thank you for contacting us. We will respond to you as soon as possible.'); |
|||
} |
|||
} |
@ -0,0 +1,18 @@ |
|||
<?php |
|||
|
|||
use yii\helpers\Url; |
|||
|
|||
class HomeCest |
|||
{ |
|||
public function ensureThatHomePageWorks(AcceptanceTester $I) |
|||
{ |
|||
$I->amOnPage(Url::toRoute('/site/index')); |
|||
$I->see('My Company'); |
|||
|
|||
$I->seeLink('About'); |
|||
$I->click('About'); |
|||
$I->wait(2); // wait for page to be opened
|
|||
|
|||
$I->see('This is the About page.'); |
|||
} |
|||
} |
@ -0,0 +1,21 @@ |
|||
<?php |
|||
|
|||
use yii\helpers\Url; |
|||
|
|||
class LoginCest |
|||
{ |
|||
public function ensureThatLoginWorks(AcceptanceTester $I) |
|||
{ |
|||
$I->amOnPage(Url::toRoute('/site/login')); |
|||
$I->see('Login', 'h1'); |
|||
|
|||
$I->amGoingTo('try to login with correct credentials'); |
|||
$I->fillField('input[name="LoginForm[username]"]', 'admin'); |
|||
$I->fillField('input[name="LoginForm[password]"]', 'admin'); |
|||
$I->click('login-button'); |
|||
$I->wait(2); // wait for button to be clicked
|
|||
|
|||
$I->expectTo('see user info'); |
|||
$I->see('Logout'); |
|||
} |
|||
} |
@ -0,0 +1 @@ |
|||
<?php |
@ -0,0 +1,29 @@ |
|||
#!/usr/bin/env php |
|||
<?php |
|||
/** |
|||
* Yii console bootstrap file. |
|||
* |
|||
* @link https://www.yiiframework.com/ |
|||
* @copyright Copyright (c) 2008 Yii Software LLC |
|||
* @license https://www.yiiframework.com/license/ |
|||
*/ |
|||
|
|||
defined('YII_DEBUG') or define('YII_DEBUG', true); |
|||
defined('YII_ENV') or define('YII_ENV', 'test'); |
|||
|
|||
require __DIR__ . '/../../vendor/autoload.php'; |
|||
require __DIR__ . '/../../vendor/yiisoft/yii2/Yii.php'; |
|||
|
|||
$config = yii\helpers\ArrayHelper::merge( |
|||
require __DIR__ . '/../../config/console.php', |
|||
[ |
|||
'components' => [ |
|||
'db' => require __DIR__ . '/../../config/test_db.php' |
|||
] |
|||
] |
|||
); |
|||
|
|||
|
|||
$application = new yii\console\Application($config); |
|||
$exitCode = $application->run(); |
|||
exit($exitCode); |
@ -0,0 +1,20 @@ |
|||
@echo off |
|||
|
|||
rem ------------------------------------------------------------- |
|||
rem Yii command line bootstrap script for Windows. |
|||
rem |
|||
rem @author Qiang Xue <qiang.xue@gmail.com> |
|||
rem @link https://www.yiiframework.com/ |
|||
rem @copyright Copyright (c) 2008 Yii Software LLC |
|||
rem @license https://www.yiiframework.com/license/ |
|||
rem ------------------------------------------------------------- |
|||
|
|||
@setlocal |
|||
|
|||
set YII_PATH=%~dp0 |
|||
|
|||
if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe |
|||
|
|||
"%PHP_COMMAND%" "%YII_PATH%yii" %* |
|||
|
|||
@endlocal |
@ -0,0 +1,14 @@ |
|||
# Codeception Test Suite Configuration |
|||
|
|||
# suite for functional (integration) tests. |
|||
# emulate web requests and make application process them. |
|||
# (tip: better to use with frameworks). |
|||
|
|||
# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES. |
|||
#basic/web/index.php |
|||
actor: FunctionalTester |
|||
modules: |
|||
enabled: |
|||
- Filesystem |
|||
- Yii2 |
|||
- Asserts |
@ -0,0 +1,57 @@ |
|||
<?php |
|||
|
|||
class ContactFormCest |
|||
{ |
|||
public function _before(\FunctionalTester $I) |
|||
{ |
|||
$I->amOnRoute('site/contact'); |
|||
} |
|||
|
|||
public function openContactPage(\FunctionalTester $I) |
|||
{ |
|||
$I->see('Contact', 'h1'); |
|||
} |
|||
|
|||
public function submitEmptyForm(\FunctionalTester $I) |
|||
{ |
|||
$I->submitForm('#contact-form', []); |
|||
$I->expectTo('see validations errors'); |
|||
$I->see('Contact', 'h1'); |
|||
$I->see('Name cannot be blank'); |
|||
$I->see('Email cannot be blank'); |
|||
$I->see('Subject cannot be blank'); |
|||
$I->see('Body cannot be blank'); |
|||
$I->see('The verification code is incorrect'); |
|||
} |
|||
|
|||
public function submitFormWithIncorrectEmail(\FunctionalTester $I) |
|||
{ |
|||
$I->submitForm('#contact-form', [ |
|||
'ContactForm[name]' => 'tester', |
|||
'ContactForm[email]' => 'tester.email', |
|||
'ContactForm[subject]' => 'test subject', |
|||
'ContactForm[body]' => 'test content', |
|||
'ContactForm[verifyCode]' => 'testme', |
|||
]); |
|||
$I->expectTo('see that email address is wrong'); |
|||
$I->dontSee('Name cannot be blank', '.help-inline'); |
|||
$I->see('Email is not a valid email address.'); |
|||
$I->dontSee('Subject cannot be blank', '.help-inline'); |
|||
$I->dontSee('Body cannot be blank', '.help-inline'); |
|||
$I->dontSee('The verification code is incorrect', '.help-inline'); |
|||
} |
|||
|
|||
public function submitFormSuccessfully(\FunctionalTester $I) |
|||
{ |
|||
$I->submitForm('#contact-form', [ |
|||
'ContactForm[name]' => 'tester', |
|||
'ContactForm[email]' => 'tester@example.com', |
|||
'ContactForm[subject]' => 'test subject', |
|||
'ContactForm[body]' => 'test content', |
|||
'ContactForm[verifyCode]' => 'testme', |
|||
]); |
|||
$I->seeEmailIsSent(); |
|||
$I->dontSeeElement('#contact-form'); |
|||
$I->see('Thank you for contacting us. We will respond to you as soon as possible.'); |
|||
} |
|||
} |
@ -0,0 +1,59 @@ |
|||
<?php |
|||
|
|||
class LoginFormCest |
|||
{ |
|||
public function _before(\FunctionalTester $I) |
|||
{ |
|||
$I->amOnRoute('site/login'); |
|||
} |
|||
|
|||
public function openLoginPage(\FunctionalTester $I) |
|||
{ |
|||
$I->see('Login', 'h1'); |
|||
|
|||
} |
|||
|
|||
// demonstrates `amLoggedInAs` method
|
|||
public function internalLoginById(\FunctionalTester $I) |
|||
{ |
|||
$I->amLoggedInAs(100); |
|||
$I->amOnPage('/'); |
|||
$I->see('Logout (admin)'); |
|||
} |
|||
|
|||
// demonstrates `amLoggedInAs` method
|
|||
public function internalLoginByInstance(\FunctionalTester $I) |
|||
{ |
|||
$I->amLoggedInAs(\app\models\User::findByUsername('admin')); |
|||
$I->amOnPage('/'); |
|||
$I->see('Logout (admin)'); |
|||
} |
|||
|
|||
public function loginWithEmptyCredentials(\FunctionalTester $I) |
|||
{ |
|||
$I->submitForm('#login-form', []); |
|||
$I->expectTo('see validations errors'); |
|||
$I->see('Username cannot be blank.'); |
|||
$I->see('Password cannot be blank.'); |
|||
} |
|||
|
|||
public function loginWithWrongCredentials(\FunctionalTester $I) |
|||
{ |
|||
$I->submitForm('#login-form', [ |
|||
'LoginForm[username]' => 'admin', |
|||
'LoginForm[password]' => 'wrong', |
|||
]); |
|||
$I->expectTo('see validations errors'); |
|||
$I->see('Incorrect username or password.'); |
|||
} |
|||
|
|||
public function loginSuccessfully(\FunctionalTester $I) |
|||
{ |
|||
$I->submitForm('#login-form', [ |
|||
'LoginForm[username]' => 'admin', |
|||
'LoginForm[password]' => 'admin', |
|||
]); |
|||
$I->see('Logout (admin)'); |
|||
$I->dontSeeElement('form#login-form'); |
|||
} |
|||
} |
@ -0,0 +1 @@ |
|||
<?php |
@ -0,0 +1,11 @@ |
|||
# Codeception Test Suite Configuration |
|||
|
|||
# suite for unit (internal) tests. |
|||
# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES. |
|||
|
|||
actor: UnitTester |
|||
modules: |
|||
enabled: |
|||
- Asserts |
|||
- Yii2: |
|||
part: [orm, email, fixtures] |
@ -0,0 +1,3 @@ |
|||
<?php |
|||
|
|||
// add unit testing specific bootstrap code here
|
@ -0,0 +1,41 @@ |
|||
<?php |
|||
|
|||
namespace tests\unit\models; |
|||
|
|||
use app\models\ContactForm; |
|||
use yii\mail\MessageInterface; |
|||
|
|||
class ContactFormTest extends \Codeception\Test\Unit |
|||
{ |
|||
/** |
|||
* @var \UnitTester |
|||
*/ |
|||
public $tester; |
|||
|
|||
public function testEmailIsSentOnContact() |
|||
{ |
|||
$model = new ContactForm(); |
|||
|
|||
$model->attributes = [ |
|||
'name' => 'Tester', |
|||
'email' => 'tester@example.com', |
|||
'subject' => 'very important letter subject', |
|||
'body' => 'body of current message', |
|||
'verifyCode' => 'testme', |
|||
]; |
|||
|
|||
verify($model->contact('admin@example.com'))->notEmpty(); |
|||
|
|||
// using Yii2 module actions to check email was sent
|
|||
$this->tester->seeEmailIsSent(); |
|||
|
|||
/** @var MessageInterface $emailMessage */ |
|||
$emailMessage = $this->tester->grabLastSentEmail(); |
|||
verify($emailMessage)->instanceOf('yii\mail\MessageInterface'); |
|||
verify($emailMessage->getTo())->arrayHasKey('admin@example.com'); |
|||
verify($emailMessage->getFrom())->arrayHasKey('noreply@example.com'); |
|||
verify($emailMessage->getReplyTo())->arrayHasKey('tester@example.com'); |
|||
verify($emailMessage->getSubject())->equals('very important letter subject'); |
|||
verify($emailMessage->toString())->stringContainsString('body of current message'); |
|||
} |
|||
} |
@ -0,0 +1,51 @@ |
|||
<?php |
|||
|
|||
namespace tests\unit\models; |
|||
|
|||
use app\models\LoginForm; |
|||
|
|||
class LoginFormTest extends \Codeception\Test\Unit |
|||
{ |
|||
private $model; |
|||
|
|||
protected function _after() |
|||
{ |
|||
\Yii::$app->user->logout(); |
|||
} |
|||
|
|||
public function testLoginNoUser() |
|||
{ |
|||
$this->model = new LoginForm([ |
|||
'username' => 'not_existing_username', |
|||
'password' => 'not_existing_password', |
|||
]); |
|||
|
|||
verify($this->model->login())->false(); |
|||
verify(\Yii::$app->user->isGuest)->true(); |
|||
} |
|||
|
|||
public function testLoginWrongPassword() |
|||
{ |
|||
$this->model = new LoginForm([ |
|||
'username' => 'demo', |
|||
'password' => 'wrong_password', |
|||
]); |
|||
|
|||
verify($this->model->login())->false(); |
|||
verify(\Yii::$app->user->isGuest)->true(); |
|||
verify($this->model->errors)->arrayHasKey('password'); |
|||
} |
|||
|
|||
public function testLoginCorrect() |
|||
{ |
|||
$this->model = new LoginForm([ |
|||
'username' => 'demo', |
|||
'password' => 'demo', |
|||
]); |
|||
|
|||
verify($this->model->login())->true(); |
|||
verify(\Yii::$app->user->isGuest)->false(); |
|||
verify($this->model->errors)->arrayHasNotKey('password'); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,44 @@ |
|||
<?php |
|||
|
|||
namespace tests\unit\models; |
|||
|
|||
use app\models\User; |
|||
|
|||
class UserTest extends \Codeception\Test\Unit |
|||
{ |
|||
public function testFindUserById() |
|||
{ |
|||
verify($user = User::findIdentity(100))->notEmpty(); |
|||
verify($user->username)->equals('admin'); |
|||
|
|||
verify(User::findIdentity(999))->empty(); |
|||
} |
|||
|
|||
public function testFindUserByAccessToken() |
|||
{ |
|||
verify($user = User::findIdentityByAccessToken('100-token'))->notEmpty(); |
|||
verify($user->username)->equals('admin'); |
|||
|
|||
verify(User::findIdentityByAccessToken('non-existing'))->empty(); |
|||
} |
|||
|
|||
public function testFindUserByUsername() |
|||
{ |
|||
verify($user = User::findByUsername('admin'))->notEmpty(); |
|||
verify(User::findByUsername('not-admin'))->empty(); |
|||
} |
|||
|
|||
/** |
|||
* @depends testFindUserByUsername |
|||
*/ |
|||
public function testValidateUser() |
|||
{ |
|||
$user = User::findByUsername('admin'); |
|||
verify($user->validateAuthKey('test100key'))->notEmpty(); |
|||
verify($user->validateAuthKey('test102key'))->empty(); |
|||
|
|||
verify($user->validatePassword('admin'))->notEmpty(); |
|||
verify($user->validatePassword('123456'))->empty(); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,261 @@ |
|||
<?php |
|||
|
|||
namespace tests\unit\widgets; |
|||
|
|||
use app\widgets\Alert; |
|||
use Yii; |
|||
|
|||
class AlertTest extends \Codeception\Test\Unit |
|||
{ |
|||
public function testSingleErrorMessage() |
|||
{ |
|||
$message = 'This is an error message'; |
|||
|
|||
Yii::$app->session->setFlash('error', $message); |
|||
|
|||
$renderingResult = Alert::widget(); |
|||
|
|||
verify($renderingResult)->stringContainsString($message); |
|||
verify($renderingResult)->stringContainsString('alert-danger'); |
|||
|
|||
verify($renderingResult)->stringNotContainsString('alert-success'); |
|||
verify($renderingResult)->stringNotContainsString('alert-info'); |
|||
verify($renderingResult)->stringNotContainsString('alert-warning'); |
|||
} |
|||
|
|||
public function testMultipleErrorMessages() |
|||
{ |
|||
$firstMessage = 'This is the first error message'; |
|||
$secondMessage = 'This is the second error message'; |
|||
|
|||
Yii::$app->session->setFlash('error', [$firstMessage, $secondMessage]); |
|||
|
|||
$renderingResult = Alert::widget(); |
|||
|
|||
verify($renderingResult)->stringContainsString($firstMessage); |
|||
verify($renderingResult)->stringContainsString($secondMessage); |
|||
verify($renderingResult)->stringContainsString('alert-danger'); |
|||
|
|||
verify($renderingResult)->stringNotContainsString('alert-success'); |
|||
verify($renderingResult)->stringNotContainsString('alert-info'); |
|||
verify($renderingResult)->stringNotContainsString('alert-warning'); |
|||
} |
|||
|
|||
public function testSingleDangerMessage() |
|||
{ |
|||
$message = 'This is a danger message'; |
|||
|
|||
Yii::$app->session->setFlash('danger', $message); |
|||
|
|||
$renderingResult = Alert::widget(); |
|||
|
|||
verify($renderingResult)->stringContainsString($message); |
|||
verify($renderingResult)->stringContainsString('alert-danger'); |
|||
|
|||
verify($renderingResult)->stringNotContainsString('alert-success'); |
|||
verify($renderingResult)->stringNotContainsString('alert-info'); |
|||
verify($renderingResult)->stringNotContainsString('alert-warning'); |
|||
} |
|||
|
|||
public function testMultipleDangerMessages() |
|||
{ |
|||
$firstMessage = 'This is the first danger message'; |
|||
$secondMessage = 'This is the second danger message'; |
|||
|
|||
Yii::$app->session->setFlash('danger', [$firstMessage, $secondMessage]); |
|||
|
|||
$renderingResult = Alert::widget(); |
|||
|
|||
verify($renderingResult)->stringContainsString($firstMessage); |
|||
verify($renderingResult)->stringContainsString($secondMessage); |
|||
verify($renderingResult)->stringContainsString('alert-danger'); |
|||
|
|||
verify($renderingResult)->stringNotContainsString('alert-success'); |
|||
verify($renderingResult)->stringNotContainsString('alert-info'); |
|||
verify($renderingResult)->stringNotContainsString('alert-warning'); |
|||
} |
|||
|
|||
public function testSingleSuccessMessage() |
|||
{ |
|||
$message = 'This is a success message'; |
|||
|
|||
Yii::$app->session->setFlash('success', $message); |
|||
|
|||
$renderingResult = Alert::widget(); |
|||
|
|||
verify($renderingResult)->stringContainsString($message); |
|||
verify($renderingResult)->stringContainsString('alert-success'); |
|||
|
|||
verify($renderingResult)->stringNotContainsString('alert-danger'); |
|||
verify($renderingResult)->stringNotContainsString('alert-info'); |
|||
verify($renderingResult)->stringNotContainsString('alert-warning'); |
|||
} |
|||
|
|||
public function testMultipleSuccessMessages() |
|||
{ |
|||
$firstMessage = 'This is the first danger message'; |
|||
$secondMessage = 'This is the second danger message'; |
|||
|
|||
Yii::$app->session->setFlash('success', [$firstMessage, $secondMessage]); |
|||
|
|||
$renderingResult = Alert::widget(); |
|||
|
|||
verify($renderingResult)->stringContainsString($firstMessage); |
|||
verify($renderingResult)->stringContainsString($secondMessage); |
|||
verify($renderingResult)->stringContainsString('alert-success'); |
|||
|
|||
verify($renderingResult)->stringNotContainsString('alert-danger'); |
|||
verify($renderingResult)->stringNotContainsString('alert-info'); |
|||
verify($renderingResult)->stringNotContainsString('alert-warning'); |
|||
} |
|||
|
|||
public function testSingleInfoMessage() |
|||
{ |
|||
$message = 'This is an info message'; |
|||
|
|||
Yii::$app->session->setFlash('info', $message); |
|||
|
|||
$renderingResult = Alert::widget(); |
|||
|
|||
verify($renderingResult)->stringContainsString($message); |
|||
verify($renderingResult)->stringContainsString('alert-info'); |
|||
|
|||
verify($renderingResult)->stringNotContainsString('alert-danger'); |
|||
verify($renderingResult)->stringNotContainsString('alert-success'); |
|||
verify($renderingResult)->stringNotContainsString('alert-warning'); |
|||
} |
|||
|
|||
public function testMultipleInfoMessages() |
|||
{ |
|||
$firstMessage = 'This is the first info message'; |
|||
$secondMessage = 'This is the second info message'; |
|||
|
|||
Yii::$app->session->setFlash('info', [$firstMessage, $secondMessage]); |
|||
|
|||
$renderingResult = Alert::widget(); |
|||
|
|||
verify($renderingResult)->stringContainsString($firstMessage); |
|||
verify($renderingResult)->stringContainsString($secondMessage); |
|||
verify($renderingResult)->stringContainsString('alert-info'); |
|||
|
|||
verify($renderingResult)->stringNotContainsString('alert-danger'); |
|||
verify($renderingResult)->stringNotContainsString('alert-success'); |
|||
verify($renderingResult)->stringNotContainsString('alert-warning'); |
|||
} |
|||
|
|||
public function testSingleWarningMessage() |
|||
{ |
|||
$message = 'This is a warning message'; |
|||
|
|||
Yii::$app->session->setFlash('warning', $message); |
|||
|
|||
$renderingResult = Alert::widget(); |
|||
|
|||
verify($renderingResult)->stringContainsString($message); |
|||
verify($renderingResult)->stringContainsString('alert-warning'); |
|||
|
|||
verify($renderingResult)->stringNotContainsString('alert-danger'); |
|||
verify($renderingResult)->stringNotContainsString('alert-success'); |
|||
verify($renderingResult)->stringNotContainsString('alert-info'); |
|||
} |
|||
|
|||
public function testMultipleWarningMessages() |
|||
{ |
|||
$firstMessage = 'This is the first warning message'; |
|||
$secondMessage = 'This is the second warning message'; |
|||
|
|||
Yii::$app->session->setFlash('warning', [$firstMessage, $secondMessage]); |
|||
|
|||
$renderingResult = Alert::widget(); |
|||
|
|||
verify($renderingResult)->stringContainsString($firstMessage); |
|||
verify($renderingResult)->stringContainsString($secondMessage); |
|||
verify($renderingResult)->stringContainsString('alert-warning'); |
|||
|
|||
verify($renderingResult)->stringNotContainsString('alert-danger'); |
|||
verify($renderingResult)->stringNotContainsString('alert-success'); |
|||
verify($renderingResult)->stringNotContainsString('alert-info'); |
|||
} |
|||
|
|||
public function testSingleMixedMessages() { |
|||
$errorMessage = 'This is an error message'; |
|||
$dangerMessage = 'This is a danger message'; |
|||
$successMessage = 'This is a success message'; |
|||
$infoMessage = 'This is a info message'; |
|||
$warningMessage = 'This is a warning message'; |
|||
|
|||
Yii::$app->session->setFlash('error', $errorMessage); |
|||
Yii::$app->session->setFlash('danger', $dangerMessage); |
|||
Yii::$app->session->setFlash('success', $successMessage); |
|||
Yii::$app->session->setFlash('info', $infoMessage); |
|||
Yii::$app->session->setFlash('warning', $warningMessage); |
|||
|
|||
$renderingResult = Alert::widget(); |
|||
|
|||
verify($renderingResult)->stringContainsString($errorMessage); |
|||
verify($renderingResult)->stringContainsString($dangerMessage); |
|||
verify($renderingResult)->stringContainsString($successMessage); |
|||
verify($renderingResult)->stringContainsString($infoMessage); |
|||
verify($renderingResult)->stringContainsString($warningMessage); |
|||
|
|||
verify($renderingResult)->stringContainsString('alert-danger'); |
|||
verify($renderingResult)->stringContainsString('alert-success'); |
|||
verify($renderingResult)->stringContainsString('alert-info'); |
|||
verify($renderingResult)->stringContainsString('alert-warning'); |
|||
} |
|||
|
|||
public function testMultipleMixedMessages() { |
|||
$firstErrorMessage = 'This is the first error message'; |
|||
$secondErrorMessage = 'This is the second error message'; |
|||
$firstDangerMessage = 'This is the first danger message'; |
|||
$secondDangerMessage = 'This is the second'; |
|||
$firstSuccessMessage = 'This is the first success message'; |
|||
$secondSuccessMessage = 'This is the second success message'; |
|||
$firstInfoMessage = 'This is the first info message'; |
|||
$secondInfoMessage = 'This is the second info message'; |
|||
$firstWarningMessage = 'This is the first warning message'; |
|||
$secondWarningMessage = 'This is the second warning message'; |
|||
|
|||
Yii::$app->session->setFlash('error', [$firstErrorMessage, $secondErrorMessage]); |
|||
Yii::$app->session->setFlash('danger', [$firstDangerMessage, $secondDangerMessage]); |
|||
Yii::$app->session->setFlash('success', [$firstSuccessMessage, $secondSuccessMessage]); |
|||
Yii::$app->session->setFlash('info', [$firstInfoMessage, $secondInfoMessage]); |
|||
Yii::$app->session->setFlash('warning', [$firstWarningMessage, $secondWarningMessage]); |
|||
|
|||
$renderingResult = Alert::widget(); |
|||
|
|||
verify($renderingResult)->stringContainsString($firstErrorMessage); |
|||
verify($renderingResult)->stringContainsString($secondErrorMessage); |
|||
verify($renderingResult)->stringContainsString($firstDangerMessage); |
|||
verify($renderingResult)->stringContainsString($secondDangerMessage); |
|||
verify($renderingResult)->stringContainsString($firstSuccessMessage); |
|||
verify($renderingResult)->stringContainsString($secondSuccessMessage); |
|||
verify($renderingResult)->stringContainsString($firstInfoMessage); |
|||
verify($renderingResult)->stringContainsString($secondInfoMessage); |
|||
verify($renderingResult)->stringContainsString($firstWarningMessage); |
|||
verify($renderingResult)->stringContainsString($secondWarningMessage); |
|||
|
|||
verify($renderingResult)->stringContainsString('alert-danger'); |
|||
verify($renderingResult)->stringContainsString('alert-success'); |
|||
verify($renderingResult)->stringContainsString('alert-info'); |
|||
verify($renderingResult)->stringContainsString('alert-warning'); |
|||
} |
|||
|
|||
public function testFlashIntegrity() |
|||
{ |
|||
$errorMessage = 'This is an error message'; |
|||
$unrelatedMessage = 'This is a message that is not related to the alert widget'; |
|||
|
|||
Yii::$app->session->setFlash('error', $errorMessage); |
|||
Yii::$app->session->setFlash('unrelated', $unrelatedMessage); |
|||
|
|||
Alert::widget(); |
|||
|
|||
// Simulate redirect
|
|||
Yii::$app->session->close(); |
|||
Yii::$app->session->open(); |
|||
|
|||
verify(Yii::$app->session->getFlash('error'))->empty(); |
|||
verify(Yii::$app->session->getFlash('unrelated'))->equals($unrelatedMessage); |
|||
} |
|||
} |
@ -0,0 +1,2 @@ |
|||
# local configuration |
|||
vagrant-local.yml |
@ -0,0 +1,22 @@ |
|||
# Your personal GitHub token |
|||
github_token: <your-personal-github-token> |
|||
# Read more: https://github.com/blog/1509-personal-api-tokens |
|||
# You can generate it here: https://github.com/settings/tokens |
|||
|
|||
# Guest OS timezone |
|||
timezone: Europe/London |
|||
|
|||
# Are we need check box updates for every 'vagrant up'? |
|||
box_check_update: false |
|||
|
|||
# Virtual machine name |
|||
machine_name: yii2basic |
|||
|
|||
# Virtual machine IP |
|||
ip: 192.168.83.137 |
|||
|
|||
# Virtual machine CPU cores number |
|||
cpus: 1 |
|||
|
|||
# Virtual machine RAM |
|||
memory: 1024 |
@ -0,0 +1,38 @@ |
|||
server { |
|||
charset utf-8; |
|||
client_max_body_size 128M; |
|||
sendfile off; |
|||
|
|||
listen 80; ## listen for ipv4 |
|||
#listen [::]:80 default_server ipv6only=on; ## listen for ipv6 |
|||
|
|||
server_name yii2basic.test; |
|||
root /app/web/; |
|||
index index.php; |
|||
|
|||
access_log /app/vagrant/nginx/log/yii2basic.access.log; |
|||
error_log /app/vagrant/nginx/log/yii2basic.error.log; |
|||
|
|||
location / { |
|||
# Redirect everything that isn't a real file to index.php |
|||
try_files $uri $uri/ /index.php$is_args$args; |
|||
} |
|||
|
|||
# uncomment to avoid processing of calls to non-existing static files by Yii |
|||
#location ~ \.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ { |
|||
# try_files $uri =404; |
|||
#} |
|||
#error_page 404 /404.html; |
|||
|
|||
location ~ \.php$ { |
|||
include fastcgi_params; |
|||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; |
|||
#fastcgi_pass 127.0.0.1:9000; |
|||
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock; |
|||
try_files $uri =404; |
|||
} |
|||
|
|||
location ~ /\.(ht|svn|git) { |
|||
deny all; |
|||
} |
|||
} |
@ -0,0 +1,3 @@ |
|||
#nginx logs |
|||
yii2basic.access.log |
|||
yii2basic.error.log |
@ -0,0 +1,18 @@ |
|||
#!/usr/bin/env bash |
|||
|
|||
#== Bash helpers == |
|||
|
|||
function info { |
|||
echo " " |
|||
echo "--> $1" |
|||
echo " " |
|||
} |
|||
|
|||
#== Provision script == |
|||
|
|||
info "Provision-script user: `whoami`" |
|||
|
|||
info "Restart web-stack" |
|||
service php7.2-fpm restart |
|||
service nginx restart |
|||
service mysql restart |
@ -0,0 +1,79 @@ |
|||
#!/usr/bin/env bash |
|||
|
|||
#== Import script args == |
|||
|
|||
timezone=$(echo "$1") |
|||
readonly IP=$2 |
|||
|
|||
#== Bash helpers == |
|||
|
|||
function info { |
|||
echo " " |
|||
echo "--> $1" |
|||
echo " " |
|||
} |
|||
|
|||
#== Provision script == |
|||
|
|||
info "Provision-script user: `whoami`" |
|||
|
|||
export DEBIAN_FRONTEND=noninteractive |
|||
|
|||
info "Configure timezone" |
|||
timedatectl set-timezone ${timezone} --no-ask-password |
|||
|
|||
info "Add the VM IP to the list of allowed IPs" |
|||
awk -v ip=$IP -f /app/vagrant/provision/provision.awk /app/config/web.php |
|||
|
|||
info "Prepare root password for MySQL" |
|||
debconf-set-selections <<< 'mariadb-server mysql-server/root_password password' |
|||
debconf-set-selections <<< 'mariadb-server mysql-server/root_password_again password' |
|||
echo "Done!" |
|||
|
|||
info "Update OS software" |
|||
apt-get update |
|||
apt-get upgrade -y |
|||
|
|||
info "Install additional software" |
|||
apt-get install -y php7.2-curl php7.2-cli php7.2-intl php7.2-mysqlnd php7.2-gd php7.2-fpm php7.2-mbstring php7.2-xml unzip nginx mariadb-server-10.1 php.xdebug |
|||
|
|||
info "Configure MySQL" |
|||
sed -i 's/.*bind-address.*/bind-address = 0.0.0.0/' /etc/mysql/mariadb.conf.d/50-server.cnf |
|||
mysql <<< "CREATE USER 'root'@'%' IDENTIFIED BY ''" |
|||
mysql <<< "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%'" |
|||
mysql <<< "DROP USER 'root'@'localhost'" |
|||
mysql <<< 'FLUSH PRIVILEGES' |
|||
echo "Done!" |
|||
|
|||
info "Configure PHP-FPM" |
|||
sed -i 's/user = www-data/user = vagrant/g' /etc/php/7.2/fpm/pool.d/www.conf |
|||
sed -i 's/group = www-data/group = vagrant/g' /etc/php/7.2/fpm/pool.d/www.conf |
|||
sed -i 's/owner = www-data/owner = vagrant/g' /etc/php/7.2/fpm/pool.d/www.conf |
|||
cat << EOF > /etc/php/7.2/mods-available/xdebug.ini |
|||
zend_extension=xdebug.so |
|||
xdebug.remote_enable=1 |
|||
xdebug.remote_connect_back=1 |
|||
xdebug.remote_port=9000 |
|||
xdebug.remote_autostart=1 |
|||
EOF |
|||
echo "Done!" |
|||
|
|||
info "Configure NGINX" |
|||
sed -i 's/user www-data/user vagrant/g' /etc/nginx/nginx.conf |
|||
echo "Done!" |
|||
|
|||
info "Enabling site configuration" |
|||
ln -s /app/vagrant/nginx/app.conf /etc/nginx/sites-enabled/app.conf |
|||
echo "Done!" |
|||
|
|||
info "Removing default site configuration" |
|||
rm /etc/nginx/sites-enabled/default |
|||
echo "Done!" |
|||
|
|||
info "Initialize databases for MySQL" |
|||
mysql <<< 'CREATE DATABASE yii2basic' |
|||
mysql <<< 'CREATE DATABASE yii2basic_test' |
|||
echo "Done!" |
|||
|
|||
info "Install composer" |
|||
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer |
@ -0,0 +1,31 @@ |
|||
#!/usr/bin/env bash |
|||
|
|||
#== Import script args == |
|||
|
|||
github_token=$(echo "$1") |
|||
|
|||
#== Bash helpers == |
|||
|
|||
function info { |
|||
echo " " |
|||
echo "--> $1" |
|||
echo " " |
|||
} |
|||
|
|||
#== Provision script == |
|||
|
|||
info "Provision-script user: `whoami`" |
|||
|
|||
info "Configure composer" |
|||
composer config --global github-oauth.github.com ${github_token} |
|||
echo "Done!" |
|||
|
|||
info "Install project dependencies" |
|||
cd /app |
|||
composer --no-progress --prefer-dist install |
|||
|
|||
info "Create bash-alias 'app' for vagrant user" |
|||
echo 'alias app="cd /app"' | tee /home/vagrant/.bash_aliases |
|||
|
|||
info "Enabling colorized prompt for guest console" |
|||
sed -i "s/#force_color_prompt=yes/force_color_prompt=yes/" /home/vagrant/.bashrc |
@ -0,0 +1,50 @@ |
|||
### |
|||
# Modifying Yii2's files for Vagrant VM |
|||
# |
|||
# @author HA3IK <golubha3ik@gmail.com> |
|||
# @version 1.0.0 |
|||
|
|||
BEGIN { |
|||
print "AWK BEGINs its work:" |
|||
IGNORECASE = 1 |
|||
|
|||
# Correct IP - wildcard last octet |
|||
match(ip, /(([0-9]+\.)+)/, arr) |
|||
ip = arr[1] "*" |
|||
} |
|||
# BODY |
|||
{ |
|||
# Check if it's the same file |
|||
if (FILENAME != isFile["same"]){ |
|||
msg = "- Work with: " FILENAME |
|||
# Close a previous file |
|||
close(isFile["same"]) |
|||
# Delete previous data |
|||
delete isFile |
|||
# Save current file |
|||
isFile["same"] = FILENAME |
|||
# Define array index for the file |
|||
switch (FILENAME){ |
|||
case /config\/web\.php$/: |
|||
isFile["IsConfWeb"] = 1 |
|||
msg = msg " - add allowed IP: " ip |
|||
break |
|||
} |
|||
# Print the concatenated message for the file |
|||
print msg |
|||
} |
|||
|
|||
# IF config/web.php |
|||
if (isFile["IsConfWeb"]){ |
|||
# IF line has "allowedIPs" and doesn't has our IP |
|||
if (match($0, "allowedIPs") && !match($0, ip)){ |
|||
match($0, /([^\]]+)(.+)/, arr) |
|||
$0 = sprintf("%s, '%s'%s", arr[1], ip, arr[2]) |
|||
} |
|||
# Rewrite the file |
|||
print $0 > FILENAME |
|||
} |
|||
} |
|||
END { |
|||
print "AWK ENDs its work." |
|||
} |
@ -0,0 +1,83 @@ |
|||
<?php |
|||
|
|||
/** @var yii\web\View $this */ |
|||
/** @var string $content */ |
|||
|
|||
use app\assets\AppAsset; |
|||
use app\widgets\Alert; |
|||
use yii\bootstrap5\Breadcrumbs; |
|||
use yii\bootstrap5\Html; |
|||
use yii\bootstrap5\Nav; |
|||
use yii\bootstrap5\NavBar; |
|||
|
|||
AppAsset::register($this); |
|||
|
|||
$this->registerCsrfMetaTags(); |
|||
$this->registerMetaTag(['charset' => Yii::$app->charset], 'charset'); |
|||
$this->registerMetaTag(['name' => 'viewport', 'content' => 'width=device-width, initial-scale=1, shrink-to-fit=no']); |
|||
$this->registerMetaTag(['name' => 'description', 'content' => $this->params['meta_description'] ?? '']); |
|||
$this->registerMetaTag(['name' => 'keywords', 'content' => $this->params['meta_keywords'] ?? '']); |
|||
$this->registerLinkTag(['rel' => 'icon', 'type' => 'image/x-icon', 'href' => Yii::getAlias('@web/favicon.ico')]); |
|||
?>
|
|||
<?php $this->beginPage() ?>
|
|||
<!DOCTYPE html> |
|||
<html lang="<?= Yii::$app->language ?>" class="h-100"> |
|||
<head> |
|||
<title><?= Html::encode($this->title) ?></title>
|
|||
<?php $this->head() ?>
|
|||
</head> |
|||
<body class="d-flex flex-column h-100"> |
|||
<?php $this->beginBody() ?>
|
|||
|
|||
<header id="header"> |
|||
<?php |
|||
NavBar::begin([ |
|||
'brandLabel' => Yii::$app->name, |
|||
'brandUrl' => Yii::$app->homeUrl, |
|||
'options' => ['class' => 'navbar-expand-md navbar-dark bg-dark fixed-top'] |
|||
]); |
|||
echo Nav::widget([ |
|||
'options' => ['class' => 'navbar-nav'], |
|||
'items' => [ |
|||
['label' => 'Home', 'url' => ['/site/index']], |
|||
['label' => 'About', 'url' => ['/site/about']], |
|||
['label' => 'Contact', 'url' => ['/site/contact']], |
|||
Yii::$app->user->isGuest |
|||
? ['label' => 'Login', 'url' => ['/site/login']] |
|||
: '<li class="nav-item">' |
|||
. Html::beginForm(['/site/logout']) |
|||
. Html::submitButton( |
|||
'Logout (' . Yii::$app->user->identity->username . ')', |
|||
['class' => 'nav-link btn btn-link logout'] |
|||
) |
|||
. Html::endForm() |
|||
. '</li>' |
|||
] |
|||
]); |
|||
NavBar::end(); |
|||
?>
|
|||
</header> |
|||
|
|||
<main id="main" class="flex-shrink-0" role="main"> |
|||
<div class="container"> |
|||
<?php if (!empty($this->params['breadcrumbs'])): ?>
|
|||
<?= Breadcrumbs::widget(['links' => $this->params['breadcrumbs']]) ?>
|
|||
<?php endif ?>
|
|||
<?= Alert::widget() ?>
|
|||
<?= $content ?>
|
|||
</div> |
|||
</main> |
|||
|
|||
<footer id="footer" class="mt-auto py-3 bg-light"> |
|||
<div class="container"> |
|||
<div class="row text-muted"> |
|||
<div class="col-md-6 text-center text-md-start">© My Company <?= date('Y') ?></div>
|
|||
<div class="col-md-6 text-center text-md-end"><?= Yii::powered() ?></div>
|
|||
</div> |
|||
</div> |
|||
</footer> |
|||
|
|||
<?php $this->endBody() ?>
|
|||
</body> |
|||
</html> |
|||
<?php $this->endPage() ?>
|
@ -0,0 +1,18 @@ |
|||
<?php |
|||
|
|||
/** @var yii\web\View $this */ |
|||
|
|||
use yii\helpers\Html; |
|||
|
|||
$this->title = 'About'; |
|||
$this->params['breadcrumbs'][] = $this->title; |
|||
?>
|
|||
<div class="site-about"> |
|||
<h1><?= Html::encode($this->title) ?></h1>
|
|||
|
|||
<p> |
|||
This is the About page. You may modify the following file to customize its content: |
|||
</p> |
|||
|
|||
<code><?= __FILE__ ?></code>
|
|||
</div> |
@ -0,0 +1,68 @@ |
|||
<?php |
|||
|
|||
/** @var yii\web\View $this */ |
|||
/** @var yii\bootstrap5\ActiveForm $form */ |
|||
/** @var app\models\ContactForm $model */ |
|||
|
|||
use yii\bootstrap5\ActiveForm; |
|||
use yii\bootstrap5\Html; |
|||
use yii\captcha\Captcha; |
|||
|
|||
$this->title = 'Contact'; |
|||
$this->params['breadcrumbs'][] = $this->title; |
|||
?>
|
|||
<div class="site-contact"> |
|||
<h1><?= Html::encode($this->title) ?></h1>
|
|||
|
|||
<?php if (Yii::$app->session->hasFlash('contactFormSubmitted')): ?>
|
|||
|
|||
<div class="alert alert-success"> |
|||
Thank you for contacting us. We will respond to you as soon as possible. |
|||
</div> |
|||
|
|||
<p> |
|||
Note that if you turn on the Yii debugger, you should be able |
|||
to view the mail message on the mail panel of the debugger. |
|||
<?php if (Yii::$app->mailer->useFileTransport): ?>
|
|||
Because the application is in development mode, the email is not sent but saved as |
|||
a file under <code><?= Yii::getAlias(Yii::$app->mailer->fileTransportPath) ?></code>.
|
|||
Please configure the <code>useFileTransport</code> property of the <code>mail</code> |
|||
application component to be false to enable email sending. |
|||
<?php endif; ?>
|
|||
</p> |
|||
|
|||
<?php else: ?>
|
|||
|
|||
<p> |
|||
If you have business inquiries or other questions, please fill out the following form to contact us. |
|||
Thank you. |
|||
</p> |
|||
|
|||
<div class="row"> |
|||
<div class="col-lg-5"> |
|||
|
|||
<?php $form = ActiveForm::begin(['id' => 'contact-form']); ?>
|
|||
|
|||
<?= $form->field($model, 'name')->textInput(['autofocus' => true]) ?>
|
|||
|
|||
<?= $form->field($model, 'email') ?>
|
|||
|
|||
<?= $form->field($model, 'subject') ?>
|
|||
|
|||
<?= $form->field($model, 'body')->textarea(['rows' => 6]) ?>
|
|||
|
|||
<?= $form->field($model, 'verifyCode')->widget(Captcha::class, [ |
|||
'template' => '<div class="row"><div class="col-lg-3">{image}</div><div class="col-lg-6">{input}</div></div>', |
|||
]) ?>
|
|||
|
|||
<div class="form-group"> |
|||
<?= Html::submitButton('Submit', ['class' => 'btn btn-primary', 'name' => 'contact-button']) ?>
|
|||
</div> |
|||
|
|||
<?php ActiveForm::end(); ?>
|
|||
|
|||
</div> |
|||
</div> |
|||
|
|||
<?php endif; ?>
|
|||
</div> |
@ -0,0 +1,27 @@ |
|||
<?php |
|||
|
|||
/** @var yii\web\View $this */ |
|||
/** @var string $name */ |
|||
/** @var string $message */ |
|||
/** @var Exception$exception */ |
|||
|
|||
use yii\helpers\Html; |
|||
|
|||
$this->title = $name; |
|||
?>
|
|||
<div class="site-error"> |
|||
|
|||
<h1><?= Html::encode($this->title) ?></h1>
|
|||
|
|||
<div class="alert alert-danger"> |
|||
<?= nl2br(Html::encode($message)) ?>
|
|||
</div> |
|||
|
|||
<p> |
|||
The above error occurred while the Web server was processing your request. |
|||
</p> |
|||
<p> |
|||
Please contact us if you think this is a server error. Thank you. |
|||
</p> |
|||
|
|||
</div> |
@ -0,0 +1,53 @@ |
|||
<?php |
|||
|
|||
/** @var yii\web\View $this */ |
|||
|
|||
$this->title = 'My Yii Application'; |
|||
?>
|
|||
<div class="site-index"> |
|||
|
|||
<div class="jumbotron text-center bg-transparent mt-5 mb-5"> |
|||
<h1 class="display-4">Congratulations!</h1> |
|||
|
|||
<p class="lead">You have successfully created your Yii-powered application.</p> |
|||
|
|||
<p><a class="btn btn-lg btn-success" href="https://www.yiiframework.com">Get started with Yii</a></p> |
|||
</div> |
|||
|
|||
<div class="body-content"> |
|||
|
|||
<div class="row"> |
|||
<div class="col-lg-4 mb-3"> |
|||
<h2>Heading</h2> |
|||
|
|||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et |
|||
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip |
|||
ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu |
|||
fugiat nulla pariatur.</p> |
|||
|
|||
<p><a class="btn btn-outline-secondary" href="https://www.yiiframework.com/doc/">Yii Documentation »</a></p> |
|||
</div> |
|||
<div class="col-lg-4 mb-3"> |
|||
<h2>Heading</h2> |
|||
|
|||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et |
|||
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip |
|||
ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu |
|||
fugiat nulla pariatur.</p> |
|||
|
|||
<p><a class="btn btn-outline-secondary" href="https://www.yiiframework.com/forum/">Yii Forum »</a></p> |
|||
</div> |
|||
<div class="col-lg-4"> |
|||
<h2>Heading</h2> |
|||
|
|||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et |
|||
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip |
|||
ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu |
|||
fugiat nulla pariatur.</p> |
|||
|
|||
<p><a class="btn btn-outline-secondary" href="https://www.yiiframework.com/extensions/">Yii Extensions »</a></p> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
@ -0,0 +1,55 @@ |
|||
<?php |
|||
|
|||
/** @var yii\web\View $this */ |
|||
/** @var yii\bootstrap5\ActiveForm $form */ |
|||
|
|||
/** @var app\models\LoginForm $model */ |
|||
|
|||
use yii\bootstrap5\ActiveForm; |
|||
use yii\bootstrap5\Html; |
|||
|
|||
$this->title = 'Login'; |
|||
$this->params['breadcrumbs'][] = $this->title; |
|||
?>
|
|||
<div class="site-login"> |
|||
<h1><?= Html::encode($this->title) ?></h1>
|
|||
|
|||
<p>Please fill out the following fields to login:</p> |
|||
|
|||
<div class="row"> |
|||
<div class="col-lg-5"> |
|||
|
|||
<?php $form = ActiveForm::begin([ |
|||
'id' => 'login-form', |
|||
'fieldConfig' => [ |
|||
'template' => "{label}\n{input}\n{error}", |
|||
'labelOptions' => ['class' => 'col-lg-1 col-form-label mr-lg-3'], |
|||
'inputOptions' => ['class' => 'col-lg-3 form-control'], |
|||
'errorOptions' => ['class' => 'col-lg-7 invalid-feedback'], |
|||
], |
|||
]); ?>
|
|||
|
|||
<?= $form->field($model, 'username')->textInput(['autofocus' => true]) ?>
|
|||
|
|||
<?= $form->field($model, 'password')->passwordInput() ?>
|
|||
|
|||
<?= $form->field($model, 'rememberMe')->checkbox([ |
|||
'template' => "<div class=\"custom-control custom-checkbox\">{input} {label}</div>\n<div class=\"col-lg-8\">{error}</div>", |
|||
]) ?>
|
|||
|
|||
<div class="form-group"> |
|||
<div> |
|||
<?= Html::submitButton('Login', ['class' => 'btn btn-primary', 'name' => 'login-button']) ?>
|
|||
</div> |
|||
</div> |
|||
|
|||
<?php ActiveForm::end(); ?>
|
|||
|
|||
<div style="color:#999;"> |
|||
You may login with <strong>admin/admin</strong> or <strong>demo/demo</strong>.<br> |
|||
To modify the username/password, please check out the code <code>app\models\User::$users</code>. |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
</div> |
@ -0,0 +1,4 @@ |
|||
RewriteEngine on |
|||
RewriteCond %{REQUEST_FILENAME} !-d |
|||
RewriteCond %{REQUEST_FILENAME} !-f |
|||
RewriteRule . index.php [L] |
@ -0,0 +1,2 @@ |
|||
* |
|||
!.gitignore |
@ -0,0 +1,88 @@ |
|||
main > .container { |
|||
padding: 70px 15px 20px; |
|||
} |
|||
|
|||
.footer { |
|||
background-color: #f5f5f5; |
|||
font-size: .9em; |
|||
height: 60px; |
|||
} |
|||
|
|||
.footer > .container { |
|||
padding-right: 15px; |
|||
padding-left: 15px; |
|||
} |
|||
|
|||
.not-set { |
|||
color: #c55; |
|||
font-style: italic; |
|||
} |
|||
|
|||
/* add sorting icons to gridview sort links */ |
|||
a.asc:after, a.desc:after { |
|||
content: ''; |
|||
left: 3px; |
|||
display: inline-block; |
|||
width: 0; |
|||
height: 0; |
|||
border: solid 5px transparent; |
|||
margin: 4px 4px 2px 4px; |
|||
background: transparent; |
|||
} |
|||
|
|||
a.asc:after { |
|||
border-bottom: solid 7px #212529; |
|||
border-top-width: 0; |
|||
} |
|||
|
|||
a.desc:after { |
|||
border-top: solid 7px #212529; |
|||
border-bottom-width: 0; |
|||
} |
|||
|
|||
.grid-view th { |
|||
white-space: nowrap; |
|||
} |
|||
|
|||
.hint-block { |
|||
display: block; |
|||
margin-top: 5px; |
|||
color: #999; |
|||
} |
|||
|
|||
.error-summary { |
|||
color: #a94442; |
|||
background: #fdf7f7; |
|||
border-left: 3px solid #eed3d7; |
|||
padding: 10px 20px; |
|||
margin: 0 0 15px 0; |
|||
} |
|||
|
|||
/* align the logout "link" (button in form) of the navbar */ |
|||
.nav li > form > button.logout { |
|||
padding-top: 7px; |
|||
color: rgba(255, 255, 255, 0.5); |
|||
} |
|||
|
|||
@media(max-width:767px) { |
|||
.nav li > form > button.logout { |
|||
display:block; |
|||
text-align: left; |
|||
width: 100%; |
|||
padding: 10px 0; |
|||
} |
|||
} |
|||
|
|||
.nav > li > form > button.logout:focus, |
|||
.nav > li > form > button.logout:hover { |
|||
text-decoration: none; |
|||
color: rgba(255, 255, 255, 0.75); |
|||
} |
|||
|
|||
.nav > li > form > button.logout:focus { |
|||
outline: none; |
|||
} |
|||
|
|||
.form-group { |
|||
margin-bottom: 1rem; |
|||
} |
@ -0,0 +1,16 @@ |
|||
<?php |
|||
|
|||
// NOTE: Make sure this file is not accessible when deployed to production
|
|||
if (!in_array(@$_SERVER['REMOTE_ADDR'], ['127.0.0.1', '::1'])) { |
|||
die('You are not allowed to access this file.'); |
|||
} |
|||
|
|||
defined('YII_DEBUG') or define('YII_DEBUG', true); |
|||
defined('YII_ENV') or define('YII_ENV', 'test'); |
|||
|
|||
require __DIR__ . '/../vendor/autoload.php'; |
|||
require __DIR__ . '/../vendor/yiisoft/yii2/Yii.php'; |
|||
|
|||
$config = require __DIR__ . '/../config/test.php'; |
|||
|
|||
(new yii\web\Application($config))->run(); |
@ -0,0 +1,12 @@ |
|||
<?php |
|||
|
|||
// comment out the following two lines when deployed to production
|
|||
defined('YII_DEBUG') or define('YII_DEBUG', true);//定義兩個常量,查看你當前是運行環境還是開發環境
|
|||
defined('YII_ENV') or define('YII_ENV', 'dev'); |
|||
|
|||
require __DIR__ . '/../vendor/autoload.php'; |
|||
require __DIR__ . '/../vendor/yiisoft/yii2/Yii.php'; |
|||
|
|||
$config = require __DIR__ . '/../config/web.php'; |
|||
|
|||
(new yii\web\Application($config))->run(); |
@ -0,0 +1,2 @@ |
|||
User-agent: * |
|||
Disallow: |
@ -0,0 +1,73 @@ |
|||
<?php |
|||
|
|||
namespace app\widgets; |
|||
|
|||
use Yii; |
|||
|
|||
/** |
|||
* Alert widget renders a message from session flash. All flash messages are displayed |
|||
* in the sequence they were assigned using setFlash. You can set message as following: |
|||
* |
|||
* ```php |
|||
* Yii::$app->session->setFlash('error', 'This is the message'); |
|||
* Yii::$app->session->setFlash('success', 'This is the message'); |
|||
* Yii::$app->session->setFlash('info', 'This is the message'); |
|||
* ``` |
|||
* |
|||
* Multiple messages could be set as follows: |
|||
* |
|||
* ```php |
|||
* Yii::$app->session->setFlash('error', ['Error 1', 'Error 2']); |
|||
* ``` |
|||
* |
|||
* @author Kartik Visweswaran <kartikv2@gmail.com> |
|||
* @author Alexander Makarov <sam@rmcreative.ru> |
|||
*/ |
|||
class Alert extends \yii\bootstrap5\Widget |
|||
{ |
|||
/** |
|||
* @var array the alert types configuration for the flash messages. |
|||
* This array is setup as $key => $value, where: |
|||
* - key: the name of the session flash variable |
|||
* - value: the bootstrap alert type (i.e. danger, success, info, warning) |
|||
*/ |
|||
public $alertTypes = [ |
|||
'error' => 'alert-danger', |
|||
'danger' => 'alert-danger', |
|||
'success' => 'alert-success', |
|||
'info' => 'alert-info', |
|||
'warning' => 'alert-warning' |
|||
]; |
|||
/** |
|||
* @var array the options for rendering the close button tag. |
|||
* Array will be passed to [[\yii\bootstrap\Alert::closeButton]]. |
|||
*/ |
|||
public $closeButton = []; |
|||
|
|||
|
|||
/** |
|||
* {@inheritdoc} |
|||
*/ |
|||
public function run() |
|||
{ |
|||
$session = Yii::$app->session; |
|||
$appendClass = isset($this->options['class']) ? ' ' . $this->options['class'] : ''; |
|||
|
|||
foreach (array_keys($this->alertTypes) as $type) { |
|||
$flash = $session->getFlash($type); |
|||
|
|||
foreach ((array) $flash as $i => $message) { |
|||
echo \yii\bootstrap5\Alert::widget([ |
|||
'body' => $message, |
|||
'closeButton' => $this->closeButton, |
|||
'options' => array_merge($this->options, [ |
|||
'id' => $this->getId() . '-' . $type . '-' . $i, |
|||
'class' => $this->alertTypes[$type] . $appendClass, |
|||
]), |
|||
]); |
|||
} |
|||
|
|||
$session->removeFlash($type); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,21 @@ |
|||
#!/usr/bin/env php |
|||
<?php |
|||
/** |
|||
* Yii console bootstrap file. |
|||
* |
|||
* @link https://www.yiiframework.com/ |
|||
* @copyright Copyright (c) 2008 Yii Software LLC |
|||
* @license https://www.yiiframework.com/license/ |
|||
*/ |
|||
|
|||
defined('YII_DEBUG') or define('YII_DEBUG', true); |
|||
defined('YII_ENV') or define('YII_ENV', 'dev'); |
|||
|
|||
require __DIR__ . '/vendor/autoload.php'; |
|||
require __DIR__ . '/vendor/yiisoft/yii2/Yii.php'; |
|||
|
|||
$config = require __DIR__ . '/config/console.php'; |
|||
|
|||
$application = new yii\console\Application($config); |
|||
$exitCode = $application->run(); |
|||
exit($exitCode); |
@ -0,0 +1,20 @@ |
|||
@echo off |
|||
|
|||
rem ------------------------------------------------------------- |
|||
rem Yii command line bootstrap script for Windows. |
|||
rem |
|||
rem @author Qiang Xue <qiang.xue@gmail.com> |
|||
rem @link https://www.yiiframework.com/ |
|||
rem @copyright Copyright (c) 2008 Yii Software LLC |
|||
rem @license https://www.yiiframework.com/license/ |
|||
rem ------------------------------------------------------------- |
|||
|
|||
@setlocal |
|||
|
|||
set YII_PATH=%~dp0 |
|||
|
|||
if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe |
|||
|
|||
"%PHP_COMMAND%" "%YII_PATH%yii" %* |
|||
|
|||
@endlocal |
Write
Preview
Loading…
Cancel
Save
Reference in new issue