diff --git a/composer.json b/composer.json index 00f8aff3..41a9c3e5 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ "rakibtg/sleekdb": "^2.13", "laminas/laminas-text": "^2.9", "swagger-api/swagger-ui": "^4.14", - "zircote/swagger-php": "^4.4", + "zircote/swagger-php": "~4.4", "psr/simple-cache": "^1.0", "vaibhavpandeyvpz/phemail": "^1.1" diff --git a/src/Console/Commands/ModuleGenerateCommand.php b/src/Console/Commands/ModuleGenerateCommand.php index 4f230166..819f1770 100644 --- a/src/Console/Commands/ModuleGenerateCommand.php +++ b/src/Console/Commands/ModuleGenerateCommand.php @@ -14,9 +14,10 @@ namespace Quantum\Console\Commands; +use Quantum\Libraries\Module\ModuleManager; use Quantum\Libraries\Storage\FileSystem; use Quantum\Console\QtCommand; -use Quantum\Di\Di; +use Exception; /** * Class OpenApiUiAssetsCommand @@ -24,7 +25,6 @@ */ class ModuleGenerateCommand extends QtCommand { - /** * File System * @var FileSystem @@ -62,199 +62,29 @@ class ModuleGenerateCommand extends QtCommand * @var array */ protected $options = [ - ['yes', 'y', 'none', 'Module enabled status'] - ]; - - /** - * Folder names - * @var string[][] - */ - protected $folders = [ - DS, - DS . 'Controllers', - DS . 'Models', - DS . 'Config', - DS . 'Views', - DS . 'Views' . DS . 'layouts', - DS . 'Views' . DS . 'partials', + ['yes', 'y', 'none', 'Module enabled status'], + ['template', 't', 'optional', 'The module template', 'web'], + ['demo', 'd', 'optional', 'Use demo template', 'no'], ]; /** * Executes the command - * @throws \Quantum\Exceptions\FileSystemException - * @throws \Quantum\Exceptions\DiException + * @throws Exception */ public function exec() { - $this->fs = Di::get(FileSystem::class); - $newModuleName = ucfirst($this->getArgument('module')); + try { + $moduleName = $this->getArgument('module'); - $modulesConfigPath = base_dir() . DS . 'shared' . DS . 'config' . DS . 'modules.php'; - $modules = require_once $modulesConfigPath; + $moduleManager = new ModuleManager($moduleName, $this->getOption('template'), $this->getOption('demo'), $this->getOption('yes')); - foreach ($modules['modules'] as $module => $options) { - if ($module == $newModuleName || $options['prefix'] == strtolower($newModuleName)) { - $this->error('A module or prefix named ' . $newModuleName . ' already exists'); - return; - } - } - - $this->fs->put( - $modulesConfigPath, - str_replace( - "'modules' => [", - $this->addModuleConfig($newModuleName), - $this->fs->get($modulesConfigPath) - ) - ); + $moduleManager->addModuleConfig(); - foreach ($this->folders as $folder) { - $this->fs->makeDirectory(modules_dir() . DS . $newModuleName . $folder); - } - - $files = [ - 'Controllers' . DS . 'MainController.php' => $this->controllerTemplate($newModuleName), - 'Views' . DS . 'index.php' => $this->viewTemplate($newModuleName), - 'Views' . DS . 'layouts' . DS . 'main.php' => $this->viewLayoutsTemplate(), - 'Views' . DS . 'partials' . DS . 'bubbles.php' => $this->viewBubblesTemplate(), - 'Config' . DS . 'routes.php' => $this->routesTemplate(), - ]; + $moduleManager->writeContents(); - foreach ($files as $file => $value) { - $this->fs->put(modules_dir() . DS . $newModuleName . DS . $file, $value); + $this->info($moduleName . ' module resources successfully published'); + } catch (Exception $e) { + $this->error($e->getMessage()); } - - $this->info($newModuleName . ' module resources successfully published'); - } - - /** - * Add module to config - * @param string $module - * @return string - */ - private function addModuleConfig(string $module): string - { - $enabled = $this->getOption('yes') ? "true" : "false"; - - return "'modules' => [ - '" . $module . "' => [ - 'prefix' => '" . strtolower($module) . "', - 'enabled' => " . $enabled . ", - ],"; - } - - /** - * Controller template - * @param string $moduleName - * @return string - */ - private function controllerTemplate($moduleName) - { - return 'setLayout(\'layouts' . DS . 'main\'); - $view->setParams([ - \'title\' => config()->get(\'app_name\'), - ]); - $response->html($view->render(\'index\')); - } -};'; - } - - /** - * View template - * @param string $moduleName - * @return string - */ - private function viewTemplate($moduleName) - { - return '
-
-
-
- <?php echo config()->get(\'app_name\') ?> -
-

' . strtoupper($moduleName) . ' HOME PAGE

-
-
-
-'; - } - - /** - * View bubbles template - * @param string $moduleName - * @return string - */ - private function viewBubblesTemplate() - { - return ''; - } - - /** - * viewLayouts template - * @return string - */ - private function viewLayoutsTemplate() - { - return ' - - - - - <?php echo $title ?> - - - - url(\'css/materialize.min.css\') ?>\' type=\'text/css\' media=\'screen,projection\' /> - url(\'css/custom.css\') ?>\' type=\'text/css\' /> - - - -
- - - - - - -'; - } - - /** - * Routes template - * @return string - */ - private function routesTemplate() - { - return 'get(\'/\', \'MainController\', \'index\'); -};'; } } diff --git a/src/Libraries/Module/ModuleManager.php b/src/Libraries/Module/ModuleManager.php new file mode 100644 index 00000000..ef86208e --- /dev/null +++ b/src/Libraries/Module/ModuleManager.php @@ -0,0 +1,107 @@ +moduleName = $moduleName; + + $this->template = $template; + + $this->demo = $demo; + + $this->optionEnabled = $enabled; + + $type = $this->demo == "yes" ? "Demo" : "Default"; + + $this->templatePath = __DIR__ . DS . "Templates" . DS . $type . DS . ucfirst($this->template); + + $this->modulePath = modules_dir() . DS . $this->moduleName; + + $this->fs = Di::get(FileSystem::class); + } + + public function writeContents() + { + if (!$this->fs->isDirectory(modules_dir())) { + $this->fs->makeDirectory(modules_dir()); + } + $this->copyDirectoryWithTemplates($this->templatePath, $this->modulePath); + } + + public function addModuleConfig() + { + $modulesConfigPath = base_dir() . DS . 'shared' . DS . 'config' . DS . 'modules.php'; + $modules = require $modulesConfigPath; + + foreach ($modules['modules'] as $module => $options) { + if ($module == $this->moduleName || $options['prefix'] == strtolower($this->moduleName)) { + throw new \Exception("A module or prefix named '$this->moduleName' already exists"); + } + } + + $this->fs->put( + $modulesConfigPath, + str_replace( + "'modules' => [", + $this->writeModuleConfig($this->moduleName), + $this->fs->get($modulesConfigPath) + ) + ); + } + + private function copyDirectoryWithTemplates($src, $dst) { + if (!$this->fs->isDirectory($src)) { + throw new \Exception("Directory '$src' does not exist"); + } + + if (!$this->fs->isDirectory($dst)) { + $this->fs->makeDirectory($dst); + } + + $dir = $this->fs->listDirectory($src); + + foreach ($dir as $file) { + $srcPath = $file; + $dstPath = str_replace($src, $dst, $file); + + if ($this->fs->isDirectory($srcPath)) { + $this->copyDirectoryWithTemplates($srcPath, $dstPath); + } else { + $processedContent = require_once $srcPath; + $this->fs->put($dstPath, $processedContent); + } + } + } + + /** + * Add module to config + * @param string $module + * @return string + */ + private function writeModuleConfig(string $module): string + { + $enabled = $this->optionEnabled ? "true" : "false"; + + $prefix = $this->template == "web" && $this->demo == "yes" ? "" : strtolower($module); + + return "'modules' => [ + '" . $module . "' => [ + 'prefix' => '" . $prefix . "', + 'enabled' => " . $enabled . ", + ],"; + } +} \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Default/Api/Config/cors.php b/src/Libraries/Module/Templates/Default/Api/Config/cors.php new file mode 100644 index 00000000..fb67dbc5 --- /dev/null +++ b/src/Libraries/Module/Templates/Default/Api/Config/cors.php @@ -0,0 +1,17 @@ + \'*\', + \'Access-Control-Allow-Headers\' => \'Origin, X-Requested-With, Content-Type, Accept, Authorization, refresh_token\', + \'Access-Control-Allow-Methods\' => \'GET, POST, PUT, DELETE, OPTIONS\', + \'Access-Control-Allow-Credentials\' => true, +]; +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Default/Api/Config/routes.php b/src/Libraries/Module/Templates/Default/Api/Config/routes.php new file mode 100644 index 00000000..28b2af32 --- /dev/null +++ b/src/Libraries/Module/Templates/Default/Api/Config/routes.php @@ -0,0 +1,20 @@ +group("openapi", function ($route) { + $route->get("docs", function (Quantum\Http\Response $response) { + $response->html(partial("openApi/openApi")); + }); + + $route->get("spec", function (Quantum\Http\Response $response) { + $fs = Quantum\Di\Di::get(Quantum\Libraries\Storage\FileSystem::class); + $response->json((array) json_decode($fs->get(modules_dir() . "\Api\Resources\openapi\spec.json"))); + }); + }); + $route->get(\'/\', \'MainController\', \'index\'); +};'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Default/Api/Controllers/Abstracts/ApiController.php b/src/Libraries/Module/Templates/Default/Api/Controllers/Abstracts/ApiController.php new file mode 100644 index 00000000..bd926c60 --- /dev/null +++ b/src/Libraries/Module/Templates/Default/Api/Controllers/Abstracts/ApiController.php @@ -0,0 +1,56 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.9.0 + */ + +namespace Modules\Api\Controllers\Abstracts; + +use Quantum\Mvc\QtController; + +/** + * Class ApiController + * @package Modules\Api + * @OA\Info( + * title="Quantum API documentation", + * version="2.9.0", + * description=" *Quantum Documentation: https://quantum.softberg.org/en/docs/v1/overview" + * ), + * @OA\SecurityScheme( + * securityScheme="bearer_token", + * type="apiKey", + * name="Authorization", + * in="header" + * ) + */ +abstract class ApiController extends QtController +{ + + /** + * Status error + */ + const STATUS_ERROR = \'error\'; + + /** + * Status success + */ + const STATUS_SUCCESS = \'success\'; + + /** + * CSRF verification + * @var bool + */ + public $csrfVerification = false; + +} +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Default/Api/Controllers/Abstracts/OpenApiMainController.php b/src/Libraries/Module/Templates/Default/Api/Controllers/Abstracts/OpenApiMainController.php new file mode 100644 index 00000000..5e111291 --- /dev/null +++ b/src/Libraries/Module/Templates/Default/Api/Controllers/Abstracts/OpenApiMainController.php @@ -0,0 +1,64 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.9.0 + */ + +namespace Modules\\' . $this->moduleName . '\Controllers\Abstracts; + +use Quantum\Http\Response; +use Quantum\Http\Request; + +/** + * Class OpenApiPostController + * @package Modules\Api + */ +abstract class OpenApiMainController extends ApiController +{ + + /** + * @OA\Info( + * title="' . $this->moduleName . '", + * version="1.0.0", + * description="This is the ' . $this->moduleName . ' module." + * ) + */ + + /** + * @OA\Tag( + * name="' . $this->moduleName . '", + * description="Operations about the ' . $this->moduleName . '" + * ) + */ + + /** + * @OA\Get( + * path="/' . strtolower($this->moduleName) . '", + * tags={"' . $this->moduleName . '"}, + * summary="Get status of ' . $this->moduleName . '", + * description="Returns status of ' . $this->moduleName . ' module.", + * @OA\Response( + * response=200, + * description="Successful response", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="status", type="string", example="success"), + * @OA\Property(property="message", type="string", example="' . $this->moduleName . ' module.") + * ) + * ) + * ) + */ + abstract public function index(Response $response); + +} +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Default/Api/Controllers/MainController.php b/src/Libraries/Module/Templates/Default/Api/Controllers/MainController.php new file mode 100644 index 00000000..bf06a543 --- /dev/null +++ b/src/Libraries/Module/Templates/Default/Api/Controllers/MainController.php @@ -0,0 +1,22 @@ +moduleName . '\Controllers; + +use Quantum\Factory\ViewFactory; +use Quantum\Mvc\QtController; +use Quantum\Http\Response; + +class MainController extends QtController +{ + private $name = "' . $this->moduleName . '"; + + public function index(Response $response) + { + $response->json([ + \'status\' => \'success\', + \'message\' => $this->name . \' module.\' + ]); + } +};'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Default/Web/Config/routes.php b/src/Libraries/Module/Templates/Default/Web/Config/routes.php new file mode 100644 index 00000000..59cdc7b1 --- /dev/null +++ b/src/Libraries/Module/Templates/Default/Web/Config/routes.php @@ -0,0 +1,10 @@ +get(\'/\', \'MainController\', \'index\'); +};'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Default/Web/Controllers/MainController.php b/src/Libraries/Module/Templates/Default/Web/Controllers/MainController.php new file mode 100644 index 00000000..8e14bab1 --- /dev/null +++ b/src/Libraries/Module/Templates/Default/Web/Controllers/MainController.php @@ -0,0 +1,21 @@ +moduleName . '\Controllers; + +use Quantum\Factory\ViewFactory; +use Quantum\Mvc\QtController; +use Quantum\Http\Response; + +class MainController extends QtController +{ + public function index(Response $response, ViewFactory $view) + { + $view->setLayout(\'layouts' . DS . 'main\'); + $view->setParams([ + \'title\' => config()->get(\'app_name\'), + ]); + $response->html($view->render(\'index\')); + } +};'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Default/Web/Views/index.php b/src/Libraries/Module/Templates/Default/Web/Views/index.php new file mode 100644 index 00000000..f0188676 --- /dev/null +++ b/src/Libraries/Module/Templates/Default/Web/Views/index.php @@ -0,0 +1,13 @@ + +
+
+
+ <?php echo config()->get(\'app_name\') ?> +
+

' . strtoupper($this->moduleName) . ' HOME PAGE

+
+
+ +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Default/Web/Views/layouts/main.php b/src/Libraries/Module/Templates/Default/Web/Views/layouts/main.php new file mode 100644 index 00000000..068891da --- /dev/null +++ b/src/Libraries/Module/Templates/Default/Web/Views/layouts/main.php @@ -0,0 +1,24 @@ + + + + + + <?php echo $title ?> + + + + url(\'css/materialize.min.css\') ?>\' type=\'text/css\' media=\'screen,projection\' /> + url(\'css/custom.css\') ?>\' type=\'text/css\' /> + + + +
+ + + + + + +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Default/Web/Views/partials/bubbles.php b/src/Libraries/Module/Templates/Default/Web/Views/partials/bubbles.php new file mode 100644 index 00000000..efd7c49e --- /dev/null +++ b/src/Libraries/Module/Templates/Default/Web/Views/partials/bubbles.php @@ -0,0 +1,14 @@ + +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Api/Config/auth.php b/src/Libraries/Module/Templates/Demo/Api/Config/auth.php new file mode 100644 index 00000000..2249161d --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Api/Config/auth.php @@ -0,0 +1,25 @@ + \'api\', + \'service\' => Shared\Services\AuthService::class, + \'claims\' => [ + \'jti\' => uniqid(), + \'iss\' => \'issuer\', + \'aud\' => \'audience\', + \'iat\' => time(), + \'nbf\' => time() + 1, + \'exp\' => time() + 3600 // 1 hour + ] +]; +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Api/Config/cors.php b/src/Libraries/Module/Templates/Demo/Api/Config/cors.php new file mode 100644 index 00000000..fb67dbc5 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Api/Config/cors.php @@ -0,0 +1,17 @@ + \'*\', + \'Access-Control-Allow-Headers\' => \'Origin, X-Requested-With, Content-Type, Accept, Authorization, refresh_token\', + \'Access-Control-Allow-Methods\' => \'GET, POST, PUT, DELETE, OPTIONS\', + \'Access-Control-Allow-Credentials\' => true, +]; +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Api/Config/database.php b/src/Libraries/Module/Templates/Demo/Api/Config/database.php new file mode 100644 index 00000000..b812e28a --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Api/Config/database.php @@ -0,0 +1,55 @@ + \'sleekdb\', + + /** + * --------------------------------------------------------- + * Database Connections + * --------------------------------------------------------- + * + * You can define as many database configurations as you want. + * + * driver : mysql, pgsql, sqlite + * host : The database server (localhost) + * dbname : The database name + * username : Username of the database server + * password : Password of the database server + * charset : Default charset + */ + \'mysql\' => [ + \'driver\' => env("DB_DRIVER", "mysql"), + \'host\' => env("DB_HOST", "localhost"), + \'dbname\' => env("DB_NAME"), + \'username\' => env("DB_USERNAME", "root"), + \'password\' => env("DB_PASSWORD"), + \'charset\' => env("DB_CHARSET", \'utf8\'), + \'orm\' => \Quantum\Libraries\Database\Idiorm\IdiormDbal::class + ], + \'sleekdb\' => [ + \'driver\' => \'sleekdb\', + \'config\' => [ + \'auto_cache\' => false, + \'cache_lifetime\' => null, + \'timeout\' => false, + \'search\' => [ + \'min_length\' => 2, + \'mode\' => \'or\', + \'score_key\' => \'scoreKey\', + \'algorithm\' => 1 + ], + ], + \'database_dir\' => base_dir() . DS . \'shared\' . DS . \'store\', + \'orm\' => \Quantum\Libraries\Database\Sleekdb\SleekDbal::class + ], +]; +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Api/Config/routes.php b/src/Libraries/Module/Templates/Demo/Api/Config/routes.php new file mode 100644 index 00000000..59c91515 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Api/Config/routes.php @@ -0,0 +1,38 @@ +group("openapi", function ($route) { + $route->get("docs", function (Quantum\Http\Response $response) { + $response->html(partial("openApi/openApi")); + }); + + $route->get("spec", function (Quantum\Http\Response $response) { + $fs = Quantum\Di\Di::get(Quantum\Libraries\Storage\FileSystem::class); + $response->json((array) json_decode($fs->get(modules_dir() . "\Api\Resources\openapi\spec.json"))); + }); + }); + + $route->get(\'[:alpha:2]?/posts\', \'PostController\', \'getPosts\'); + $route->get(\'[:alpha:2]?/post/[id=:any]\', \'PostController\', \'getPost\')->middlewares([\'Post\']); + + $route->post(\'[:alpha:2]?/signin\', \'AuthController\', \'signin\'); + $route->post(\'[:alpha:2]?/signup\', \'AuthController\', \'signup\')->middlewares([\'Signup\']); + $route->post(\'[:alpha:2]?/forget\', \'AuthController\', \'forget\')->middlewares([\'Forget\']); + $route->get(\'[:alpha:2]?/activate/[token=:any]\', \'AuthController\', \'activate\')->middlewares([\'Activate\']); + $route->post(\'[:alpha:2]?/reset/[token=:any]\', \'AuthController\', \'reset\')->middlewares([\'Reset\']); + $route->get(\'[:alpha:2]?/resend/[code=:any]\', \'AuthController\', \'resend\')->middlewares([\'Resend\']); + $route->post(\'[:alpha:2]?/verify\', \'AuthController\', \'verify\')->middlewares([\'Verify\']); + + $route->group(\'auth\', function ($route) { + $route->get(\'[:alpha:2]?/me\', \'AuthController\', \'me\'); + $route->get(\'[:alpha:2]?/signout\', \'AuthController\', \'signout\')->middlewares([\'Signout\']); + $route->get(\'[:alpha:2]?/my-posts\', \'PostController\', \'myPosts\')->middlewares([\'Editor\']); + $route->post(\'[:alpha:2]?/my-posts/create\', \'PostController\', \'create\')->middlewares([\'Editor\']); + $route->add(\'[:alpha:2]?/my-posts/amend/[id=:any]\', \'PUT\', \'PostController\', \'amend\')->middlewares([\'Editor\', \'Owner\']); + $route->add(\'[:alpha:2]?/my-posts/delete/[id=:any]\', \'DELETE\', \'PostController\', \'delete\')->middlewares([\'Editor\', \'Owner\']); + $route->add(\'[:alpha:2]?/my-posts/delete-image/[id=:any]\', \'DELETE\', \'PostController\', \'deleteImage\')->middlewares([\'Editor\', \'Owner\']); + })->middlewares([\'Auth\']); +}; +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Api/Controllers/Abstracts/ApiController.php b/src/Libraries/Module/Templates/Demo/Api/Controllers/Abstracts/ApiController.php new file mode 100644 index 00000000..bd926c60 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Api/Controllers/Abstracts/ApiController.php @@ -0,0 +1,56 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.9.0 + */ + +namespace Modules\Api\Controllers\Abstracts; + +use Quantum\Mvc\QtController; + +/** + * Class ApiController + * @package Modules\Api + * @OA\Info( + * title="Quantum API documentation", + * version="2.9.0", + * description=" *Quantum Documentation: https://quantum.softberg.org/en/docs/v1/overview" + * ), + * @OA\SecurityScheme( + * securityScheme="bearer_token", + * type="apiKey", + * name="Authorization", + * in="header" + * ) + */ +abstract class ApiController extends QtController +{ + + /** + * Status error + */ + const STATUS_ERROR = \'error\'; + + /** + * Status success + */ + const STATUS_SUCCESS = \'success\'; + + /** + * CSRF verification + * @var bool + */ + public $csrfVerification = false; + +} +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Api/Controllers/Abstracts/OpenApiAuthController.php b/src/Libraries/Module/Templates/Demo/Api/Controllers/Abstracts/OpenApiAuthController.php new file mode 100644 index 00000000..98397fa2 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Api/Controllers/Abstracts/OpenApiAuthController.php @@ -0,0 +1,401 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.9.0 + */ + +namespace Modules\Api\Controllers\Abstracts; + +use Quantum\Http\Response; +use Quantum\Http\Request; + +/** + * Class OpenApiAuthController + * @package Modules\Api + */ +abstract class OpenApiAuthController extends ApiController +{ + + /** + * Sign in action + * @OA\Post( + * path="/api/signin", + * tags={"Authentication"}, + * summary="Sign in action", + * operationId="userSignIn", + * @OA\RequestBody( + * @OA\MediaType( + * mediaType="application/json", + * @OA\Schema( + * @OA\Property( + * property="email", + * type="string" + * ), + * @OA\Property( + * property="password", + * type="string" + * ), + * example={"email": "rgaylord@gmail.com", "password": "password"} + * ) + * ) + * ), + * @OA\Response( + * response=200, + * description="Success", + * @OA\MediaType( + * mediaType="application/json", + * ) + * ), + * @OA\Response( + * response=422, + * description="Unprocessable Entity" + * ), + * @OA\Response( + * response=500, + * description="Internal Server Error" + * ) + * ) + * @param Request $request + * @param Response $response + */ + abstract public function signin(Request $request, Response $response); + + /** + * Gets the logged-in user data + * @OA\Get( + * path="/api/me", + * tags={"User"}, + * summary="Gets the logged-in user data", + * operationId="me", + * security={ + * {"bearer_token": {}} + * }, + * @OA\Response( + * response=200, + * description="Success", + * @OA\MediaType( + * mediaType="application/json", + * ) + * ), + * @OA\Response( + * response=401, + * description="Unauthorized Request" + * ), + * @OA\Response( + * response=500, + * description="Internal Server Error" + * ) + * ) + * @param Response $response + */ + abstract public function me(Response $response); + + /** + * Sign out action + * @OA\Get( + * path="/api/signout", + * tags={"Authentication"}, + * summary="Sign out action", + * operationId="signout", + * @OA\Parameter( + * name="refresh_token", + * description="Refresh token", + * required=true, + * in="header", + * @OA\Schema( + * type="string" + * ) + * ), + * @OA\Response( + * response=200, + * description="Success", + * @OA\MediaType( + * mediaType="application/json", + * ) + * ), + * @OA\Response( + * response=422, + * description="Unprocessable Entity" + * ), + * @OA\Response( + * response=401, + * description="Unauthorized Request" + * ), + * @OA\Response( + * response=500, + * description="Internal Server Error" + * ) + * ) + * @param Response $response + */ + abstract public function signout(Response $response); + + /** + * Sign up action + * @OA\Post( + * path="/api/signup", + * tags={"Authentication"}, + * summary="Sign up action", + * operationId="signUpApi", + * @OA\RequestBody( + * @OA\MediaType( + * mediaType="application/json", + * @OA\Schema( + * @OA\Property( + * property="email", + * type="string", + * ), + * @OA\Property( + * property="password", + * type="string" + * ), + * @OA\Property( + * property="firstname", + * type="string", + * ), + * @OA\Property( + * property="lastname", + * type="string", + * ), + * example={"email": "mail@example.com", "password": "password", "firstname": "Jon", "lastname": "Smit"} + * ) + * ) + * ), + * @OA\Response( + * response=200, + * description="Success", + * @OA\MediaType( + * mediaType="application/json", + * ) + * ), + * @OA\Response( + * response=422, + * description="Unprocessable Entity" + * ), + * @OA\Response( + * response=500, + * description="Internal Server Error" + * ) + * ) + * @param Request $request + * @param Response $response + */ + abstract public function signup(Request $request, Response $response); + + /** + * Activate action + * @OA\Get( + * path="/api/activate/{activate_token}", + * tags={"Authentication"}, + * summary="Activate action", + * operationId="activateProfile", + * @OA\Parameter( + * name="activate_token", + * description="Activate token", + * required=true, + * in="path", + * @OA\Schema( + * type="string" + * ) + * ), + * @OA\Response( + * response=200, + * description="Success", + * @OA\MediaType( + * mediaType="application/json", + * ) + * ), + * @OA\Response( + * response=422, + * description="Unprocessable Entity" + * ), + * @OA\Response( + * response=500, + * description="Internal Server Error" + * ) + * ) + * @param Request $request + * @param Response $response + */ + abstract public function activate(Request $request, Response $response); + + /** + * Forget action + * @OA\Post( + * path="/api/forget", + * tags={"Authentication"}, + * summary="Forget action", + * operationId="forgetPassword", + * @OA\RequestBody( + * @OA\MediaType( + * mediaType="application/json", + * @OA\Schema( + * @OA\Property( + * property="username", + * type="string" + * ), + * example={"email": "mail@example.com"} + * ) + * ) + * ), + * @OA\Response( + * response=200, + * description="Success", + * @OA\MediaType( + * mediaType="application/json", + * ) + * ), + * @OA\Response( + * response=422, + * description="Unprocessable Entity" + * ), + * @OA\Response( + * response=500, + * description="Internal Server Error" + * ) + * ) + * @param Request $request + * @param Response $response + */ + abstract public function forget(Request $request, Response $response); + + /** + * Reset action + * @OA\Post( + * path="/api/reset/{reset_token}", + * tags={"Authentication"}, + * summary="Reset action", + * operationId="resetPassword", + * @OA\Parameter( + * name="reset_token", + * description="Reset token", + * required=true, + * in="path", + * @OA\Schema( + * type="string" + * ) + * ), + * @OA\RequestBody( + * @OA\MediaType( + * mediaType="application/json", + * @OA\Schema( + * @OA\Property( + * property="password", + * type="string" + * ), + * @OA\Property( + * property="repeat_password", + * type="string" + * ), + * example={"password": "password", "repeat_password": "password"} + * ) + * ) + * ), + * @OA\Response( + * response=200, + * description="Success", + * @OA\MediaType( + * mediaType="application/json", + * ) + * ), + * @OA\Response( + * response=422, + * description="Unprocessable Entity" + * ), + * @OA\Response( + * response=500, + * description="Internal Server Error" + * ) + * ) + * @param Request $request + * @param Response $response + */ + abstract public function reset(Request $request, Response $response); + + /** + * Verify action + * @OA\Post( + * path="/api/verify", + * tags={"Authentication"}, + * summary="Verify action", + * operationId="accountVerify", + * @OA\RequestBody( + * @OA\MediaType( + * mediaType="application/json", + * @OA\Schema( + * @OA\Property( + * property="otp_code", + * type="string" + * ), + * example={"otp": "123456", "code": "otp_token"} + * ) + * ) + * ), + * @OA\Response( + * response=200, + * description="Success", + * @OA\MediaType( + * mediaType="application/json", + * ) + * ), + * @OA\Response( + * response=422, + * description="Unprocessable Entity" + * ), + * @OA\Response( + * response=500, + * description="Internal Server Error" + * ) + * ) + * @param Request $request + * @param Response $response + */ + abstract public function verify(Request $request, Response $response); + + /** + * Resend action + * @OA\Get( + * path="/api/resend/{otp_token}", + * tags={"Authentication"}, + * summary="Resend action", + * operationId="resendOTP", + * @OA\Parameter( + * name="otp_token", + * description="OTP token", + * required=true, + * in="path", + * @OA\Schema( + * type="string" + * ) + * ), + * @OA\Response( + * response=200, + * description="Success", + * @OA\MediaType( + * mediaType="application/json", + * ) + * ), + * @OA\Response( + * response=422, + * description="Unprocessable Entity" + * ), + * @OA\Response( + * response=500, + * description="Internal Server Error" + * ) + * ) + * @param Response $response + */ + abstract public function resend(Response $response); +} +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Api/Controllers/Abstracts/OpenApiPostController.php b/src/Libraries/Module/Templates/Demo/Api/Controllers/Abstracts/OpenApiPostController.php new file mode 100644 index 00000000..84ac1081 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Api/Controllers/Abstracts/OpenApiPostController.php @@ -0,0 +1,334 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.9.0 + */ + +namespace Modules\Api\Controllers\Abstracts; + +use Quantum\Http\Response; +use Quantum\Http\Request; + +/** + * Class OpenApiPostController + * @package Modules\Api + */ +abstract class OpenApiPostController extends ApiController +{ + + /** + * Get posts action + * @OA\Get( + * path="/api/posts", + * tags={"Posts"}, + * summary="Get posts action", + * operationId="posts", + * @OA\Response( + * response=200, + * description="Success", + * @OA\MediaType( + * mediaType="application/json", + * ) + * ), + * @OA\Response( + * response=500, + * description="Internal Server Error" + * ) + * ) + * @param Response $response + */ + abstract public function posts(Response $response); + + /** + * Get post action + * @OA\Get( + * path="/api/post/{id}", + * tags={"Posts"}, + * summary="Get post action", + * operationId="post", + * @OA\Parameter( + * name="id", + * description="Post Id", + * required=true, + * in="path", + * @OA\Schema( + * type="string" + * ) + * ), + * @OA\Response( + * response=200, + * description="Success", + * @OA\MediaType( + * mediaType="application/json", + * ) + * ), + * @OA\Response( + * response=404, + * description="Not Found" + * ), + * @OA\Response( + * response=500, + * description="Internal Server Error" + * ) + * ) + * @param string|null $lang + * @param string $postId + * @param Response $response + */ + abstract public function post(?string $lang, string $postId, Response $response); + + /** + * Get my posts action + * @OA\Get( + * path="/api/my-posts", + * tags={"Posts"}, + * summary="Get my posts action", + * operationId="myPosts", + * security={ + * {"bearer_token": {}} + * }, + * @OA\Response( + * response=200, + * description="Success", + * @OA\MediaType( + * mediaType="application/json", + * ) + * ), + * @OA\Response( + * response=401, + * description="Unauthorized Request" + * ), + * @OA\Response( + * response=500, + * description="Internal Server Error" + * ) + * ) + * @param Response $response + */ + abstract public function myPosts(Response $response); + + /** + * Create post action + * @OA\Post( + * path="/api/my-posts/create", + * tags={"Posts"}, + * summary="Create post action", + * operationId="create", + * security={ + * {"bearer_token": {} + * }}, + * @OA\RequestBody( + * @OA\MediaType( + * mediaType="multipart/form-data", + * @OA\Schema( + * type="object", + * required={"title", "content"}, + * @OA\Property( + * property="title", + * type="string", + * ), + * @OA\Property( + * property="content", + * type="string", + * ), + * @OA\Property( + * property="image", + * type="file", + * ) + * ) + * ) + * ), + * @OA\Response( + * response=200, + * description="Success", + * @OA\MediaType( + * mediaType="application/json", + * ) + * ), + * @OA\Response( + * response=401, + * description="Unauthorized Request" + * ), + * @OA\Response( + * response=422, + * description="Unprocessable Entity" + * ), + * @OA\Response( + * response=500, + * description="Internal Server Error" + * ) + * ) + * @param Request $request + * @param Response $response + */ + abstract public function create(Request $request, Response $response); + + /** + * Amend post action + * @OA\Put( + * path="/api/my-posts/amend/{id}", + * tags={"Posts"}, + * summary="Amend post action", + * operationId="amend", + * security={ + * {"bearer_token": {} + * }}, + * @OA\Parameter( + * name="id", + * description="Post id", + * required=true, + * in="path", + * @OA\Schema( + * type="string" + * ) + * ), + * @OA\RequestBody( + * @OA\MediaType( + * mediaType="multipart/form-data", + * @OA\Schema( + * type="object", + * required={"title", "content"}, + * @OA\Property( + * property="title", + * type="string", + * ), + * @OA\Property( + * property="content", + * type="string", + * ), + * @OA\Property( + * property="image", + * type="file", + * ) + * ) + * ) + * ), + * @OA\Response( + * response=200, + * description="Success", + * @OA\MediaType( + * mediaType="application/json", + * ) + * ), + * @OA\Response( + * response=401, + * description="Unauthorized Request" + * ), + * @OA\Response( + * response=422, + * description="Unprocessable Entity" + * ), + * @OA\Response( + * response=500, + * description="Internal Server Error" + * ) + * ) + * @param Request $request + * @param Response $response + * @param string|null $lang + * @param string $postId + */ + abstract public function amend(Request $request, Response $response, ?string $lang, string $postId); + + /** + * Delete post action + * @OA\Delete( + * path="/api/my-posts/delete/{id}", + * tags={"Posts"}, + * summary="Delete post action", + * operationId="delete", + * security={ + * {"bearer_token": {}} + * }, + * @OA\Parameter( + * name="id", + * description="Post id", + * required=true, + * in="path", + * @OA\Schema( + * type="string" + * ) + * ), + * @OA\Response( + * response=200, + * description="Success", + * @OA\MediaType( + * mediaType="application/json", + * ) + * ), + * @OA\Response( + * response=401, + * description="Unauthorized Request" + * ), + * @OA\Response( + * response=422, + * description="Unprocessable Entity" + * ), + * @OA\Response( + * response=500, + * description="Internal Server Error" + * ) + * ) + * @param Response $response + * @param string|null $lang + * @param string $postId + */ + abstract public function delete(Response $response, ?string $lang, string $postId); + + /** + * Delete post image action + * @OA\Delete( + * path="/api/my-posts/delete-image/{id}", + * tags={"Posts"}, + * summary="Delete post image action", + * operationId="deleteImage", + * security={ + * {"bearer_token": {} + * }}, + * @OA\Parameter( + * name="id", + * description="Post id", + * required=true, + * in="path", + * @OA\Schema( + * type="string" + * ) + * ), + * @OA\Response( + * response=200, + * description="Success", + * @OA\MediaType( + * mediaType="application/json", + * ) + * ), + * @OA\Response( + * response=401, + * description="Unauthorized Request" + * ), + * @OA\Response( + * response=422, + * description="Unprocessable Entity" + * ), + * @OA\Response( + * response=500, + * description="Internal Server Error" + * ) + * ) + * @param Response $response + * @param string|null $lang + * @param string $postId + */ + abstract public function deleteImage(Response $response, ?string $lang, string $postId); + +} +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Api/Controllers/AuthController.php b/src/Libraries/Module/Templates/Demo/Api/Controllers/AuthController.php new file mode 100644 index 00000000..4563c346 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Api/Controllers/AuthController.php @@ -0,0 +1,174 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.9.0 + */ + +namespace Modules\Api\Controllers; + +use Modules\Api\Controllers\Abstracts\OpenApiAuthController; +use Quantum\Exceptions\AuthException; +use Quantum\Http\Response; +use Quantum\Http\Request; + +/** + * Class AuthController + * @package Modules\Api + */ +class AuthController extends OpenApiAuthController +{ + + /** + * @inheritDoc + */ + public function signin(Request $request, Response $response) + { + try { + $code = auth()->signin($request->get(\'email\'), $request->get(\'password\')); + + if (filter_var(config()->get(\'2FA\'), FILTER_VALIDATE_BOOLEAN)) { + $response->set(\'code\', $code); + } + + $response->json([ + \'status\' => self::STATUS_SUCCESS + ]); + } catch (AuthException $e) { + $response->json([ + \'status\' => self::STATUS_ERROR, + \'message\' => $e->getMessage() + ], 422); + } + } + + /** + * @inheritDoc + */ + public function me(Response $response) + { + $response->json([ + \'status\' => self::STATUS_SUCCESS, + \'data\' => [ + \'firstname\' => auth()->user()->firstname, + \'lastname\' => auth()->user()->lastname, + \'email\' => auth()->user()->email + ] + ]); + } + + /** + * @inheritDoc + */ + public function signout(Response $response) + { + if (auth()->signout()) { + $response->json([ + \'status\' => self::STATUS_SUCCESS + ]); + } else { + $response->json([ + \'status\' => self::STATUS_ERROR, + \'message\' => t(\'validation.unauthorizedRequest\') + ]); + } + } + + /** + * @inheritDoc + */ + public function signup(Request $request, Response $response) + { + auth()->signup($request->all()); + + $response->json([ + \'status\' => self::STATUS_SUCCESS, + \'message\' => t(\'common.successfully_signed_up\') + ]); + } + + /** + * @inheritDoc + */ + public function activate(Request $request, Response $response) + { + auth()->activate($request->get(\'activation_token\')); + + $response->json([ + \'status\' => self::STATUS_SUCCESS, + \'message\' => t(\'common.account_activated\') + ]); + } + + /** + * @inheritDoc + */ + public function forget(Request $request, Response $response) + { + auth()->forget($request->get(\'email\')); + + $response->json([ + \'status\' => self::STATUS_SUCCESS, + \'message\' => t(\'common.check_email\') + ]); + } + + /** + * @inheritDoc + */ + public function reset(Request $request, Response $response) + { + auth()->reset($request->get(\'reset_token\'), $request->get(\'password\')); + + $response->json([ + \'status\' => self::STATUS_SUCCESS + ]); + } + + /** + * @inheritDoc + */ + public function verify(Request $request, Response $response) + { + try { + auth()->verifyOtp((int)$request->get(\'otp\'), $request->get(\'code\')); + + $response->json([ + \'status\' => self::STATUS_SUCCESS + ]); + } catch (AuthException $e) { + $response->json([ + \'status\' => self::STATUS_ERROR, + \'message\' => $e->getMessage() + ]); + } + } + + /** + * @inheritDoc + */ + public function resend(Response $response) + { + try { + $response->json([ + \'status\' => self::STATUS_SUCCESS, + \'code\' => auth()->resendOtp(route_param(\'code\')) + ]); + } catch (AuthException $e) { + $response->json([ + \'status\' => self::STATUS_ERROR, + \'message\' => $e->getMessage() + ]); + } + } +} +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Api/Controllers/PostController.php b/src/Libraries/Module/Templates/Demo/Api/Controllers/PostController.php new file mode 100644 index 00000000..e82bb5de --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Api/Controllers/PostController.php @@ -0,0 +1,188 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.9.0 + */ + +namespace Modules\Api\Controllers; + +use Modules\Api\Controllers\Abstracts\OpenApiPostController; +use Quantum\Factory\ServiceFactory; +use Shared\Services\PostService; +use Quantum\Http\Response; +use Quantum\Http\Request; + +/** + * Class PostController + * @package Modules\Api + */ +class PostController extends OpenApiPostController +{ + + /** + * Post service + * @var PostService + */ + public $postService; + + /** + * Works before an action + */ + public function __before() + { + $this->postService = ServiceFactory::get(PostService::class); + } + + /** + * @inheritDoc + */ + public function posts(Response $response) + { + $response->json([ + \'status\' => \'success\', + \'data\' => $this->postService->getPosts() + ]); + } + + /** + * @inheritDoc + */ + public function post(?string $lang, string $postId, Response $response) + { + $response->json([ + \'status\' => \'success\', + \'data\' => $this->postService->getPost($postId) + ]); + } + + /** + * @inheritDoc + */ + public function myPosts(Response $response) + { + $response->json([ + \'status\' => \'success\', + \'data\' => $this->postService->getMyPosts((int)auth()->user()->id) + ]); + } + + /** + * @inheritDoc + */ + public function create(Request $request, Response $response) + { + $postData = [ + \'user_id\' => (int)auth()->user()->id, + \'title\' => $request->get(\'title\', null, true), + \'content\' => $request->get(\'content\', null, true), + \'image\' => \'\', + \'updated_at\' => date(\'Y-m-d H:i:s\'), + ]; + + if ($request->hasFile(\'image\')) { + $imageName = $this->postService->saveImage( + $request->getFile(\'image\'), + auth()->user()->uuid, + slugify($request->get(\'title\')) + ); + + $postData[\'image\'] = $imageName; + } + + $this->postService->addPost($postData); + + $response->json([ + \'status\' => \'success\', + \'message\' => t(\'common.created_successfully\') + ]); + } + + /** + * @inheritDoc + */ + public function amend(Request $request, Response $response, ?string $lang, string $postId) + { + $postData = [ + \'title\' => $request->get(\'title\', null, true), + \'content\' => $request->get(\'content\', null, true), + \'updated_at\' => date(\'Y-m-d H:i:s\'), + ]; + + $post = $this->postService->getPost($postId, false); + + if ($request->hasFile(\'image\')) { + if ($post[\'image\']) { + $this->postService->deleteImage(auth()->user()->uuid . DS . $post[\'image\']); + } + + $imageName = $this->postService->saveImage( + $request->getFile(\'image\'), + auth()->user()->uuid, + slugify($request->get(\'title\')) + ); + + $postData[\'image\'] = $imageName; + } + + $this->postService->updatePost($postId, $postData); + + $response->json([ + \'status\' => \'success\', + \'message\' => t(\'common.updated_successfully\') + ]); + } + + /** + * @inheritDoc + */ + public function delete(Response $response, ?string $lang, string $postId) + { + $post = $this->postService->getPost($postId, false); + + if ($post[\'image\']) { + $this->postService->deleteImage(auth()->user()->uuid . DS . $post[\'image\']); + } + + $this->postService->deletePost($postId); + + $response->json([ + \'status\' => \'success\', + \'message\' => t(\'common.deleted_successfully\') + ]); + } + + /** + * @inheritDoc + */ + public function deleteImage(Response $response, ?string $lang, string $postId) + { + $post = $this->postService->getPost($postId, false); + + if ($post[\'image\']) { + $this->postService->deleteImage(auth()->user()->uuid . DS . $post[\'image\']); + } + + $this->postService->updatePost($postId, [ + \'title\' => $post[\'title\'], + \'content\' => $post[\'content\'], + \'image\' => \'\', + \'updated_at\' => date(\'Y-m-d H:i:s\'), + ]); + + $response->json([ + \'status\' => \'success\', + \'message\' => t(\'common.deleted_successfully\') + ]); + } +} +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Api/Middlewares/Activate.php b/src/Libraries/Module/Templates/Demo/Api/Middlewares/Activate.php new file mode 100644 index 00000000..1f2f206c --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Api/Middlewares/Activate.php @@ -0,0 +1,69 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.9.0 + */ + +namespace Modules\Api\Middlewares; + +use Quantum\Middleware\QtMiddleware; +use Quantum\Factory\ModelFactory; +use Quantum\Http\Response; +use Quantum\Http\Request; +use Shared\Models\User; +use Closure; + +/** + * Class Activate + * @package Modules\Api + */ +class Activate extends QtMiddleware +{ + + /** + * @param Request $request + * @param Response $response + * @param Closure $next + * @return mixed + */ + public function apply(Request $request, Response $response, Closure $next) + { + $token = route_param(\'token\'); + + if (!$token || !$this->checkToken($token)) { + $response->json([ + \'status\' => \'error\', + \'message\' => [t(\'validation.nonExistingRecord\', \'token\')] + ], 422); + + stop(); + } + + $request->set(\'activation_token\', $token); + + return $next($request, $response); + } + + /** + * Check token + * @param string $token + * @return bool + */ + private function checkToken(string $token): bool + { + $userModel = ModelFactory::get(User::class); + return !empty($userModel->findOneBy(\'activation_token\', $token)->asArray()); + } + +} +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Api/Middlewares/Auth.php b/src/Libraries/Module/Templates/Demo/Api/Middlewares/Auth.php new file mode 100644 index 00000000..950fa350 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Api/Middlewares/Auth.php @@ -0,0 +1,52 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.9.0 + */ + +namespace Modules\Api\Middlewares; + +use Quantum\Middleware\QtMiddleware; +use Quantum\Http\Response; +use Quantum\Http\Request; +use Closure; + +/** + * Class Auth + * @package Modules\Api + */ +class Auth extends QtMiddleware +{ + + /** + * @param Request $request + * @param Response $response + * @param Closure $next + * @return mixed + */ + public function apply(Request $request, Response $response, Closure $next) + { + if (!auth()->check()) { + $response->json([ + \'status\' => \'error\', + \'message\' => t(\'validation.unauthorizedRequest\') + ], 401); + + stop(); + } + + return $next($request, $response); + } + +} +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Api/Middlewares/Editor.php b/src/Libraries/Module/Templates/Demo/Api/Middlewares/Editor.php new file mode 100644 index 00000000..d887f214 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Api/Middlewares/Editor.php @@ -0,0 +1,106 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.9.0 + */ + +namespace Modules\Api\Middlewares; + +use Quantum\Libraries\Validation\Validator; +use Quantum\Libraries\Validation\Rule; +use Quantum\Middleware\QtMiddleware; +use Quantum\Http\Response; +use Quantum\Http\Request; +use Closure; + +/** + * Class Editor + * @package Modules\Api + */ +class Editor extends QtMiddleware +{ + + /** + * Roles + */ + const ROLES = [\'admin\', \'editor\']; + + /** + * @var Validator + */ + private $validator; + + /** + * Class constructor + * @param Request $request + */ + public function __construct(Request $request) + { + $this->validator = new Validator(); + + if ($request->hasFile(\'image\')) { + $this->validator->addRules([ + \'image\' => [ + Rule::set(\'fileSize\', 2 * pow(1024, 2)), + Rule::set(\'fileExtension\', [\'jpeg\', \'jpg\', \'png\']), + ] + ]); + } + + $this->validator->addRules([ + \'title\' => [ + Rule::set(\'required\'), + Rule::set(\'minLen\', 10), + Rule::set(\'maxLen\', 50), + ], + \'content\' => [ + Rule::set(\'required\'), + Rule::set(\'minLen\', 10), + Rule::set(\'maxLen\', 1000), + ] + ]); + } + + /** + * @param Request $request + * @param Response $response + * @param Closure $next + * @return mixed + */ + public function apply(Request $request, Response $response, Closure $next) + { + if (!in_array(auth()->user()->role, self::ROLES)) { + $response->json([ + \'status\' => \'error\', + \'message\' => t(\'validation.unauthorizedRequest\') + ], 401); + + stop(); + } + + if ($request->isMethod(\'post\') || $request->isMethod(\'put\')) { + if (!$this->validator->isValid($request->all())) { + $response->json([ + \'status\' => \'error\', + \'message\' => $this->validator->getErrors() + ], 422); + + stop(); + } + } + + return $next($request, $response); + } + +} +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Api/Middlewares/Forget.php b/src/Libraries/Module/Templates/Demo/Api/Middlewares/Forget.php new file mode 100644 index 00000000..033b118a --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Api/Middlewares/Forget.php @@ -0,0 +1,96 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.9.0 + */ + +namespace Modules\Api\Middlewares; + +use Quantum\Libraries\Validation\Validator; +use Quantum\Libraries\Validation\Rule; +use Quantum\Middleware\QtMiddleware; +use Quantum\Factory\ModelFactory; +use Quantum\Http\Response; +use Quantum\Http\Request; +use Shared\Models\User; +use Closure; + +/** + * Class Forget + * @package Modules\Api + */ +class Forget extends QtMiddleware +{ + + /** + * @var Validator + */ + private $validator; + + /** + * Class constructor + */ + public function __construct() + { + $this->validator = new Validator(); + + $this->validator->addRule(\'email\', [ + Rule::set(\'required\'), + Rule::set(\'email\') + ]); + } + + /** + * @param Request $request + * @param Response $response + * @param Closure $next + * @return mixed + */ + public function apply(Request $request, Response $response, Closure $next) + { + if ($request->isMethod(\'post\')) { + if (!$this->validator->isValid($request->all())) { + $response->json([ + \'status\' => \'error\', + \'message\' => $this->validator->getErrors() + ], 422); + + stop(); + } + + if (!$this->emailExists($request->get(\'email\'))) { + $response->json([ + \'status\' => \'error\', + \'message\' => [t(\'validation.nonExistingRecord\', $request->get(\'email\'))] + ], 422); + + stop(); + } + } + + return $next($request, $response); + } + + /** + * Check for email existence + * @param string $email + * @return bool + */ + private function emailExists(string $email): bool + { + $userModel = ModelFactory::get(User::class); + return !empty($userModel->findOneBy(\'email\', $email)->asArray()); + } + +} +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Api/Middlewares/Owner.php b/src/Libraries/Module/Templates/Demo/Api/Middlewares/Owner.php new file mode 100644 index 00000000..7a3aa912 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Api/Middlewares/Owner.php @@ -0,0 +1,58 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.9.0 + */ + +namespace Modules\Api\Middlewares; + +use Quantum\Middleware\QtMiddleware; +use Quantum\Factory\ServiceFactory; +use Shared\Services\PostService; +use Quantum\Http\Response; +use Quantum\Http\Request; +use Closure; + +/** + * Class Owner + * @package Modules\Api + */ +class Owner extends QtMiddleware +{ + + /** + * @param Request $request + * @param Response $response + * @param Closure $next + * @return mixed + */ + public function apply(Request $request, Response $response, Closure $next) + { + $postId = (string)route_param(\'id\'); + + $post = ServiceFactory::get(PostService::class)->getPost($postId, false); + + if (!$post || $post[\'user_id\'] != auth()->user()->id) { + $response->json([ + \'status\' => \'error\', + \'message\' => t(\'common.post_not_found\') + ], 404); + + stop(); + } + + return $next($request, $response); + } + +} +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Api/Middlewares/Post.php b/src/Libraries/Module/Templates/Demo/Api/Middlewares/Post.php new file mode 100644 index 00000000..85c87bf9 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Api/Middlewares/Post.php @@ -0,0 +1,58 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.9.0 + */ + +namespace Modules\Api\Middlewares; + +use Quantum\Middleware\QtMiddleware; +use Quantum\Factory\ServiceFactory; +use Shared\Services\PostService; +use Quantum\Http\Response; +use Quantum\Http\Request; +use Closure; + +/** + * Class Post + * @package Modules\Api + */ +class Post extends QtMiddleware +{ + + /** + * @param Request $request + * @param Response $response + * @param Closure $next + * @return mixed + */ + public function apply(Request $request, Response $response, Closure $next) + { + $postId = (string)route_param(\'id\'); + + $post = ServiceFactory::get(PostService::class)->getPost($postId, false); + + if (!$post) { + $response->json([ + \'status\' => \'error\', + \'message\' => t(\'common.post_not_found\') + ], 404); + + stop(); + } + + return $next($request, $response); + } + +} +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Api/Middlewares/Resend.php b/src/Libraries/Module/Templates/Demo/Api/Middlewares/Resend.php new file mode 100644 index 00000000..f5f0bbb3 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Api/Middlewares/Resend.php @@ -0,0 +1,52 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.9.0 + */ + +namespace Modules\Api\Middlewares; + +use Quantum\Middleware\QtMiddleware; +use Quantum\Http\Response; +use Quantum\Http\Request; +use Closure; + +/** + * Class Resend + * @package Modules\Api + */ +class Resend extends QtMiddleware +{ + + /** + * @param Request $request + * @param Response $response + * @param Closure $next + * @return mixed + */ + public function apply(Request $request, Response $response, Closure $next) + { + if (!route_param(\'code\')) { + $response->json([ + \'status\' => \'error\', + \'message\' => t(\'validation.required\', \'code\') + ], 422); + + stop(); + } + + return $next($request, $response); + } + +} +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Api/Middlewares/Reset.php b/src/Libraries/Module/Templates/Demo/Api/Middlewares/Reset.php new file mode 100644 index 00000000..58c6d1cd --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Api/Middlewares/Reset.php @@ -0,0 +1,124 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.9.0 + */ + +namespace Modules\Api\Middlewares; + +use Quantum\Libraries\Validation\Validator; +use Quantum\Libraries\Validation\Rule; +use Quantum\Middleware\QtMiddleware; +use Quantum\Factory\ModelFactory; +use Quantum\Http\Response; +use Quantum\Http\Request; +use Shared\Models\User; +use Closure; + +/** + * Class Reset + * @package Modules\Api + */ +class Reset extends QtMiddleware +{ + + /** + * @var Validator + */ + private $validator; + + /** + * Class constructor + */ + public function __construct() + { + $this->validator = new Validator(); + + $this->validator->addRules([ + \'password\' => [ + Rule::set(\'required\'), + Rule::set(\'minLen\', 6) + ], + \'repeat_password\' => [ + Rule::set(\'required\'), + Rule::set(\'minLen\', 6) + ] + ]); + } + + /** + * @param Request $request + * @param Response $response + * @param Closure $next + * @return mixed + */ + public function apply(Request $request, Response $response, Closure $next) + { + $token = route_param(\'token\'); + + if (!$token || !$this->checkToken($token)) { + $response->json([ + \'status\' => \'error\', + \'message\' => [t(\'validation.nonExistingRecord\', \'token\')] + ], 422); + + stop(); + } + + if (!$this->validator->isValid($request->all())) { + $response->json([ + \'status\' => \'error\', + \'message\' => $this->validator->getErrors() + ], 422); + + stop(); + } + + if (!$this->confirmPassword($request->get(\'password\'), $request->get(\'repeat_password\'))) { + $response->json([ + \'status\' => \'error\', + \'message\' => t(\'validation.nonEqualValues\') + ], 422); + + stop(); + } + + $request->set(\'reset_token\', $token); + + return $next($request, $response); + } + + /** + * Check token + * @param string $token + * @return bool + */ + private function checkToken(string $token): bool + { + $userModel = ModelFactory::get(User::class); + return !empty($userModel->findOneBy(\'reset_token\', $token)->asArray()); + } + + /** + * Checks the password and repeat password + * @param string $newPassword + * @param string $repeatPassword + * @return bool + */ + private function confirmPassword(string $newPassword, string $repeatPassword): bool + { + return $newPassword == $repeatPassword; + } + +} +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Api/Middlewares/Signout.php b/src/Libraries/Module/Templates/Demo/Api/Middlewares/Signout.php new file mode 100644 index 00000000..7bca3de6 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Api/Middlewares/Signout.php @@ -0,0 +1,51 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.9.0 + */ +namespace Modules\Api\Middlewares; + +use Quantum\Middleware\QtMiddleware; +use Quantum\Http\Response; +use Quantum\Http\Request; +use Closure; + +/** + * Class Signout + * @package Modules\Api + */ +class Signout extends QtMiddleware +{ + + /** + * @param Request $request + * @param Response $response + * @param Closure $next + * @return mixed + */ + public function apply(Request $request, Response $response, Closure $next) + { + if (!Request::hasHeader(\'refresh_token\')) { + $response->json([ + \'status\' => \'error\', + \'message\' => [t(\'validation.nonExistingRecord\', \'token\')] + ], 422); + + stop(); + } + + return $next($request, $response); + } + +} +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Api/Middlewares/Signup.php b/src/Libraries/Module/Templates/Demo/Api/Middlewares/Signup.php new file mode 100644 index 00000000..0167b61f --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Api/Middlewares/Signup.php @@ -0,0 +1,92 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.9.0 + */ + +namespace Modules\Api\Middlewares; + +use Quantum\Libraries\Validation\Validator; +use Quantum\Libraries\Validation\Rule; +use Quantum\Middleware\QtMiddleware; +use Quantum\Factory\ModelFactory; +use Quantum\Http\Response; +use Quantum\Http\Request; +use Shared\Models\User; +use Closure; + +/** + * Class Signup + * @package Modules\Api + */ +class Signup extends QtMiddleware +{ + + /** + * @var Validator + */ + private $validator; + + /** + * Class constructor + */ + public function __construct() + { + $this->validator = new Validator(); + + $this->validator->addValidation(\'uniqueUser\', function ($value) { + $userModel = ModelFactory::get(User::class); + return empty($userModel->findOneBy(\'email\', $value)->asArray()); + }); + + $this->validator->addRules([ + \'email\' => [ + Rule::set(\'required\'), + Rule::set(\'email\'), + Rule::set(\'uniqueUser\') + ], + \'password\' => [ + Rule::set(\'required\'), + Rule::set(\'minLen\', 6) + ], + \'firstname\' => [ + Rule::set(\'required\') + ], + \'lastname\' => [ + Rule::set(\'required\') + ], + ]); + } + + /** + * @param Request $request + * @param Response $response + * @param Closure $next + * @return mixed + */ + public function apply(Request $request, Response $response, Closure $next) + { + if (!$this->validator->isValid($request->all())) { + $response->json([ + \'status\' => \'error\', + \'message\' => $this->validator->getErrors() + ], 422); + + stop(); + } + + return $next($request, $response); + } + +} +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Api/Middlewares/Verify.php b/src/Libraries/Module/Templates/Demo/Api/Middlewares/Verify.php new file mode 100644 index 00000000..2468b967 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Api/Middlewares/Verify.php @@ -0,0 +1,78 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.9.0 + */ + +namespace Modules\Api\Middlewares; + +use Quantum\Libraries\Validation\Validator; +use Quantum\Libraries\Validation\Rule; +use Quantum\Middleware\QtMiddleware; +use Quantum\Http\Response; +use Quantum\Http\Request; +use Closure; + +/** + * Class Verify + * @package Modules\Api + */ + +class Verify extends QtMiddleware +{ + + /** + * @var Validator + */ + private $validator; + + /** + * Class constructor + */ + public function __construct() + { + $this->validator = new Validator(); + + $this->validator->addRules([ + \'otp\' => [ + Rule::set(\'required\') + ], + \'code\' => [ + Rule::set(\'required\') + ], + ]); + } + + /** + * @param Request $request + * @param Response $response + * @param Closure $next + * @return mixed + */ + public function apply(Request $request, Response $response, Closure $next) + { + if ($request->isMethod(\'post\')) { + if (!$this->validator->isValid($request->all())) { + + $response->json([ + \'status\' => \'error\', + \'message\' => $this->validator->getErrors() + ], 422); + + stop(); + } + } + + return $next($request, $response); + } + +}'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Config/auth.php b/src/Libraries/Module/Templates/Demo/Web/Config/auth.php new file mode 100644 index 00000000..70d8239c --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Config/auth.php @@ -0,0 +1,18 @@ + 'web', + 'service' => Shared\Services\AuthService::class +]; + +"; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Config/database.php b/src/Libraries/Module/Templates/Demo/Web/Config/database.php new file mode 100644 index 00000000..1bb318a5 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Config/database.php @@ -0,0 +1,54 @@ + 'sleekdb', + + /** + * --------------------------------------------------------- + * Database Connections + * --------------------------------------------------------- + * + * You can define as many database configurations as you want. + * + * driver : mysql, pgsql, sqlite + * host : The database server (localhost) + * dbname : The database name + * username : Username of the database server + * password : Password of the database server + * charset : Default charset + */ + 'mysql' => array( + 'driver' => env(\"DB_DRIVER\", \"mysql\"), + 'host' => env(\"DB_HOST\", \"localhost\"), + 'dbname' => env(\"DB_NAME\"), + 'username' => env(\"DB_USERNAME\", \"root\"), + 'password' => env(\"DB_PASSWORD\"), + 'charset' => env(\"DB_CHARSET\", 'utf8'), + 'orm' => \Quantum\Libraries\Database\Idiorm\IdiormDbal::class + ), + 'sleekdb' => [ + 'driver' => 'sleekdb', + 'config' => [ + 'auto_cache' => false, + 'cache_lifetime' => null, + 'timeout' => false, + 'search' => [ + 'min_length' => 2, + 'mode' => 'or', + 'score_key' => 'scoreKey', + 'algorithm' => 1 + ], + ], + 'database_dir' => base_dir() . DS . 'shared' . DS . 'store', + 'orm' => \Quantum\Libraries\Database\Sleekdb\SleekDbal::class + ], +];"; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Config/routes.php b/src/Libraries/Module/Templates/Demo/Web/Config/routes.php new file mode 100644 index 00000000..aa66b8cc --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Config/routes.php @@ -0,0 +1,62 @@ +get(\'[:alpha:2]?\', function (Response $response, ViewFactory $view) { + $view->setLayout(\'layouts/main\'); + + $view->setParams([ + \'title\' => config()->get(\'app_name\'), + \'langs\' => config()->get(\'langs\') + ]); + + $response->html($view->render(\'index\')); + })->name(\'home\'); + + $route->get(\'[:alpha:2]?/about\', function (Response $response, ViewFactory $view) { + $view->setLayout(\'layouts/main\'); + + $view->setParams([ + \'title\' => t(\'common.about\') . \' | \' . config()->get(\'app_name\'), + \'langs\' => config()->get(\'langs\') + ]); + + $response->html($view->render(\'about\')); + })->name(\'about\'); + + $route->get(\'/auth\', \'DropboxController\', \'auth\'); + $route->get(\'/confirm\', \'DropboxController\', \'confirm\'); + $route->get(\'/test\', \'DropboxController\', \'test\'); + $route->get(\'/image/[:any]\', \'DropboxController\', \'image\'); + $route->get(\'/upload\', \'DropboxController\', \'upload\'); + $route->get(\'/list\', \'DropboxController\', \'list\'); + + $route->get(\'[:alpha:2]?/posts\', \'PostController\', \'posts\'); + $route->get(\'[:alpha:2]?/post/[id=:any]\', \'PostController\', \'post\')->middlewares([\'Post\']); + + $route->group(\'guest\', function ($route) { + $route->add(\'[:alpha:2]?/signin\', \'GET|POST\', \'AuthController\', \'signin\')->name(\'signin\'); + $route->add(\'[:alpha:2]?/signup\', \'GET|POST\', \'AuthController\', \'signup\')->middlewares([\'Signup\'])->name(\'signup\'); + $route->get(\'[:alpha:2]?/activate/[token=:any]\', \'AuthController\', \'activate\')->middlewares([\'Activate\']); + $route->add(\'[:alpha:2]?/forget\', \'GET|POST\', \'AuthController\', \'forget\')->middlewares([\'Forget\']); + $route->add(\'[:alpha:2]?/reset/[token=:any]\', \'GET|POST\', \'AuthController\', \'reset\')->middlewares([\'Reset\']); + $route->get(\'[:alpha:2]?/resend/[code=:any]\', \'AuthController\', \'resend\')->middlewares([\'Resend\']); + $route->add(\'[:alpha:2]?/verify/[code=:any]?\', \'GET|POST\', \'AuthController\', \'verify\')->middlewares([\'Verify\']); + })->middlewares([\'Guest\']); + + $route->group(\'auth\', function ($route) { + $route->get(\'[:alpha:2]?/signout\', \'AuthController\', \'signout\'); + $route->get(\'[:alpha:2]?/my-posts\', \'PostController\', \'myPosts\')->middlewares([\'Editor\']); + $route->get(\'[:alpha:2]?/my-posts/create\', \'PostController\', \'createFrom\')->middlewares([\'Editor\']); + $route->post(\'[:alpha:2]?/my-posts/create\', \'PostController\', \'create\')->middlewares([\'Editor\']); + $route->get(\'[:alpha:2]?/my-posts/amend/[id=:any]\', \'PostController\', \'amendForm\')->middlewares([\'Editor\', \'Owner\']); + $route->post(\'[:alpha:2]?/my-posts/amend/[id=:any]\', \'PostController\', \'amend\')->middlewares([\'Editor\', \'Owner\']); + $route->get(\'[:alpha:2]?/my-posts/delete/[id=:any]\', \'PostController\', \'delete\')->middlewares([\'Editor\', \'Owner\']); + $route->get(\'[:alpha:2]?/my-posts/delete-image/[id=:any]\', \'PostController\', \'deleteImage\')->middlewares([\'Editor\', \'Owner\']); + })->middlewares([\'Auth\']); +}; +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Controllers/AuthController.php b/src/Libraries/Module/Templates/Demo/Web/Controllers/AuthController.php new file mode 100644 index 00000000..e3ab4c1d --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Controllers/AuthController.php @@ -0,0 +1,227 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.9.0 + */ + +namespace Modules\Web\Controllers; + +use Quantum\Exceptions\AuthException; +use Quantum\Factory\ViewFactory; +use Quantum\Mvc\QtController; +use Quantum\Http\Response; +use Quantum\Http\Request; + +/** + * Class AuthController + * @package Modules\Web\Controllers + */ +class AuthController extends QtController +{ + + /** + * Auth layout + */ + const LAYOUT = \'layouts/main\'; + + /** + * Signin view + */ + const VIEW_SIGNIN = \'auth/signin\'; + + /** + * Signup view + */ + const VIEW_SIGNUP = \'auth/signup\'; + + /** + * Forget view + */ + const VIEW_FORGET = \'auth/forget\'; + + /** + * Reset view + */ + const VIEW_RESET = \'auth/reset\'; + + /** + * Reset view + */ + const VIEW_VERIFY = \'auth/verify\'; + + /** + * Magic __before + * @param ViewFactory $view + */ + public function __before(ViewFactory $view) + { + $view->setLayout(self::LAYOUT); + } + + /** + * Sign in action + * @param Request $request + * @param Response $response + * @param ViewFactory $view + */ + public function signin(Request $request, Response $response, ViewFactory $view) + { + if ($request->isMethod(\'post\')) { + try { + $code = auth()->signin($request->get(\'email\'), $request->get(\'password\'), !!$request->get(\'remember\')); + + if (filter_var(config()->get(\'2FA\'), FILTER_VALIDATE_BOOLEAN)) { + redirect(base_url(true) . \'/\' . current_lang() . \'/verify/\' . $code); + } else { + redirect(base_url(true) . \'/\' . current_lang()); + } + } catch (AuthException $e) { + session()->setFlash(\'error\', $e->getMessage()); + redirect(base_url(true) . \'/\' . current_lang() . \'/signin\'); + } + } else { + $view->setParams([ + \'title\' => t(\'common.signin\') . \' | \' . config()->get(\'app_name\'), + \'langs\' => config()->get(\'langs\') + ]); + + $response->html($view->render(self::VIEW_SIGNIN)); + } + } + + /** + * Sign out action + */ + public function signout() + { + auth()->signout(); + redirect(base_url(true) . \'/\' . current_lang()); + } + + /** + * Sign up action + * @param Request $request + * @param Response $response + * @param ViewFactory $view + */ + public function signup(Request $request, Response $response, ViewFactory $view) + { + if ($request->isMethod(\'post\')) { + auth()->signup($request->all()); + session()->setFlash(\'success\', t(\'common.check_email_signup\')); + redirect(base_url(true) . \'/\' . current_lang() . \'/signup\'); + } else { + $view->setParams([ + \'title\' => t(\'common.signup\') . \' | \' . config()->get(\'app_name\'), + \'langs\' => config()->get(\'langs\') + ]); + + $response->html($view->render(self::VIEW_SIGNUP)); + } + } + + /** + * Activate action + * @param Request $request + */ + public function activate(Request $request) + { + auth()->activate($request->get(\'activation_token\')); + redirect(base_url(true) . \'/\' . current_lang() . \'/signin\'); + } + + /** + * Forget action + * @param Request $request + * @param Response $response + * @param ViewFactory $view + */ + public function forget(Request $request, Response $response, ViewFactory $view) + { + if ($request->isMethod(\'post\')) { + auth()->forget($request->get(\'email\')); + session()->setFlash(\'success\', t(\'common.check_email\')); + redirect(base_url(true) . \'/\' . current_lang() . \'/forget\'); + } else { + $view->setParams([ + \'title\' => t(\'common.forget_password\') . \' | \' . config()->get(\'app_name\'), + \'langs\' => config()->get(\'langs\'), + ]); + + $response->html($view->render(self::VIEW_FORGET)); + } + } + + /** + * Reset action + * @param Request $request + * @param Response $response + * @param ViewFactory $view + */ + public function reset(Request $request, Response $response, ViewFactory $view) + { + if ($request->isMethod(\'post\')) { + auth()->reset($request->get(\'reset_token\'), $request->get(\'password\')); + redirect(base_url(true) . \'/\' . current_lang() . \'/signin\'); + } else { + $view->setParams([ + \'title\' => t(\'common.reset_password\') . \' | \' . config()->get(\'app_name\'), + \'langs\' => config()->get(\'langs\'), + \'reset_token\' => $request->get(\'reset_token\') + ]); + + $response->html($view->render(self::VIEW_RESET)); + } + } + + /** + * Verify OTP action + * @param Request $request + * @param Response $response + * @param ViewFactory $view + */ + public function verify(Request $request, Response $response, ViewFactory $view) + { + if ($request->isMethod(\'post\')) { + try { + auth()->verifyOtp((int)$request->get(\'otp\'), $request->get(\'code\')); + redirect(base_url(true) . \'/\' . current_lang()); + } catch (AuthException $e) { + session()->setFlash(\'error\', $e->getMessage()); + redirect(base_url(true) . \'/\' . current_lang() . \'/verify/\' . $request->get(\'code\')); + } + } else { + $view->setParams([ + \'title\' => t(\'common.2fa\') . \' | \' . config()->get(\'app_name\'), + \'langs\' => config()->get(\'langs\'), + \'code\' => route_param(\'code\') + ]); + + $response->html($view->render(self::VIEW_VERIFY)); + } + } + + /** + * Resend OTP action + */ + public function resend() + { + try { + $otpToken = auth()->resendOtp(route_param(\'code\')); + redirect(base_url(true) . \'/\' . current_lang() . \'/verify/\' . $otpToken); + } catch (AuthException $e) { + redirect(base_url(true) . \'/\' . current_lang() . \'/signin\'); + } + } +} +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Controllers/PostController.php b/src/Libraries/Module/Templates/Demo/Web/Controllers/PostController.php new file mode 100644 index 00000000..85b823c5 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Controllers/PostController.php @@ -0,0 +1,245 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.8.0 + */ + +namespace Modules\Web\Controllers; + +use Quantum\Factory\ServiceFactory; +use Quantum\Factory\ViewFactory; +use Shared\Services\AuthService; +use Shared\Services\PostService; +use Quantum\Mvc\QtController; +use Quantum\Http\Response; +use Quantum\Http\Request; + +/** + * Class PostController + * @package Modules\Web\Controllers + */ +class PostController extends QtController +{ + + /** + * Post service + * @var PostService + */ + public $postService; + + /** + * Post service + * @var AuthService + */ + public $userService; + + /** + * Works before an action + * @param ViewFactory $view + */ + public function __before(ViewFactory $view) + { + $this->postService = ServiceFactory::get(PostService::class); + $this->userService = ServiceFactory::get(AuthService::class); + + $view->setLayout(\'layouts/main\'); + } + + /** + * Get posts action + * @param Response $response + * @param ViewFactory $view + */ + public function posts(Response $response, ViewFactory $view) + { + $view->setParams([ + \'title\' => t(\'common.posts\') . \' | \' . config()->get(\'app_name\'), + \'langs\' => config()->get(\'langs\'), + \'posts\' => $this->postService->getPosts() + ]); + + $response->html($view->render(\'post/post\')); + } + + /** + * Get post action + * @param string|null $lang + * @param string $postId + * @param Response $response + * @param ViewFactory $view + */ + public function post(?string $lang, string $postId, Response $response, ViewFactory $view) + { + $post = $this->postService->getPost($postId); + + $view->setParams([ + \'title\' => $post[\'title\'] . \' | \' . config()->get(\'app_name\'), + \'langs\' => config()->get(\'langs\'), + \'post\' => $post + ]); + + $response->html($view->render(\'post/single\')); + } + + /** + * Get my posts action + * @param Request $request + * @param Response $response + * @param ViewFactory $view + */ + public function myPosts(Request $request, Response $response, ViewFactory $view) + { + $view->setParams([ + \'title\' => t(\'common.my_posts\') . \' | \' . config()->get(\'app_name\'), + \'langs\' => config()->get(\'langs\'), + \'posts\' => $this->postService->getMyPosts((int)auth()->user()->id) + ]); + + $response->html($view->render(\'post/my-posts\')); + } + + /** + * Create post form + * @param Response $response + * @param ViewFactory $view + */ + public function createFrom(Response $response, ViewFactory $view) + { + $view->setParams([ + \'title\' => t(\'common.new_post\') . \' | \' . config()->get(\'app_name\'), + \'langs\' => config()->get(\'langs\') + ]); + + $response->html($view->render(\'post/form\')); + } + + /** + * Create post action + * @param Request $request + */ + public function create(Request $request) + { + $postData = [ + \'user_id\' => (int)auth()->user()->id, + \'title\' => $request->get(\'title\', null, true), + \'content\' => $request->get(\'content\', null, true), + \'image\' => \'\', + \'updated_at\' => date(\'Y-m-d H:i:s\'), + ]; + + if ($request->hasFile(\'image\')) { + $imageName = $this->postService->saveImage( + $request->getFile(\'image\'), + auth()->user()->uuid, + slugify($request->get(\'title\')) + ); + + $postData[\'image\'] = $imageName; + } + + $this->postService->addPost($postData); + + redirect(base_url(true) . \'/\' . current_lang() . \'/my-posts\'); + } + + public function amendForm(Response $response, ViewFactory $view, ?string $lang, string $postId) + { + $post = $this->postService->getPost($postId); + + $view->setParams([ + \'title\' => $post[\'title\'] . \' | \' . config()->get(\'app_name\'), + \'langs\' => config()->get(\'langs\'), + \'post\' => $post + ]); + + $response->html($view->render(\'post/form\')); + } + + /** + * Amend post action + * @param Request $request + * @param string|null $lang + * @param string $postId + */ + public function amend(Request $request, ?string $lang, string $postId) + { + $postData = [ + \'title\' => $request->get(\'title\', null, true), + \'content\' => $request->get(\'content\', null, true), + \'updated_at\' => date(\'Y-m-d H:i:s\'), + ]; + + $post = $this->postService->getPost($postId, false); + + if ($request->hasFile(\'image\')) { + if ($post[\'image\']) { + $this->postService->deleteImage(auth()->user()->uuid . DS . $post[\'image\']); + } + + $imageName = $this->postService->saveImage( + $request->getFile(\'image\'), + auth()->user()->uuid, + slugify($request->get(\'title\')) + ); + + $postData[\'image\'] = $imageName; + } + + $this->postService->updatePost($postId, $postData); + + redirect(base_url(true) . \'/\' . current_lang() . \'/my-posts\'); + } + + /** + * Delete post action + * @param string|null $lang + * @param string $postId + */ + public function delete(?string $lang, string $postId) + { + $post = $this->postService->getPost($postId, false); + + if ($post[\'image\']) { + $this->postService->deleteImage(auth()->user()->uuid . DS . $post[\'image\']); + } + + $this->postService->deletePost($postId); + + redirect(base_url(true) . \'/\' . current_lang() . \'/my-posts\'); + } + + /** + * Delete post image action + * @param string|null $lang + * @param string $postId + */ + public function deleteImage(?string $lang, string $postId) + { + $post = $this->postService->getPost($postId, false); + + if ($post[\'image\']) { + $this->postService->deleteImage(auth()->user()->uuid . DS . $post[\'image\']); + } + + $this->postService->updatePost($postId, [ + \'title\' => $post[\'title\'], + \'content\' => $post[\'content\'], + \'image\' => \'\', + \'updated_at\' => date(\'Y-m-d H:i:s\'), + ]); + + redirect(base_url(true) . \'/\' . current_lang() . \'/my-posts\'); + } + +} +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Middlewares/Activate.php b/src/Libraries/Module/Templates/Demo/Web/Middlewares/Activate.php new file mode 100644 index 00000000..66b58ad4 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Middlewares/Activate.php @@ -0,0 +1,65 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.8.0 + */ + +namespace Modules\Web\Middlewares; + +use Quantum\Middleware\QtMiddleware; +use Quantum\Factory\ModelFactory; +use Quantum\Http\Response; +use Quantum\Http\Request; +use Shared\Models\User; +use Closure; + +/** + * Class Activate + * @package Modules\Web\Middlewares + */ +class Activate extends QtMiddleware +{ + + /** + * @param Request $request + * @param Response $response + * @param Closure $next + * @return mixed + */ + public function apply(Request $request, Response $response, Closure $next) + { + $token = (string) route_param(\'token\'); + + if (!$this->checkToken($token)) { + stop(function () use ($response) { + $response->html(partial(\'errors/404\'), 404); + }); + } + + $request->set(\'activation_token\', $token); + + return $next($request, $response); + } + + /** + * Check token + * @param string $token + * @return bool + */ + private function checkToken(string $token): bool + { + $userModel = ModelFactory::get(User::class); + return !empty($userModel->findOneBy(\'activation_token\', $token)->asArray()); + } + +}'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Middlewares/Auth.php b/src/Libraries/Module/Templates/Demo/Web/Middlewares/Auth.php new file mode 100644 index 00000000..87639389 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Middlewares/Auth.php @@ -0,0 +1,46 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.8.0 + */ + +namespace Modules\Web\Middlewares; + +use Quantum\Middleware\QtMiddleware; +use Quantum\Http\Response; +use Quantum\Http\Request; +use Closure; + +/** + * Class Auth + * @package Modules\Web\Middlewares + */ +class Auth extends QtMiddleware +{ + + /** + * @param Request $request + * @param Response $response + * @param Closure $next + * @return mixed + */ + public function apply(Request $request, Response $response, Closure $next) + { + if (!auth()->check()) { + redirect(base_url(true) . \'/\' . current_lang() . \'/signin\'); + } + + return $next($request, $response); + } + +}'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Middlewares/Editor.php b/src/Libraries/Module/Templates/Demo/Web/Middlewares/Editor.php new file mode 100644 index 00000000..fe5d4800 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Middlewares/Editor.php @@ -0,0 +1,98 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.8.0 + */ + +namespace Modules\Web\Middlewares; + +use Quantum\Libraries\Validation\Validator; +use Quantum\Libraries\Validation\Rule; +use Quantum\Middleware\QtMiddleware; +use Quantum\Http\Response; +use Quantum\Http\Request; +use Closure; + +/** + * Class Editor + * @package Modules\Web\Middlewares + */ +class Editor extends QtMiddleware +{ + + /** + * Roles + */ + const ROLES = [\'admin\', \'editor\']; + + /** + * @var Validator + */ + private $validator; + + /** + * Class constructor + * @param Request $request + */ + public function __construct(Request $request) + { + $this->validator = new Validator(); + + if ($request->hasFile(\'image\')) { + $this->validator->addRules([ + \'image\' => [ + Rule::set(\'fileSize\', 2 * pow(1024, 2)), + Rule::set(\'fileExtension\', [\'jpeg\', \'jpg\', \'png\']), + ] + ]); + } + + $this->validator->addRules([ + \'title\' => [ + Rule::set(\'required\'), + Rule::set(\'minLen\', 10), + Rule::set(\'maxLen\', 50) + ], + \'content\' => [ + Rule::set(\'required\'), + Rule::set(\'minLen\', 10), + Rule::set(\'maxLen\', 1000), + ], + ]); + } + + /** + * @param Request $request + * @param Response $response + * @param Closure $next + * @return mixed + */ + public function apply(Request $request, Response $response, Closure $next) + { + if (!in_array(auth()->user()->role, self::ROLES)) { + redirect(base_url(true) . \'/\' . current_lang()); + } + + if ($request->isMethod(\'post\')) { + if (!$this->validator->isValid($request->all())) { + $data = $request->all(); + unset($data[\'image\']); + session()->setFlash(\'error\', $this->validator->getErrors()); + redirectWith(get_referrer(), $data); + } + } + + return $next($request, $response); + } + +}'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Middlewares/Forget.php b/src/Libraries/Module/Templates/Demo/Web/Middlewares/Forget.php new file mode 100644 index 00000000..0b9ac8f6 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Middlewares/Forget.php @@ -0,0 +1,94 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.8.0 + */ + +namespace Modules\Web\Middlewares; + +use Quantum\Libraries\Validation\Validator; +use Quantum\Libraries\Validation\Rule; +use Quantum\Middleware\QtMiddleware; +use Quantum\Factory\ModelFactory; +use Quantum\Http\Response; +use Quantum\Http\Request; +use Shared\Models\User; +use Exception; +use Closure; + +/** + * Class Forget + * @package Modules\Web\Middlewares + */ +class Forget extends QtMiddleware +{ + + /** + * @var Validator + */ + private $validator; + + /** + * Class constructor + */ + public function __construct() + { + $this->validator = new Validator(); + + $this->validator->addRule(\'email\', [ + Rule::set(\'required\'), + Rule::set(\'email\') + ]); + } + + /** + * @param Request $request + * @param Response $response + * @param Closure $next + * @return mixed + */ + public function apply(Request $request, Response $response, Closure $next) + { + if ($request->isMethod(\'post\')) { + if (!$this->validator->isValid($request->all())) { + session()->setFlash(\'error\', $this->validator->getErrors()); + redirect(base_url(true) . \'/\' . current_lang() . \'/forget\'); + } + + if (!$this->emailExists($request->get(\'email\'))) { + session()->setFlash(\'error\', [ + \'email\' => [ + t(\'validation.nonExistingRecord\', $request->get(\'email\')) + ] + ]); + + redirect(base_url(true) . \'/\' . current_lang() . \'/forget\'); + } + } + + return $next($request, $response); + } + + /** + * Check for email existence + * @param string $email + * @return bool + * @throws Exception + */ + private function emailExists(string $email): bool + { + $userModel = ModelFactory::get(User::class); + return !empty($userModel->findOneBy(\'email\', $email)->asArray()); + } + +}'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Middlewares/Guest.php b/src/Libraries/Module/Templates/Demo/Web/Middlewares/Guest.php new file mode 100644 index 00000000..b05c26d2 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Middlewares/Guest.php @@ -0,0 +1,46 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.8.0 + */ + +namespace Modules\Web\Middlewares; + +use Quantum\Middleware\QtMiddleware; +use Quantum\Http\Response; +use Quantum\Http\Request; +use Closure; + +/** + * Class Guest + * @package Modules\Web\Middlewares + */ +class Guest extends QtMiddleware +{ + + /** + * @param Request $request + * @param Response $response + * @param Closure $next + * @return mixed + */ + public function apply(Request $request, Response $response, Closure $next) + { + if (auth()->check()) { + redirect(get_referrer() ?? base_url(true) . \'/\' . current_lang()); + } + + return $next($request, $response); + } + +}'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Middlewares/Owner.php b/src/Libraries/Module/Templates/Demo/Web/Middlewares/Owner.php new file mode 100644 index 00000000..1b5b85bd --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Middlewares/Owner.php @@ -0,0 +1,53 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.8.0 + */ + +namespace Modules\Web\Middlewares; + +use Quantum\Middleware\QtMiddleware; +use Quantum\Factory\ServiceFactory; +use Shared\Services\PostService; +use Quantum\Http\Response; +use Quantum\Http\Request; +use Closure; + +/** + * Class Editor + * @package Modules\Web\Middlewares + */ +class Owner extends QtMiddleware +{ + + /** + * @param Request $request + * @param Response $response + * @param Closure $next + * @return mixed + */ + public function apply(Request $request, Response $response, Closure $next) + { + $postId = (string) route_param(\'id\'); + + $post = ServiceFactory::get(PostService::class)->getPost($postId, false); + + if (!$post || $post[\'user_id\'] != auth()->user()->id) { + $response->html(partial(\'errors/404\'), 404); + stop(); + } + + return $next($request, $response); + } + +}'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Middlewares/Post.php b/src/Libraries/Module/Templates/Demo/Web/Middlewares/Post.php new file mode 100644 index 00000000..c037c0d9 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Middlewares/Post.php @@ -0,0 +1,53 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.8.0 + */ + +namespace Modules\Web\Middlewares; + +use Quantum\Middleware\QtMiddleware; +use Quantum\Factory\ServiceFactory; +use Shared\Services\PostService; +use Quantum\Http\Response; +use Quantum\Http\Request; +use Closure; + +/** + * Class Editor + * @package Modules\Web\Middlewares + */ +class Post extends QtMiddleware +{ + + /** + * @param Request $request + * @param Response $response + * @param Closure $next + * @return mixed + */ + public function apply(Request $request, Response $response, Closure $next) + { + $postId = (string) route_param(\'id\'); + + $post = ServiceFactory::get(PostService::class)->getPost($postId, false); + + if (!$post) { + $response->html(partial(\'errors/404\'), 404); + stop(); + } + + return $next($request, $response); + } + +}'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Middlewares/Resend.php b/src/Libraries/Module/Templates/Demo/Web/Middlewares/Resend.php new file mode 100644 index 00000000..a7713144 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Middlewares/Resend.php @@ -0,0 +1,46 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.8.0 + */ + +namespace Modules\Web\Middlewares; + +use Quantum\Middleware\QtMiddleware; +use Quantum\Http\Response; +use Quantum\Http\Request; +use Closure; + +/** + * Class Resend + * @package Modules\Web\Middlewares + */ +class Resend extends QtMiddleware +{ + + /** + * @param Request $request + * @param Response $response + * @param Closure $next + * @return mixed + */ + public function apply(Request $request, Response $response, Closure $next) + { + if (!route_param(\'code\')) { + redirect(base_url(true) . \'/\' . current_lang() . \'/signin\'); + } + + return $next($request, $response); + } + +}'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Middlewares/Reset.php b/src/Libraries/Module/Templates/Demo/Web/Middlewares/Reset.php new file mode 100644 index 00000000..1399b236 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Middlewares/Reset.php @@ -0,0 +1,117 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.8.0 + */ + +namespace Modules\Web\Middlewares; + +use Quantum\Libraries\Validation\Validator; +use Quantum\Libraries\Validation\Rule; +use Quantum\Middleware\QtMiddleware; +use Quantum\Factory\ModelFactory; +use Quantum\Http\Response; +use Quantum\Http\Request; +use Shared\Models\User; +use Closure; + +/** + * Class Reset + * @package Modules\Web\Middlewares + */ +class Reset extends QtMiddleware +{ + + /** + * @var Validator + */ + private $validator; + + /** + * Class constructor + */ + public function __construct() + { + $this->validator = new Validator(); + + $this->validator->addRule(\'password\', [ + Rule::set(\'required\'), + Rule::set(\'minLen\', 6) + ]); + } + + /** + * @param Request $request + * @param Response $response + * @param Closure $next + * @return mixed + */ + public function apply(Request $request, Response $response, Closure $next) + { + $token = route_param(\'token\'); + + if ($token && $request->isMethod(\'post\')) { + if (!$this->checkToken($token)) { + session()->setFlash(\'error\', [ + \'password\' => [ + t(\'validation.nonExistingRecord\', \'token\') + ] + ]); + + redirect(get_referrer()); + } + + if (!$this->validator->isValid($request->all())) { + session()->setFlash(\'error\', $this->validator->getErrors()); + redirect(get_referrer()); + } + + if (!$this->confirmPassword($request->get(\'password\'), $request->get(\'repeat_password\'))) { + session()->setFlash(\'error\', t(\'validation.nonEqualValues\')); + redirect(get_referrer()); + } + } elseif ($request->isMethod(\'get\')) { + if (!$this->checkToken($token)) { + $response->html(partial(\'errors/404\'), 404); + stop(); + } + } + + $request->set(\'reset_token\', $token); + + return $next($request, $response); + } + + /** + * Check token + * @param string $token + * @return bool + */ + private function checkToken(string $token): bool + { + $userModel = ModelFactory::get(User::class); + return !empty($userModel->findOneBy(\'reset_token\', $token)->asArray()); + } + + /** + * Checks the password and repeat password + * @param string $newPassword + * @param string $repeatPassword + * @return bool + */ + private function confirmPassword(string $newPassword, string $repeatPassword): bool + { + return $newPassword == $repeatPassword; + } + +}'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Middlewares/Signup.php b/src/Libraries/Module/Templates/Demo/Web/Middlewares/Signup.php new file mode 100644 index 00000000..19f90d73 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Middlewares/Signup.php @@ -0,0 +1,101 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.8.0 + */ + +namespace Modules\Web\Middlewares; + +use Quantum\Libraries\Validation\Validator; +use Quantum\Libraries\Validation\Rule; +use Quantum\Middleware\QtMiddleware; +use Quantum\Factory\ModelFactory; +use Quantum\Http\Response; +use Quantum\Http\Request; +use Shared\Models\User; +use Closure; + +/** + * Class Signup + * @package Modules\Web\Middlewares + */ +class Signup extends QtMiddleware +{ + + /** + * @var Validator + */ + private $validator; + + /** + * Class constructor + * @throws \Exception + */ + public function __construct() + { + $this->validator = new Validator(); + + $this->validator->addValidation(\'uniqueUser\', function ($value) { + $userModel = ModelFactory::get(User::class); + return empty($userModel->findOneBy(\'email\', $value)->asArray()); + }); + + $this->validator->addRules([ + \'email\' => [ + Rule::set(\'required\'), + Rule::set(\'email\'), + Rule::set(\'uniqueUser\') + ], + \'password\' => [ + Rule::set(\'required\'), + Rule::set(\'minLen\', 6) + ], + \'firstname\' => [ + Rule::set(\'required\') + ], + \'lastname\' => [ + Rule::set(\'required\') + ], + \'recaptcha\' => [ + Rule::set(\'required\'), + Rule::set(\'recaptcha\') + ] + ]); + } + + /** + * @param Request $request + * @param Response $response + * @param Closure $next + * @return mixed + */ + public function apply(Request $request, Response $response, Closure $next) + { + if ($request->isMethod(\'post\')) { + + if($request->has(\'g-recaptcha-response\')) { + $request->set(\'recaptcha\', $request->get(\'g-recaptcha-response\')); + $request->delete(\'g-recaptcha-response\'); + } + + if (!$this->validator->isValid($request->all())) { + session()->setFlash(\'error\', $this->validator->getErrors()); + redirectWith(base_url(true) . \'/\' . current_lang() . \'/signup\', $request->all()); + } + } + + return $next($request, $response); + } + +} +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Middlewares/Verify.php b/src/Libraries/Module/Templates/Demo/Web/Middlewares/Verify.php new file mode 100644 index 00000000..07d1ff97 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Middlewares/Verify.php @@ -0,0 +1,87 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 2.8.0 + */ + +namespace Modules\Web\Middlewares; + +use Quantum\Libraries\Validation\Validator; +use Quantum\Libraries\Validation\Rule; +use Quantum\Middleware\QtMiddleware; +use Quantum\Factory\ModelFactory; +use Quantum\Http\Response; +use Quantum\Http\Request; +use Shared\Models\User; +use Closure; + +/** + * Class Verify + * @package Modules\Web\Middlewares + */ +class Verify extends QtMiddleware +{ + /** + * @var Validator + */ + private $validator; + + /** + * Class constructor + */ + public function __construct() + { + $this->validator = new Validator(); + + $this->validator->addRules([ + \'otp\' => [ + Rule::set(\'required\') + ], + \'code\' => [ + Rule::set(\'required\') + ] + ]); + } + + /** + * @param Request $request + * @param Response $response + * @param Closure $next + * @return mixed + */ + public function apply(Request $request, Response $response, Closure $next) + { + if ($request->isMethod(\'post\')) { + if (!$this->validator->isValid($request->all())) { + session()->setFlash(\'error\', $this->validator->getErrors()); + redirectWith(base_url(true) . \'/\' . current_lang() . \'/verify\', $request->all()); + } + } else { + $token = (string)route_param(\'code\'); + + if (!$this->checkToken($token)) { + stop(function () use ($response) { + $response->html(partial(\'errors/404\'), 404); + }); + } + + } + + return $next($request, $response); + } + + private function checkToken(string $token): bool + { + $userModel = ModelFactory::get(User::class); + return !empty($userModel->findOneBy(\'otp_token\', $token)->asArray()); + } +}'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/about.php b/src/Libraries/Module/Templates/Demo/Web/Views/about.php new file mode 100644 index 00000000..5a1f4cc0 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/about.php @@ -0,0 +1,31 @@ + +
    +

    + +
    +
    +
    + +

    +
    + + +

    +
      +
    • +
      + > composer create-project quantum/project [project name] +
    • +
    • +
      + > php qt serve +
    • +
    +
    +
    +
    + + +"; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/auth/forget.php b/src/Libraries/Module/Templates/Demo/Web/Views/auth/forget.php new file mode 100644 index 00000000..1124a1c6 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/auth/forget.php @@ -0,0 +1,42 @@ + +
    +
    +
    +

    + + has(\'error\')): ?> + + + + has(\'success\')): ?> + + + +
    +
    +
    +
    + + +
    +
    +
    + +
    +
    +
    + + +
    +
    +
    +
    +
    +
    +
    +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/auth/reset.php b/src/Libraries/Module/Templates/Demo/Web/Views/auth/reset.php new file mode 100644 index 00000000..6924d540 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/auth/reset.php @@ -0,0 +1,46 @@ + +
    +
    +
    +

    + + has(\'error\')): ?> + + + + has(\'success\')): ?> + + + +
    +
    +
    +
    +
    + + + visibility + visibility_off +
    +
    + + + visibility + visibility_off +
    +
    + + +
    +
    +
    +
    +
    +
    +
    +
    +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/auth/signin.php b/src/Libraries/Module/Templates/Demo/Web/Views/auth/signin.php new file mode 100644 index 00000000..e271375d --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/auth/signin.php @@ -0,0 +1,66 @@ + +
    +
    +
    +

    + + has(\'error\')): ?> + + + +
    +
    + +
    +
    +
    +
    +
    + +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/auth/signup.php b/src/Libraries/Module/Templates/Demo/Web/Views/auth/signup.php new file mode 100644 index 00000000..c42524bc --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/auth/signup.php @@ -0,0 +1,64 @@ + +
    +
    +
    +

    + + has(\'error\')) : ?> + + + + has(\'success\')): ?> + + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + + + visibility + visibility_off +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    + +
    +
    + +
    +
    +
    + + +
    +
    +
    +
    +
    +
    +
    +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/auth/verify.php b/src/Libraries/Module/Templates/Demo/Web/Views/auth/verify.php new file mode 100644 index 00000000..b4eb5ded --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/auth/verify.php @@ -0,0 +1,48 @@ + +
    +
    +
    +

    + + has(\'error\')): ?> + + + + has(\'success\')): ?> + + + +
    +
    +
    +
    +
    + + + + + + +
    + +
    + +
    +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/index.php b/src/Libraries/Module/Templates/Demo/Web/Views/index.php new file mode 100644 index 00000000..ca744bdb --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/index.php @@ -0,0 +1,22 @@ + +
    +
    +
    + +
    +

    +
    +
    +
    +
    +
    + +
    +
    + +"; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/layouts/main.php b/src/Libraries/Module/Templates/Demo/Web/Views/layouts/main.php new file mode 100644 index 00000000..35dfd57c --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/layouts/main.php @@ -0,0 +1,30 @@ + + + + + + <?php echo $title ?> + + + + + + + +
    + +
    + + + + + + + + + + +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/partials/bubbles.php b/src/Libraries/Module/Templates/Demo/Web/Views/partials/bubbles.php new file mode 100644 index 00000000..b5db5dc7 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/partials/bubbles.php @@ -0,0 +1,16 @@ + +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • + +"; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/partials/footer.php b/src/Libraries/Module/Templates/Demo/Web/Views/partials/footer.php new file mode 100644 index 00000000..eeba5671 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/partials/footer.php @@ -0,0 +1,16 @@ + +
    +
    +
    + © 2018 - +
    +
    + +
    +
    +
    + +"; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/partials/language.php b/src/Libraries/Module/Templates/Demo/Web/Views/partials/language.php new file mode 100644 index 00000000..20de720e --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/partials/language.php @@ -0,0 +1,17 @@ + + +language + + + +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/partials/logo.php b/src/Libraries/Module/Templates/Demo/Web/Views/partials/logo.php new file mode 100644 index 00000000..520959d8 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/partials/logo.php @@ -0,0 +1,7 @@ +\"> + /assets/images/quantum-logo-white.png\" alt=\"\"/> + +"; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/partials/messages/error.php b/src/Libraries/Module/Templates/Demo/Web/Views/partials/messages/error.php new file mode 100644 index 00000000..a02939ad --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/partials/messages/error.php @@ -0,0 +1,20 @@ + + getFlash(\'error\') ?> + + + + + + + + +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/partials/messages/success.php b/src/Libraries/Module/Templates/Demo/Web/Views/partials/messages/success.php new file mode 100644 index 00000000..73db9ed6 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/partials/messages/success.php @@ -0,0 +1,8 @@ + + getFlash(\'success\'); ?> + + +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/partials/navbar.php b/src/Libraries/Module/Templates/Demo/Web/Views/partials/navbar.php new file mode 100644 index 00000000..bc81eeb3 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/partials/navbar.php @@ -0,0 +1,60 @@ + +
    + + menu + + +
    + + +"; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/partials/sidebar.php b/src/Libraries/Module/Templates/Demo/Web/Views/partials/sidebar.php new file mode 100644 index 00000000..da33615a --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/partials/sidebar.php @@ -0,0 +1,56 @@ + +
  • + \"> +home + + +
  • +
  • + /posts\"> + assignment + + +
  • +check()) : ?> +
  • + + person + user()->firstname . ' ' . auth()->user()->lastname ?> + arrow_drop_down + + +
  • + +
  • + + /signup\"> + person_add + + + +
  • +
  • + + /signin\"> + exit_to_app + + + +
  • + + 'sidenav-dropdown2']) ?> + + +"; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/post/form.php b/src/Libraries/Module/Templates/Demo/Web/Views/post/form.php new file mode 100644 index 00000000..b946e935 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/post/form.php @@ -0,0 +1,68 @@ + +
    + +
    +
    +

    + + has(\'error\')) : ?> + + + + t(\'common.the_image\')]) ?> + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    + Image + +
    +
    + +
    +
    + +
    + + + close + + + +
    + +
    + + + + + +
    +
    +
    +
    +
    +
    +
    +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/post/my-posts.php b/src/Libraries/Module/Templates/Demo/Web/Views/post/my-posts.php new file mode 100644 index 00000000..b830db29 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/post/my-posts.php @@ -0,0 +1,25 @@ + +

    +
    + + +
    + +

    ...

    + + + t(\'common.the_post\')]) ?> + + check()): ?> +
    + add +
    + +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/post/partials/back.php b/src/Libraries/Module/Templates/Demo/Web/Views/post/partials/back.php new file mode 100644 index 00000000..96b90f02 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/post/partials/back.php @@ -0,0 +1,7 @@ +\"> + arrow_back + +"; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/post/partials/modal.php b/src/Libraries/Module/Templates/Demo/Web/Views/post/partials/modal.php new file mode 100644 index 00000000..84878fa3 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/post/partials/modal.php @@ -0,0 +1,13 @@ + + + + +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/post/partials/my-post-item.php b/src/Libraries/Module/Templates/Demo/Web/Views/post/partials/my-post-item.php new file mode 100644 index 00000000..ff63271c --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/post/partials/my-post-item.php @@ -0,0 +1,24 @@ + + + + + + + + + + + +

    +
    +

    + + edit + + + delete + + +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/post/partials/post-item.php b/src/Libraries/Module/Templates/Demo/Web/Views/post/partials/post-item.php new file mode 100644 index 00000000..d31ba9c9 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/post/partials/post-item.php @@ -0,0 +1,31 @@ + +
    + +
    + + + + + +
    +
    +
    + + + + + +

    +
    +
    +
    +
    + + +
    +
    +
    +
    +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/post/post.php b/src/Libraries/Module/Templates/Demo/Web/Views/post/post.php new file mode 100644 index 00000000..09cc4193 --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/post/post.php @@ -0,0 +1,18 @@ + +

    +
    + + + $post]) ?> + + +
    + +

    ...

    + + + t(\'common.the_post\')]) ?> + +'; \ No newline at end of file diff --git a/src/Libraries/Module/Templates/Demo/Web/Views/post/single.php b/src/Libraries/Module/Templates/Demo/Web/Views/post/single.php new file mode 100644 index 00000000..898f975f --- /dev/null +++ b/src/Libraries/Module/Templates/Demo/Web/Views/post/single.php @@ -0,0 +1,18 @@ + +
    + +

    +
    + + +
    + + + + +

    +
    + +'; \ No newline at end of file