diff --git a/mirzaev/skillparts/system/controllers/SiteController.php b/mirzaev/skillparts/system/controllers/SiteController.php new file mode 100644 index 0000000..6e8a85d --- /dev/null +++ b/mirzaev/skillparts/system/controllers/SiteController.php @@ -0,0 +1,128 @@ + [ + 'class' => AccessControl::className(), + 'only' => ['logout'], + 'rules' => [ + [ + 'actions' => ['logout'], + 'allow' => true, + 'roles' => ['@'], + ], + ], + ], + 'verbs' => [ + 'class' => VerbFilter::className(), + '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'); + } +} diff --git a/mirzaev/skillparts/system/mail/layouts/html.php b/mirzaev/skillparts/system/mail/layouts/html.php new file mode 100644 index 0000000..bddbc61 --- /dev/null +++ b/mirzaev/skillparts/system/mail/layouts/html.php @@ -0,0 +1,22 @@ + +beginPage() ?> + + + + + <?= Html::encode($this->title) ?> + head() ?> + + + beginBody() ?> + + endBody() ?> + + +endPage() ?> diff --git a/mirzaev/skillparts/system/models/ContactForm.php b/mirzaev/skillparts/system/models/ContactForm.php new file mode 100644 index 0000000..f001d21 --- /dev/null +++ b/mirzaev/skillparts/system/models/ContactForm.php @@ -0,0 +1,65 @@ + '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; + } +} diff --git a/mirzaev/skillparts/system/models/LoginForm.php b/mirzaev/skillparts/system/models/LoginForm.php new file mode 100644 index 0000000..66d6580 --- /dev/null +++ b/mirzaev/skillparts/system/models/LoginForm.php @@ -0,0 +1,81 @@ +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; + } +} diff --git a/mirzaev/skillparts/system/models/User.php b/mirzaev/skillparts/system/models/User.php new file mode 100644 index 0000000..2e3fb25 --- /dev/null +++ b/mirzaev/skillparts/system/models/User.php @@ -0,0 +1,104 @@ + [ + '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; + } +} diff --git a/mirzaev/skillparts/system/runtime/.gitignore b/mirzaev/skillparts/system/runtime/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/mirzaev/skillparts/system/runtime/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/mirzaev/skillparts/system/tests/_bootstrap.php b/mirzaev/skillparts/system/tests/_bootstrap.php new file mode 100644 index 0000000..131da42 --- /dev/null +++ b/mirzaev/skillparts/system/tests/_bootstrap.php @@ -0,0 +1,6 @@ +amOnPage(Url::toRoute('/site/about')); + $I->see('About', 'h1'); + } +} diff --git a/mirzaev/skillparts/system/tests/acceptance/ContactCest.php b/mirzaev/skillparts/system/tests/acceptance/ContactCest.php new file mode 100644 index 0000000..90f9848 --- /dev/null +++ b/mirzaev/skillparts/system/tests/acceptance/ContactCest.php @@ -0,0 +1,34 @@ +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.'); + } +} diff --git a/mirzaev/skillparts/system/tests/acceptance/HomeCest.php b/mirzaev/skillparts/system/tests/acceptance/HomeCest.php new file mode 100644 index 0000000..e65df16 --- /dev/null +++ b/mirzaev/skillparts/system/tests/acceptance/HomeCest.php @@ -0,0 +1,18 @@ +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.'); + } +} diff --git a/mirzaev/skillparts/system/tests/acceptance/LoginCest.php b/mirzaev/skillparts/system/tests/acceptance/LoginCest.php new file mode 100644 index 0000000..6f5cb2f --- /dev/null +++ b/mirzaev/skillparts/system/tests/acceptance/LoginCest.php @@ -0,0 +1,21 @@ +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'); + } +} diff --git a/mirzaev/skillparts/system/tests/acceptance/_bootstrap.php b/mirzaev/skillparts/system/tests/acceptance/_bootstrap.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/mirzaev/skillparts/system/tests/acceptance/_bootstrap.php @@ -0,0 +1 @@ + [ + 'db' => require __DIR__ . '/../../config/test_db.php' + ] + ] +); + + +$application = new yii\console\Application($config); +$exitCode = $application->run(); +exit($exitCode); diff --git a/mirzaev/skillparts/system/tests/bin/yii.bat b/mirzaev/skillparts/system/tests/bin/yii.bat new file mode 100644 index 0000000..d516b3a --- /dev/null +++ b/mirzaev/skillparts/system/tests/bin/yii.bat @@ -0,0 +1,20 @@ +@echo off + +rem ------------------------------------------------------------- +rem Yii command line bootstrap script for Windows. +rem +rem @author Qiang Xue +rem @link http://www.yiiframework.com/ +rem @copyright Copyright (c) 2008 Yii Software LLC +rem @license http://www.yiiframework.com/license/ +rem ------------------------------------------------------------- + +@setlocal + +set YII_PATH=%~dp0 + +if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe + +"%PHP_COMMAND%" "%YII_PATH%yii" %* + +@endlocal diff --git a/mirzaev/skillparts/system/tests/functional.suite.yml b/mirzaev/skillparts/system/tests/functional.suite.yml new file mode 100644 index 0000000..374c6df --- /dev/null +++ b/mirzaev/skillparts/system/tests/functional.suite.yml @@ -0,0 +1,13 @@ +# 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 +class_name: FunctionalTester +modules: + enabled: + - Filesystem + - Yii2 diff --git a/mirzaev/skillparts/system/tests/functional/ContactFormCest.php b/mirzaev/skillparts/system/tests/functional/ContactFormCest.php new file mode 100644 index 0000000..ad81678 --- /dev/null +++ b/mirzaev/skillparts/system/tests/functional/ContactFormCest.php @@ -0,0 +1,57 @@ +amOnPage(['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.'); + } +} diff --git a/mirzaev/skillparts/system/tests/functional/LoginFormCest.php b/mirzaev/skillparts/system/tests/functional/LoginFormCest.php new file mode 100644 index 0000000..7a83a27 --- /dev/null +++ b/mirzaev/skillparts/system/tests/functional/LoginFormCest.php @@ -0,0 +1,59 @@ +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'); + } +} \ No newline at end of file diff --git a/mirzaev/skillparts/system/tests/functional/_bootstrap.php b/mirzaev/skillparts/system/tests/functional/_bootstrap.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/mirzaev/skillparts/system/tests/functional/_bootstrap.php @@ -0,0 +1 @@ +model = $this->getMockBuilder('app\models\ContactForm') + ->setMethods(['validate']) + ->getMock(); + + $this->model->expects($this->once()) + ->method('validate') + ->willReturn(true); + + $this->model->attributes = [ + 'name' => 'Tester', + 'email' => 'tester@example.com', + 'subject' => 'very important letter subject', + 'body' => 'body of current message', + ]; + + expect_that($this->model->contact('admin@example.com')); + + // using Yii2 module actions to check email was sent + $this->tester->seeEmailIsSent(); + + /** @var MessageInterface $emailMessage */ + $emailMessage = $this->tester->grabLastSentEmail(); + expect('valid email is sent', $emailMessage)->isInstanceOf('yii\mail\MessageInterface'); + expect($emailMessage->getTo())->hasKey('admin@example.com'); + expect($emailMessage->getFrom())->hasKey('noreply@example.com'); + expect($emailMessage->getReplyTo())->hasKey('tester@example.com'); + expect($emailMessage->getSubject())->equals('very important letter subject'); + expect($emailMessage->toString())->stringContainsString('body of current message'); + } +} diff --git a/mirzaev/skillparts/system/tests/unit/models/LoginFormTest.php b/mirzaev/skillparts/system/tests/unit/models/LoginFormTest.php new file mode 100644 index 0000000..ecdb145 --- /dev/null +++ b/mirzaev/skillparts/system/tests/unit/models/LoginFormTest.php @@ -0,0 +1,51 @@ +user->logout(); + } + + public function testLoginNoUser() + { + $this->model = new LoginForm([ + 'username' => 'not_existing_username', + 'password' => 'not_existing_password', + ]); + + expect_not($this->model->login()); + expect_that(\Yii::$app->user->isGuest); + } + + public function testLoginWrongPassword() + { + $this->model = new LoginForm([ + 'username' => 'demo', + 'password' => 'wrong_password', + ]); + + expect_not($this->model->login()); + expect_that(\Yii::$app->user->isGuest); + expect($this->model->errors)->hasKey('password'); + } + + public function testLoginCorrect() + { + $this->model = new LoginForm([ + 'username' => 'demo', + 'password' => 'demo', + ]); + + expect_that($this->model->login()); + expect_not(\Yii::$app->user->isGuest); + expect($this->model->errors)->hasntKey('password'); + } + +} diff --git a/mirzaev/skillparts/system/tests/unit/models/UserTest.php b/mirzaev/skillparts/system/tests/unit/models/UserTest.php new file mode 100644 index 0000000..cb61278 --- /dev/null +++ b/mirzaev/skillparts/system/tests/unit/models/UserTest.php @@ -0,0 +1,44 @@ +username)->equals('admin'); + + expect_not(User::findIdentity(999)); + } + + public function testFindUserByAccessToken() + { + expect_that($user = User::findIdentityByAccessToken('100-token')); + expect($user->username)->equals('admin'); + + expect_not(User::findIdentityByAccessToken('non-existing')); + } + + public function testFindUserByUsername() + { + expect_that($user = User::findByUsername('admin')); + expect_not(User::findByUsername('not-admin')); + } + + /** + * @depends testFindUserByUsername + */ + public function testValidateUser($user) + { + $user = User::findByUsername('admin'); + expect_that($user->validateAuthKey('test100key')); + expect_not($user->validateAuthKey('test102key')); + + expect_that($user->validatePassword('admin')); + expect_not($user->validatePassword('123456')); + } + +} diff --git a/mirzaev/skillparts/system/views/layouts/main.php b/mirzaev/skillparts/system/views/layouts/main.php new file mode 100644 index 0000000..eefcf8f --- /dev/null +++ b/mirzaev/skillparts/system/views/layouts/main.php @@ -0,0 +1,114 @@ + +beginPage() ?> + + + + + + + + registerCsrfMetaTags() ?> + <?= Html::encode($this->title) ?> + head() ?> + + + + beginBody() ?> + +
+ +
+
+
+ Аватар + Место под слоган +
+ +
+ +
+
+
+
+
+
Запчасти для спецтехники
+
+
+
+
+
+ +
+
+ + +
+ +
+ + + + + + + +
+
+
+
+ +
+ +
+ + + + endBody() ?> + + + +endPage() ?> \ No newline at end of file diff --git a/mirzaev/skillparts/system/views/site/about.php b/mirzaev/skillparts/system/views/site/about.php new file mode 100644 index 0000000..68d5cf3 --- /dev/null +++ b/mirzaev/skillparts/system/views/site/about.php @@ -0,0 +1,18 @@ +title = 'About'; +$this->params['breadcrumbs'][] = $this->title; +?> +
+

