diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index 06c2fec7..f4fe7477 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -39,7 +39,7 @@ jobs: - 3306/tcp options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 env: - DB_DATABASE: db_test + DB_DATABASE: testbench DB_USERNAME: root DB_PASSWORD: root steps: diff --git a/config/system.php b/config/system.php index d50e5c01..a0951233 100644 --- a/config/system.php +++ b/config/system.php @@ -141,7 +141,7 @@ | */ - 'urlPolicy' => env('IGNITER_URL_POLICY', 'detect'), + 'urlPolicy' => env('IGNITER_URL_POLICY', 'force'), /* |-------------------------------------------------------------------------- diff --git a/database/migrations/admin/2025_11_15_165912_add_indexes.php b/database/migrations/admin/2025_11_15_165912_add_indexes.php new file mode 100644 index 00000000..5f5433b8 --- /dev/null +++ b/database/migrations/admin/2025_11_15_165912_add_indexes.php @@ -0,0 +1,24 @@ +index(['object_type', 'object_id', 'created_at'], 'idx_status_history_object_created'); + $table->index(['object_type', 'object_id', 'status_history_id'], 'idx_status_history_object_status'); + }); + } + + public function down(): void + { + Schema::table('status_history', function(Blueprint $table) { + $table->dropIndex('idx_status_history_object_created'); + $table->dropIndex('idx_status_history_object_status'); + }); + } +}; diff --git a/database/migrations/system/2025_03_29_164243_remove_deprecated_code_from_mail_layouts.php b/database/migrations/system/2025_03_29_164243_remove_deprecated_code_from_mail_layouts.php index e5cbb1a9..fe64cb31 100644 --- a/database/migrations/system/2025_03_29_164243_remove_deprecated_code_from_mail_layouts.php +++ b/database/migrations/system/2025_03_29_164243_remove_deprecated_code_from_mail_layouts.php @@ -1,6 +1,7 @@ index(['attachment_type', 'attachment_id', 'priority'], 'idx_media_attachments_type_id_priority'); + }); + + Schema::table('languages', function(Blueprint $table) { + $table->index(['status', 'is_default'], 'idx_languages_status_default'); + }); + + Schema::table('countries', function(Blueprint $table) { + $table->index(['status', 'is_default'], 'idx_countries_status_default'); + }); + + Schema::table('currencies', function(Blueprint $table) { + $table->index(['currency_status', 'is_default'], 'idx_currencies_status_default'); + }); + } + + public function down(): void + { + Schema::table('media_attachments', function(Blueprint $table) { + $table->dropIndex('idx_media_attachments_type_id_priority'); + }); + + Schema::table('languages', function(Blueprint $table) { + $table->dropIndex('idx_languages_status_default'); + }); + + Schema::table('countries', function(Blueprint $table) { + $table->dropIndex('idx_countries_status_default'); + }); + + Schema::table('currencies', function(Blueprint $table) { + $table->dropIndex('idx_currencies_status_default'); + }); + } +}; diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 677ece1b..dbcc65dd 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -21,8 +21,7 @@ - - + diff --git a/rector.php b/rector.php index 63af21de..238de956 100644 --- a/rector.php +++ b/rector.php @@ -8,7 +8,6 @@ use Rector\CodeQuality\Rector\Ternary\SwitchNegatedTernaryRector; use Rector\CodingStyle\Rector\ClassMethod\NewlineBeforeNewAssignSetRector; use Rector\CodingStyle\Rector\FuncCall\FunctionFirstClassCallableRector; -use Rector\CodingStyle\Rector\FunctionLike\FunctionLikeToFirstClassCallableRector; use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\ClassMethod\RemoveUselessParamTagRector; use Rector\Strict\Rector\Empty_\DisallowedEmptyRuleFixerRector; @@ -45,7 +44,6 @@ __DIR__.'/src/Flame/Database/Concerns/HasRelationships.php', ], FunctionFirstClassCallableRector::class, - FunctionLikeToFirstClassCallableRector::class, SimplifyEmptyCheckOnEmptyArrayRector::class, StrictStringParamConcatRector::class => [ __DIR__.'/src/Flame/Database/Concerns/HasRelationships.php', diff --git a/resources/models/main/theme.php b/resources/models/main/theme.php index 56d88bf6..868943a1 100644 --- a/resources/models/main/theme.php +++ b/resources/models/main/theme.php @@ -83,7 +83,7 @@ ], 'reset' => [ 'label' => 'lang:igniter::system.themes.button_reset_to_default', - 'class' => 'btn btn-secondary', + 'class' => 'btn btn-default', 'context' => 'edit', 'data-request' => 'onReset', 'data-request-confirm' => 'lang:igniter::admin.alert_warning_confirm', diff --git a/resources/views/system/_partials/pagination/simple_default.blade.php b/resources/views/system/_partials/pagination/simple_default.blade.php index eeeb9117..82acee8b 100644 --- a/resources/views/system/_partials/pagination/simple_default.blade.php +++ b/resources/views/system/_partials/pagination/simple_default.blade.php @@ -1,5 +1,5 @@ @if ($paginator->hasPages()) -
    +
      @if ($paginator->onFirstPage())
    • @lang('pagination.previous')
    • @else diff --git a/src/Admin/Widgets/Lists.php b/src/Admin/Widgets/Lists.php index 352d306e..5e98dd0b 100644 --- a/src/Admin/Widgets/Lists.php +++ b/src/Admin/Widgets/Lists.php @@ -220,20 +220,23 @@ protected function prepareModel(): Builder $primarySearchable = []; $relationSearchable = []; + // Get the grammar from the model's connection to ensure it's properly initialized + $schemaGrammar = $query->getQuery()->getGrammar(); + if (!empty($this->searchTerm) && ($searchableColumns = $this->getSearchableColumns())) { foreach ($searchableColumns as $column) { // Relation if ($this->isColumnRelated($column)) { $table = DB::getTablePrefix().$this->model->{$column->relation}()->getModel()->getTable(); $columnName = isset($column->sqlSelect) - ? DB::raw($this->parseTableName($column->sqlSelect, $table))->getValue(DB::connection()->getSchemaGrammar()) + ? DB::raw($this->parseTableName($column->sqlSelect, $table))->getValue($schemaGrammar) : $table.'.'.$column->valueFrom; $relationSearchable[$column->relation][] = $columnName; } // Primary else { $columnName = isset($column->sqlSelect) - ? DB::raw($this->parseTableName($column->sqlSelect, $primaryTable))->getValue(DB::connection()->getSchemaGrammar()) + ? DB::raw($this->parseTableName($column->sqlSelect, $primaryTable))->getValue($schemaGrammar) : DB::getTablePrefix().$primaryTable.'.'.$column->columnName; $primarySearchable[] = $columnName; diff --git a/src/Flame/Composer/Manager.php b/src/Flame/Composer/Manager.php index d8e3dd64..381aea6b 100644 --- a/src/Flame/Composer/Manager.php +++ b/src/Flame/Composer/Manager.php @@ -23,7 +23,7 @@ */ class Manager { - protected const string REPOSITORY_HOST = 'satis.tastyigniter.com'; + protected const string REPOSITORY_HOST = 'composer.tastyigniter.com'; /** The primary composer instance. */ protected ?ClassLoader $loader = null; @@ -86,9 +86,10 @@ protected function loadInstalledPackages(bool $fresh = false): Collection ->mapWithKeys(function(array $package): array { if ($package['name'] === PackageInfo::CORE) { $package['code'] = PackageInfo::CORE_CODE; - $package['type'] = PackageInfo::CORE_TYPE; } + $package['type'] = $this->getPackageType($package); + return [$this->getPackageCode($package) => $package]; }); } @@ -128,6 +129,20 @@ protected function getPackageCode(array $package): mixed array_get($package, 'extra.tastyigniter-theme.code'))); } + protected function getPackageType(array $package): ?string + { + if (array_get($package, 'name') === PackageInfo::CORE) { + return PackageInfo::CORE_TYPE; + } + + $packageType = 'extension'; + if (array_get($package, 'extra.tastyigniter-theme')) { + $packageType = 'theme'; + } + + return $packageType; + } + // // // diff --git a/src/Flame/Database/Relations/BelongsToMany.php b/src/Flame/Database/Relations/BelongsToMany.php index b622d7f1..c43b4f10 100644 --- a/src/Flame/Database/Relations/BelongsToMany.php +++ b/src/Flame/Database/Relations/BelongsToMany.php @@ -36,7 +36,6 @@ class BelongsToMany extends BelongsToManyBase * @param string $foreignPivotKey * @param string $relatedPivotKey * @param string $relationName - * @return void */ public function __construct( Builder $query, diff --git a/src/Flame/Database/Relations/HasMany.php b/src/Flame/Database/Relations/HasMany.php index 4636c3a1..d86c1672 100644 --- a/src/Flame/Database/Relations/HasMany.php +++ b/src/Flame/Database/Relations/HasMany.php @@ -20,7 +20,6 @@ class HasMany extends HasManyBase /** * Create a new has many relationship instance. - * @return void */ public function __construct(Builder $query, Model $parent, $foreignKey, $localKey, $relationName = null) { diff --git a/src/Flame/Database/Relations/HasManyThrough.php b/src/Flame/Database/Relations/HasManyThrough.php index 9406453f..4ba97a6c 100644 --- a/src/Flame/Database/Relations/HasManyThrough.php +++ b/src/Flame/Database/Relations/HasManyThrough.php @@ -19,7 +19,6 @@ class HasManyThrough extends HasManyThroughBase /** * Create a new has many relationship instance. * @param string $relationName - * @return void */ public function __construct( Builder $query, diff --git a/src/Flame/Database/Relations/HasOne.php b/src/Flame/Database/Relations/HasOne.php index 8ef8da6e..fed55a10 100644 --- a/src/Flame/Database/Relations/HasOne.php +++ b/src/Flame/Database/Relations/HasOne.php @@ -19,7 +19,6 @@ class HasOne extends HasOneBase /** * Create a new has many relationship instance. - * @return void */ public function __construct(Builder $query, Model $parent, $foreignKey, $localKey, $relationName = null) { diff --git a/src/Flame/Database/Relations/HasOneThrough.php b/src/Flame/Database/Relations/HasOneThrough.php index b424f93b..6267523a 100644 --- a/src/Flame/Database/Relations/HasOneThrough.php +++ b/src/Flame/Database/Relations/HasOneThrough.php @@ -19,7 +19,6 @@ class HasOneThrough extends HasOneThroughBase /** * Create a new has many relationship instance. * @param string $relationName - * @return void */ public function __construct( Builder $query, diff --git a/src/Flame/Database/Relations/MorphMany.php b/src/Flame/Database/Relations/MorphMany.php index e59f99b2..55dbc363 100644 --- a/src/Flame/Database/Relations/MorphMany.php +++ b/src/Flame/Database/Relations/MorphMany.php @@ -20,7 +20,6 @@ class MorphMany extends MorphManyBase /** * Create a new has many relationship instance. - * @return void */ public function __construct(Builder $query, Model $parent, $type, $id, $localKey, $relationName = null) { diff --git a/src/Flame/Database/Relations/MorphOne.php b/src/Flame/Database/Relations/MorphOne.php index a1f7460f..82b02d3e 100644 --- a/src/Flame/Database/Relations/MorphOne.php +++ b/src/Flame/Database/Relations/MorphOne.php @@ -19,7 +19,6 @@ class MorphOne extends MorphOneBase /** * Create a new has many relationship instance. - * @return void */ public function __construct(Builder $query, Model $parent, $type, $id, $localKey, $relationName = null) { diff --git a/src/Flame/Database/Relations/MorphToMany.php b/src/Flame/Database/Relations/MorphToMany.php index 6e220c8b..e953119e 100644 --- a/src/Flame/Database/Relations/MorphToMany.php +++ b/src/Flame/Database/Relations/MorphToMany.php @@ -45,7 +45,6 @@ class MorphToMany extends BelongsToMany * @param string $otherKey * @param string $relationName * @param bool $inverse - * @return void */ public function __construct( Builder $query, diff --git a/src/Flame/Database/Traits/HasQueryModifier.php b/src/Flame/Database/Traits/HasQueryModifier.php index 545efe61..3cb35cca 100644 --- a/src/Flame/Database/Traits/HasQueryModifier.php +++ b/src/Flame/Database/Traits/HasQueryModifier.php @@ -41,7 +41,8 @@ public function scopeApplyFilters(Builder $builder, array $options = []): Builde collect($this->queryModifierFilters) ->each(function($value, $key) use ($builder, $options) { $params = (array)$value; - if ($filterValue = array_get($options, $key, array_get($params, 'default'))) { + if (array_key_exists($key, $options)) { + $filterValue = array_get($options, $key, array_get($params, 'default')); (new FiltersScope)($builder, $filterValue, $params[0]); } }); diff --git a/src/Flame/Exception/ApplicationException.php b/src/Flame/Exception/ApplicationException.php index 45b5e52a..ccf9dec0 100644 --- a/src/Flame/Exception/ApplicationException.php +++ b/src/Flame/Exception/ApplicationException.php @@ -5,8 +5,6 @@ namespace Igniter\Flame\Exception; use Exception; -use Illuminate\Http\Request; -use Illuminate\Http\Response; class ApplicationException extends Exception { @@ -15,25 +13,8 @@ class ApplicationException extends Exception * @param int $code Error code. * @param Exception|null $previous Previous exception. */ - public function __construct($message = '', $code = 500, ?Exception $previous = null) + public function __construct(string $message = '', int $code = 500, ?Exception $previous = null) { parent::__construct($message, $code, $previous); } - - public function render(Request $request): Response - { - $message = $this->getMessage(); - - if (config('app.debug', false)) { - $message = sprintf('"%s" on line %s of %s', - $this->getMessage(), - $this->getLine(), - $this->getFile() - ); - - $message .= $this->getTraceAsString(); - } - - return response($message, $this->code); - } } diff --git a/src/Flame/Support/Helpers/helpers.php b/src/Flame/Support/Helpers/helpers.php index 07303702..8aa5cf5a 100644 --- a/src/Flame/Support/Helpers/helpers.php +++ b/src/Flame/Support/Helpers/helpers.php @@ -806,7 +806,7 @@ function name_to_array(string $string): array array_unshift($result, $matches[1]); } - return array_filter($result, fn($val): bool => (bool)strlen($val)); + return array_filter($result, fn($val): bool => (bool)strlen((string) $val)); } } diff --git a/src/Flame/Support/Igniter.php b/src/Flame/Support/Igniter.php index f56a09aa..2b07a37e 100644 --- a/src/Flame/Support/Igniter.php +++ b/src/Flame/Support/Igniter.php @@ -13,7 +13,7 @@ class Igniter { - protected const string VERSION = 'v4.0.15'; + protected const string VERSION = 'minor (v4.2.0)'; /** * The base path for extensions. diff --git a/src/Flame/Translation/Models/Translation.php b/src/Flame/Translation/Models/Translation.php index 3d4be9b1..dfb4bc95 100644 --- a/src/Flame/Translation/Models/Translation.php +++ b/src/Flame/Translation/Models/Translation.php @@ -135,7 +135,7 @@ public static function getFresh($locale, $group, $namespace = null) ->get(); } - public static function getCached($locale, $group, $namespace = null) + public static function getCached(string $locale, $group, $namespace = null) { $cacheKey = static::getCacheKey($locale, $group, $namespace); diff --git a/src/Main/Classes/MediaLibrary.php b/src/Main/Classes/MediaLibrary.php index 5953fe5e..ad9b9265 100644 --- a/src/Main/Classes/MediaLibrary.php +++ b/src/Main/Classes/MediaLibrary.php @@ -153,7 +153,7 @@ public function rename(string $path, string $newPath): bool public function deleteFiles(string|array $paths): bool { - return $this->getStorageDisk()->delete(array_map(fn(string $path): string => $this->getMediaPath($path), (array)$paths)); + return $this->getStorageDisk()->delete(array_map($this->getMediaPath(...), (array)$paths)); } public function deleteFolder(string $path): bool diff --git a/src/System/Classes/PackageInfo.php b/src/System/Classes/PackageInfo.php index 925fbee4..1fdeaedd 100644 --- a/src/System/Classes/PackageInfo.php +++ b/src/System/Classes/PackageInfo.php @@ -13,7 +13,7 @@ class PackageInfo public const string CORE_CODE = 'tastyigniter'; - public const string CORE_TYPE = 'tastyigniter-core'; + public const string CORE_TYPE = 'core'; public const array CORE_MANIFEST = [ 'code' => self::CORE_CODE, diff --git a/src/System/Console/Commands/ExtensionInstall.php b/src/System/Console/Commands/ExtensionInstall.php index 2c914777..e21425e7 100644 --- a/src/System/Console/Commands/ExtensionInstall.php +++ b/src/System/Console/Commands/ExtensionInstall.php @@ -31,10 +31,10 @@ public function handle(): void $extensionName = $this->argument('name'); $updateManager = resolve(UpdateManager::class)->setLogsOutput($this->output); - $itemDetail = $updateManager->requestItemDetail([ + $itemDetail = rescue(fn() => $updateManager->requestItemDetail([ 'name' => $extensionName, 'type' => 'extension', - ]); + ]), fn() => null, false) ?? []; if (!$itemDetail || !array_has($itemDetail, 'package') || array_get($itemDetail, 'code') !== $extensionName) { $this->output->writeln(sprintf('Extension %s not found', $extensionName)); diff --git a/src/System/Models/Settings.php b/src/System/Models/Settings.php index d9b830a4..8ee19f13 100644 --- a/src/System/Models/Settings.php +++ b/src/System/Models/Settings.php @@ -142,7 +142,7 @@ public static function onboardingMailIsComplete(): bool public function getValueAttribute() { - return ($value = @unserialize($this->attributes['value'] ?? '')) + return ($value = @unserialize($this->attributes['value'] ?? '')) !== false ? $value : $this->attributes['value']; } diff --git a/src/System/Traits/ManagesUpdates.php b/src/System/Traits/ManagesUpdates.php index bc4a7703..9079bf08 100644 --- a/src/System/Traits/ManagesUpdates.php +++ b/src/System/Traits/ManagesUpdates.php @@ -88,6 +88,8 @@ public function onApplyUpdate(): array [$response, $success] = $this->processInstallOrUpdate($itemsToUpdate->all(), isUpdate: true); + $updateManager->requestUpdateList(true); + return [ 'message' => implode('
      ', $response), 'success' => $success, diff --git a/tests/src/Admin/Widgets/ListsTest.php b/tests/src/Admin/Widgets/ListsTest.php index 7d3b687e..04514b3d 100644 --- a/tests/src/Admin/Widgets/ListsTest.php +++ b/tests/src/Admin/Widgets/ListsTest.php @@ -273,7 +273,10 @@ public function filterColumns(array &$listColumn): array it('returns visible column', function() { LocationFacade::setModel(Location::factory()->create()); - $this->listsWidget->columns['notify_customer'] = 'Notify Customer'; + $this->listsWidget->columns['notify_customer'] = [ + 'label' => 'Notify Customer', + 'type' => 'text', + ]; $this->listsWidget->columns['status_color'] = [ 'label' => 'Status Color', 'type' => 'text', @@ -334,6 +337,7 @@ public function filterColumns(array &$listColumn): array }); it('returns list column value', function($columnName, ?string $type, $value, $expected, array $config) { + $this->travelTo('2024-12-31 23:59:59'); $listColumn = new ListColumn($columnName, 'Test Column'); $listColumn->displayAs($type, $config); diff --git a/tests/src/Flame/Composer/ManagerTest.php b/tests/src/Flame/Composer/ManagerTest.php index fa4550d0..82a9393a 100644 --- a/tests/src/Flame/Composer/ManagerTest.php +++ b/tests/src/Flame/Composer/ManagerTest.php @@ -224,8 +224,8 @@ $manager->addAuthCredentials('username', 'password'); $config = json_decode(file_get_contents(base_path('auth.json')), true); - expect($config['http-basic']['satis.tastyigniter.com']['username'])->toBe('username') - ->and($config['http-basic']['satis.tastyigniter.com']['password'])->toBe('password'); + expect($config['http-basic']['composer.tastyigniter.com']['username'])->toBe('username') + ->and($config['http-basic']['composer.tastyigniter.com']['password'])->toBe('password'); unlink(base_path('auth.json')); }); @@ -238,7 +238,7 @@ $config = json_decode(file_get_contents(__DIR__.'/composer.json'), true); expect($config['repositories'])->toBeArray() ->and($config['repositories'][0]['type'])->toBe('composer') - ->and($config['repositories'][0]['url'])->toBe('https://satis.tastyigniter.com'); + ->and($config['repositories'][0]['url'])->toBe('https://composer.tastyigniter.com'); unlink(__DIR__.'/composer.json'); }); @@ -246,7 +246,7 @@ it('does not modify composer config if repository exists', function() { file_put_contents(__DIR__.'/composer.json', json_encode([ 'repositories' => [ - ['type' => 'composer', 'url' => 'https://satis.tastyigniter.com'], + ['type' => 'composer', 'url' => 'https://composer.tastyigniter.com'], ['type' => 'composer', 'url' => 'https://packagist.org'], ], ])); @@ -256,7 +256,7 @@ $config = json_decode(file_get_contents(__DIR__.'/composer.json'), true); expect($config['repositories'])->toBeArray() ->and($config['repositories'][0]['type'])->toBe('composer') - ->and($config['repositories'][0]['url'])->toBe('https://satis.tastyigniter.com') + ->and($config['repositories'][0]['url'])->toBe('https://composer.tastyigniter.com') ->and($config['repositories'][1]['type'])->toBe('composer') ->and($config['repositories'][1]['url'])->toBe('https://packagist.org'); diff --git a/tests/src/Flame/Exception/ApplicationExceptionTest.php b/tests/src/Flame/Exception/ApplicationExceptionTest.php deleted file mode 100644 index a9d4c8b9..00000000 --- a/tests/src/Flame/Exception/ApplicationExceptionTest.php +++ /dev/null @@ -1,33 +0,0 @@ - false]); - $exception = new ApplicationException('Default error message'); - $request = new Request; - $response = $exception->render($request); - expect($response)->toBeInstanceOf(Response::class) - ->and($response->getStatusCode())->toBe(500) - ->and($response->getContent())->toBe('Default error message'); -}); - -it('renders response with debug information when app debug is true', function() { - config(['app.debug' => true]); - $exception = new ApplicationException('Debug error message'); - $request = new Request; - $response = $exception->render($request); - expect($response)->toBeInstanceOf(Response::class) - ->and($response->getStatusCode())->toBe(500) - ->and($response->getContent())->toContain('Debug error message') - ->and($response->getContent())->toContain('on line') - ->and($response->getContent())->toContain('of') - ->and($response->getContent())->toContain($exception->getTraceAsString()); - config(['app.debug' => false]); -}); diff --git a/tests/src/System/Classes/UpdateManagerTest.php b/tests/src/System/Classes/UpdateManagerTest.php index d3fc902f..e9c15cfd 100644 --- a/tests/src/System/Classes/UpdateManagerTest.php +++ b/tests/src/System/Classes/UpdateManagerTest.php @@ -472,7 +472,7 @@ function mockRequestUpdateItems() $composerManager->shouldReceive('listInstalledPackages')->andReturn(collect([ [ 'name' => 'tastyigniter/core', - 'type' => 'tastyigniter-core', + 'type' => 'core', 'version' => '1.0.0', ], [ diff --git a/tests/src/System/Traits/CacheMakerTest.php b/tests/src/System/Traits/CacheMakerTest.php index 80b398d0..bc36bf20 100644 --- a/tests/src/System/Traits/CacheMakerTest.php +++ b/tests/src/System/Traits/CacheMakerTest.php @@ -11,10 +11,12 @@ function makeCacheMaker(): object { return new class { - use CacheMaker; + use CacheMaker { CacheMaker::getCacheKey as parentGetCacheKey; } protected function getCacheKey(): string { + $this->parentGetCacheKey(); + return 'class_id'; } };