commit 225b326a5ef98ae99091a3a17a8cdcb05a3b837c Author: Daeng Deni Mardaeni Date: Wed Aug 7 08:47:07 2024 +0700 Initial Commit diff --git a/app/Exports/RolesExport.php b/app/Exports/RolesExport.php new file mode 100644 index 0000000..7dbc4a3 --- /dev/null +++ b/app/Exports/RolesExport.php @@ -0,0 +1,38 @@ +id, + $row->name, + $row->created_at + ]; + } + public function headings(): array{ + return [ + 'ID', + 'Role', + 'Created At' + ]; + } + + public function columnFormats(): array{ + return [ + 'A' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_NUMBER, + 'C' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_DATETIME + ]; + } +} diff --git a/app/Exports/UsersExport.php b/app/Exports/UsersExport.php new file mode 100644 index 0000000..747e342 --- /dev/null +++ b/app/Exports/UsersExport.php @@ -0,0 +1,41 @@ +id, + $row->name, + $row->email, + $row->created_at + ]; + } + public function headings(): array{ + return [ + 'ID', + 'Name', + 'Email', + 'Created At' + ]; + } + + public function columnFormats(): array{ + return [ + 'A' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_NUMBER, + 'C' => \PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_DATETIME + ]; + } +} diff --git a/app/Http/Controllers/.gitkeep b/app/Http/Controllers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/app/Http/Controllers/PermissionsController.php b/app/Http/Controllers/PermissionsController.php new file mode 100644 index 0000000..bb63264 --- /dev/null +++ b/app/Http/Controllers/PermissionsController.php @@ -0,0 +1,69 @@ +middleware(function ($request, $next) { + $this->user = Auth::guard('web')->user(); + return $next($request); + }); + }*/ + + /** + * Display a listing of the resource. + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function index() + { + // Check if the authenticated user has the required permission to view roles + if (is_null($this->user) || !$this->user->can('roles.view')) { + //abort(403, 'Sorry! You are not allowed to view roles.'); + } + + // Fetch all roles from the database + $roles = Role::all(); + + // Return the view for displaying the roles + return view('usermanagement::roles.index', compact('roles')); + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\Request $request + * + * @return \Illuminate\Http\RedirectResponse + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function store(RoleRequest $request) + { + // Check if the authenticated user has the required permission to store roles + if (is_null($this->user) || !$this->user->can('roles.store')) { + //abort(403, 'Sorry! You are not allowed to store roles.'); + } + + // Create a new role using the provided request data + Role::create($request->all()); + + // Redirect back to the roles index with a success message + return redirect()->route('users.roles.index')->with('success', 'Role created successfully.'); + } + + /** + * Show the form for creating a new resource. + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function create() + { + // Check if the authenticated user has the required permission to create roles + if (is_null($this->user) || !$this->user->can('roles.create')) { + //abort(403, 'Sorry! You are not allowed to create roles.'); + } + + // Return the view for creating a new role + return view('usermanagement::roles.create'); + } + + /** + * Display the specified resource. + * + * @param int $id + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function show($id) + { + // Check if the authenticated user has the required permission to view roles + if (is_null($this->user) || !$this->user->can('roles.view')) { + abort(403, 'Sorry! You are not allowed to view roles.'); + } + + // Fetch the specified role from the database + $role = Role::find($id); + + // Return the view for displaying the role + return view('usermanagement::roles.show', compact('role')); + } + + /** + * Show the form for editing the specified resource. + * + * @param int $id + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function edit($id) + { + // Check if the authenticated user has the required permission to edit roles + if (is_null($this->user) || !$this->user->can('roles.edit')) { + //abort(403, 'Sorry! You are not allowed to edit roles.'); + } + + // Fetch the specified role from the database + $role = Role::find($id); + + // Return the view for editing the role + return view('usermanagement::roles.create', compact('role')); + } + + + /** + * Update the specified role in storage. + * + * @param \Modules\Usermanagement\Http\Requests\RoleRequest $request The request object containing the role data. + * @param int $id The unique identifier of the role to be updated. + * + * @return \Illuminate\Http\RedirectResponse Redirects back to the roles index with a success message upon successful update. + * + * @throws \Illuminate\Auth\Access\AuthorizationException If the authenticated user does not have the required permission to update roles. + */ + public function update(RoleRequest $request, $id) + { + // Check if the authenticated user has the required permission to update roles + if (is_null($this->user) || !$this->user->can('roles.update')) { + //abort(403, 'Sorry! You are not allowed to update roles.'); + } + + // Fetch the specified role from the database + $role = Role::find($id); + + // Update the role using the provided request data + $role->update($request->all()); + + // Redirect back to the roles index with a success message + return redirect()->route('users.roles.index')->with('success', 'Role updated successfully.'); + } + + /** + * Remove the specified resource from storage. + * + * @param int $id + * + * @return \Illuminate\Http\RedirectResponse + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function destroy($id) + { + // Check if the authenticated user has the required permission to delete roles + if (is_null($this->user) || !$this->user->can('roles.delete')) { + //abort(403, 'Sorry! You are not allowed to delete roles.'); + } + + // Fetch the specified role from the database + $role = Role::find($id); + + // Delete the role + $role->delete(); + + // Redirect back to the roles index with a success message + echo json_encode(['message' => 'User deleted successfully.', 'success' => true]); + } + + /** + * Restore a deleted role. + * + * @param int $id + * + * @return \Illuminate\Http\RedirectResponse + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function restore($id) + { + // Check if the authenticated user has the required permission to restore roles + if (is_null($this->user) || !$this->user->can('roles.restore')) { + abort(403, 'Sorry! You are not allowed to restore roles.'); + } + + // Fetch the specified role from the database + $role = Role::withTrashed()->find($id); + + // Restore the role + $role->restore(); + + // Redirect back to the roles index with a success message + return redirect()->route('users.roles.index')->with('success', 'Role restored successfully.'); + } + + /** + * Process support datatables ajax request. + * + * @param \Illuminate\Http\Request $request + * + * @return \Illuminate\Http\JsonResponse + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function dataForDatatables(Request $request) + { + if (is_null($this->user) || !$this->user->can('roles.view')) { + //abort(403, 'Sorry! You are not allowed to view users.'); + } + + // Retrieve data from the database + $query = Role::query(); + + // Apply search filter if provided + if ($request->has('search') && !empty($request->get('search'))) { + $search = $request->get('search'); + $query->where(function ($q) use ($search) { + $q->where('name', 'LIKE', "%$search%"); + }); + } + + // Apply sorting if provided + if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) { + $order = $request->get('sortOrder'); + $column = $request->get('sortField'); + $query->orderBy($column, $order); + } + + // Get the total count of records + $totalRecords = $query->count(); + + // Apply pagination if provided + if ($request->has('start') && $request->has('length')) { + $start = $request->get('start'); + $length = $request->get('length'); + $query->skip($start)->take($length); + } + + // Get the filtered count of records + $filteredRecords = $query->count(); + + // Get the data for the current page + $roles = $query->get(); + + // Calculate the page count + $pageCount = ceil($totalRecords); + + // Calculate the current page number + $currentPage = 0 + 1; + + // Return the response data as a JSON object + return response()->json([ + 'draw' => $request->get('draw'), + 'recordsTotal' => $totalRecords, + 'recordsFiltered' => $filteredRecords, + 'pageCount' => $pageCount, + 'page' => $currentPage, + 'totalCount' => $totalRecords, + 'data' => $roles, + ]); + } + + public function export() + { + return Excel::download(new RolesExport, 'roles.xlsx'); + } + } diff --git a/app/Http/Controllers/UsersController.php b/app/Http/Controllers/UsersController.php new file mode 100644 index 0000000..8592373 --- /dev/null +++ b/app/Http/Controllers/UsersController.php @@ -0,0 +1,249 @@ +middleware(function ($request, $next) { + // $this->user = Auth::guard('web')->user(); + // return $next($request); + // }); + // } + + /** + * Display a listing of the resource. + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function index() + { + if (is_null($this->user) || !$this->user->can('users.view')) { + //abort(403, 'Sorry! You are not allowed to view users.'); + } + + return view('usermanagement::users.index'); + } + + /** + * Process support datatables ajax request. + * + * @param \Illuminate\Http\Request $request + * + * @return \Illuminate\Http\JsonResponse + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function dataForDatatables(Request $request) + { + if (is_null($this->user) || !$this->user->can('users.view')) { + //abort(403, 'Sorry! You are not allowed to view users.'); + } + + // Retrieve data from the database + $query = User::query(); + + // Apply search filter if provided + if ($request->has('search') && !empty($request->get('search'))) { + $search = $request->get('search'); + $query->where(function ($q) use ($search) { + $q->where('name', 'LIKE', "%$search%") + ->orWhere('email', 'LIKE', "%$search%"); + }); + } + + // Apply sorting if provided + if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) { + $order = $request->get('sortOrder'); + $column = $request->get('sortField'); + $query->orderBy($column, $order); + } + + // Get the total count of records + $totalRecords = $query->count(); + + // Apply pagination if provided + if ($request->has('start') && $request->has('length')) { + $start = $request->get('start'); + $length = $request->get('length'); + $query->skip($start)->take($length); + } + + // Get the filtered count of records + $filteredRecords = $query->count(); + + // Get the data for the current page + $users = $query->get(); + + // Calculate the page count + $pageCount = ceil($totalRecords); + + // Calculate the current page number + $currentPage = 0 + 1; + + // Return the response data as a JSON object + return response()->json([ + 'draw' => $request->get('draw'), + 'recordsTotal' => $totalRecords, + 'recordsFiltered' => $filteredRecords, + 'pageCount' => $pageCount, + 'page' => $currentPage, + 'totalCount' => $totalRecords, + 'data' => $users, + ]); + } + + /** + * Show the form for editing the specified resource. + * + * @param int $id + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function edit($id) + { + if (is_null($this->user) || !$this->user->can('users.edit')) { + //abort(403, 'Sorry! You are not allowed to edit users.'); + } + + $user = User::find($id); + $roles = Role::all(); + return view('usermanagement::users.create', compact('user', 'roles')); + } + + /** + * Update the specified resource in storage. + * + * @param \Modules\Usermanagement\Http\Requests\User $request + * @param int $id + * + * @return \Illuminate\Http\RedirectResponse + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function update(UserRequest $request, $id) + { + if (is_null($this->user) || !$this->user->can('users.update')) { + //abort(403, 'Sorry! You are not allowed to update users.'); + } + + $user = User::find($id); + $user->update($request->all()); + + return redirect()->route('users.index')->with('success', 'User updated successfully.'); + } + + /** + * Remove the specified resource from storage. + * + * @param int $id + * + * @return \Illuminate\Http\RedirectResponse + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function destroy($id) + { + if (is_null($this->user) || !$this->user->can('users.delete')) { + //abort(403, 'Sorry! You are not allowed to delete users.'); + } + + $user = User::find($id); + $user->delete(); + + echo json_encode(['message' => 'User deleted successfully.', 'success' => true]); + } + + /** + * Restore the specified resource from storage. + * + * @param int $id + * + * @return \Illuminate\Http\RedirectResponse + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function restore($id) + { + if (is_null($this->user) || !$this->user->can('users.restore')) { + abort(403, 'Sorry! You are not allowed to restore users.'); + } + + $user = User::withTrashed()->find($id); + $user->restore(); + + return redirect()->route('users.index')->with('success', 'User restored successfully.'); + } + + /** + * Store a newly created resource in storage. + * + * This function handles the creation of a new user in the application. It validates the incoming request data, + * creates a new user record in the database, and redirects the user to the users index page with a success message. + * + * @param \Modules\Usermanagement\Http\Requests\User $request The incoming request containing the user data. + * + * @return \Illuminate\Http\RedirectResponse Redirects to the users index page with a success message upon successful creation. + * @return \Illuminate\Http\RedirectResponse Redirects to the users create page upon validation failure. + */ + public function store(UserRequest $request) + { + $validated = $request->validated(); + + if ($validated) { + $user = User::create($validated); + + if ($user) { + return redirect()->route('users.index')->with('success', 'User created successfully.'); + } + } + + return redirect()->route('users.create'); + } + + /** + * Show the form for creating a new resource. + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function create() + { + if (is_null($this->user) || !$this->user->can('users.create')) { + //abort(403, 'Sorry! You are not allowed to create a user.'); + } + + $roles = Role::all(); + return view('usermanagement::users.create', compact('roles')); + } + + public function export() + { + return Excel::download(new UsersExport, 'users.xlsx'); + } + + } diff --git a/app/Http/Requests/ChangePassword.php b/app/Http/Requests/ChangePassword.php new file mode 100644 index 0000000..a22d245 --- /dev/null +++ b/app/Http/Requests/ChangePassword.php @@ -0,0 +1,23 @@ + 'required|string|min:8|confirmed', + 'current_password' => 'required|string|min:8' + ]; + } + } diff --git a/app/Http/Requests/Login.php b/app/Http/Requests/Login.php new file mode 100644 index 0000000..2e243b9 --- /dev/null +++ b/app/Http/Requests/Login.php @@ -0,0 +1,22 @@ + 'required|email', + 'password' => 'required' + ]; + } + } diff --git a/app/Http/Requests/RoleRequest.php b/app/Http/Requests/RoleRequest.php new file mode 100644 index 0000000..ae7e025 --- /dev/null +++ b/app/Http/Requests/RoleRequest.php @@ -0,0 +1,46 @@ + 'required|string|in:web,api', + ]; + + if ($this->method() === 'PUT') { + $rules['name'] = 'required|string|max:255|unique:roles,name,' . $this->id; + } else { + $rules['name'] = 'required|string|max:255'; + } + + return $rules; + } + + public function prepareForValidation() + { + $this->merge([ + 'guard_names' => 'web', + ]); + } + } + + + diff --git a/app/Http/Requests/User.php b/app/Http/Requests/User.php new file mode 100644 index 0000000..98b0b0d --- /dev/null +++ b/app/Http/Requests/User.php @@ -0,0 +1,49 @@ + 'required|string|max:255', + ]; + + if ($this->password || $this->method() === 'POST') { + $rules['email'] = 'required|email|unique:users,email'; + $rules['password'] = 'required|string|min:8|confirmed'; + } + + if ($this->method() === 'PUT') { + $rules['email'] = 'required|email|unique:users,email,' . $this->id; + } + + return $rules; + } + + public function passedValidation() + { + $this->merge([ + 'password' => Hash::make($this->password) + ]); + } + } + + + diff --git a/app/Models/Base.php b/app/Models/Base.php new file mode 100644 index 0000000..e224571 --- /dev/null +++ b/app/Models/Base.php @@ -0,0 +1,51 @@ +connection = $module->database; + } + + /** + * Retrieves the activity log options for the User Management. + * + * @return LogOptions The activity log options. + */ + public function getActivitylogOptions() + : LogOptions + { + return LogOptions::defaults()->logAll()->useLogName('User Management : '); + } + } diff --git a/app/Models/Permission.php b/app/Models/Permission.php new file mode 100644 index 0000000..d66e52b --- /dev/null +++ b/app/Models/Permission.php @@ -0,0 +1,33 @@ +logAll()->useLogName('User Management|Permissions : '); + } + + /** + * Retrieve the permission group associated with this permission. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo The permission group relationship. + */ + public function group() + { + return $this->belongsTo(PermissionGroup::class, 'permission_group_id'); + } + } diff --git a/app/Models/PermissionGroup.php b/app/Models/PermissionGroup.php new file mode 100644 index 0000000..9e37b8d --- /dev/null +++ b/app/Models/PermissionGroup.php @@ -0,0 +1,60 @@ +get(); + } + + /** + * Returns a relationship instance for the Permission model. + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function permission() + { + return $this->hasMany(Permission::class); + } + + /** + * Retrieves the roles associated with a given permission group. + * + * @param object $group The permission group object. + * + * @return array The array of roles associated with the permission group. + */ + public function roles($group) + { + $permission = Permission::where('permission_group_id', $group->id)->first(); + + $data = []; + $roles = Role::all(); + + foreach ($roles as $role) { + if ($role->hasPermissionTo($permission->name)) { + array_push($data, $role); + } + } + + return $data; + } + + } diff --git a/app/Models/Role.php b/app/Models/Role.php new file mode 100644 index 0000000..d54c527 --- /dev/null +++ b/app/Models/Role.php @@ -0,0 +1,25 @@ +logAll()->useLogName('User Management|Roles : '); + } + + } diff --git a/app/Models/User.php b/app/Models/User.php new file mode 100644 index 0000000..03de691 --- /dev/null +++ b/app/Models/User.php @@ -0,0 +1,73 @@ + + */ + protected $fillable = [ + 'name', + 'email', + 'password', + ]; + + /** + * The attributes that should be hidden for serialization. + * + * These are the attributes that will be hidden when the model is converted to an array or JSON. + * + * @var array + */ + protected $hidden = [ + 'password', + 'remember_token', + ]; + + /** + * Get the attributes that should be cast. + * + * This method defines how the attributes should be cast when accessed. + * In this case, 'email_verified_at' is cast to 'datetime', 'password' is cast to 'hashed', and 'id' is cast to 'string'. + * + * @return array + */ + protected function casts() + : array + { + return [ + 'email_verified_at' => 'datetime', + 'password' => 'hashed', + 'id' => 'string', + ]; + } + } + diff --git a/app/Providers/.gitkeep b/app/Providers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php new file mode 100644 index 0000000..a8b37dc --- /dev/null +++ b/app/Providers/EventServiceProvider.php @@ -0,0 +1,32 @@ +> + */ + protected $listen = []; + + /** + * Indicates if events should be discovered. + * + * @var bool + */ + protected static $shouldDiscoverEvents = true; + + /** + * Configure the proper event listeners for email verification. + * + * @return void + */ + protected function configureEmailVerification(): void + { + + } +} diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php new file mode 100644 index 0000000..518c0de --- /dev/null +++ b/app/Providers/RouteServiceProvider.php @@ -0,0 +1,49 @@ +mapApiRoutes(); + + $this->mapWebRoutes(); + } + + /** + * Define the "web" routes for the application. + * + * These routes all receive session state, CSRF protection, etc. + */ + protected function mapWebRoutes(): void + { + Route::middleware('web')->group(module_path('Usermanagement', '/routes/web.php')); + } + + /** + * Define the "api" routes for the application. + * + * These routes are typically stateless. + */ + protected function mapApiRoutes(): void + { + Route::middleware('api')->prefix('api')->name('api.')->group(module_path('Usermanagement', '/routes/api.php')); + } +} diff --git a/app/Providers/UsermanagementServiceProvider.php b/app/Providers/UsermanagementServiceProvider.php new file mode 100644 index 0000000..ebcf175 --- /dev/null +++ b/app/Providers/UsermanagementServiceProvider.php @@ -0,0 +1,124 @@ +registerCommands(); + $this->registerCommandSchedules(); + $this->registerTranslations(); + $this->registerConfig(); + $this->registerViews(); + $this->loadMigrationsFrom(module_path($this->moduleName, 'database/migrations')); + + if (class_exists('Breadcrumbs')) { + require __DIR__ . '/../../routes/breadcrumbs.php'; + } + } + + /** + * Register the service provider. + */ + public function register(): void + { + $this->app->register(EventServiceProvider::class); + $this->app->register(RouteServiceProvider::class); + } + + /** + * Register commands in the format of Command::class + */ + protected function registerCommands(): void + { + // $this->commands([]); + } + + /** + * Register command Schedules. + */ + protected function registerCommandSchedules(): void + { + // $this->app->booted(function () { + // $schedule = $this->app->make(Schedule::class); + // $schedule->command('inspire')->hourly(); + // }); + } + + /** + * Register translations. + */ + public function registerTranslations(): void + { + $langPath = resource_path('lang/modules/'.$this->moduleNameLower); + + if (is_dir($langPath)) { + $this->loadTranslationsFrom($langPath, $this->moduleNameLower); + $this->loadJsonTranslationsFrom($langPath); + } else { + $this->loadTranslationsFrom(module_path($this->moduleName, 'lang'), $this->moduleNameLower); + $this->loadJsonTranslationsFrom(module_path($this->moduleName, 'lang')); + } + } + + /** + * Register config. + */ + protected function registerConfig(): void + { + $this->publishes([module_path($this->moduleName, 'config/config.php') => config_path($this->moduleNameLower.'.php')], 'config'); + $this->mergeConfigFrom(module_path($this->moduleName, 'config/config.php'), $this->moduleNameLower); + } + + /** + * Register views. + */ + public function registerViews(): void + { + $viewPath = resource_path('views/modules/'.$this->moduleNameLower); + $sourcePath = module_path($this->moduleName, 'resources/views'); + + $this->publishes([$sourcePath => $viewPath], ['views', $this->moduleNameLower.'-module-views']); + + $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower); + + $componentNamespace = str_replace('/', '\\', config('modules.namespace').'\\'.$this->moduleName.'\\'.ltrim(config('modules.paths.generator.component-class.path'), config('modules.paths.app_folder', ''))); + Blade::componentNamespace($componentNamespace, $this->moduleNameLower); + } + + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides(): array + { + return []; + } + + /** + * @return array + */ + private function getPublishableViewPaths(): array + { + $paths = []; + foreach (config('view.paths') as $path) { + if (is_dir($path.'/modules/'.$this->moduleNameLower)) { + $paths[] = $path.'/modules/'.$this->moduleNameLower; + } + } + + return $paths; + } +} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..8b1bba1 --- /dev/null +++ b/composer.json @@ -0,0 +1,30 @@ +{ + "name": "putrakuningan/usermanagement", + "description": "", + "authors": [ + { + "name": "Nicolas Widart", + "email": "n.widart@gmail.com" + } + ], + "extra": { + "laravel": { + "providers": [ + ], + "aliases": { + } + } + }, + "autoload": { + "psr-4": { + "Modules\\Usermanagement\\": "app/", + "Modules\\Usermanagement\\Database\\Factories\\": "database/factories/", + "Modules\\Usermanagement\\Database\\Seeders\\": "database/seeders/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\Usermanagement\\Tests\\": "tests/" + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..15ab3ad --- /dev/null +++ b/composer.lock @@ -0,0 +1,18 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "2864198e9d1b1265f9eb249b3c2e2689", + "packages": [], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.6.0" +} diff --git a/config/.gitkeep b/config/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/config/config.php b/config/config.php new file mode 100644 index 0000000..6d588f3 --- /dev/null +++ b/config/config.php @@ -0,0 +1,5 @@ + 'Usermanagement', +]; diff --git a/database/migrations/.gitkeep b/database/migrations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php new file mode 100644 index 0000000..a9c56ee --- /dev/null +++ b/database/migrations/0001_01_01_000000_create_users_table.php @@ -0,0 +1,53 @@ +id(); + $table->string('name'); + $table->string('email')->unique(); + $table->timestamp('email_verified_at')->nullable(); + $table->string('password'); + $table->rememberToken(); + $table->timestamps(); + $table->softDeletes(); + $table->unsignedBigInteger('created_by')->nullable(); + $table->unsignedBigInteger('updated_by')->nullable(); + $table->unsignedBigInteger('deleted_by')->nullable(); + }); + + Schema::create('password_reset_tokens', function (Blueprint $table) { + $table->string('email')->primary(); + $table->string('token'); + $table->timestamp('created_at')->nullable(); + }); + + Schema::create('sessions', function (Blueprint $table) { + $table->string('id')->primary(); + $table->foreignId('user_id')->nullable()->index(); + $table->string('ip_address', 45)->nullable(); + $table->text('user_agent')->nullable(); + $table->longText('payload'); + $table->integer('last_activity')->index(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('users'); + Schema::dropIfExists('password_reset_tokens'); + Schema::dropIfExists('sessions'); + } +}; diff --git a/database/migrations/2024_07_28_111853_create_permission_tables.php b/database/migrations/2024_07_28_111853_create_permission_tables.php new file mode 100644 index 0000000..96f1392 --- /dev/null +++ b/database/migrations/2024_07_28_111853_create_permission_tables.php @@ -0,0 +1,141 @@ +engine('InnoDB'); + $table->bigIncrements('id'); // permission id + $table->string('name'); // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format) + $table->string('guard_name'); // For MyISAM use string('guard_name', 25); + $table->timestamps(); + $table->softDeletes(); + $table->unique(['name', 'guard_name']); + }); + + Schema::create($tableNames['roles'], function (Blueprint $table) use ($teams, $columnNames) { + //$table->engine('InnoDB'); + $table->bigIncrements('id'); // role id + if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing + $table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable(); + $table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index'); + } + $table->string('name'); // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format) + $table->string('guard_name'); // For MyISAM use string('guard_name', 25); + $table->timestamps(); + $table->softDeletes(); + if ($teams || config('permission.testing')) { + $table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']); + } else { + $table->unique(['name', 'guard_name']); + } + }); + + Schema::create($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames, $columnNames, $pivotPermission, $teams) { + $table->unsignedBigInteger($pivotPermission); + + $table->string('model_type'); + $table->unsignedBigInteger($columnNames['model_morph_key']); + $table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index'); + + $table->foreign($pivotPermission) + ->references('id') // permission id + ->on($tableNames['permissions']) + ->onDelete('cascade'); + if ($teams) { + $table->unsignedBigInteger($columnNames['team_foreign_key']); + $table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index'); + + $table->primary([$columnNames['team_foreign_key'], $pivotPermission, $columnNames['model_morph_key'], 'model_type'], + 'model_has_permissions_permission_model_type_primary'); + } else { + $table->primary([$pivotPermission, $columnNames['model_morph_key'], 'model_type'], + 'model_has_permissions_permission_model_type_primary'); + } + + }); + + Schema::create($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames, $columnNames, $pivotRole, $teams) { + $table->unsignedBigInteger($pivotRole); + + $table->string('model_type'); + $table->unsignedBigInteger($columnNames['model_morph_key']); + $table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index'); + + $table->foreign($pivotRole) + ->references('id') // role id + ->on($tableNames['roles']) + ->onDelete('cascade'); + if ($teams) { + $table->unsignedBigInteger($columnNames['team_foreign_key']); + $table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index'); + + $table->primary([$columnNames['team_foreign_key'], $pivotRole, $columnNames['model_morph_key'], 'model_type'], + 'model_has_roles_role_model_type_primary'); + } else { + $table->primary([$pivotRole, $columnNames['model_morph_key'], 'model_type'], + 'model_has_roles_role_model_type_primary'); + } + }); + + Schema::create($tableNames['role_has_permissions'], function (Blueprint $table) use ($tableNames, $pivotRole, $pivotPermission) { + $table->unsignedBigInteger($pivotPermission); + $table->unsignedBigInteger($pivotRole); + + $table->foreign($pivotPermission) + ->references('id') // permission id + ->on($tableNames['permissions']) + ->onDelete('cascade'); + + $table->foreign($pivotRole) + ->references('id') // role id + ->on($tableNames['roles']) + ->onDelete('cascade'); + + $table->primary([$pivotPermission, $pivotRole], 'role_has_permissions_permission_id_role_id_primary'); + }); + + app('cache') + ->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null) + ->forget(config('permission.cache.key')); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + $tableNames = config('permission.table_names'); + + if (empty($tableNames)) { + throw new \Exception('Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.'); + } + + Schema::drop($tableNames['role_has_permissions']); + Schema::drop($tableNames['model_has_roles']); + Schema::drop($tableNames['model_has_permissions']); + Schema::drop($tableNames['roles']); + Schema::drop($tableNames['permissions']); + } +}; diff --git a/database/migrations/2024_07_31_022613_create_permission_groups_table.php b/database/migrations/2024_07_31_022613_create_permission_groups_table.php new file mode 100644 index 0000000..a25f3e3 --- /dev/null +++ b/database/migrations/2024_07_31_022613_create_permission_groups_table.php @@ -0,0 +1,34 @@ +id(); + $table->string('name'); + $table->string('slug'); + $table->timestamps(); + $table->softDeletes(); + + $table->unsignedBigInteger('created_by')->nullable(); + $table->unsignedBigInteger('updated_by')->nullable(); + $table->unsignedBigInteger('deleted_by')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('permission_groups'); + } +}; diff --git a/database/migrations/2024_07_31_022646_update_permissions_table.php b/database/migrations/2024_07_31_022646_update_permissions_table.php new file mode 100644 index 0000000..2311da7 --- /dev/null +++ b/database/migrations/2024_07_31_022646_update_permissions_table.php @@ -0,0 +1,34 @@ +string('module')->after('id')->nullable(); + $table->foreignIdFor(PermissionGroup::class); + }); + } + + /** + * Reverse the migrations. + */ + public function down() + : void + { + Schema::withoutForeignKeyConstraints(function () { + Schema::table('permissions', function (Blueprint $table) { + $table->dropColumn('module'); + $table->dropColumn('permission_group_id'); + }); + }); + } + }; diff --git a/database/migrations/2024_07_31_023136_update_users_table.php b/database/migrations/2024_07_31_023136_update_users_table.php new file mode 100644 index 0000000..91f719a --- /dev/null +++ b/database/migrations/2024_07_31_023136_update_users_table.php @@ -0,0 +1,32 @@ +string('profile_photo_path', 2048)->nullable()->after('email'); + $table->datetime('last_login_at')->nullable(); + $table->string('last_login_ip')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('profile_photo_path'); + $table->dropColumn('last_login_at'); + $table->dropColumn('last_login_ip'); + }); + } +}; diff --git a/database/seeders/.gitkeep b/database/seeders/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/database/seeders/UsermanagementDatabaseSeeder.php b/database/seeders/UsermanagementDatabaseSeeder.php new file mode 100644 index 0000000..b080e5d --- /dev/null +++ b/database/seeders/UsermanagementDatabaseSeeder.php @@ -0,0 +1,16 @@ +call([]); + } +} diff --git a/menu.json b/menu.json new file mode 100644 index 0000000..e69de29 diff --git a/module.json b/module.json new file mode 100644 index 0000000..18768a6 --- /dev/null +++ b/module.json @@ -0,0 +1,53 @@ +{ + "name": "Usermanagement", + "alias": "usermanagement", + "database": "", + "description": "", + "keywords": [], + "priority": 0, + "providers": [ + "Modules\\Usermanagement\\Providers\\UsermanagementServiceProvider" + ], + "files": [], + "menu": { + "main": [], + "master": [], + "system": [ + { + "title": "User Management", + "path": "users", + "icon": "ki-filled ki-users text-lg", + "classes": "", + "attributes": [], + "permission": "", + "roles": [], + "sub": [ + { + "title": "Users", + "path": "users", + "classes": "", + "attributes": [], + "permission": "", + "roles": [] + }, + { + "title": "Roles", + "path": "users.roles", + "classes": "", + "attributes": [], + "permission": "", + "roles": [] + }, + { + "title": "Permissions", + "path": "users.permissions", + "classes": "", + "attributes": [], + "permission": "", + "roles": [] + } + ] + } + ] + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..d6fbfc8 --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build" + }, + "devDependencies": { + "axios": "^1.1.2", + "laravel-vite-plugin": "^0.7.5", + "sass": "^1.69.5", + "postcss": "^8.3.7", + "vite": "^4.0.0" + } +} diff --git a/resources/assets/.gitkeep b/resources/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/resources/assets/js/app.js b/resources/assets/js/app.js new file mode 100644 index 0000000..e69de29 diff --git a/resources/assets/sass/app.scss b/resources/assets/sass/app.scss new file mode 100644 index 0000000..bf284d0 --- /dev/null +++ b/resources/assets/sass/app.scss @@ -0,0 +1,4 @@ +.login{ + margin-top: 100px; + margin-bottom: 100px; +} diff --git a/resources/views/.gitkeep b/resources/views/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/resources/views/roles/create.blade.php b/resources/views/roles/create.blade.php new file mode 100644 index 0000000..161061e --- /dev/null +++ b/resources/views/roles/create.blade.php @@ -0,0 +1,48 @@ +@extends('layouts.main') + +@section('breadcrumbs') + {{ Breadcrumbs::render(request()->route()->getName()) }} +@endsection + +@section('content') +
+ @if(isset($role->id)) +
+ + @method('PUT') + @else + + @endif + @csrf +
+
+

+ {{ isset($role->id) ? 'Edit' : 'Add' }} Role +

+
+ Back +
+
+
+ +
+ +
+ + @error('name') + {{ $message }} + @enderror +
+
+
+ +
+
+
+
+
+@endsection diff --git a/resources/views/roles/index.blade.php b/resources/views/roles/index.blade.php new file mode 100644 index 0000000..4020501 --- /dev/null +++ b/resources/views/roles/index.blade.php @@ -0,0 +1,165 @@ +@extends('layouts.main') + +@section('breadcrumbs') + {{ Breadcrumbs::render('users.roles') }} +@endsection + +@section('content') +
+
+
+
+

+ List of Roles +

+
+
+ +
+
+ + + +
+ Export to Excel + Add Role +
+
+
+
+
+ + + + + + + + +
+ + + Role + + Action
+
+ +
+
+
+
+@endsection + +@push('scripts') + + + +@endpush + diff --git a/resources/views/users/create.blade.php b/resources/views/users/create.blade.php new file mode 100644 index 0000000..a54fd2b --- /dev/null +++ b/resources/views/users/create.blade.php @@ -0,0 +1,100 @@ +@extends('layouts.main') + +@section('breadcrumbs') + {{ Breadcrumbs::render(request()->route()->getName()) }} +@endsection + +@section('content') +
+ @if(isset($user->id)) +
+ + @method('PUT') + @else + + @endif + @csrf +
+
+

+ {{ isset($user->id) ? 'Edit' : 'Add' }} User +

+
+ +
+
+
+ +
+ +
+ + @error('name') + {{ $message }} + @enderror +
+
+
+ +
+ + @error('email') + {{ $message }} + @enderror +
+
+
+ + +
+
+ +
+ + +
+
+ @error('password') + {{ $message }} + @enderror +
+
+
+ +
+
+ +
+ + +
+
+ @error('password_confirmation') + {{ $message }} + @enderror +
+
+ +
+ +
+
+
+
+
+@endsection diff --git a/resources/views/users/index.blade.php b/resources/views/users/index.blade.php new file mode 100644 index 0000000..7f52ba7 --- /dev/null +++ b/resources/views/users/index.blade.php @@ -0,0 +1,172 @@ +@extends('layouts.main') + +@section('breadcrumbs') + {{ Breadcrumbs::render('users') }} +@endsection + +@section('content') +
+
+
+
+

+ List of Users +

+
+
+ +
+
+ + + +
+ Export to Excel + Add User +
+
+
+
+
+ + + + + + + + + +
+ + + Name + + + Email + + Action
+
+ +
+
+
+
+@endsection + +@push('scripts') + + + +@endpush + diff --git a/routes/.gitkeep b/routes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/routes/api.php b/routes/api.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/routes/api.php @@ -0,0 +1 @@ +push('Users', route('users.index')); + }); + + Breadcrumbs::for('users.create', function (BreadcrumbTrail $trail) { + $trail->parent('users'); + $trail->push('Add User', route('users.create')); + }); + + Breadcrumbs::for('users.edit', function (BreadcrumbTrail $trail) { + $trail->parent('users'); + $trail->push('Edit User'); + }); + + Breadcrumbs::for('users.roles', function (BreadcrumbTrail $trail) { + $trail->parent('users'); + $trail->push('Roles', route('users.roles.index')); + }); + + Breadcrumbs::for('users.roles.create', function (BreadcrumbTrail $trail) { + $trail->parent('users.roles'); + $trail->push('Add Role', route('users.roles.create')); + }); + + Breadcrumbs::for('users.roles.edit', function (BreadcrumbTrail $trail) { + $trail->parent('users.roles'); + $trail->push('Edit Role'); + }); diff --git a/routes/web.php b/routes/web.php new file mode 100644 index 0000000..45b2ee5 --- /dev/null +++ b/routes/web.php @@ -0,0 +1,43 @@ +group(function () { + + Route::name('users.')->prefix('users')->group(function () { + Route::get('restore/{id}', [UsersController::class,'restore'])->name('restore'); + Route::get('datatables', [UsersController::class, 'dataForDatatables'])->name('datatables'); + Route::get('export', [UsersController::class, 'export'])->name('export'); + }); + Route::resource('users', UsersController::class); + + Route::name('users.')->group(function () { + Route::name('roles.')->prefix('roles')->group(function () { + Route::get('restore/{id}', [RolesController::class,'restore'])->name('restore'); + Route::get('datatables', [RolesController::class, 'dataForDatatables'])->name('datatables'); + Route::get('export', [RolesController ::class, 'export'])->name('export'); + }); + Route::resource('roles', RolesController::class); + + Route::resource('permissions', PermissionsController::class); + Route::name('permissions.')->prefix('permissions')->group(function () { + Route::get('restore/{id}', [PermissionsController::class,'restore'])->name('restore'); + Route::get('datatables', [PermissionsController::class, 'dataForDatatables'])->name('datatables'); + Route::get('export', [PermissionsController ::class, 'export'])->name('export'); + }); + }); +//}); + diff --git a/tests/Feature/.gitkeep b/tests/Feature/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/Unit/.gitkeep b/tests/Unit/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/vendor/autoload.php b/vendor/autoload.php new file mode 100644 index 0000000..3b71623 --- /dev/null +++ b/vendor/autoload.php @@ -0,0 +1,25 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * @see https://www.php-fig.org/psr/psr-0/ + * @see https://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + /** @var \Closure(string):void */ + private static $includeFile; + + /** @var string|null */ + private $vendorDir; + + // PSR-4 + /** + * @var array> + */ + private $prefixLengthsPsr4 = array(); + /** + * @var array> + */ + private $prefixDirsPsr4 = array(); + /** + * @var list + */ + private $fallbackDirsPsr4 = array(); + + // PSR-0 + /** + * List of PSR-0 prefixes + * + * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2'))) + * + * @var array>> + */ + private $prefixesPsr0 = array(); + /** + * @var list + */ + private $fallbackDirsPsr0 = array(); + + /** @var bool */ + private $useIncludePath = false; + + /** + * @var array + */ + private $classMap = array(); + + /** @var bool */ + private $classMapAuthoritative = false; + + /** + * @var array + */ + private $missingClasses = array(); + + /** @var string|null */ + private $apcuPrefix; + + /** + * @var array + */ + private static $registeredLoaders = array(); + + /** + * @param string|null $vendorDir + */ + public function __construct($vendorDir = null) + { + $this->vendorDir = $vendorDir; + self::initializeIncludeClosure(); + } + + /** + * @return array> + */ + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); + } + + return array(); + } + + /** + * @return array> + */ + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + /** + * @return list + */ + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + /** + * @return list + */ + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + /** + * @return array Array of classname => path + */ + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + * + * @return void + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + * + * @return void + */ + public function add($prefix, $paths, $prepend = false) + { + $paths = (array) $paths; + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + $paths = (array) $paths; + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 base directories + * + * @return void + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + * + * @return void + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + * + * @return void + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + * + * @return void + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + * + * @return void + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + + if (null === $this->vendorDir) { + return; + } + + if ($prepend) { + self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; + } else { + unset(self::$registeredLoaders[$this->vendorDir]); + self::$registeredLoaders[$this->vendorDir] = $this; + } + } + + /** + * Unregisters this instance as an autoloader. + * + * @return void + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + + if (null !== $this->vendorDir) { + unset(self::$registeredLoaders[$this->vendorDir]); + } + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return true|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + $includeFile = self::$includeFile; + $includeFile($file); + + return true; + } + + return null; + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + /** + * Returns the currently registered loaders keyed by their corresponding vendor directories. + * + * @return array + */ + public static function getRegisteredLoaders() + { + return self::$registeredLoaders; + } + + /** + * @param string $class + * @param string $ext + * @return string|false + */ + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath . '\\'; + if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } + + /** + * @return void + */ + private static function initializeIncludeClosure() + { + if (self::$includeFile !== null) { + return; + } + + /** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + * + * @param string $file + * @return void + */ + self::$includeFile = \Closure::bind(static function($file) { + include $file; + }, null, null); + } +} diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php new file mode 100644 index 0000000..51e734a --- /dev/null +++ b/vendor/composer/InstalledVersions.php @@ -0,0 +1,359 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer; + +use Composer\Autoload\ClassLoader; +use Composer\Semver\VersionParser; + +/** + * This class is copied in every Composer installed project and available to all + * + * See also https://getcomposer.org/doc/07-runtime.md#installed-versions + * + * To require its presence, you can require `composer-runtime-api ^2.0` + * + * @final + */ +class InstalledVersions +{ + /** + * @var mixed[]|null + * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null + */ + private static $installed; + + /** + * @var bool|null + */ + private static $canGetVendors; + + /** + * @var array[] + * @psalm-var array}> + */ + private static $installedByVendor = array(); + + /** + * Returns a list of all package names which are present, either by being installed, replaced or provided + * + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackages() + { + $packages = array(); + foreach (self::getInstalled() as $installed) { + $packages[] = array_keys($installed['versions']); + } + + if (1 === \count($packages)) { + return $packages[0]; + } + + return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); + } + + /** + * Returns a list of all package names with a specific type e.g. 'library' + * + * @param string $type + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackagesByType($type) + { + $packagesByType = array(); + + foreach (self::getInstalled() as $installed) { + foreach ($installed['versions'] as $name => $package) { + if (isset($package['type']) && $package['type'] === $type) { + $packagesByType[] = $name; + } + } + } + + return $packagesByType; + } + + /** + * Checks whether the given package is installed + * + * This also returns true if the package name is provided or replaced by another package + * + * @param string $packageName + * @param bool $includeDevRequirements + * @return bool + */ + public static function isInstalled($packageName, $includeDevRequirements = true) + { + foreach (self::getInstalled() as $installed) { + if (isset($installed['versions'][$packageName])) { + return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false; + } + } + + return false; + } + + /** + * Checks whether the given package satisfies a version constraint + * + * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: + * + * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') + * + * @param VersionParser $parser Install composer/semver to have access to this class and functionality + * @param string $packageName + * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package + * @return bool + */ + public static function satisfies(VersionParser $parser, $packageName, $constraint) + { + $constraint = $parser->parseConstraints((string) $constraint); + $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); + + return $provided->matches($constraint); + } + + /** + * Returns a version constraint representing all the range(s) which are installed for a given package + * + * It is easier to use this via isInstalled() with the $constraint argument if you need to check + * whether a given version of a package is installed, and not just whether it exists + * + * @param string $packageName + * @return string Version constraint usable with composer/semver + */ + public static function getVersionRanges($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + $ranges = array(); + if (isset($installed['versions'][$packageName]['pretty_version'])) { + $ranges[] = $installed['versions'][$packageName]['pretty_version']; + } + if (array_key_exists('aliases', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); + } + if (array_key_exists('replaced', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); + } + if (array_key_exists('provided', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); + } + + return implode(' || ', $ranges); + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['version'])) { + return null; + } + + return $installed['versions'][$packageName]['version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getPrettyVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['pretty_version'])) { + return null; + } + + return $installed['versions'][$packageName]['pretty_version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference + */ + public static function getReference($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['reference'])) { + return null; + } + + return $installed['versions'][$packageName]['reference']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. + */ + public static function getInstallPath($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @return array + * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool} + */ + public static function getRootPackage() + { + $installed = self::getInstalled(); + + return $installed[0]['root']; + } + + /** + * Returns the raw installed.php data for custom implementations + * + * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. + * @return array[] + * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} + */ + public static function getRawData() + { + @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + self::$installed = include __DIR__ . '/installed.php'; + } else { + self::$installed = array(); + } + } + + return self::$installed; + } + + /** + * Returns the raw data of all installed.php which are currently loaded for custom implementations + * + * @return array[] + * @psalm-return list}> + */ + public static function getAllRawData() + { + return self::getInstalled(); + } + + /** + * Lets you reload the static array from another file + * + * This is only useful for complex integrations in which a project needs to use + * this class but then also needs to execute another project's autoloader in process, + * and wants to ensure both projects have access to their version of installed.php. + * + * A typical case would be PHPUnit, where it would need to make sure it reads all + * the data it needs from this class, then call reload() with + * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure + * the project in which it runs can then also use this class safely, without + * interference between PHPUnit's dependencies and the project's dependencies. + * + * @param array[] $data A vendor/composer/installed.php data set + * @return void + * + * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $data + */ + public static function reload($data) + { + self::$installed = $data; + self::$installedByVendor = array(); + } + + /** + * @return array[] + * @psalm-return list}> + */ + private static function getInstalled() + { + if (null === self::$canGetVendors) { + self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); + } + + $installed = array(); + + if (self::$canGetVendors) { + foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + if (isset(self::$installedByVendor[$vendorDir])) { + $installed[] = self::$installedByVendor[$vendorDir]; + } elseif (is_file($vendorDir.'/composer/installed.php')) { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require $vendorDir.'/composer/installed.php'; + $installed[] = self::$installedByVendor[$vendorDir] = $required; + if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { + self::$installed = $installed[count($installed) - 1]; + } + } + } + } + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require __DIR__ . '/installed.php'; + self::$installed = $required; + } else { + self::$installed = array(); + } + } + + if (self::$installed !== array()) { + $installed[] = self::$installed; + } + + return $installed; + } +} diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE new file mode 100644 index 0000000..f27399a --- /dev/null +++ b/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) Nils Adermann, Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php new file mode 100644 index 0000000..0fb0a2c --- /dev/null +++ b/vendor/composer/autoload_classmap.php @@ -0,0 +1,10 @@ + $vendorDir . '/composer/InstalledVersions.php', +); diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php new file mode 100644 index 0000000..15a2ff3 --- /dev/null +++ b/vendor/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ + array($baseDir . '/tests'), + 'Modules\\Usermanagement\\Database\\Seeders\\' => array($baseDir . '/database/seeders'), + 'Modules\\Usermanagement\\Database\\Factories\\' => array($baseDir . '/database/factories'), + 'Modules\\Usermanagement\\' => array($baseDir . '/app'), +); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php new file mode 100644 index 0000000..765b43b --- /dev/null +++ b/vendor/composer/autoload_real.php @@ -0,0 +1,36 @@ +register(true); + + return $loader; + } +} diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php new file mode 100644 index 0000000..ab40c20 --- /dev/null +++ b/vendor/composer/autoload_static.php @@ -0,0 +1,51 @@ + + array ( + 'Modules\\Usermanagement\\Tests\\' => 29, + 'Modules\\Usermanagement\\Database\\Seeders\\' => 40, + 'Modules\\Usermanagement\\Database\\Factories\\' => 42, + 'Modules\\Usermanagement\\' => 23, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'Modules\\Usermanagement\\Tests\\' => + array ( + 0 => __DIR__ . '/../..' . '/tests', + ), + 'Modules\\Usermanagement\\Database\\Seeders\\' => + array ( + 0 => __DIR__ . '/../..' . '/database/seeders', + ), + 'Modules\\Usermanagement\\Database\\Factories\\' => + array ( + 0 => __DIR__ . '/../..' . '/database/factories', + ), + 'Modules\\Usermanagement\\' => + array ( + 0 => __DIR__ . '/../..' . '/app', + ), + ); + + public static $classMap = array ( + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInit2864198e9d1b1265f9eb249b3c2e2689::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit2864198e9d1b1265f9eb249b3c2e2689::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInit2864198e9d1b1265f9eb249b3c2e2689::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json new file mode 100644 index 0000000..87fda74 --- /dev/null +++ b/vendor/composer/installed.json @@ -0,0 +1,5 @@ +{ + "packages": [], + "dev": true, + "dev-package-names": [] +} diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php new file mode 100644 index 0000000..4e7f2a2 --- /dev/null +++ b/vendor/composer/installed.php @@ -0,0 +1,23 @@ + array( + 'name' => 'putrakuningan/usermanagement', + 'pretty_version' => '1.0.0+no-version-set', + 'version' => '1.0.0.0', + 'reference' => null, + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev' => true, + ), + 'versions' => array( + 'putrakuningan/usermanagement' => array( + 'pretty_version' => '1.0.0+no-version-set', + 'version' => '1.0.0.0', + 'reference' => null, + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev_requirement' => false, + ), + ), +); diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..7c6fa6e --- /dev/null +++ b/vite.config.js @@ -0,0 +1,4 @@ +export const paths = [ + 'Modules/Usermanagement/resources/assets/sass/app.scss', + 'Modules/Usermanagement/resources/assets/js/app.js', +];