title) ?>

+ +

+ This is the About page. You may modify the following file to customize its content: +

+ + +
diff --git a/mirzaev/skillparts/system/views/site/contact.php b/mirzaev/skillparts/system/views/site/contact.php new file mode 100644 index 0000000..f4c1e74 --- /dev/null +++ b/mirzaev/skillparts/system/views/site/contact.php @@ -0,0 +1,68 @@ +title = 'Contact'; +$this->params['breadcrumbs'][] = $this->title; +?> +
+

title) ?>

+ + session->hasFlash('contactFormSubmitted')): ?> + +
+ Thank you for contacting us. We will respond to you as soon as possible. +
+ +

+ 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. + mailer->useFileTransport): ?> + Because the application is in development mode, the email is not sent but saved as + a file under mailer->fileTransportPath) ?>. + Please configure the useFileTransport property of the mail + application component to be false to enable email sending. + +

+ + + +

+ If you have business inquiries or other questions, please fill out the following form to contact us. + Thank you. +

+ +
+
+ + 'contact-form']); ?> + + field($model, 'name')->textInput(['autofocus' => true]) ?> + + field($model, 'email') ?> + + field($model, 'subject') ?> + + field($model, 'body')->textarea(['rows' => 6]) ?> + + field($model, 'verifyCode')->widget(Captcha::className(), [ + 'template' => '
{image}
{input}
', + ]) ?> + +
+ 'btn btn-primary', 'name' => 'contact-button']) ?> +
+ + + +
+
+ + +
diff --git a/mirzaev/skillparts/system/views/site/error.php b/mirzaev/skillparts/system/views/site/error.php new file mode 100644 index 0000000..0ba2574 --- /dev/null +++ b/mirzaev/skillparts/system/views/site/error.php @@ -0,0 +1,27 @@ +title = $name; +?> +
+ +

