¨4.0.1¨

This commit is contained in:
¨NW¨
2023-12-03 14:07:47 +00:00
parent c08b36d1b6
commit f35052522d
1112 changed files with 43019 additions and 24987 deletions

View File

@@ -25,14 +25,15 @@ class ScaffoldEntityCommand extends Command
/**
* The instance of EntityGenerator.
*
* @var \FleetCart\Scaffold\Module\Generators\EntityGenerator
* @var EntityGenerator
*/
private $entityGenerator;
private EntityGenerator $entityGenerator;
/**
* Create a new command instance.
*
* @param \FleetCart\Scaffold\Module\Generators\EntityGenerator $entityGenerator
* @param EntityGenerator $entityGenerator
*/
public function __construct(EntityGenerator $entityGenerator)
{
@@ -41,12 +42,13 @@ class ScaffoldEntityCommand extends Command
$this->entityGenerator = $entityGenerator;
}
/**
* Execute the console command.
*
* @return void
*/
public function handle()
public function handle(): void
{
$this->entityGenerator
->module($this->argument('module'))
@@ -55,12 +57,13 @@ class ScaffoldEntityCommand extends Command
$this->info('Entity generated.');
}
/**
* Get the console command arguments.
*
* @return array
*/
protected function getArguments()
protected function getArguments(): array
{
return [
['entity', InputArgument::REQUIRED, 'The name of the entity.'],

View File

@@ -4,6 +4,7 @@ namespace FleetCart\Console\Commands;
use Illuminate\Console\Command;
use FleetCart\Scaffold\Module\ModuleScaffold;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
class ScaffoldModuleCommand extends Command
{
@@ -24,14 +25,15 @@ class ScaffoldModuleCommand extends Command
/**
* The instance of ModuleScaffold.
*
* @var \FleetCart\Scaffold\Module\ModuleScaffold
* @var ModuleScaffold
*/
private $scaffolder;
private ModuleScaffold $scaffolder;
/**
* Create a new command instance.
*
* @param \FleetCart\Scaffold\Module\ModuleScaffold $scaffolder
* @param ModuleScaffold $scaffolder
*/
public function __construct(ModuleScaffold $scaffolder)
{
@@ -40,12 +42,14 @@ class ScaffoldModuleCommand extends Command
$this->scaffolder = $scaffolder;
}
/**
* Execute the console command.
*
* @return void
* @throws FileNotFoundException
*/
public function handle()
public function handle(): void
{
$module = $this->askModuleName();
$entities = $this->askEntities();
@@ -55,49 +59,68 @@ class ScaffoldModuleCommand extends Command
$this->info('Module has been generated.');
}
/**
* Ask for module name.
*
* @return array
*/
private function askModuleName()
private function askModuleName(): array
{
do {
$moduleName = $this->ask('Please enter the module name in the following format: vendor/name');
do {
$moduleName = $this->ask('Please enter the module name in the following format: vendor/name');
} while (!$this->moduleNameIsValid($moduleName));
list($vendor, $name) = $this->extractModuleName($moduleName);
[$vendor, $name] = $this->extractModuleName($moduleName);
} while ($this->moduleExists($name));
return compact('vendor', 'name');
}
/**
* Validate the given module name.
*
* @param string $moduleName
*
* @return bool
*/
private function moduleNameIsValid(string $moduleName): bool
{
$name = explode('/', $moduleName);
if (count($name) !== 2) {
$this->error('Module name must be in the following format: vendor/name');
return false;
}
return true;
}
/**
* Extract the given module name.
*
* @param string $moduleName
*
* @return array
*/
private function extractModuleName($moduleName)
private function extractModuleName(string $moduleName): array
{
do {
$name = explode('/', $moduleName);
if (count($name) !== 2) {
$this->error('Module name must be in the following format: vendor/name');
$moduleName = $this->ask('Please enter the module name in the following format: vendor/name');
}
} while (count($name) !== 2);
$name = explode('/', $moduleName);
return [$name[0], ucfirst(camel_case($name[1]))];
}
/**
* Determine the given module is exists.
*
* @param string $name
*/
private function moduleExists($name)
private function moduleExists($name): bool
{
if (is_dir(config('modules.paths.modules') . "/{$name}")) {
$this->error("The module [$name] is already exists.");
@@ -108,17 +131,18 @@ class ScaffoldModuleCommand extends Command
return false;
}
/**
* Ask for entities.
*
* @return array
*/
private function askEntities()
private function askEntities(): array
{
$entities = [];
do {
$entity = $this->ask('Enter entity name. Leaving option empty will continue script', false);
$entity = $this->ask('Enter entity name. To Continue Press Enter .', false);
if ($entity !== '') {
$entities[] = ucfirst($entity);

View File

@@ -17,13 +17,15 @@ class Kernel extends ConsoleKernel
Commands\ScaffoldEntityCommand::class,
];
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @param Schedule $schedule
*
* @return void
*/
protected function schedule(Schedule $schedule)
protected function schedule(Schedule $schedule): void
{
// $schedule->command('inspire')
// ->hourly();

View File

@@ -5,8 +5,12 @@ namespace FleetCart\Exceptions;
use Throwable;
use Illuminate\Http\Request;
use Swift_TransportException;
use Illuminate\Http\Response;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Modules\Sms\Exceptions\SmsException;
use Illuminate\Validation\ValidationException;
use Modules\Checkout\Http\Exceptions\CheckoutException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@@ -32,62 +36,60 @@ class Handler extends ExceptionHandler
'password_confirmation',
];
/**
* Report or log an exception.
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
* @param \Throwable $e
* @param Throwable $e
*
* @return void
* @throws Throwable
*/
public function report(Throwable $e)
public function report(Throwable $e): void
{
parent::report($e);
}
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Throwable $e
* @return \Illuminate\Http\Response
* @param Request $request
* @param Throwable $e
*
* @return Response|JsonResponse|RedirectResponse
* @throws Throwable
*/
public function render($request, Throwable $e)
{
if ($e instanceof Swift_TransportException) {
return $this->handleSwiftException($request, $e);
}
if ($e instanceof SmsException) {
return $this->handleSmsException($request, $e);
}
if ($e instanceof ValidationException && $request->ajax()) {
return response()->json([
return match (true) {
$e instanceof Swift_TransportException => $this->handleSwiftException($request, $e),
$e instanceof SmsException => $this->handleSmsException($request, $e),
$e instanceof ValidationException && $request->ajax() => response()->json([
'message' => trans('core::messages.the_given_data_was_invalid'),
'errors' => $e->validator->getMessageBag(),
], 422);
}
if ($this->shouldRedirectToAdminDashboard($e)) {
return redirect()->route('admin.dashboard.index');
}
if ($this->shouldShowNotFoundPage($e)) {
return response()->view('errors.404');
}
return parent::render($request, $e);
], 422),
$e instanceof CheckoutException => response()->json([
'message' => $e->getMessage(),
], Response::HTTP_FORBIDDEN),
$this->shouldRedirectToAdminDashboard($e) => redirect()->route('admin.dashboard.index'),
$this->shouldShowNotFoundPage($e) => response()->view('errors.404'),
default => parent::render($request, $e),
};
}
/**
* Handle swift transport exception.
*
* @param \Illuminate\Http\Request $request
* @param \Swift_TransportException $e
* @param Request $request
* @param Swift_TransportException $e
*
* @return mixed
*
* @throws \Swift_TransportException
* @throws Swift_TransportException
*/
private function handleSwiftException(Request $request, Swift_TransportException $e)
{
@@ -103,14 +105,16 @@ class Handler extends ExceptionHandler
->with('error', trans('core::messages.mail_is_not_configured'));
}
/**
* Handle sms exception.
*
* @param \Illuminate\Http\Request $request
* @param \Modules\Sms\Exceptions\SmsException $e
* @return mixed
* @param Request $request
* @param SmsException $e
*
* @throws \Modules\Sms\Exceptions\SmsException
* @return RedirectResponse
*
* @throws SmsException
*/
private function handleSmsException(Request $request, SmsException $e)
{
@@ -125,28 +129,43 @@ class Handler extends ExceptionHandler
return back()->withInput()->with('error', $e->getMessage());
}
/**
* Determine whether response should redirect to the admin dashboard.
*
* @param \Throwable $e
* @param Throwable $e
*
* @return bool
*/
private function shouldRedirectToAdminDashboard(Throwable $e)
private function shouldRedirectToAdminDashboard(Throwable $e): bool
{
if (config('app.debug') || ! $this->inAdminPanel()) {
if (config('app.debug') || !$this->inAdminPanel()) {
return false;
}
return $e instanceof NotFoundHttpException || $e instanceof ModelNotFoundException;
}
/**
* Determine if the request is from admin panel.
*
* @return bool
*/
private function inAdminPanel(): bool
{
return $this->container->has('inAdminPanel') && $this->container['inAdminPanel'];
}
/**
* Determine if the response should show not found page.
*
* @param \Throwable $e
* @param Throwable $e
*
* @return bool
*/
private function shouldShowNotFoundPage(Throwable $e)
private function shouldShowNotFoundPage(Throwable $e): bool
{
if ($this->inAdminPanel()) {
return false;
@@ -154,14 +173,4 @@ class Handler extends ExceptionHandler
return $e instanceof NotFoundHttpException || $e instanceof ModelNotFoundException;
}
/**
* Determine if the request is from admin panel.
*
* @return bool
*/
private function inAdminPanel()
{
return $this->container->has('inAdminPanel') && $this->container['inAdminPanel'];
}
}

View File

@@ -3,20 +3,22 @@
namespace FleetCart\Exceptions;
use Exception;
use FleetCart\License;
use Illuminate\Http\RedirectResponse;
class InvalidLicenseException extends Exception
{
protected $message;
public function __construct($message)
{
$this->message = $message;
parent::__construct();
resolve(License::class)->deleteLicenseFile();
$this->message = $message;
}
public function render()
public function render(): RedirectResponse
{
return redirect()->route('license.create')
->with('error', $this->message);

View File

@@ -9,10 +9,10 @@ class FleetCart
*
* @var string
*/
const VERSION = '3.1.0';
const VERSION = '4.0.1';
/**
* The envato item ID.
* The Envato item ID.
*
* @var string
*/

View File

@@ -5,12 +5,20 @@ namespace FleetCart\Http\Controllers;
use Exception;
use FleetCart\Install\App;
use FleetCart\Install\Store;
use Illuminate\Http\Response;
use FleetCart\Install\Database;
use FleetCart\Install\Permission;
use Illuminate\Http\JsonResponse;
use FleetCart\Install\Requirement;
use Illuminate\Routing\Controller;
use Illuminate\Contracts\View\View;
use FleetCart\Install\AdminAccount;
use Illuminate\Contracts\View\Factory;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Artisan;
use FleetCart\Http\Requests\InstallRequest;
use Jackiedo\DotenvEditor\Facades\DotenvEditor;
use Illuminate\Contracts\Foundation\Application;
use FleetCart\Http\Middleware\RedirectIfInstalled;
class InstallController extends Controller
@@ -20,50 +28,56 @@ class InstallController extends Controller
$this->middleware(RedirectIfInstalled::class);
}
public function preInstallation(Requirement $requirement)
public function installation(Requirement $requirement, Permission $permission): Factory|View|Application
{
return view('install.pre_installation', compact('requirement'));
return view('install.install', compact('requirement', 'permission'));
}
public function getConfiguration(Requirement $requirement)
{
if (! $requirement->satisfied()) {
return redirect()->route('install.pre_installation');
}
return view('install.configuration', compact('requirement'));
}
public function postConfiguration(
public function install(
InstallRequest $request,
Database $database,
AdminAccount $admin,
Store $store,
App $app
) {
Database $database,
AdminAccount $admin,
Store $store,
App $app
): JsonResponse
{
@set_time_limit(0);
try {
$database->setup($request->db);
$admin->setup($request->admin);
$store->setup($request->store);
Artisan::call('optimize:clear');
$database->setup($request);
$admin->setup($request);
$store->setup($request);
$app->setup();
DotenvEditor::setKey('APP_INSTALLED', 'true')->save();
Artisan::call('key:generate', ['--force' => true]);
$success = true;
$message = "Congratulations! FleetCart installed successfully";
} catch (Exception $e) {
return back()->withInput()
->with('error', $e->getMessage());
$success = false;
$message = $e->getMessage();
try {
if (Schema::hasTable('migrations')) {
Artisan::call('migrate:rollback', ['--force' => true]);
}
} catch (Exception $e) {
$message .= '<br><br>' . $e->getMessage();
}
} finally {
return response()->json(
[
'success' => $success,
'message' => $message,
],
$success ? Response::HTTP_OK : Response::HTTP_INTERNAL_SERVER_ERROR
);
}
return redirect('install/complete');
}
public function complete()
{
if (config('app.installed')) {
return redirect()->route('home');
}
DotenvEditor::setKey('APP_INSTALLED', 'true')->save();
return view('install.complete');
}
}

View File

@@ -4,6 +4,10 @@ namespace FleetCart\Http\Controllers;
use FleetCart\License;
use Illuminate\Routing\Controller;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\Foundation\Application;
use FleetCart\Http\Middleware\RedirectIfShouldNotCreateLicense;
class LicenseController extends Controller
@@ -13,18 +17,23 @@ class LicenseController extends Controller
$this->middleware(RedirectIfShouldNotCreateLicense::class);
}
public function create()
public function create(): Factory|View|Application
{
return view('license.create');
}
public function store(License $license)
public function store(License $license): RedirectResponse
{
request()->validate([
'purchase_code' => 'required',
], [
'required' => 'The purchase code field is required.',
]);
request()->validate(
[
'purchase_code' => 'required',
],
[
'required' => 'The purchase code field is required.',
]
);
$license->activate(request('purchase_code'));

View File

@@ -2,7 +2,26 @@
namespace FleetCart\Http;
use FleetCart\Http\Middleware\RunUpdater;
use FleetCart\Http\Middleware\TrimStrings;
use FleetCart\Http\Middleware\TrustProxies;
use FleetCart\Http\Middleware\EncryptCookies;
use FleetCart\Http\Middleware\VerifyCsrfToken;
use Illuminate\Auth\Middleware\RequirePassword;
use Illuminate\Http\Middleware\SetCacheHeaders;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Routing\Middleware\ValidateSignature;
use FleetCart\Http\Middleware\ConvertStringBooleans;
use Illuminate\Auth\Middleware\EnsureEmailIsVerified;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\View\Middleware\ShareErrorsFromSession;
use FleetCart\Http\Middleware\CheckForMaintenanceMode;
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use FleetCart\Http\Middleware\RedirectToInstallerIfNotInstalled;
use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
class Kernel extends HttpKernel
{
@@ -14,16 +33,16 @@ class Kernel extends HttpKernel
* @var array
*/
protected $middleware = [
\FleetCart\Http\Middleware\EncryptCookies::class,
\Illuminate\Session\Middleware\StartSession::class,
\FleetCart\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\FleetCart\Http\Middleware\TrimStrings::class,
\FleetCart\Http\Middleware\ConvertStringBooleans::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
\FleetCart\Http\Middleware\TrustProxies::class,
\FleetCart\Http\Middleware\RedirectToInstallerIfNotInstalled::class,
\FleetCart\Http\Middleware\RunUpdater::class,
EncryptCookies::class,
StartSession::class,
CheckForMaintenanceMode::class,
ValidatePostSize::class,
TrimStrings::class,
ConvertStringBooleans::class,
ConvertEmptyStringsToNull::class,
TrustProxies::class,
RedirectToInstallerIfNotInstalled::class,
RunUpdater::class,
];
/**
@@ -33,10 +52,10 @@ class Kernel extends HttpKernel
*/
protected $middlewareGroups = [
'web' => [
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\FleetCart\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
AddQueuedCookiesToResponse::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
SubstituteBindings::class,
],
'api' => [
'throttle:60,1',
@@ -52,11 +71,11 @@ class Kernel extends HttpKernel
* @var array
*/
protected $routeMiddleware = [
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'bindings' => SubstituteBindings::class,
'cache.headers' => SetCacheHeaders::class,
'password.confirm' => RequirePassword::class,
'signed' => ValidateSignature::class,
'throttle' => ThrottleRequests::class,
'verified' => EnsureEmailIsVerified::class,
];
}

View File

@@ -3,6 +3,9 @@
namespace FleetCart\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Illuminate\Foundation\Http\Exceptions\MaintenanceModeException;
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode as BaseCheckForMaintenanceMode;
class CheckForMaintenanceMode extends BaseCheckForMaintenanceMode
@@ -16,17 +19,19 @@ class CheckForMaintenanceMode extends BaseCheckForMaintenanceMode
'*/admin*',
];
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param Request $request
* @param Closure $next
*
* @return mixed
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
* @throws \Illuminate\Foundation\Http\Exceptions\MaintenanceModeException
* @throws HttpException
* @throws MaintenanceModeException
*/
public function handle($request, Closure $next)
public function handle($request, Closure $next): mixed
{
if (
config('app.installed')

View File

@@ -9,11 +9,12 @@ class ConvertStringBooleans extends TransformsRequest
/**
* Transform the given value.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param $key
* @param $value
*
* @return mixed
*/
protected function transform($key, $value)
protected function transform($key, $value): mixed
{
if ($value === 'true' || $value === 'TRUE') {
return true;

View File

@@ -0,0 +1,29 @@
<?php
namespace FleetCart\Http\Middleware;
use Closure;
use FleetCart\License;
class LicenseChecker
{
private $license;
public function __construct(License $license)
{
$this->license = $license;
}
public function handle($request, Closure $next)
{
if ($this->license->shouldRecheck()) {
$this->license->recheck();
} else if ($this->license->shouldCreateLicense()) {
return redirect()->route('license.create');
}
return $next($request);
}
}

View File

@@ -3,17 +3,19 @@
namespace FleetCart\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class RedirectIfInstalled
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param Request $request
* @param Closure $next
*
* @return mixed
*/
public function handle($request, Closure $next)
public function handle($request, Closure $next): mixed
{
if (config('app.installed')) {
return redirect()->route('home');

View File

@@ -4,26 +4,30 @@ namespace FleetCart\Http\Middleware;
use Closure;
use FleetCart\License;
use Illuminate\Http\Request;
class RedirectIfShouldNotCreateLicense
{
private $license;
public function __construct(License $license)
{
$this->license = $license;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param Request $request
* @param Closure $next
*
* @return mixed
*/
public function handle($request, Closure $next)
public function handle($request, Closure $next): mixed
{
if ($this->license->valid() || ! $this->license->shouldCreateLicense()) {
if ($this->license->valid() || !$this->license->shouldCreateLicense()) {
return redirect()->intended('/admin');
}

View File

@@ -3,20 +3,22 @@
namespace FleetCart\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class RedirectToInstallerIfNotInstalled
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param Request $request
* @param Closure $next
*
* @return mixed
*/
public function handle($request, Closure $next)
public function handle($request, Closure $next): mixed
{
if (! config('app.installed') && ! $request->is('install/*')) {
return redirect()->route('install.pre_installation');
if (!config('app.installed') && !$request->is('install')) {
return redirect()->route('install.show');
}
return $next($request);

View File

@@ -4,17 +4,19 @@ namespace FleetCart\Http\Middleware;
use Closure;
use FleetCart\Updater;
use Illuminate\Http\Request;
class RunUpdater
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param Request $request
* @param Closure $next
*
* @return mixed
*/
public function handle($request, Closure $next)
public function handle(Request $request, Closure $next): mixed
{
if (config('app.installed') && file_exists(storage_path('app/update'))) {
Updater::run();

View File

@@ -12,66 +12,68 @@ class InstallRequest extends Request
*
* @return bool
*/
public function authorize()
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
public function rules(): array
{
return [
'db.host' => 'required',
'db.port' => 'required',
'db.username' => 'required',
'db.password' => 'nullable',
'db.database' => 'required',
'admin.first_name' => 'required',
'admin.last_name' => 'required',
'admin.email' => 'required|email',
'admin.phone' => 'required',
'admin.password' => 'required|confirmed|min:6',
'store.store_name' => 'required',
'store.store_email' => 'required|email',
'store.store_phone' => 'required',
'store.search_engine' => ['required', Rule::in(['mysql', 'algolia', 'meilisearch'])],
'store.algolia_app_id' => 'required_if:store.search_engine,algolia',
'store.algolia_secret' => 'required_if:store.search_engine,algolia',
'store.meilisearch_host' => 'required_if:store.search_engine,meilisearch',
'store.meilisearch_key' => 'required_if:store.search_engine,meilisearch',
'db_host' => 'required',
'db_port' => 'required',
'db_username' => 'required',
'db_password' => 'nullable',
'db_database' => 'required',
'admin_first_name' => 'required',
'admin_last_name' => 'required',
'admin_email' => 'required|email',
'admin_phone' => 'required',
'admin_password' => 'required|confirmed|min:6',
'store_name' => 'required',
'store_email' => 'required|email',
'store_phone' => 'required',
'store_search_engine' => ['required', Rule::in(['mysql', 'algolia', 'meilisearch'])],
'algolia_app_id' => 'required_if:store_search_engine,algolia',
'algolia_secret' => 'required_if:store_search_engine,algolia',
'meilisearch_host' => 'required_if:store_search_engine,meilisearch',
'meilisearch_key' => 'required_if:store_search_engine,meilisearch',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes()
public function attributes(): array
{
return [
'db.host' => 'host',
'db.port' => 'port',
'db.username' => 'username',
'db.password' => 'password',
'db.database' => 'datbase',
'admin.first_name' => 'first name',
'admin.last_name' => 'last name',
'admin.email' => 'email',
'admin.phone' => 'phone',
'admin.password' => 'password',
'store.store_name' => 'store name',
'store.store_email' => 'store email',
'store.store_phone' => 'store phone',
'store.search_engine' => 'search engine',
'store.algolia_app_id' => 'algolia application id',
'store.algolia_secret' => 'algolia admin api key',
'store.meilisearch_host' => 'meilisearch host',
'store.meilisearch_key' => 'meilisearch key',
'db_host' => 'host',
'db_port' => 'port',
'db_username' => 'username',
'db_password' => 'password',
'db_database' => 'datbase',
'admin_first_name' => 'first name',
'admin_last_name' => 'last name',
'admin_email' => 'email',
'admin_phone' => 'phone',
'admin_password' => 'password',
'store_name' => 'store name',
'store_email' => 'store email',
'store_phone' => 'store phone',
'store_search_engine' => 'search engine',
'algolia_app_id' => 'algolia application id',
'algolia_secret' => 'algolia admin api key',
'meilisearch_host' => 'meilisearch host',
'meilisearch_key' => 'meilisearch key',
];
}
}

View File

@@ -8,99 +8,124 @@ use Cartalyst\Sentinel\Laravel\Facades\Activation;
class AdminAccount
{
public function setup($data)
public function setup($request): void
{
$role = Role::create(['name' => 'Admin', 'permissions' => $this->getAdminRolePermissions()]);
$adminUser = $this->createAdminUser($request);
$this->activateAdminUser($adminUser);
$admin = User::create([
'first_name' => $data['first_name'],
'last_name' => $data['last_name'],
'email' => $data['email'],
'phone' => $data['phone'],
'password' => bcrypt($data['password']),
]);
$activation = Activation::create($admin);
Activation::complete($admin, $activation->code);
$admin->roles()->attach($role);
$adminRole = $this->createAdminRole();
$adminUser->roles()->attach($adminRole);
}
private function getAdminRolePermissions()
private function createAdminUser($request)
{
return User::create([
'first_name' => $request['admin_first_name'],
'last_name' => $request['admin_last_name'],
'email' => $request['admin_email'],
'phone' => $request['admin_phone'],
'password' => bcrypt($request['admin_password']),
]);
}
private function activateAdminUser($adminUser): void
{
$activation = Activation::create($adminUser);
Activation::complete($adminUser, $activation->code);
}
private function createAdminRole()
{
return Role::create([
'name' => 'Admin',
'permissions' => $this->getAdminRolePermissions(),
]);
}
private function getAdminRolePermissions(): array
{
return [
// users
# users
'admin.users.index' => true,
'admin.users.create' => true,
'admin.users.edit' => true,
'admin.users.destroy' => true,
// roles
# roles
'admin.roles.index' => true,
'admin.roles.create' => true,
'admin.roles.edit' => true,
'admin.roles.destroy' => true,
// products
# products
'admin.products.index' => true,
'admin.products.create' => true,
'admin.products.edit' => true,
'admin.products.destroy' => true,
// brands
# brands
'admin.brands.index' => true,
'admin.brands.create' => true,
'admin.brands.edit' => true,
'admin.brands.destroy' => true,
// attributes
# attributes
'admin.attributes.index' => true,
'admin.attributes.create' => true,
'admin.attributes.edit' => true,
'admin.attributes.destroy' => true,
// attribute sets
# attribute sets
'admin.attribute_sets.index' => true,
'admin.attribute_sets.create' => true,
'admin.attribute_sets.edit' => true,
'admin.attribute_sets.destroy' => true,
// options
#variations
'admin.variations.index' => true,
'admin.variations.create' => true,
'admin.variations.edit' => true,
'admin.variations.destroy' => true,
# options
'admin.options.index' => true,
'admin.options.create' => true,
'admin.options.edit' => true,
'admin.options.destroy' => true,
// filters
# filters
'admin.filters.index' => true,
'admin.filters.create' => true,
'admin.filters.edit' => true,
'admin.filters.destroy' => true,
// reviews
# reviews
'admin.reviews.index' => true,
'admin.reviews.create' => true,
'admin.reviews.edit' => true,
'admin.reviews.destroy' => true,
// categories
# categories
'admin.categories.index' => true,
'admin.categories.create' => true,
'admin.categories.edit' => true,
'admin.categories.destroy' => true,
// tags
# tags
'admin.tags.index' => true,
'admin.tags.create' => true,
'admin.tags.edit' => true,
'admin.tags.destroy' => true,
// orders
# orders
'admin.orders.index' => true,
'admin.orders.show' => true,
'admin.orders.edit' => true,
// flash sales
# flash sales
'admin.flash_sales.index' => true,
'admin.flash_sales.create' => true,
'admin.flash_sales.edit' => true,
'admin.flash_sales.destroy' => true,
// transactions
# transactions
'admin.transactions.index' => true,
// coupons
# coupons
'admin.coupons.index' => true,
'admin.coupons.create' => true,
'admin.coupons.edit' => true,
'admin.coupons.destroy' => true,
// menus
# menus
'admin.menus.index' => true,
'admin.menus.create' => true,
'admin.menus.edit' => true,
@@ -109,39 +134,39 @@ class AdminAccount
'admin.menu_items.create' => true,
'admin.menu_items.edit' => true,
'admin.menu_items.destroy' => true,
// Media
# Media
'admin.media.index' => true,
'admin.media.create' => true,
'admin.media.destroy' => true,
// pages
# pages
'admin.pages.index' => true,
'admin.pages.create' => true,
'admin.pages.edit' => true,
'admin.pages.destroy' => true,
// currency rates
# currency rates
'admin.currency_rates.index' => true,
'admin.currency_rates.edit' => true,
// tax
# tax
'admin.taxes.index' => true,
'admin.taxes.create' => true,
'admin.taxes.edit' => true,
'admin.taxes.destroy' => true,
// translations
# translations
'admin.translations.index' => true,
'admin.translations.edit' => true,
// appearance
# appearance
'admin.sliders.index' => true,
'admin.sliders.create' => true,
'admin.sliders.edit' => true,
'admin.sliders.destroy' => true,
// import
# import
'admin.importer.index' => true,
'admin.importer.create' => true,
// reports
# reports
'admin.reports.index' => true,
// settings
# settings
'admin.settings.edit' => true,
// storefront
# storefront
'admin.storefront.edit' => true,
];
}

View File

@@ -4,15 +4,13 @@ namespace FleetCart\Install;
use Modules\User\Entities\Role;
use Modules\Setting\Entities\Setting;
use Illuminate\Support\Facades\Artisan;
use Modules\Currency\Entities\CurrencyRate;
use Jackiedo\DotenvEditor\Facades\DotenvEditor;
class App
{
public function setup()
public function setup(): void
{
$this->generateAppKey();
$this->setEnvVariables();
$this->createCustomerRole();
$this->setAppSettings();
@@ -20,12 +18,8 @@ class App
$this->createStorageFolder();
}
private function generateAppKey()
{
Artisan::call('key:generate', ['--force' => true]);
}
private function setEnvVariables()
private function setEnvVariables(): void
{
$env = DotenvEditor::load();
@@ -37,12 +31,14 @@ class App
$env->save();
}
private function createCustomerRole()
private function createCustomerRole(): void
{
Role::create(['name' => 'Customer']);
}
private function setAppSettings()
private function setAppSettings(): void
{
Setting::setMany([
'active_theme' => 'Storefront',
@@ -58,7 +54,6 @@ class App
'supported_currencies' => ['USD'],
'default_currency' => 'USD',
'send_order_invoice_email' => false,
'store_email' => 'admin@fleetcart.test',
'newsletter_enabled' => false,
'search_engine' => 'mysql',
'local_pickup_cost' => 0,
@@ -97,12 +92,14 @@ class App
]);
}
private function createDefaultCurrencyRate()
private function createDefaultCurrencyRate(): void
{
CurrencyRate::create(['currency' => 'USD', 'rate' => 1]);
}
private function createStorageFolder()
private function createStorageFolder(): void
{
if (!is_dir(public_path('storage'))) {
mkdir(public_path('storage'));

View File

@@ -8,47 +8,51 @@ use Jackiedo\DotenvEditor\Facades\DotenvEditor;
class Database
{
public function setup($data)
public function setup($request): void
{
$this->checkDatabaseConnection($data);
$this->setEnvVariables($data);
$this->setupDatabaseConnection($request);
$this->setEnvVariables($request);
$this->migrateDatabase();
}
private function checkDatabaseConnection($data)
{
$this->setupDatabaseConnectionConfig($data);
private function setupDatabaseConnection($request): void
{
DB::purge('mysql');
$this->setupDatabaseConnectionConfig($request);
DB::connection('mysql')->reconnect();
DB::connection('mysql')->getPdo();
}
private function setupDatabaseConnectionConfig($data)
private function setupDatabaseConnectionConfig($request): void
{
config([
'database.default' => 'mysql',
'database.connections.mysql.host' => $data['host'],
'database.connections.mysql.port' => $data['port'],
'database.connections.mysql.database' => $data['database'],
'database.connections.mysql.username' => $data['username'],
'database.connections.mysql.password' => $data['password'],
'database.connections.mysql.host' => $request['db_host'],
'database.connections.mysql.port' => $request['db_port'],
'database.connections.mysql.database' => $request['db_database'],
'database.connections.mysql.username' => $request['db_username'],
'database.connections.mysql.password' => $request['db_password'],
]);
}
private function setEnvVariables($data)
private function setEnvVariables($request): void
{
$env = DotenvEditor::load();
$env->setKey('DB_HOST', $data['host']);
$env->setKey('DB_PORT', $data['port']);
$env->setKey('DB_DATABASE', $data['database']);
$env->setKey('DB_USERNAME', $data['username']);
$env->setKey('DB_PASSWORD', $data['password']);
$env->setKey('DB_HOST', $request['db_host']);
$env->setKey('DB_PORT', $request['db_port']);
$env->setKey('DB_DATABASE', $request['db_database']);
$env->setKey('DB_USERNAME', $request['db_username']);
$env->setKey('DB_PASSWORD', $request['db_password']);
$env->save();
}
private function migrateDatabase()
private function migrateDatabase(): void
{
Artisan::call('migrate', ['--force' => true]);
}

View File

@@ -0,0 +1,30 @@
<?php
namespace FleetCart\Install;
class Permission
{
public function provided(): bool
{
return collect($this->files())
->merge($this->directories())
->every(fn ($item) => $item);
}
public function files(): array
{
return [
'.env' => is_writable(base_path('.env')),
];
}
public function directories(): array
{
return [
'storage' => is_writable(storage_path()),
'bootstrap/cache' => is_writable(app()->bootstrapPath('cache')),
];
}
}

View File

@@ -4,36 +4,42 @@ namespace FleetCart\Install;
class Requirement
{
public function extensions()
private const extensions = [
'intl' => 'Intl',
'pdo' => 'PDO',
'json' => 'JSON',
'ctype' => 'Ctype',
'xml' => 'XML',
'tokenizer' => 'Tokenizer',
'mbstring' => 'Mbstring',
'openssl' => 'OpenSSL',
];
public function satisfied(): bool
{
return collect($this->php())
->merge($this->extensions())
->every(fn ($item) => $item);
}
public function php(): array
{
return [
'PHP >= 8.0.2' => version_compare(phpversion(), '8.0.2'),
'Intl PHP Extension' => extension_loaded('intl'),
'OpenSSL PHP Extension' => extension_loaded('openssl'),
'PDO PHP Extension' => extension_loaded('pdo'),
'Mbstring PHP Extension' => extension_loaded('mbstring'),
'Tokenizer PHP Extension' => extension_loaded('tokenizer'),
'XML PHP Extension' => extension_loaded('xml'),
'Ctype PHP Extension' => extension_loaded('ctype'),
'JSON PHP Extension' => extension_loaded('json'),
];
}
public function directories()
{
return [
'.env' => is_writable(base_path('.env')),
'storage' => is_writable(storage_path()),
'bootstrap/cache' => is_writable(app()->bootstrapPath('cache')),
];
}
public function satisfied()
public function extensions(): array
{
return collect($this->extensions())
->merge($this->directories())
->every(function ($item) {
return $item;
});
$extensions = [];
foreach (self::extensions as $extension => $name) {
$extensions[$name . ' PHP Extension'] = extension_loaded($extension);
}
return $extensions;
}
}

View File

@@ -6,17 +6,19 @@ use Modules\Setting\Entities\Setting;
class Store
{
public function setup($data)
public function setup($request): void
{
Setting::setMany([
'translatable' => [
'store_name' => $data['store_name'],
'store_name' => $request['store_name'],
],
'store_email' => $data['store_email'],
'store_phone' => $data['store_phone'],
'search_engine' => $data['search_engine'],
'algolia_app_id' => $data['algolia_app_id'],
'algolia_secret' => $data['algolia_secret'],
'store_email' => $request['store_email'],
'store_phone' => $request['store_phone'],
'search_engine' => $request['store_search_engine'],
'algolia_app_id' => $request['algolia_app_id'],
'algolia_secret' => $request['algolia_secret'],
'meilisearch_host' => $request['meilisearch_host'],
'meilisearch_key' => $request['meilisearch_key'],
]);
}
}

156
app/License.php Normal file
View File

@@ -0,0 +1,156 @@
<?php
namespace FleetCart;
use stdClass;
use Carbon\Carbon;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\File;
use GuzzleHttp\Exception\ServerException;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Exception\ConnectException;
use FleetCart\Exceptions\InvalidLicenseException;
class License
{
private $license;
private string $licenseFilePath;
private string $endpoint = 'https://license.envaysoft.com';
public function __construct()
{
$this->licenseFilePath = storage_path('app/license');
}
public function shouldRecheck()
{
if ($this->valid()) {
return Carbon::parse($this->getLicenseFromFile()->next_check)->isPast();
}
}
public function valid()
{
return $this->getLicenseFromFile()?->valid;
}
public function shouldCreateLicense(): bool
{
return match (true) {
$this->inFrontend(), $this->runningInLocal(), $this->valid() => false,
default => true,
};
}
/**
* @throws InvalidLicenseException|GuzzleException
*/
public function recheck(): void
{
$this->activate(
$this->getLicenseFromFile()->purchase_code
);
}
/**
* @throws InvalidLicenseException|GuzzleException
*/
public function activate($purchaseCode): void
{
$license = new stdClass();
$client = new Client(['base_uri' => $this->endpoint]);
try {
$client->post('/api/v1/licenses', [
'form_params' => [
'item_id' => FleetCart::ITEM_ID,
'domain' => request()->root(),
'purchase_code' => $purchaseCode,
],
]);
$license->valid = true;
} catch (ClientException $e) {
$license->valid = false;
$response = json_decode($e->getResponse()->getBody());
if ($response->status === 'success' && !$response->valid) {
throw new InvalidLicenseException('The purchase code is invalid.');
}
if ($response->status === 'error') {
throw new InvalidLicenseException($response->message);
}
} catch (ConnectException|ServerException|RequestException $e) {
$license->valid = true;
} finally {
$license->purchase_code = $purchaseCode;
$license->next_check = now()->addDays(1);
$this->deleteLicenseFile();
$this->storeLicenseToFile(json_encode($license));
}
}
/**
* The following function implements Singleton pattern
* to retrieve license from file.
*/
private function getLicenseFromFile()
{
if (!is_null($this->license)) {
return $this->license;
}
if (!file_exists($this->licenseFilePath)) {
return null;
}
$this->license = json_decode(
decrypt(
file_get_contents(
$this->licenseFilePath
)
)
);
return $this->license;
}
private function inFrontend(): bool
{
if (request()->is('license')) {
return false;
}
return !request()->is('*admin*');
}
private function runningInLocal(): bool
{
return app()->isLocal() || in_array(request()->ip(), ['127.0.0.1', '::1']);
}
private function deleteLicenseFile(): void
{
File::delete($this->licenseFilePath);
}
private function storeLicenseToFile($license): void
{
file_put_contents($this->licenseFilePath, encrypt($license));
}
}

View File

@@ -16,9 +16,21 @@ class AppServiceProvider extends ServiceProvider
*
* @return void
*/
public function boot()
public function boot(): void
{
/*
|--------------------------------------------------------------------------
| Configure default String length of the generated migrations
|--------------------------------------------------------------------------
| Laravel uses the utf8mb4 character set by default, which includes
| support for storing "emojis" in the database. if you are running a
| version of MySQL older than the 5.7.7 release or MariaDB older than
| the 10.2.2 release, you need to configure the default string length
| generated by migrations in order for MySQL to create indexes for
| them.
*/
Schema::defaultStringLength(191);
Paginator::useBootstrap();
if (Request::secure()) {
@@ -26,14 +38,15 @@ class AppServiceProvider extends ServiceProvider
}
}
/**
* Register the service provider.
*
* @return void
*/
public function register()
public function register(): void
{
if (! config('app.installed')) {
if (!config('app.installed')) {
$this->app->register(DotenvEditorServiceProvider::class);
}
}

View File

@@ -16,16 +16,18 @@ class RouteServiceProvider extends ServiceProvider
*/
protected $namespace = 'FleetCart\Http\Controllers';
/**
* Define the routes for the application.
*
* @return void
*/
public function map()
public function map(): void
{
$this->mapWebRoutes();
}
/**
* Define the "web" routes for the application.
*
@@ -33,7 +35,7 @@ class RouteServiceProvider extends ServiceProvider
*
* @return void
*/
protected function mapWebRoutes()
protected function mapWebRoutes(): void
{
Route::middleware('web')
->namespace($this->namespace)

View File

@@ -18,14 +18,16 @@ class EntityGenerator extends Generator
'views/shortcuts.stub' => 'Resources/views/admin/$ENTITY_NAME$/partials/shortcuts.blade.php',
];
/**
* Generate the given entities.
*
* @param array $entities
* @param bool $generateSidebar
*
* @return void
*/
public function generate(array $entities, $generateSidebar = true)
public function generate(array $entities, $generateSidebar = true): void
{
if (count($entities) !== 0 && $generateSidebar) {
$this->generateSidebarExtender($entities);
@@ -44,11 +46,13 @@ class EntityGenerator extends Generator
}
}
/**
* Generate a filled sidebar view composer
* Or an empty one of no entities.
*
* @param $entities
*
* @return void
*/
private function generateSidebarExtender($entities)
@@ -59,10 +63,12 @@ class EntityGenerator extends Generator
);
}
/**
* Append permissions.
*
* @param string $entity
*
* @return void
*/
private function appendPermissions($entity)
@@ -75,10 +81,12 @@ class EntityGenerator extends Generator
);
}
/**
* Generate migrations file for eloquent entities.
*
* @param string $entity
*
* @return void
*/
private function generateMigrations($entity)
@@ -99,6 +107,7 @@ class EntityGenerator extends Generator
);
}
/**
* Get the current time with microseconds.
*
@@ -114,10 +123,12 @@ class EntityGenerator extends Generator
return $date->format('Y_m_d_Hisu_');
}
/**
* Generate entity.
*
* @param string $entity
*
* @return void
*/
private function generateEntity($entity)
@@ -133,10 +144,12 @@ class EntityGenerator extends Generator
);
}
/**
* Generate the controller for the given entity.
*
* @param string $entity
*
* @return void
*/
private function generateController($entity)
@@ -149,10 +162,12 @@ class EntityGenerator extends Generator
);
}
/**
* Generate the requests for the given entity.
*
* @param string $entity
*
* @return void
*/
private function generateRequests($entity)
@@ -165,32 +180,12 @@ class EntityGenerator extends Generator
);
}
/**
* Generate views for the given entity.
*
* @param string $entity
* @return void
*/
private function generateViews($entity)
{
$entityName = snake_case(str_plural($entity));
$this->createDirectory("Resources/views/admin/{$entityName}/partials");
foreach ($this->views as $stub => $view) {
$view = str_replace('$ENTITY_NAME$', $entityName, $view);
$this->finder->put(
$this->getModulesPath($view),
$this->getContentForStub($stub, $entity)
);
}
}
/**
* Generate language files for the given entity.
*
* @param string $entity
*
* @return void
*/
private function generateLang($entity)
@@ -215,10 +210,36 @@ class EntityGenerator extends Generator
);
}
/**
* Generate views for the given entity.
*
* @param string $entity
*
* @return void
*/
private function generateViews($entity)
{
$entityName = snake_case(str_plural($entity));
$this->createDirectory("Resources/views/admin/{$entityName}/partials");
foreach ($this->views as $stub => $view) {
$view = str_replace('$ENTITY_NAME$', $entityName, $view);
$this->finder->put(
$this->getModulesPath($view),
$this->getContentForStub($stub, $entity)
);
}
}
/**
* Append the routes for the given entity to the routes file.
*
* @param string $entity
*
* @return void
*/
private function appendRoutes($entity)
@@ -231,10 +252,12 @@ class EntityGenerator extends Generator
);
}
/**
* Append sidebar extender.
*
* @param string $entity
*
* @return void
*/
private function appendSidebarExtender($entity)

View File

@@ -9,10 +9,12 @@ class FilesGenerator extends Generator
/**
* Generate the given files.
*
* @param array $files
* @param array $files
*
* @return void
* @throws FileNotFoundException
*/
public function generate(array $files)
public function generate(array $files): void
{
foreach ($files as $stub => $file) {
$this->finder->put(
@@ -22,30 +24,32 @@ class FilesGenerator extends Generator
}
}
/**
* Generate the base module service provider.
*
* @return $this
* @return void
* @throws FileNotFoundException
*/
public function generateModuleProvider()
public function generateModuleServiceProvider(): void
{
$this->finder->put(
$this->getModulesPath("Providers/{$this->name}ServiceProvider.php"),
$this->getContentFor('providers/module-service-provider.stub')
);
return $this;
}
/**
* Get the content for the given file.
*
* @param string $stub
*
* @return string
*
* @throws FileNotFoundException
*/
private function getContentFor($stub)
private function getContentFor(string $stub): string
{
$stub = $this->finder->get($this->getStubPath($stub));

View File

@@ -3,101 +3,103 @@
namespace FleetCart\Scaffold\Module\Generators;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
abstract class Generator
{
/**
* The instance of Filesystem.
*
* @var \Illuminate\Filesystem\Filesystem
* @var Filesystem
*/
protected $finder;
protected Filesystem $finder;
/**
* Name of the module.
*
* @var string
*/
protected $name;
protected string $name;
/**
* Create a new instance.
*
* @param \Illuminate\Filesystem\Filesystem $finder
* @param Filesystem $finder
*/
public function __construct(Filesystem $finder)
{
$this->finder = $finder;
}
/**
* Generate the given files.
*
* @param array $files
*
* @return void
*/
abstract public function generate(array $files);
abstract public function generate(array $files): void;
/**
* Set the module name.
*
* @param string $name
*
* @return $this
*/
public function module($name)
public function module(string $name): static
{
$this->name = ucfirst($name);
return $this;
}
/**
* Return the current module path.
*
* @param string $path
* @return string
*/
protected function getModulesPath($path = '')
{
return config('modules.paths.modules') . "/{$this->name}/{$path}";
}
/**
* Create directory if not exists.
*
* @param string $path
*
* @return string
*/
protected function createDirectory($path)
protected function createDirectory($path): string
{
$path = $this->getModulesPath($path);
if (! $this->finder->isDirectory($path)) {
if (!$this->finder->isDirectory($path)) {
$this->finder->makeDirectory($path, 0755, true);
}
return $path;
}
/**
* Get stub path for the given stub file.
* Return the current module path.
*
* @param string $path
*
* @param string $filename
* @return string
*/
protected function getStubPath($filename)
protected function getModulesPath(string $path = ''): string
{
return __DIR__ . "/../stubs/{$filename}";
return config('modules.paths.modules') . "/{$this->name}/{$path}";
}
/**
* Get content of the given stub.
*
* @param string $stub
* @param string $class
*
* @return string
* @throws FileNotFoundException
*/
protected function getContentForStub($stub, $class = '')
protected function getContentForStub(string $stub, string $class = ''): string
{
$stub = $this->finder->get($this->getStubPath($stub));
@@ -131,4 +133,17 @@ abstract class Generator
kebab_case(str_plural($class)),
], $stub);
}
/**
* Get stub path for the given stub file.
*
* @param string $filename
*
* @return string
*/
protected function getStubPath($filename)
{
return __DIR__ . "/../stubs/{$filename}";
}
}

View File

@@ -6,6 +6,7 @@ use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Facades\Artisan;
use FleetCart\Scaffold\Module\Generators\FilesGenerator;
use FleetCart\Scaffold\Module\Generators\EntityGenerator;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
class ModuleScaffold
{
@@ -14,53 +15,49 @@ class ModuleScaffold
*
* @var string
*/
protected $vendor;
protected string $vendor;
/**
* The module name which will be generated.
*
* @var string
*/
protected $name;
/**
* The instance of Filesystem.
*
* @var \Illuminate\Filesystem\Filesystem
*/
private $finder;
/**
* The instance of EntityGenerator.
*
* @var \FleetCart\Scaffold\Module\Generators\EntityGenerator
*/
private $entityGenerator;
/**
* The instance of FilesGenerator.
*
* @var \FleetCart\Scaffold\Module\Generators\FilesGenerator
*/
private $filesGenerator;
protected string $name;
/**
* Array of files to be generated.
*
* @var array
*/
protected $files = [
'config/assets.stub' => 'Config/assets.php',
protected array $stubsToFilesMap = [
'config/permissions.stub' => 'Config/permissions.php',
'routes/routes.stub' => 'Routes/admin.php',
];
/**
* The instance of Filesystem.
*
* @var Filesystem
*/
private Filesystem $finder;
/**
* The instance of EntityGenerator.
*
* @var EntityGenerator
*/
private EntityGenerator $entityGenerator;
/**
* The instance of FilesGenerator.
*
* @var FilesGenerator
*/
private FilesGenerator $filesGenerator;
/**
* Create a new instance.
*
* @param \Illuminate\Filesystem\Filesystem $finder
* @param \FleetCart\Scaffold\Module\Generators\EntityGenerator $entityGenerator
* @param \FleetCart\Scaffold\Module\Generators\FilesGenerator $filesGenerator
* @param Filesystem $finder
* @param EntityGenerator $entityGenerator
* @param FilesGenerator $filesGenerator
*/
public function __construct(Filesystem $finder, EntityGenerator $entityGenerator, FilesGenerator $filesGenerator)
{
@@ -69,124 +66,129 @@ class ModuleScaffold
$this->filesGenerator = $filesGenerator;
}
/**
* @param array $module
* @param array $entities
*
* @return void
* @throws FileNotFoundException
*/
public function scaffold(array $module, array $entities)
public function scaffold(array $module, array $entities): void
{
$this->vendor = $module['vendor'];
$this->name = $module['name'];
Artisan::call('module:make', ['name' => [$this->name]]);
$this->generateModule();
$this->renameVendorName();
$this->addFolders();
$this->removeUnneededFiles();
$this->addDataToComposerJsonFile();
$this->removeUnneededFilesAndFolders();
$this->modifyComposerJsonFile();
$this->filesGenerator->module($this->name)
->generateModuleProvider()
->generate($this->files);
$this->filesGenerator->module($this->name)->generateModuleServiceProvider();
$this->filesGenerator->module($this->name)->generate($this->stubsToFilesMap);
$this->addDataToModuleJson();
$this->modifyModuleJsonFile();
$this->cleanupModuleJsonFile();
$this->entityGenerator->module($this->getName())->generate($entities);
}
/**
* Generate default module.
*
* @return void
*/
public function generateModule(): void
{
Artisan::call('module:make', ['name' => [$this->name]]);
}
/**
* Get studly cased module name.
*
* @return string
*/
public function getName()
public function getName(): string
{
return studly_case($this->name);
}
/**
* Get the path on the module.
*
* @return string
*/
private function getModulesPath($path = '')
{
return config('modules.paths.modules') . "/{$this->getName()}/{$path}";
}
/**
* Get the paths on the module.
*
* @param array $paths
* @return array
*/
private function getModulesPaths(array $paths)
{
$list = [];
foreach ($paths as $path) {
$list[] = $this->getModulesPath($path);
}
return $list;
}
/**
* Remove vendor name from composer.json file.
*
* @return void
* @throws FileNotFoundException
*/
private function renameVendorName()
private function renameVendorName(): void
{
$composerJsonContent = $this->finder->get($this->getModulesPath('composer.json'));
$composerJsonPath = $this->getModulesPath('composer.json');
$composerJsonContent = $this->finder->get($composerJsonPath);
$composerJsonContent = str_replace('nwidart', $this->vendor, $composerJsonContent);
$this->finder->put($this->getModulesPath('composer.json'), $composerJsonContent);
$this->finder->put($composerJsonPath, $composerJsonContent);
}
/**
* Remove view files.
* Get the path on the module.
*
* @return void
* @param string $path
*
* @return string
*/
private function removeViewFiles()
private function getModulesPath(string $path = ''): string
{
$this->finder->delete($this->getModulesPath('Resources/views/index.blade.php'));
$this->finder->delete($this->getModulesPath('Resources/views/layouts/master.blade.php'));
$this->finder->deleteDirectory($this->getModulesPath('Resources/views/layouts'));
return config('modules.paths.modules') . "/{$this->getName()}/{$path}";
}
/**
* Add required folders.
*
* @return void
*/
private function addFolders()
private function addFolders(): void
{
$this->finder->makeDirectory($this->getModulesPath('Sidebar'));
$this->addSidebarFolder();
}
/**
* Add Sidebar folder.
*
* @return void
*/
private function addSidebarFolder(): void
{
$directoryPath = $this->getModulesPath('Sidebar');
$this->finder->makeDirectory($directoryPath);
}
/**
* Remove unneeded files and folders.
*
* @return void
*/
private function removeUnneededFiles()
private function removeUnneededFilesAndFolders(): void
{
$this->renameVendorName();
$this->removeViewFiles();
$this->removeUnneededFiles();
$this->removeUnneededFolders();
}
$this->finder->deleteDirectory($this->getModulesPath('Database/factories'));
$this->finder->deleteDirectory($this->getModulesPath('Database/Seeders'));
$this->finder->deleteDirectory($this->getModulesPath('Events'));
$this->finder->deleteDirectory($this->getModulesPath('Console'));
$this->finder->deleteDirectory($this->getModulesPath('Http/Middleware'));
$this->finder->deleteDirectory($this->getModulesPath('Jobs'));
$this->finder->deleteDirectory($this->getModulesPath('Mail'));
$this->finder->deleteDirectory($this->getModulesPath('Resources/assets'));
$this->finder->deleteDirectory($this->getModulesPath('Tests'));
/**
* Remove unneeded files.
*
* @return void
*/
private function removeUnneededFiles(): void
{
$files = $this->getModulesPaths([
'Config/.gitkeep',
'Config/config.php',
@@ -199,6 +201,8 @@ class ModuleScaffold
'Providers/RouteServiceProvider.php',
'Resources/lang/.gitkeep',
'Resources/views/.gitkeep',
'Resources/views/index.blade.php',
'Resources/views/layouts/master.blade.php',
'Routes/.gitkeep',
'Routes/web.php',
'Routes/api.php',
@@ -209,12 +213,116 @@ class ModuleScaffold
$this->finder->delete($files);
}
/**
* Get the paths on the module.
*
* @param array $paths
*
* @return array
*/
private function getModulesPaths(array $paths): array
{
$list = [];
foreach ($paths as $path) {
$list[] = $this->getModulesPath($path);
}
return $list;
}
/**
* Remove unneeded folders.
*
* @return void
*/
private function removeUnneededFolders(): void
{
$this->finder->deleteDirectory($this->getModulesPath('Database/factories'));
$this->finder->deleteDirectory($this->getModulesPath('Database/Seeders'));
$this->finder->deleteDirectory($this->getModulesPath('Events'));
$this->finder->deleteDirectory($this->getModulesPath('Console'));
$this->finder->deleteDirectory($this->getModulesPath('Http/Middleware'));
$this->finder->deleteDirectory($this->getModulesPath('Jobs'));
$this->finder->deleteDirectory($this->getModulesPath('Mail'));
$this->finder->deleteDirectory($this->getModulesPath('Resources/assets'));
$this->finder->deleteDirectory($this->getModulesPath('Resources/views/layouts'));
$this->finder->deleteDirectory($this->getModulesPath('Tests'));
}
/**
* Add data to composer.json file.
*
* @return void
* @throws FileNotFoundException
*/
private function modifyComposerJsonFile(): void
{
$moduleName = ucfirst($this->name);
$composerJsonPath = $this->getModulesPath('composer.json');
$composerJsonFile = fopen($composerJsonPath, "r");
$composerJsonFileAsArray = [];
while (!feof($composerJsonFile)) {
$composerJsonFileAsArray[] = rtrim(fgets($composerJsonFile));
}
$composerJsonText = '';
foreach ($composerJsonFileAsArray as $lineNumber => $textLine) {
if ($lineNumber === 2) {
$composerJsonText .= " \"description\": \"The FleetCart {$moduleName} Module.\"," . PHP_EOL;
continue;
} else if ($lineNumber >= 9 && $lineNumber <= 23) {
continue;
}
$composerJsonText .= $textLine . PHP_EOL;
}
$search = <<<JSON
],
JSON;
$replace = <<<JSON
],
"require": {
"php": ">=8.0.2"
},
"autoload": {
"psr-4": {
"Modules\\\\{$moduleName}\\\\": ""
}
},
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev"
}
JSON;
$composerJson = str_replace($search, $replace, $composerJsonText);
$this->finder->put($this->getModulesPath('composer.json'), $composerJson);
}
/**
* Add data to module.json file.
*
* @return void
* @throws FileNotFoundException
*/
private function addDataToModuleJson()
private function modifyModuleJsonFile(): void
{
$moduleJson = $this->finder->get($this->getModulesPath('module.json'));
@@ -223,22 +331,27 @@ class ModuleScaffold
$this->finder->put($this->getModulesPath('module.json'), $moduleJson);
}
/**
* Set the module priority for composer.json file.
*
* @param $content
*
* @return string
*/
private function setModulePriority($content)
private function setModulePriority($content): string
{
return str_replace('"priority": 0,', '"priority": 100,', $content);
}
/**
* Remove unneeded data from module.json file.
*
* @return void
* @throws FileNotFoundException
*/
private function cleanupModuleJsonFile()
private function cleanupModuleJsonFile(): void
{
$moduleJson = $this->finder->get($this->getModulesPath('module.json'));
@@ -274,60 +387,4 @@ JSON;
$this->finder->put($this->getModulesPath('module.json'), $moduleJson);
}
/**
* Add data to composer.json file.
*
* @return void
*/
private function addDataToComposerJsonFile()
{
$composerJson = $this->finder->get($this->getModulesPath('composer.json'));
$moduleName = ucfirst($this->name);
$composerJsonText = '';
foreach (explode(PHP_EOL, $composerJson) as $lineNumber => $textLine) {
if ($lineNumber === 2) {
$composerJsonText .= " \"description\": \"The FleetCart {$moduleName} Module.\"," . PHP_EOL;
continue;
} elseif ($lineNumber >= 9 && $lineNumber <= 23) {
continue;
}
$composerJsonText .= $textLine . PHP_EOL;
}
$search = <<<JSON
],
JSON;
$replace = <<<JSON
],
"require": {
"php": ">=7.3.0"
},
"autoload": {
"psr-4": {
"Modules\\\\{$moduleName}\\\\": ""
}
},
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev"
}
JSON;
$composerJson = str_replace($search, $replace, $composerJsonText);
$this->finder->put($this->getModulesPath('composer.json'), $composerJson);
}
}

View File

@@ -2,13 +2,10 @@
namespace Modules\$MODULE$\Providers;
use Modules\Support\Traits\AddsAsset;
use Illuminate\Support\ServiceProvider;
class $MODULE$ServiceProvider extends ServiceProvider
{
use AddsAsset;
/**
* Bootstrap the application services.
*

View File

@@ -14,3 +14,10 @@
@endsection
@include('$LOWERCASE_MODULE_NAME$::admin.$PLURAL_SNAKE_CASE_ENTITY_NAME$.partials.shortcuts')
@push('globals')
@vite([
'Modules/Variation/Resources/assets/admin/sass/main.scss',
'Modules/Variation/Resources/assets/admin/js/main.js',
])
@endpush

View File

@@ -16,3 +16,10 @@
@endsection
@include('$LOWERCASE_MODULE_NAME$::admin.$PLURAL_SNAKE_CASE_ENTITY_NAME$.partials.shortcuts')
@push('globals')
@vite([
'Modules/Variation/Resources/assets/admin/sass/main.scss',
'Modules/Variation/Resources/assets/admin/js/main.js',
])
@endpush

View File

@@ -21,7 +21,7 @@
@endcomponent
@push('scripts')
<script>
<script type="module">
new DataTable('#$PLURAL_SNAKE_CASE_ENTITY_NAME$-table .table', {
columns: [
{ data: 'checkbox', orderable: false, searchable: false, width: '3%' },

View File

@@ -7,7 +7,7 @@ use Illuminate\Support\Facades\DB;
class V2_0_0
{
public function run()
public function run(): void
{
if (version_compare(FleetCart::VERSION, '2.0.0', '=')) {
DB::delete("DELETE FROM `settings` WHERE `key` LIKE 'storefront_%' AND NOT `key` = 'storefront_copyright_text'");

View File

@@ -8,7 +8,7 @@ use Illuminate\Support\Facades\Artisan;
class Updater
{
public static function run()
public static function run(): void
{
@set_time_limit(0);
@@ -22,34 +22,40 @@ class Updater
File::delete(storage_path('app/update'));
}
private static function migrate()
private static function migrate(): void
{
if (config('app.installed')) {
Artisan::call('migrate', ['--force' => true]);
}
}
private static function clearViewCache()
private static function clearViewCache(): void
{
Artisan::call('view:clear');
}
private static function clearConfigCache()
private static function clearConfigCache(): void
{
Artisan::call('config:clear');
}
private static function clearRouteCache()
private static function clearRouteCache(): void
{
Artisan::call('route:trans:clear');
}
private static function clearAppCache()
private static function clearAppCache(): void
{
Artisan::call('cache:clear');
}
private static function runScripts()
private static function runScripts(): void
{
$previouslyRan = DB::table('updater_scripts')->get();
@@ -60,7 +66,7 @@ class Updater
$script = $file->getBasename('.php');
if (! $previouslyRan->contains($script)) {
if (!$previouslyRan->contains($script)) {
resolve("FleetCart\\Scripts\\{$script}")->run();
$ran[] = ['script' => $script];