diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml
new file mode 100644
index 0000000..6ecb734
--- /dev/null
+++ b/.github/workflows/cd.yml
@@ -0,0 +1,17 @@
+name: Laravel.php Log
+
+on: push
+
+jobs:
+ cd:
+ runs-on: ubuntu-latest
+ steps:
+ - name: cd
+ uses: tripteki/cd-package@1.0.0
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ repotoken: ${{ secrets.REPOSITORY_TOKEN }}
+ repouser: tripteki
+ repository: https://packagist.org
+ language: php
+ artifact: composer.json
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..061b3a5
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Trip Teknologi
+
+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/README.md b/README.md
new file mode 100644
index 0000000..f70cb21
--- /dev/null
+++ b/README.md
@@ -0,0 +1,92 @@
+
Log
+
+This package provides implementation of Auth Activity Log in repository pattern for Lumen and Laravel besides REST API starterpack of admin management with no intervention to codebase and keep clean.
+
+Getting Started
+---
+
+Installation :
+
+```
+$ composer require tripteki/laravelphp-log
+```
+
+How to use it :
+
+- Read detail optional instruction here [Log](https://spatie.be/docs/laravel-activitylog/v4/installation-and-setup).
+
+- Put `Tripteki\Log\Traits\LogTrait` to any of your model loggable then optionally you can configure `protected static` of `$recordName`, `$recordEvents`, and `$recordLists`.
+
+- Put `Tripteki\Log\Providers\LogServiceProvider` to service provider configuration list.
+
+- Put `Tripteki\Log\Providers\LogServiceProvider::ignoreConfig()` into `register` provider, then publish config file into your project's directory with running :
+
+```
+php artisan vendor:publish --tag=tripteki-laravelphp-log
+```
+
+- Put `Tripteki\Log\Providers\LogServiceProvider::ignoreMigrations()` into `register` provider, then publish migrations file into your project's directory with running (optionally) :
+
+```
+php artisan vendor:publish --tag=tripteki-laravelphp-log-migrations
+```
+
+- Migrate.
+
+```
+$ php artisan migrate
+```
+
+- Emit Event-Listener.
+
+```
+php artisan queue:work
+```
+
+- Publish tests file into your project's directory with running (optionally) :
+
+```
+php artisan vendor:publish --tag=tripteki-laravelphp-log-tests
+```
+
+- Sample :
+
+```php
+use Tripteki\Log\Contracts\Repository\Admin\ILogRepository as ILogAdminRepository;
+use Tripteki\Log\Contracts\Repository\ILogRepository;
+
+$logAdminRepository = app(ILogAdminRepository::class);
+
+// $logAdminRepository->get(5); //
+// $logAdminRepository->all(); //
+
+$repository = app(ILogRepository::class);
+// $repository->setUser(...); //
+// $repository->getUser(); //
+
+// $repository->archive(5); //
+// $repository->unarchive(5); //
+// $repository->get(5); //
+// $repository->all(); //
+```
+
+- Generate swagger files into your project's directory with putting this into your annotation configuration (optionally) :
+
+```
+base_path("app/Http/Controllers/Log")
+```
+
+```
+base_path("app/Http/Controllers/Admin/Log")
+```
+
+Usage
+---
+
+`php artisan adminer:install:log`
+
+Author
+---
+
+- Trip Teknologi ([@tripteki](https://linkedin.com/company/tripteki))
+- Hasby Maulana ([@hsbmaulana](https://linkedin.com/in/hsbmaulana))
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..af47163
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,57 @@
+{
+ "name": "tripteki/laravelphp-log",
+ "version": "1.0.0",
+ "description": "Trip Teknologi's Laravel.php Logs",
+
+ "readme": "README.md",
+ "license": "MIT",
+ "authors": [ { "name": "Trip Teknologi", "email": "tripteki.company@gmail.com" } ],
+ "homepage": "https://github.com/tripteki/laravelphp-log",
+ "support": { "issues": "https://github.com/tripteki/laravelphp-log/issues" },
+
+ "require": {
+
+ "php": "^8.0.2",
+
+ "tripteki/laravelphp-repository": "^1.0.0",
+ "tripteki/laravelphp-helpers": "^1.0.0",
+ "tripteki/laravelphp-adminer": "^1.0.0",
+ "tripteki/laravelphp-import-export": "^1.0.0",
+ "tripteki/laravelphp-request-response-query": "^1.0.0",
+
+ "spatie/laravel-activitylog": "^4.7.2"
+ },
+
+ "require-dev": {},
+
+ "suggest": {
+
+ "laravel/lumen-framework": "Required when using lumen framework (^9.0).",
+ "laravel/framework": "Required when using laravel framework (^9.0)."
+ },
+
+ "autoload": {
+
+ "psr-4": {
+
+ "Tripteki\\Log\\": "src/"
+ }
+ },
+
+ "autoload-dev": {},
+
+ "extra": {
+
+ "laravel": {
+
+ "dont-discover": [],
+
+ "providers": [
+
+ "Tripteki\\Log\\Providers\\LogServiceProvider"
+ ],
+
+ "aliases": []
+ }
+ }
+}
diff --git a/config/log.php b/config/log.php
new file mode 100644
index 0000000..4705577
--- /dev/null
+++ b/config/log.php
@@ -0,0 +1,18 @@
+ true,
+
+ "default_log_name" => "log",
+
+ "activity_model" => Tripteki\Log\Models\Admin\Log::class,
+ "table_name" => "logs",
+ "database_connection" => null,
+
+ "default_auth_driver" => null,
+
+ "subject_returns_soft_deleted_models" => false,
+
+ "delete_records_older_than_days" => 365,
+];
diff --git a/database/migrations/2023_01_20_000000_create_logs_table.php b/database/migrations/2023_01_20_000000_create_logs_table.php
new file mode 100644
index 0000000..8b43e8e
--- /dev/null
+++ b/database/migrations/2023_01_20_000000_create_logs_table.php
@@ -0,0 +1,73 @@
+keytype = app(AuthModelContract::class)->getKeyType();
+ }
+
+ /**
+ * @return void
+ */
+ public function up()
+ {
+ $keytype = $this->keytype;
+
+ Schema::connection(config("activitylog.database_connection"))->create(config("activitylog.table_name"), function (Blueprint $table) use ($keytype) {
+
+ $table->uuid("id");
+ $table->string("log_name")->nullable();
+ $table->text("description");
+
+ if (! LogServiceProvider::shouldSubjectUid()) {
+
+ $table->nullableMorphs("subject", "subject");
+
+ } else {
+
+ $table->nullableUuidMorphs("subject", "subject");
+ }
+
+ if ($keytype == "int") {
+
+ $table->nullableMorphs("causer", "causer");
+
+ } else if ($keytype == "string") {
+
+ $table->nullableUuidMorphs("causer", "causer");
+ }
+
+ $table->uuid("batch_uuid")->nullable();
+ $table->json("properties")->nullable();
+ $table->string("event")->nullable();
+ $table->timestamps();
+ $table->softDeletes();
+
+ $table->primary("id");
+ $table->index("log_name");
+ });
+ }
+
+ /**
+ * @return void
+ */
+ public function down()
+ {
+ Schema::connection(config("activitylog.database_connection"))->dropIfExists(config("activitylog.table_name"));
+ }
+};
diff --git a/src/Console/Commands/InstallCommand.php b/src/Console/Commands/InstallCommand.php
new file mode 100644
index 0000000..053d9fc
--- /dev/null
+++ b/src/Console/Commands/InstallCommand.php
@@ -0,0 +1,78 @@
+helper = $helper;
+ }
+
+ /**
+ * @return int
+ */
+ public function handle()
+ {
+ $this->installStack();
+
+ return 0;
+ }
+
+ /**
+ * @return int|null
+ */
+ protected function installStack()
+ {
+ (new Filesystem)->ensureDirectoryExists(base_path("routes/user"));
+ (new Filesystem)->ensureDirectoryExists(base_path("routes/admin"));
+ (new Filesystem)->copy(__DIR__."/../../../stubs/routes/user/log.php", base_path("routes/user/log.php"));
+ (new Filesystem)->copy(__DIR__."/../../../stubs/routes/admin/log.php", base_path("routes/admin/log.php"));
+ $this->helper->putRoute("api.php", "user/log.php");
+ $this->helper->putRoute("api.php", "admin/log.php");
+
+ (new Filesystem)->ensureDirectoryExists(app_path("Http/Controllers/Log"));
+ (new Filesystem)->copyDirectory(__DIR__."/../../../stubs/app/Http/Controllers/Log", app_path("Http/Controllers/Log"));
+ (new Filesystem)->ensureDirectoryExists(app_path("Http/Requests/Logs"));
+ (new Filesystem)->copyDirectory(__DIR__."/../../../stubs/app/Http/Requests/Logs", app_path("Http/Requests/Logs"));
+ (new Filesystem)->ensureDirectoryExists(app_path("Http/Controllers/Admin/Log"));
+ (new Filesystem)->copyDirectory(__DIR__."/../../../stubs/app/Http/Controllers/Admin/Log", app_path("Http/Controllers/Admin/Log"));
+ (new Filesystem)->ensureDirectoryExists(app_path("Imports/Logs"));
+ (new Filesystem)->copyDirectory(__DIR__."/../../../stubs/app/Imports/Logs", app_path("Imports/Logs"));
+ (new Filesystem)->ensureDirectoryExists(app_path("Exports/Logs"));
+ (new Filesystem)->copyDirectory(__DIR__."/../../../stubs/app/Exports/Logs", app_path("Exports/Logs"));
+ (new Filesystem)->ensureDirectoryExists(app_path("Http/Requests/Admin/Logs"));
+ (new Filesystem)->copyDirectory(__DIR__."/../../../stubs/app/Http/Requests/Admin/Logs", app_path("Http/Requests/Admin/Logs"));
+ (new Filesystem)->ensureDirectoryExists(app_path("Http/Responses"));
+
+ $this->helper->putTrait($this->helper->classToFile(get_class(app(AuthModelContract::class))), \Tripteki\Log\Traits\LogCauseTrait::class);
+
+ $this->info("Adminer Log scaffolding installed successfully.");
+ }
+};
diff --git a/src/Contracts/Repository/Admin/ILogRepository.php b/src/Contracts/Repository/Admin/ILogRepository.php
new file mode 100644
index 0000000..e99ed77
--- /dev/null
+++ b/src/Contracts/Repository/Admin/ILogRepository.php
@@ -0,0 +1,11 @@
+data = $data;
+ }
+};
diff --git a/src/Events/Archiving.php b/src/Events/Archiving.php
new file mode 100644
index 0000000..838e39a
--- /dev/null
+++ b/src/Events/Archiving.php
@@ -0,0 +1,24 @@
+data = $data;
+ }
+};
diff --git a/src/Events/Logs/Any.php b/src/Events/Logs/Any.php
new file mode 100644
index 0000000..043e1c1
--- /dev/null
+++ b/src/Events/Logs/Any.php
@@ -0,0 +1,40 @@
+event = $event;
+ $this->subject = $subject;
+ $this->object = $object;
+ }
+};
diff --git a/src/Events/Logs/Update.php b/src/Events/Logs/Update.php
new file mode 100644
index 0000000..c20b521
--- /dev/null
+++ b/src/Events/Logs/Update.php
@@ -0,0 +1,40 @@
+event = $event;
+ $this->subject = $subject;
+ $this->object = $object;
+ }
+};
diff --git a/src/Events/Unarchived.php b/src/Events/Unarchived.php
new file mode 100644
index 0000000..ae86f1a
--- /dev/null
+++ b/src/Events/Unarchived.php
@@ -0,0 +1,24 @@
+data = $data;
+ }
+};
diff --git a/src/Events/Unarchiving.php b/src/Events/Unarchiving.php
new file mode 100644
index 0000000..b598a9e
--- /dev/null
+++ b/src/Events/Unarchiving.php
@@ -0,0 +1,24 @@
+data = $data;
+ }
+};
diff --git a/src/Facades/CauserResolver.php b/src/Facades/CauserResolver.php
new file mode 100644
index 0000000..fc66006
--- /dev/null
+++ b/src/Facades/CauserResolver.php
@@ -0,0 +1,10 @@
+object->activitylogOptions = $event->object->getActivitylogOptions();
+
+ if (! $event->object->shouldLogEvent($event->event)) return;
+
+ $changes = $event->object->attributeValuesToBeLogged($event->event);
+ $description = $event->object->getDescriptionForEvent($event->event);
+ $log = $event->object->getLogNameToUse();
+
+ if ($description == "") return;
+ if ($event->object->isLogEmpty($changes) && ! $event->object->activitylogOptions->submitEmptyLogs) return;
+
+ /**
+ * Pipeline.
+ */
+ $event = app(Pipeline::class)
+ ->send(new EventLogBag($event->event, $event->object, $changes, $event->object->activitylogOptions))
+ ->through(call_user_func($event->subject."::"."changesPipes"))
+ ->thenReturn();
+
+ /**
+ * Log.
+ */
+ $logger = app(ActivityLogger::class)
+ ->useLog($log)
+ ->event($event->event)
+ ->performedOn($event->object)
+ ->withProperties($event->changes);
+
+ if (method_exists($event->object, "tapActivity")) $logger->tap([ $event->object, "tapActivity", ], $event->event);
+
+ $logger->log($description);
+
+ /**
+ * Transaction.
+ */
+ $event->object->activitylogOptions = null;
+ $event->object->save();
+
+ DB::commit();
+
+ } catch (Exception $exception) {
+
+ DB::rollback();
+ }
+ }
+};
diff --git a/src/Listeners/Logs/UpdateListener.php b/src/Listeners/Logs/UpdateListener.php
new file mode 100644
index 0000000..180171d
--- /dev/null
+++ b/src/Listeners/Logs/UpdateListener.php
@@ -0,0 +1,41 @@
+object->oldAttributes = call_user_func($event->subject."::"."logChanges", app($event->subject)->setRawAttributes($object->getRawOriginal()));
+ $event->object->save();
+
+ DB::commit();
+
+ } catch (Exception $exception) {
+
+ DB::rollback();
+ }
+ }
+};
diff --git a/src/Models/Admin/Log.php b/src/Models/Admin/Log.php
new file mode 100644
index 0000000..7f0aeda
--- /dev/null
+++ b/src/Models/Admin/Log.php
@@ -0,0 +1,12 @@
+ \Tripteki\Log\Repositories\Eloquent\LogRepository::class,
+ \Tripteki\Log\Contracts\Repository\Admin\ILogRepository::class => \Tripteki\Log\Repositories\Eloquent\Admin\LogRepository::class,
+ ];
+
+ /**
+ * @var bool
+ */
+ public static $subjectUid = true;
+
+ /**
+ * @var bool
+ */
+ public static $loadConfig = true;
+
+ /**
+ * @var bool
+ */
+ public static $runsMigrations = true;
+
+ /**
+ * @return bool
+ */
+ public static function shouldSubjectUid()
+ {
+ return static::$subjectUid;
+ }
+
+ /**
+ * @return bool
+ */
+ public static function shouldLoadConfig()
+ {
+ return static::$loadConfig;
+ }
+
+ /**
+ * @return bool
+ */
+ public static function shouldRunMigrations()
+ {
+ return static::$runsMigrations;
+ }
+
+ /**
+ * @return void
+ */
+ public static function ignoreSubjectUid()
+ {
+ static::$subjectUid = false;
+ }
+
+ /**
+ * @return void
+ */
+ public static function ignoreConfig()
+ {
+ static::$loadConfig = false;
+ }
+
+ /**
+ * @return void
+ */
+ public static function ignoreMigrations()
+ {
+ static::$runsMigrations = false;
+ }
+
+ /**
+ * @return void
+ */
+ public function boot()
+ {
+ parent::boot();
+
+ $this->dataEventListener();
+
+ $this->registerPublishers();
+ $this->registerConfigs();
+ $this->registerCommands();
+ $this->registerMigrations();
+ }
+
+ /**
+ * @return void
+ */
+ protected function registerConfigs()
+ {
+ if (static::shouldLoadConfig()) {
+
+ $this->app["config"]->set("activitylog", []);
+ $this->mergeConfigFrom(__DIR__."/../../config/log.php", "activitylog");
+ }
+ }
+
+ /**
+ * @return void
+ */
+ protected function registerCommands()
+ {
+ if (! $this->app->isProduction() && $this->app->runningInConsole()) {
+
+ $this->commands(
+ [
+ InstallCommand::class,
+ ]);
+ }
+ }
+
+ /**
+ * @return void
+ */
+ protected function registerMigrations()
+ {
+ if ($this->app->runningInConsole() && static::shouldRunMigrations()) {
+
+ $this->loadMigrationsFrom(__DIR__."/../../database/migrations");
+ }
+ }
+
+ /**
+ * @return void
+ */
+ protected function registerPublishers()
+ {
+ $this->publishes(
+ [
+ __DIR__."/../../config/log.php" => config_path("activitylog.php"),
+ ],
+
+ "tripteki-laravelphp-log");
+
+ if (! static::shouldRunMigrations()) {
+
+ $this->publishes(
+ [
+ __DIR__."/../../database/migrations" => database_path("migrations"),
+ ],
+
+ "tripteki-laravelphp-log-migrations");
+ }
+
+ $this->publishes(
+ [
+ __DIR__."/../../stubs/tests/Feature/Log/LogTest.stub" => base_path("tests/Feature/Log/LogTest.php"),
+ ],
+
+ "tripteki-laravelphp-log-tests");
+ }
+
+ /**
+ * @return void
+ */
+ public function dataEventListener()
+ {
+ Log::observe(UniqueIdObserver::class);
+
+ Event::listen(Update::class, [ UpdateListener::class, "handle", ]);
+ Event::listen(Any::class, [ AnyListener::class, "handle", ]);
+ }
+};
diff --git a/src/Repositories/Eloquent/Admin/LogRepository.php b/src/Repositories/Eloquent/Admin/LogRepository.php
new file mode 100644
index 0000000..0505de9
--- /dev/null
+++ b/src/Repositories/Eloquent/Admin/LogRepository.php
@@ -0,0 +1,47 @@
+ $querystring["limit"] ?? request()->query("limit", 10),
+ "current_page" => $querystring["current_page"] ?? request()->query("current_page", 1),
+ ];
+ extract($querystringed);
+
+ $field = "updated_at";
+ $fields = [ "id", "causer_type", "causer_id", "subject_type", "subject_id", "log_name", "properties", "event", "created_at", "updated_at", ];
+
+ $content = QueryBuilder::for(\Spatie\Activitylog\ActivitylogServiceProvider::getActivityModelInstance()->with([ "subject", "causer", ])->withTrashed())->
+ defaultSort("-".$field)->
+ allowedSorts($fields)->
+ allowedFilters($fields)->
+ paginate($limit, $fields, "current_page", $current_page)->appends(empty($querystring) ? request()->query() : $querystringed);
+
+ return $content;
+ }
+
+ /**
+ * @param int|string $identifier
+ * @param array $querystring|[]
+ * @return mixed
+ */
+ public function get($identifier, $querystring = [])
+ {
+ $content = \Spatie\Activitylog\ActivitylogServiceProvider::getActivityModelInstance()->withTrashed()->findOrFail($identifier);
+ $content = $content->load([ "subject", "causer", ]);
+
+ return $content;
+ }
+};
diff --git a/src/Repositories/Eloquent/LogRepository.php b/src/Repositories/Eloquent/LogRepository.php
new file mode 100644
index 0000000..f89f889
--- /dev/null
+++ b/src/Repositories/Eloquent/LogRepository.php
@@ -0,0 +1,128 @@
+ $querystring["type"] ?? request()->query("type", "unarchived"),
+ "limit" => $querystring["limit"] ?? request()->query("limit", 10),
+ "current_page" => $querystring["current_page"] ?? request()->query("current_page", 1),
+ ];
+ extract($querystringed);
+
+ $content = $this->user;
+ $action = $content->actions();
+
+ if ($type == "archived") {
+
+ $action = $action->onlyTrashed();
+ }
+
+ $field = "updated_at";
+ $fields = [ "id", "subject_type", "subject_id", "log_name", "properties", "event", "created_at", "updated_at", ];
+
+ $content = $content->setRelation("actions",
+ QueryBuilder::for($action)->
+ defaultSort("-".$field)->
+ allowedSorts($fields)->
+ allowedFilters($fields)->
+ paginate($limit, $fields, "current_page", $current_page)->appends(empty($querystring) ? request()->query() : $querystringed));
+ $content = $content->loadCount("actions");
+
+ return collect($content)->only([ "actions_count", "actions", ]);
+ }
+
+ /**
+ * @param int|string $identifier
+ * @param array $querystring|[]
+ * @return mixed
+ */
+ public function get($identifier, $querystring = [])
+ {
+ $content = $this->user->actions()->findOrFail($identifier);
+
+ return $content;
+ }
+
+ /**
+ * @param int|string $identifier
+ * @return mixed
+ */
+ public function delete($identifier)
+ {
+ $content = $this->user->actions()->findOrFail($identifier);
+
+ DB::beginTransaction();
+
+ try {
+
+ $content->delete();
+
+ DB::commit();
+
+ } catch (Exception $exception) {
+
+ DB::rollback();
+ }
+
+ return $content;
+ }
+
+ /**
+ * @param int|string $identifier
+ * @return mixed
+ */
+ public function archive($identifier)
+ {
+ $content = $this->delete($identifier);
+
+ event(new Archived($content));
+
+ return $content;
+ }
+
+ /**
+ * @param int|string $identifier
+ * @return mixed
+ */
+ public function unarchive($identifier)
+ {
+ $content = $this->user->actions()->withTrashed()->findOrFail($identifier);
+
+ DB::beginTransaction();
+
+ try {
+
+ $content->restore();
+
+ DB::commit();
+
+ event(new Unarchived($content));
+
+ } catch (Exception $exception) {
+
+ DB::rollback();
+ }
+
+ return $content;
+ }
+};
diff --git a/src/Repositories/QueryBuilder/.gitkeep b/src/Repositories/QueryBuilder/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/src/Traits/LogCauseTrait.php b/src/Traits/LogCauseTrait.php
new file mode 100644
index 0000000..43c551c
--- /dev/null
+++ b/src/Traits/LogCauseTrait.php
@@ -0,0 +1,10 @@
+each(function ($event) use ($class) {
+
+ if ($event === "updated") {
+
+ static::updating(function (Model $model) use ($event, $class) {
+
+ event(new Update($event, $class, $model));
+ });
+ }
+
+ static::$event(function (Model $model) use ($event, $class) {
+
+ event(new Any($event, $class, $model));
+ });
+ });
+ }
+
+ /**
+ * @return array
+ */
+ public static function changesPipes(): array
+ {
+ return static::$changesPipes;
+ }
+
+ /**
+ * @return \Spatie\Activitylog\LogOptions
+ */
+ public function getActivitylogOptions(): LogOptions
+ {
+ $log = LogOptions::defaults()
+ ->logOnlyDirty()
+ ->useLogName(isset(static::$recordName) ? static::$recordName : get_class($this));
+
+ if (isset(static::$recordLists)) {
+
+ $log = $log->logOnly(static::$recordLists);
+
+ } else {
+
+ $log = $log->logFillable();
+ }
+
+ return $log;
+ }
+};
diff --git a/stubs/app/Exports/Logs/.gitkeep b/stubs/app/Exports/Logs/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/stubs/app/Http/Controllers/Admin/Log/LogAdminController.php b/stubs/app/Http/Controllers/Admin/Log/LogAdminController.php
new file mode 100644
index 0000000..0eb3bbf
--- /dev/null
+++ b/stubs/app/Http/Controllers/Admin/Log/LogAdminController.php
@@ -0,0 +1,111 @@
+logAdminRepository = $logAdminRepository;
+ }
+
+ /**
+ * @OA\Get(
+ * path="/admin/logs",
+ * tags={"Admin Log"},
+ * summary="Index",
+ * @OA\Parameter(
+ * required=false,
+ * in="query",
+ * name="limit",
+ * description="Log's Pagination Limit."
+ * ),
+ * @OA\Parameter(
+ * required=false,
+ * in="query",
+ * name="current_page",
+ * description="Log's Pagination Current Page."
+ * ),
+ * @OA\Parameter(
+ * required=false,
+ * in="query",
+ * name="order",
+ * description="Log's Pagination Order."
+ * ),
+ * @OA\Parameter(
+ * required=false,
+ * in="query",
+ * name="filter[]",
+ * description="Log's Pagination Filter."
+ * ),
+ * @OA\Response(
+ * response=200,
+ * description="Success."
+ * )
+ * )
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function index(Request $request)
+ {
+ $data = [];
+ $statecode = 200;
+
+ $data = $this->logAdminRepository->all();
+
+ return iresponse($data, $statecode);
+ }
+
+ /**
+ * @OA\Get(
+ * path="/admin/logs/{log}",
+ * tags={"Admin Log"},
+ * summary="Show",
+ * @OA\Parameter(
+ * required=true,
+ * in="path",
+ * name="log",
+ * description="Log's Log."
+ * ),
+ * @OA\Response(
+ * response=200,
+ * description="Success."
+ * ),
+ * @OA\Response(
+ * response=404,
+ * description="Not Found."
+ * )
+ * )
+ *
+ * @param \App\Http\Requests\Admin\Logs\LogShowValidation $request
+ * @param string $log
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function show(LogShowValidation $request, $log)
+ {
+ $form = $request->validated();
+ $data = [];
+ $statecode = 200;
+
+ $data = $this->logAdminRepository->get($log);
+
+ return iresponse($data, $statecode);
+ }
+};
diff --git a/stubs/app/Http/Controllers/Log/LogController.php b/stubs/app/Http/Controllers/Log/LogController.php
new file mode 100644
index 0000000..e5378d7
--- /dev/null
+++ b/stubs/app/Http/Controllers/Log/LogController.php
@@ -0,0 +1,194 @@
+logRepository = $logRepository;
+ }
+
+ /**
+ * @OA\Get(
+ * path="/logs",
+ * tags={"Logs"},
+ * summary="Index",
+ * security={{ "bearerAuth": {} }},
+ * @OA\Parameter(
+ * required=false,
+ * in="query",
+ * name="type",
+ * schema={"type": "string", "enum": {"archived", "unarchived"}},
+ * description="Log's Type."
+ * ),
+ * @OA\Parameter(
+ * required=false,
+ * in="query",
+ * name="limit",
+ * description="Log's Pagination Limit."
+ * ),
+ * @OA\Parameter(
+ * required=false,
+ * in="query",
+ * name="current_page",
+ * description="Log's Pagination Current Page."
+ * ),
+ * @OA\Parameter(
+ * required=false,
+ * in="query",
+ * name="order",
+ * description="Log's Pagination Order."
+ * ),
+ * @OA\Parameter(
+ * required=false,
+ * in="query",
+ * name="filter[]",
+ * description="Log's Pagination Filter."
+ * ),
+ * @OA\Response(
+ * response=200,
+ * description="Success."
+ * )
+ * )
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function index(Request $request)
+ {
+ $data = [];
+ $statecode = 200;
+
+ $this->logRepository->setUser($request->user());
+
+ $data = $this->logRepository->all();
+
+ return iresponse($data, $statecode);
+ }
+
+ /**
+ * @OA\Get(
+ * path="/logs/{log}",
+ * tags={"Logs"},
+ * summary="Show",
+ * security={{ "bearerAuth": {} }},
+ * @OA\Parameter(
+ * required=true,
+ * in="path",
+ * name="log",
+ * description="Log's Log."
+ * ),
+ * @OA\Response(
+ * response=200,
+ * description="Success."
+ * ),
+ * @OA\Response(
+ * response=404,
+ * description="Not Found."
+ * )
+ * )
+ *
+ * @param \App\Http\Requests\Logs\LogShowValidation $request
+ * @param string $log
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function show(LogShowValidation $request, $log)
+ {
+ $form = $request->validated();
+ $data = [];
+ $statecode = 200;
+
+ $this->logRepository->setUser($request->user());
+
+ $data = $this->logRepository->get($log);
+
+ return iresponse($data, $statecode);
+ }
+
+ /**
+ * @OA\Put(
+ * path="/logs/{context}",
+ * tags={"Logs"},
+ * summary="Update",
+ * security={{ "bearerAuth": {} }},
+ * @OA\Parameter(
+ * required=true,
+ * in="path",
+ * name="context",
+ * schema={"type": "string", "enum": {"archive", "unarchive"}},
+ * description="Log's Context."
+ * ),
+ * @OA\RequestBody(
+ * @OA\MediaType(
+ * mediaType="application/x-www-form-urlencoded",
+ * @OA\Schema(
+ * @OA\Property(
+ * property="logs",
+ * type="array",
+ * @OA\Items(type="string"),
+ * description="Log's Logs."
+ * )
+ * )
+ * )
+ * ),
+ * @OA\Response(
+ * response=201,
+ * description="Created."
+ * ),
+ * @OA\Response(
+ * response=422,
+ * description="Unprocessable Entity."
+ * ),
+ * @OA\Response(
+ * response=404,
+ * description="Not Found."
+ * )
+ * )
+ *
+ * @param \App\Http\Requests\Logs\LogUpdateValidation $request
+ * @param string $context
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function update(LogUpdateValidation $request, $context)
+ {
+ $form = $request->validated();
+ $data = [];
+ $statecode = 202;
+
+ $this->logRepository->setUser($request->user());
+
+ if ($this->logRepository->getUser()) {
+
+ foreach ($form["logs"] as $log) {
+
+ if ($context == LogUpdateValidation::ARCHIVE) $data[] = $this->logRepository->archive($log);
+ else if ($context == LogUpdateValidation::UNARCHIVE) $data[] = $this->logRepository->unarchive($log);
+ }
+
+ if ($data) {
+
+ $statecode = 201;
+ }
+ }
+
+ return iresponse($data, $statecode);
+ }
+};
diff --git a/stubs/app/Http/Requests/Admin/Logs/LogShowValidation.php b/stubs/app/Http/Requests/Admin/Logs/LogShowValidation.php
new file mode 100644
index 0000000..e7c3435
--- /dev/null
+++ b/stubs/app/Http/Requests/Admin/Logs/LogShowValidation.php
@@ -0,0 +1,40 @@
+ $this->route("log"),
+ ];
+ }
+
+ /**
+ * @return bool
+ */
+ public function authorize()
+ {
+ return true;
+ }
+
+ /**
+ * @return array
+ */
+ public function rules()
+ {
+ $provider = \Spatie\Activitylog\ActivitylogServiceProvider::getActivityModelInstance();
+
+ return [
+
+ "log" => "required|string|exists:".get_class($provider).",".keyName($provider),
+ ];
+ }
+};
diff --git a/stubs/app/Http/Requests/Logs/LogShowValidation.php b/stubs/app/Http/Requests/Logs/LogShowValidation.php
new file mode 100644
index 0000000..b75eb93
--- /dev/null
+++ b/stubs/app/Http/Requests/Logs/LogShowValidation.php
@@ -0,0 +1,51 @@
+ $this->route("log"),
+ ];
+ }
+
+ /**
+ * @return bool
+ */
+ public function authorize()
+ {
+ return true;
+ }
+
+ /**
+ * @return array
+ */
+ public function rules()
+ {
+ $provider = \Spatie\Activitylog\ActivitylogServiceProvider::getActivityModelInstance();
+
+ return [
+
+ "log" => [
+
+ "required",
+ "string",
+ Rule::exists(get_class($provider), keyName($provider))->where(function ($query) {
+
+ return $query->where("causer_type", get_class(app(AuthModelContract::class)))->where("causer_id", Auth::id());
+ }),
+ ],
+ ];
+ }
+};
diff --git a/stubs/app/Http/Requests/Logs/LogUpdateValidation.php b/stubs/app/Http/Requests/Logs/LogUpdateValidation.php
new file mode 100644
index 0000000..1923aef
--- /dev/null
+++ b/stubs/app/Http/Requests/Logs/LogUpdateValidation.php
@@ -0,0 +1,61 @@
+ $this->route("context"),
+ ];
+ }
+
+ /**
+ * @return bool
+ */
+ public function authorize()
+ {
+ return true;
+ }
+
+ /**
+ * @return array
+ */
+ public function rules()
+ {
+ $provider = \Spatie\Activitylog\ActivitylogServiceProvider::getActivityModelInstance();
+
+ return [
+
+ "context" => "required|string|in:".self::ARCHIVE.",".self::UNARCHIVE,
+ "logs" => "required|array",
+ "logs.*" => [
+
+ Rule::exists(get_class($provider), keyName($provider))->where(function ($query) {
+
+ return $query->where("causer_type", get_class(app(AuthModelContract::class)))->where("causer_id", Auth::id());
+ }),
+ ],
+ ];
+ }
+};
diff --git a/stubs/app/Http/Responses/.gitkeep b/stubs/app/Http/Responses/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/stubs/app/Imports/Logs/.gitkeep b/stubs/app/Imports/Logs/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/stubs/routes/admin/log.php b/stubs/routes/admin/log.php
new file mode 100644
index 0000000..c81c42d
--- /dev/null
+++ b/stubs/routes/admin/log.php
@@ -0,0 +1,12 @@
+middleware(config("adminer.middleware.admin"))->group(function () {
+
+ /**
+ * Logs.
+ */
+ Route::apiResource("logs", LogAdminController::class)->only([ "index", "show", ])->parameters([ "logs" => "log", ]);
+});
diff --git a/stubs/routes/user/log.php b/stubs/routes/user/log.php
new file mode 100644
index 0000000..5c11008
--- /dev/null
+++ b/stubs/routes/user/log.php
@@ -0,0 +1,14 @@
+middleware(config("adminer.middleware.user"))->group(function () {
+
+ /**
+ * Logs.
+ */
+ Route::get("logs", [ LogController::class, "index", ]);
+ Route::get("logs/{log}", [ LogController::class, "show", ]);
+ Route::put("logs/{context}", [ LogController::class, "update", ]);
+});
diff --git a/stubs/tests/Feature/Log/LogTest.stub b/stubs/tests/Feature/Log/LogTest.stub
new file mode 100644
index 0000000..c4fed29
--- /dev/null
+++ b/stubs/tests/Feature/Log/LogTest.stub
@@ -0,0 +1,24 @@
+user();
+ $this->actingAs($user);
+
+ $data = $this->get(/* config("adminer.route.user") ?? */"api"."/logs");
+ $data->assertStatus(200);
+ }
+};