title) ?>

+ +
+ +
+ +

+ The above error occurred while the Web server was processing your request. +

+

+ Please contact us if you think this is a server error. Thank you. +

+ +
diff --git a/mirzaev/skillparts/system/views/site/index.php b/mirzaev/skillparts/system/views/site/index.php new file mode 100644 index 0000000..fe15fde --- /dev/null +++ b/mirzaev/skillparts/system/views/site/index.php @@ -0,0 +1,57 @@ +title = 'SkillParts'; +?> + +
+
+

Если есть трудности с подбором запчастей
оставьте заявку нашему менеджеру

+ +
+ Вызвать менеджера +
+ +
+
+

Сопутствующие товары

+
+
+
+
+
Масла, смазки
+
+
+
+
Масла моторные
+
Масла трансмиссионные
+
Масла гидравлические
+
Смазки
+
+
+
+
+
+
Электрооборудование
+
+
+
+
Фары и свет
+
+
+
+
+
+
Инструмент
+
+
+
+
Шприцы для смазки
+
Ключи, съёмники
+
Наборы инструментов
+
+
+
+
+
\ No newline at end of file diff --git a/mirzaev/skillparts/system/views/site/login.php b/mirzaev/skillparts/system/views/site/login.php new file mode 100644 index 0000000..0944d37 --- /dev/null +++ b/mirzaev/skillparts/system/views/site/login.php @@ -0,0 +1,47 @@ +title = 'Login'; +$this->params['breadcrumbs'][] = $this->title; +?> +