feat(plugins): add spark commands to install, add, update and remove plugins using adaures' cpm

update js & php dependencies to latest and fix rector, phpstan and ecs issues
This commit is contained in:
Yassine Doghri 2025-09-22 17:34:36 +00:00
parent b5a403b990
commit 3d0db5c64a
37 changed files with 11745 additions and 6050 deletions

View File

@ -6,7 +6,7 @@
"service": "app", "service": "app",
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
"postCreateCommand": "composer install && pnpm install && pnpm run build:static && php spark migrate --all && php spark db:seed DevSeeder", "postCreateCommand": "composer install && pnpm install && pnpm run build:static && php spark migrate --all && php spark db:seed DevSeeder",
"postStartCommand": "git config --global --add safe.directory ${containerWorkspaceFolder} && crontab .devcontainer/crontab && cron && php spark serve --host 0.0.0.0", "postStartCommand": "git config --global --add safe.directory ${containerWorkspaceFolder} && crontab .devcontainer/crontab && cron && php spark serve --host 0.0.0.0 --port ${APP_PORT:-8080}",
"postAttachCommand": "crontab .devcontainer/crontab && service cron reload", "postAttachCommand": "crontab .devcontainer/crontab && service cron reload",
"shutdownAction": "stopCompose", "shutdownAction": "stopCompose",
"features": { "features": {

View File

@ -1,21 +1,19 @@
version: "3"
services: services:
app: app:
build: build:
context: . context: .
dockerfile: Dockerfile dockerfile: Dockerfile
ports:
- 8080:8080
volumes: volumes:
- ../..:/workspaces:cached - ../..:/workspaces:cached
- ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini - ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
environment: environment:
APP_PORT: ${APP_PORT:-8080} # used in devcontainer.json file
VITE_PORT: ${VITE_PORT:-5173} # used in ../vite.config.js file
CI_ENVIRONMENT: development CI_ENVIRONMENT: development
vite_environment: development vite_environment: development
app_forceGlobalSecureRequests: 0 #false app_forceGlobalSecureRequests: 0 #false
app_baseURL: http://localhost:8080/ app_baseURL: http://localhost:${APP_PORT:-8080}/
media_baseURL: http://localhost:8080/ media_baseURL: http://localhost:${APP_PORT:-8080}/
admin_gateway: cp-admin admin_gateway: cp-admin
auth_gateway: cp-auth auth_gateway: cp-auth
analytics_salt: dev_analytics_salt analytics_salt: dev_analytics_salt
@ -30,16 +28,10 @@ services:
email_SMTPHost: mailpit email_SMTPHost: mailpit
email_SMTPUser: castopod email_SMTPUser: castopod
email_SMTPPass: castopod email_SMTPPass: castopod
email_SMTPPort: 1025 email_SMTPPort: ${MAILPIT_SMTP_PORT:-1025}
depends_on: depends_on:
- redis
- mariadb - mariadb
redis:
image: redis:alpine
volumes:
- redis:/data
mariadb: mariadb:
image: mariadb:10.2 image: mariadb:10.2
volumes: volumes:
@ -70,8 +62,8 @@ services:
volumes: volumes:
- mailpit:/data - mailpit:/data
ports: ports:
- 8025:8025 - ${MAILPIT_WEBUI_PORT:-8025}:8025
- 1025:1025 - ${MAILPIT_SMTP_PORT:-1025}:1025
environment: environment:
MP_MAX_MESSAGES: 5000 MP_MAX_MESSAGES: 5000
MP_DATA_FILE: /data/mailpit.db MP_DATA_FILE: /data/mailpit.db
@ -79,7 +71,6 @@ services:
MP_SMTP_AUTH_ALLOW_INSECURE: 1 MP_SMTP_AUTH_ALLOW_INSECURE: 1
volumes: volumes:
redis:
mariadb: mariadb:
phpmyadmin: phpmyadmin:
mailpit: mailpit:

2
.gitignore vendored
View File

@ -192,3 +192,5 @@ castopod-*.tar.gz
# Plugins # Plugins
plugins/* plugins/*
!plugins/.gitkeep !plugins/.gitkeep
writable/plugins.json
writable/plugins-lock.json

View File

@ -13,7 +13,6 @@ namespace App\Entities;
use App\Models\PodcastModel; use App\Models\PodcastModel;
use Modules\Fediverse\Entities\Actor as FediverseActor; use Modules\Fediverse\Entities\Actor as FediverseActor;
use Override; use Override;
use RuntimeException;
/** /**
* @property Podcast|null $podcast * @property Podcast|null $podcast
@ -32,10 +31,6 @@ class Actor extends FediverseActor
public function getPodcast(): ?Podcast public function getPodcast(): ?Podcast
{ {
if ($this->id === null) {
throw new RuntimeException('Podcast id must be set before getting associated podcast.');
}
if (! $this->podcast instanceof Podcast) { if (! $this->podcast instanceof Podcast) {
$this->podcast = new PodcastModel() $this->podcast = new PodcastModel()
->getPodcastByActorId($this->id); ->getPodcastByActorId($this->id);

View File

@ -15,7 +15,7 @@ use CodeIgniter\Entity\Entity;
/** /**
* @property int $id * @property int $id
* @property int $parent_id * @property ?int $parent_id
* @property Category|null $parent * @property Category|null $parent
* @property string $code * @property string $code
* @property string $apple_category * @property string $apple_category

View File

@ -31,7 +31,7 @@ use Modules\Media\Models\MediaModel;
* @property Episode $episode * @property Episode $episode
* @property string $title * @property string $title
* @property double $start_time * @property double $start_time
* @property double $end_time * @property ?double $end_time
* @property double $duration * @property double $duration
* @property string $type * @property string $type
* @property int|null $media_id * @property int|null $media_id

View File

@ -55,10 +55,6 @@ class Credit extends Entity
public function getPerson(): ?Person public function getPerson(): ?Person
{ {
if ($this->person_id === null) {
throw new RuntimeException('Credit must have person_id before getting person.');
}
if (! $this->person instanceof Person) { if (! $this->person instanceof Person) {
$this->person = new PersonModel() $this->person = new PersonModel()
->getPersonById($this->person_id); ->getPersonById($this->person_id);
@ -69,10 +65,6 @@ class Credit extends Entity
public function getPodcast(): ?Podcast public function getPodcast(): ?Podcast
{ {
if ($this->podcast_id === null) {
throw new RuntimeException('Credit must have podcast_id before getting podcast.');
}
if (! $this->podcast instanceof Podcast) { if (! $this->podcast instanceof Podcast) {
$this->podcast = new PodcastModel() $this->podcast = new PodcastModel()
->getPodcastById($this->podcast_id); ->getPodcastById($this->podcast_id);
@ -97,7 +89,7 @@ class Credit extends Entity
public function getGroupLabel(): string public function getGroupLabel(): string
{ {
if ($this->person_group === null) { if ($this->person_group === '') {
return ''; return '';
} }

View File

@ -34,7 +34,6 @@ use Modules\Media\Entities\Image;
use Modules\Media\Entities\Transcript; use Modules\Media\Entities\Transcript;
use Modules\Media\Models\MediaModel; use Modules\Media\Models\MediaModel;
use Override; use Override;
use RuntimeException;
/** /**
* @property int $id * @property int $id
@ -63,8 +62,8 @@ use RuntimeException;
* @property Chapters|null $chapters * @property Chapters|null $chapters
* @property string|null $chapters_remote_url * @property string|null $chapters_remote_url
* @property string|null $parental_advisory * @property string|null $parental_advisory
* @property int $number * @property ?int $number
* @property int $season_number * @property ?int $season_number
* @property string $type * @property string $type
* @property bool $is_blocked * @property bool $is_blocked
* @property Location|null $location * @property Location|null $location
@ -398,10 +397,6 @@ class Episode extends Entity
*/ */
public function getPersons(): array public function getPersons(): array
{ {
if ($this->id === null) {
throw new RuntimeException('Episode must be created before getting persons.');
}
if ($this->persons === null) { if ($this->persons === null) {
$this->persons = new PersonModel() $this->persons = new PersonModel()
->getEpisodePersons($this->podcast_id, $this->id); ->getEpisodePersons($this->podcast_id, $this->id);
@ -417,10 +412,6 @@ class Episode extends Entity
*/ */
public function getSoundbites(): array public function getSoundbites(): array
{ {
if ($this->id === null) {
throw new RuntimeException('Episode must be created before getting soundbites.');
}
if ($this->soundbites === null) { if ($this->soundbites === null) {
$this->soundbites = new ClipModel() $this->soundbites = new ClipModel()
->getEpisodeSoundbites($this->getPodcast()->id, $this->id); ->getEpisodeSoundbites($this->getPodcast()->id, $this->id);
@ -434,10 +425,6 @@ class Episode extends Entity
*/ */
public function getPosts(): array public function getPosts(): array
{ {
if ($this->id === null) {
throw new RuntimeException('Episode must be created before getting posts.');
}
if ($this->posts === null) { if ($this->posts === null) {
$this->posts = new PostModel() $this->posts = new PostModel()
->getEpisodePosts($this->id); ->getEpisodePosts($this->id);
@ -451,10 +438,6 @@ class Episode extends Entity
*/ */
public function getComments(): array public function getComments(): array
{ {
if ($this->id === null) {
throw new RuntimeException('Episode must be created before getting comments.');
}
if ($this->comments === null) { if ($this->comments === null) {
$this->comments = new EpisodeCommentModel() $this->comments = new EpisodeCommentModel()
->getEpisodeComments($this->id); ->getEpisodeComments($this->id);
@ -591,10 +574,6 @@ class Episode extends Entity
*/ */
public function getClipCount(): int|string public function getClipCount(): int|string
{ {
if ($this->id === null) {
throw new RuntimeException('Episode must be created before getting number of video clips.');
}
return new ClipModel() return new ClipModel()
->getClipCount($this->podcast_id, $this->id); ->getClipCount($this->podcast_id, $this->id);
} }

View File

@ -24,7 +24,7 @@ use RuntimeException;
* @property Episode|null $episode * @property Episode|null $episode
* @property int $actor_id * @property int $actor_id
* @property Actor|null $actor * @property Actor|null $actor
* @property string $in_reply_to_id * @property ?string $in_reply_to_id
* @property EpisodeComment|null $reply_to_comment * @property EpisodeComment|null $reply_to_comment
* @property string $message * @property string $message
* @property string $message_html * @property string $message_html
@ -75,10 +75,6 @@ class EpisodeComment extends UuidEntity
public function getEpisode(): ?Episode public function getEpisode(): ?Episode
{ {
if ($this->episode_id === null) {
throw new RuntimeException('Comment must have an episode_id before getting episode.');
}
if (! $this->episode instanceof Episode) { if (! $this->episode instanceof Episode) {
$this->episode = new EpisodeModel() $this->episode = new EpisodeModel()
->getEpisodeById($this->episode_id); ->getEpisodeById($this->episode_id);
@ -92,10 +88,6 @@ class EpisodeComment extends UuidEntity
*/ */
public function getActor(): ?Actor public function getActor(): ?Actor
{ {
if ($this->actor_id === null) {
throw new RuntimeException('Comment must have an actor_id before getting actor.');
}
if (! $this->actor instanceof Actor) { if (! $this->actor instanceof Actor) {
$this->actor = model(ActorModel::class, false) $this->actor = model(ActorModel::class, false)
->getActorById($this->actor_id); ->getActorById($this->actor_id);
@ -109,10 +101,6 @@ class EpisodeComment extends UuidEntity
*/ */
public function getReplies(): array public function getReplies(): array
{ {
if ($this->id === null) {
throw new RuntimeException('Comment must be created before getting replies.');
}
if ($this->replies === null) { if ($this->replies === null) {
$this->replies = new EpisodeCommentModel() $this->replies = new EpisodeCommentModel()
->getCommentReplies($this->id); ->getCommentReplies($this->id);

View File

@ -23,7 +23,7 @@ use RuntimeException;
* @property string $full_name * @property string $full_name
* @property string $unique_name * @property string $unique_name
* @property string|null $information_url * @property string|null $information_url
* @property int $avatar_id * @property ?int $avatar_id
* @property ?Image $avatar * @property ?Image $avatar
* @property int $created_by * @property int $created_by
* @property int $updated_by * @property int $updated_by

View File

@ -320,10 +320,6 @@ class Podcast extends Entity
*/ */
public function getEpisodes(): array public function getEpisodes(): array
{ {
if ($this->id === null) {
throw new RuntimeException('Podcast must be created before getting episodes.');
}
if ($this->episodes === null) { if ($this->episodes === null) {
$this->episodes = new EpisodeModel() $this->episodes = new EpisodeModel()
->getPodcastEpisodes($this->id, $this->type); ->getPodcastEpisodes($this->id, $this->type);
@ -337,10 +333,6 @@ class Podcast extends Entity
*/ */
public function getEpisodesCount(): int|string public function getEpisodesCount(): int|string
{ {
if ($this->id === null) {
throw new RuntimeException('Podcast must be created before getting number of episodes.');
}
return new EpisodeModel() return new EpisodeModel()
->getPodcastEpisodesCount($this->id); ->getPodcastEpisodesCount($this->id);
} }
@ -352,10 +344,6 @@ class Podcast extends Entity
*/ */
public function getPersons(): array public function getPersons(): array
{ {
if ($this->id === null) {
throw new RuntimeException('Podcast must be created before getting persons.');
}
if ($this->persons === null) { if ($this->persons === null) {
$this->persons = new PersonModel() $this->persons = new PersonModel()
->getPodcastPersons($this->id); ->getPodcastPersons($this->id);
@ -369,10 +357,6 @@ class Podcast extends Entity
*/ */
public function getCategory(): ?Category public function getCategory(): ?Category
{ {
if ($this->id === null) {
throw new RuntimeException('Podcast must be created before getting category.');
}
if (! $this->category instanceof Category) { if (! $this->category instanceof Category) {
$this->category = new CategoryModel() $this->category = new CategoryModel()
->getCategoryById($this->category_id); ->getCategoryById($this->category_id);
@ -388,10 +372,6 @@ class Podcast extends Entity
*/ */
public function getSubscriptions(): array public function getSubscriptions(): array
{ {
if ($this->id === null) {
throw new RuntimeException('Podcasts must be created before getting subscriptions.');
}
if ($this->subscriptions === null) { if ($this->subscriptions === null) {
$this->subscriptions = new SubscriptionModel() $this->subscriptions = new SubscriptionModel()
->getPodcastSubscriptions($this->id); ->getPodcastSubscriptions($this->id);
@ -407,10 +387,6 @@ class Podcast extends Entity
*/ */
public function getContributors(): array public function getContributors(): array
{ {
if ($this->id === null) {
throw new RuntimeException('Podcasts must be created before getting contributors.');
}
if ($this->contributors === null) { if ($this->contributors === null) {
$this->contributors = new UserModel() $this->contributors = new UserModel()
->getPodcastContributors($this->id); ->getPodcastContributors($this->id);
@ -473,10 +449,6 @@ class Podcast extends Entity
*/ */
public function getPodcastingPlatforms(): array public function getPodcastingPlatforms(): array
{ {
if ($this->id === null) {
throw new RuntimeException('Podcast must be created before getting podcasting platform links.');
}
if ($this->podcasting_platforms === null) { if ($this->podcasting_platforms === null) {
$this->podcasting_platforms = new PlatformModel() $this->podcasting_platforms = new PlatformModel()
->getPlatforms($this->id, 'podcasting'); ->getPlatforms($this->id, 'podcasting');
@ -492,10 +464,6 @@ class Podcast extends Entity
*/ */
public function getSocialPlatforms(): array public function getSocialPlatforms(): array
{ {
if ($this->id === null) {
throw new RuntimeException('Podcast must be created before getting social platform links.');
}
if ($this->social_platforms === null) { if ($this->social_platforms === null) {
$this->social_platforms = new PlatformModel() $this->social_platforms = new PlatformModel()
->getPlatforms($this->id, 'social'); ->getPlatforms($this->id, 'social');
@ -511,10 +479,6 @@ class Podcast extends Entity
*/ */
public function getFundingPlatforms(): array public function getFundingPlatforms(): array
{ {
if ($this->id === null) {
throw new RuntimeException('Podcast must be created before getting funding platform links.');
}
if ($this->funding_platforms === null) { if ($this->funding_platforms === null) {
$this->funding_platforms = new PlatformModel() $this->funding_platforms = new PlatformModel()
->getPlatforms($this->id, 'funding'); ->getPlatforms($this->id, 'funding');
@ -528,10 +492,6 @@ class Podcast extends Entity
*/ */
public function getOtherCategories(): array public function getOtherCategories(): array
{ {
if ($this->id === null) {
throw new RuntimeException('Podcast must be created before getting other categories.');
}
if ($this->other_categories === null) { if ($this->other_categories === null) {
$this->other_categories = new CategoryModel() $this->other_categories = new CategoryModel()
->getPodcastCategories($this->id); ->getPodcastCategories($this->id);

View File

@ -113,10 +113,8 @@ if (! function_exists('get_rss_feed')) {
$podcastingPlatformElement->addAttribute('id', $podcastingPlatform->account_id); $podcastingPlatformElement->addAttribute('id', $podcastingPlatform->account_id);
} }
if ($podcastingPlatform->link_url !== null) {
$podcastingPlatformElement->addAttribute('url', $podcastingPlatform->link_url); $podcastingPlatformElement->addAttribute('url', $podcastingPlatform->link_url);
} }
}
$castopodSocialElement = $channel->addChild('social', null, RssFeed::PODCAST_NAMESPACE); $castopodSocialElement = $channel->addChild('social', null, RssFeed::PODCAST_NAMESPACE);
$castopodSocialElement->addAttribute('priority', '1'); $castopodSocialElement->addAttribute('priority', '1');
@ -145,9 +143,7 @@ if (! function_exists('get_rss_feed')) {
$socialElement->addAttribute('accountId', esc($socialPlatform->account_id)); $socialElement->addAttribute('accountId', esc($socialPlatform->account_id));
} }
if ($socialPlatform->link_url !== null) {
$socialElement->addAttribute('accountUrl', esc($socialPlatform->link_url)); $socialElement->addAttribute('accountUrl', esc($socialPlatform->link_url));
}
if ($socialPlatform->slug === 'mastodon') { if ($socialPlatform->slug === 'mastodon') {
$socialSignUpelement = $socialElement->addChild('socialSignUp', null, RssFeed::PODCAST_NAMESPACE); $socialSignUpelement = $socialElement->addChild('socialSignUp', null, RssFeed::PODCAST_NAMESPACE);
@ -196,10 +192,8 @@ if (! function_exists('get_rss_feed')) {
RssFeed::PODCAST_NAMESPACE, RssFeed::PODCAST_NAMESPACE,
); );
$fundingPlatformElement->addAttribute('platform', $fundingPlatform->slug); $fundingPlatformElement->addAttribute('platform', $fundingPlatform->slug);
if ($fundingPlatform->link_url !== null) {
$fundingPlatformElement->addAttribute('url', $fundingPlatform->link_url); $fundingPlatformElement->addAttribute('url', $fundingPlatform->link_url);
} }
}
foreach ($podcast->persons as $person) { foreach ($podcast->persons as $person) {
foreach ($person->roles as $role) { foreach ($person->roles as $role) {

View File

@ -45,7 +45,7 @@ class Router extends CodeIgniterRouter
// Loop through the route array looking for wildcards // Loop through the route array looking for wildcards
foreach ($routes as $routeKey => $handler) { foreach ($routes as $routeKey => $handler) {
$routeKey = $routeKey === '/' ? $routeKey : ltrim($routeKey, '/ '); $routeKey = $routeKey === '/' ? $routeKey : ltrim((string) $routeKey, '/ ');
$matchedKey = $routeKey; $matchedKey = $routeKey;

View File

@ -8,8 +8,9 @@
"require": { "require": {
"php": "^8.4", "php": "^8.4",
"adaures/ipcat-php": "^v1.0.0", "adaures/ipcat-php": "^v1.0.0",
"adaures/castopod-plugins-manager": "dev-main",
"adaures/podcast-persons-taxonomy": "^v1.0.1", "adaures/podcast-persons-taxonomy": "^v1.0.1",
"aws/aws-sdk-php": "^3.356.8", "aws/aws-sdk-php": "^3.356.22",
"chrisjean/php-ico": "^1.0.4", "chrisjean/php-ico": "^1.0.4",
"cocur/slugify": "^v4.6.0", "cocur/slugify": "^v4.6.0",
"codeigniter4/framework": "4.6.3", "codeigniter4/framework": "4.6.3",
@ -27,7 +28,7 @@
"phpseclib/phpseclib": "~2.0.48", "phpseclib/phpseclib": "~2.0.48",
"vlucas/phpdotenv": "5.6.2", "vlucas/phpdotenv": "5.6.2",
"whichbrowser/parser": "^v2.1.8", "whichbrowser/parser": "^v2.1.8",
"yassinedoghri/codeigniter-vite": "^v1.1.0", "yassinedoghri/codeigniter-vite": "^2.1.0",
"yassinedoghri/php-icons": "1.3.0", "yassinedoghri/php-icons": "1.3.0",
"yassinedoghri/podcast-feed": "dev-main" "yassinedoghri/podcast-feed": "dev-main"
}, },
@ -36,11 +37,11 @@
"codeigniter/phpstan-codeigniter": "1.5.4", "codeigniter/phpstan-codeigniter": "1.5.4",
"mikey179/vfsstream": "^v1.6.12", "mikey179/vfsstream": "^v1.6.12",
"phpstan/extension-installer": "^1.4.3", "phpstan/extension-installer": "^1.4.3",
"phpstan/phpstan": "^2.1.22", "phpstan/phpstan": "^2.1.28",
"phpunit/phpunit": "^12.3.7", "phpunit/phpunit": "^12.3.12",
"rector/rector": "^2.1.4", "rector/rector": "^2.1.7",
"symplify/coding-standard": "^12.4.3", "symplify/coding-standard": "^12.4.3",
"symplify/easy-coding-standard": "^12.5.24" "symplify/easy-coding-standard": "^12.6.0"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {

386
composer.lock generated
View File

@ -4,8 +4,62 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "656fade414e31429a68d7b3a41b2b8c5", "content-hash": "62cc52f6536e1b74b94ce8e9baf16cf3",
"packages": [ "packages": [
{
"name": "adaures/castopod-plugins-manager",
"version": "dev-main",
"source": {
"type": "git",
"url": "https://github.com/ad-aures/castopod-plugins-manager.git",
"reference": "5c491a4fb1143ba19a72a66e6af3bd1161c90046"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ad-aures/castopod-plugins-manager/zipball/5c491a4fb1143ba19a72a66e6af3bd1161c90046",
"reference": "5c491a4fb1143ba19a72a66e6af3bd1161c90046",
"shasum": ""
},
"require": {
"php": ">=8.4",
"z4kn4fein/php-semver": "^3.0"
},
"require-dev": {
"pestphp/pest": "^4.0.4",
"pestphp/pest-plugin-type-coverage": "^4.0.2",
"phpstan/extension-installer": "^1.4.3",
"phpstan/phpstan": "^2.1.22",
"rector/rector": "^2.1.4",
"symplify/coding-standard": "^12.4.3",
"symplify/easy-coding-standard": "^12.5.24"
},
"default-branch": true,
"type": "library",
"autoload": {
"files": [
"src/helpers.php"
],
"psr-4": {
"Castopod\\PluginsManager\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"AGPL-3.0-only"
],
"authors": [
{
"name": "Yassine Doghri",
"homepage": "https://yassinedoghri.com/"
}
],
"description": "A PHP library to install, update, and remove plugins on a Castopod instance.",
"support": {
"issues": "https://github.com/ad-aures/castopod-plugins-manager/issues",
"source": "https://github.com/ad-aures/castopod-plugins-manager/tree/main"
},
"time": "2025-09-22T17:11:34+00:00"
},
{ {
"name": "adaures/ipcat-php", "name": "adaures/ipcat-php",
"version": "v1.0.0", "version": "v1.0.0",
@ -206,16 +260,16 @@
}, },
{ {
"name": "aws/aws-sdk-php", "name": "aws/aws-sdk-php",
"version": "3.356.8", "version": "3.356.22",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/aws/aws-sdk-php.git", "url": "https://github.com/aws/aws-sdk-php.git",
"reference": "3efa8c62c11fedb17b90f60b2d3a9f815b406e63" "reference": "5b3f65a158a0e822586d110192ee78c6565b4f06"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/3efa8c62c11fedb17b90f60b2d3a9f815b406e63", "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/5b3f65a158a0e822586d110192ee78c6565b4f06",
"reference": "3efa8c62c11fedb17b90f60b2d3a9f815b406e63", "reference": "5b3f65a158a0e822586d110192ee78c6565b4f06",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -228,7 +282,7 @@
"guzzlehttp/psr7": "^2.4.5", "guzzlehttp/psr7": "^2.4.5",
"mtdowling/jmespath.php": "^2.8.0", "mtdowling/jmespath.php": "^2.8.0",
"php": ">=8.1", "php": ">=8.1",
"psr/http-message": "^2.0" "psr/http-message": "^1.0 || ^2.0"
}, },
"require-dev": { "require-dev": {
"andrewsville/php-token-reflection": "^1.4", "andrewsville/php-token-reflection": "^1.4",
@ -297,31 +351,31 @@
"support": { "support": {
"forum": "https://github.com/aws/aws-sdk-php/discussions", "forum": "https://github.com/aws/aws-sdk-php/discussions",
"issues": "https://github.com/aws/aws-sdk-php/issues", "issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.356.8" "source": "https://github.com/aws/aws-sdk-php/tree/3.356.22"
}, },
"time": "2025-08-29T18:06:18+00:00" "time": "2025-09-19T18:25:30+00:00"
}, },
{ {
"name": "brick/math", "name": "brick/math",
"version": "0.13.1", "version": "0.14.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/brick/math.git", "url": "https://github.com/brick/math.git",
"reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04" "reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04", "url": "https://api.github.com/repos/brick/math/zipball/113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2",
"reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04", "reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^8.1" "php": "^8.2"
}, },
"require-dev": { "require-dev": {
"php-coveralls/php-coveralls": "^2.2", "php-coveralls/php-coveralls": "^2.2",
"phpunit/phpunit": "^10.1", "phpstan/phpstan": "2.1.22",
"vimeo/psalm": "6.8.8" "phpunit/phpunit": "^11.5"
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
@ -351,7 +405,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/brick/math/issues", "issues": "https://github.com/brick/math/issues",
"source": "https://github.com/brick/math/tree/0.13.1" "source": "https://github.com/brick/math/tree/0.14.0"
}, },
"funding": [ "funding": [
{ {
@ -359,7 +413,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2025-03-29T13:50:30+00:00" "time": "2025-08-29T12:40:03+00:00"
}, },
{ {
"name": "chrisjean/php-ico", "name": "chrisjean/php-ico",
@ -562,12 +616,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/codeigniter4/queue.git", "url": "https://github.com/codeigniter4/queue.git",
"reference": "0f95012aa9671f4cfcc74af0f62c7e0c29dadd8e" "reference": "e0cd27943903e26c8fa8004cd57f88115798fc60"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/codeigniter4/queue/zipball/0f95012aa9671f4cfcc74af0f62c7e0c29dadd8e", "url": "https://api.github.com/repos/codeigniter4/queue/zipball/e0cd27943903e26c8fa8004cd57f88115798fc60",
"reference": "0f95012aa9671f4cfcc74af0f62c7e0c29dadd8e", "reference": "e0cd27943903e26c8fa8004cd57f88115798fc60",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -576,11 +630,13 @@
"require-dev": { "require-dev": {
"codeigniter4/devkit": "^1.0", "codeigniter4/devkit": "^1.0",
"codeigniter4/framework": "^4.3", "codeigniter4/framework": "^4.3",
"php-amqplib/php-amqplib": "^3.7",
"phpstan/phpstan-strict-rules": "^1.5", "phpstan/phpstan-strict-rules": "^1.5",
"predis/predis": "^2.0" "predis/predis": "^2.0"
}, },
"suggest": { "suggest": {
"ext-redis": "If you want to use RedisHandler", "ext-redis": "If you want to use RedisHandler",
"php-amqplib/php-amqplib": "If you want to use RabbitMQHandler",
"predis/predis": "If you want to use PredisHandler" "predis/predis": "If you want to use PredisHandler"
}, },
"default-branch": true, "default-branch": true,
@ -618,7 +674,7 @@
"issues": "https://github.com/codeigniter4/queue/issues", "issues": "https://github.com/codeigniter4/queue/issues",
"source": "https://github.com/codeigniter4/queue/tree/develop" "source": "https://github.com/codeigniter4/queue/tree/develop"
}, },
"time": "2025-08-19T17:42:31+00:00" "time": "2025-09-15T07:03:57+00:00"
}, },
{ {
"name": "codeigniter4/settings", "name": "codeigniter4/settings",
@ -752,12 +808,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/codeigniter4/tasks.git", "url": "https://github.com/codeigniter4/tasks.git",
"reference": "04894af5f78b0c8652f87081ef75dd1a030e2972" "reference": "e5aac87c3645e5173bb1c71f8fec0432f308b816"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/codeigniter4/tasks/zipball/04894af5f78b0c8652f87081ef75dd1a030e2972", "url": "https://api.github.com/repos/codeigniter4/tasks/zipball/e5aac87c3645e5173bb1c71f8fec0432f308b816",
"reference": "04894af5f78b0c8652f87081ef75dd1a030e2972", "reference": "e5aac87c3645e5173bb1c71f8fec0432f308b816",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -853,7 +909,7 @@
"source": "https://github.com/codeigniter4/tasks/tree/develop", "source": "https://github.com/codeigniter4/tasks/tree/develop",
"issues": "https://github.com/codeigniter4/tasks/issues" "issues": "https://github.com/codeigniter4/tasks/issues"
}, },
"time": "2025-08-13T12:00:00+00:00" "time": "2025-09-05T08:41:42+00:00"
}, },
{ {
"name": "composer/ca-bundle", "name": "composer/ca-bundle",
@ -3043,20 +3099,20 @@
}, },
{ {
"name": "ramsey/uuid", "name": "ramsey/uuid",
"version": "4.9.0", "version": "4.9.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/ramsey/uuid.git", "url": "https://github.com/ramsey/uuid.git",
"reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0" "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/4e0e23cc785f0724a0e838279a9eb03f28b092a0", "url": "https://api.github.com/repos/ramsey/uuid/zipball/81f941f6f729b1e3ceea61d9d014f8b6c6800440",
"reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0", "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13", "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14",
"php": "^8.0", "php": "^8.0",
"ramsey/collection": "^1.2 || ^2.0" "ramsey/collection": "^1.2 || ^2.0"
}, },
@ -3115,9 +3171,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/ramsey/uuid/issues", "issues": "https://github.com/ramsey/uuid/issues",
"source": "https://github.com/ramsey/uuid/tree/4.9.0" "source": "https://github.com/ramsey/uuid/tree/4.9.1"
}, },
"time": "2025-06-25T14:20:11+00:00" "time": "2025-09-04T20:59:21+00:00"
}, },
{ {
"name": "symfony/deprecation-contracts", "name": "symfony/deprecation-contracts",
@ -3587,31 +3643,31 @@
}, },
{ {
"name": "yassinedoghri/codeigniter-vite", "name": "yassinedoghri/codeigniter-vite",
"version": "v1.1.0", "version": "v2.1.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/yassinedoghri/codeigniter-vite.git", "url": "https://github.com/yassinedoghri/codeigniter-vite.git",
"reference": "b96cff850b868b992871ce7a92703567afc9db79" "reference": "95c1dd30b716e3204ce981aa564202b299f9c8a5"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/yassinedoghri/codeigniter-vite/zipball/b96cff850b868b992871ce7a92703567afc9db79", "url": "https://api.github.com/repos/yassinedoghri/codeigniter-vite/zipball/95c1dd30b716e3204ce981aa564202b299f9c8a5",
"reference": "b96cff850b868b992871ce7a92703567afc9db79", "reference": "95c1dd30b716e3204ce981aa564202b299f9c8a5",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=8.1" "php": ">=8.1"
}, },
"require-dev": { "require-dev": {
"codeigniter/phpstan-codeigniter": "v1.5.1", "codeigniter/phpstan-codeigniter": "^1.5.4",
"codeigniter4/framework": "v4.5.7", "codeigniter4/framework": "^v4.6.0",
"pestphp/pest": "v3.7.1", "pestphp/pest": "^3.8.4",
"pestphp/pest-plugin-type-coverage": "v3.2.3", "pestphp/pest-plugin-type-coverage": "^3.6.1",
"phpstan/extension-installer": "^1.4.3", "phpstan/extension-installer": "^1.4.3",
"phpstan/phpstan": "^2.1.1", "phpstan/phpstan": "^2.1.22",
"rector/rector": "^2.0.6", "rector/rector": "^2.1.4",
"symplify/coding-standard": "^12.2.3", "symplify/coding-standard": "^12.4.3",
"symplify/easy-coding-standard": "^12.5.5" "symplify/easy-coding-standard": "^12.5.24"
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
@ -3629,7 +3685,7 @@
"homepage": "https://yassinedoghri.com/" "homepage": "https://yassinedoghri.com/"
} }
], ],
"description": "A simple ViteJS integration for CodeIgniter4 projects.", "description": "An opinionated Vite integration for CodeIgniter4 projects.",
"keywords": [ "keywords": [
"codeigniter", "codeigniter",
"codeigniter4", "codeigniter4",
@ -3639,9 +3695,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/yassinedoghri/codeigniter-vite/issues", "issues": "https://github.com/yassinedoghri/codeigniter-vite/issues",
"source": "https://github.com/yassinedoghri/codeigniter-vite/tree/v1.1.0" "source": "https://github.com/yassinedoghri/codeigniter-vite/tree/v2.1.0"
}, },
"time": "2025-01-24T13:46:51+00:00" "time": "2025-09-09T18:36:45+00:00"
}, },
{ {
"name": "yassinedoghri/php-icons", "name": "yassinedoghri/php-icons",
@ -3753,6 +3809,60 @@
"source": "https://github.com/yassinedoghri/podcast-feed/tree/main" "source": "https://github.com/yassinedoghri/podcast-feed/tree/main"
}, },
"time": "2024-04-28T16:17:41+00:00" "time": "2024-04-28T16:17:41+00:00"
},
{
"name": "z4kn4fein/php-semver",
"version": "v3.0.0",
"source": {
"type": "git",
"url": "https://github.com/z4kn4fein/php-semver.git",
"reference": "049a1d81e92235c8b3c9ab30a96fcbaa929a266d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/z4kn4fein/php-semver/zipball/049a1d81e92235c8b3c9ab30a96fcbaa929a266d",
"reference": "049a1d81e92235c8b3c9ab30a96fcbaa929a266d",
"shasum": ""
},
"require": {
"php": ">=8.1"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.0",
"phpstan/phpstan": "^1.0",
"phpunit/phpunit": "^10"
},
"type": "library",
"autoload": {
"psr-4": {
"z4kn4fein\\SemVer\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Peter Csajtai",
"email": "peter.csajtai@outlook.com"
}
],
"description": "Semantic Versioning library for PHP. It implements the full semantic version 2.0.0 specification and provides ability to parse, compare, and increment semantic versions along with validation against constraints.",
"homepage": "https://github.com/z4kn4fein/php-semver",
"keywords": [
"comparison",
"semantic",
"semver",
"validation",
"version",
"versioning"
],
"support": {
"issues": "https://github.com/z4kn4fein/php-semver/issues",
"source": "https://github.com/z4kn4fein/php-semver/tree/v3.0.0"
},
"time": "2024-04-01T16:17:27+00:00"
} }
], ],
"packages-dev": [ "packages-dev": [
@ -4364,16 +4474,16 @@
}, },
{ {
"name": "friendsofphp/php-cs-fixer", "name": "friendsofphp/php-cs-fixer",
"version": "v3.86.0", "version": "v3.87.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "4a952bd19dc97879b0620f495552ef09b55f7d36" "reference": "da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/4a952bd19dc97879b0620f495552ef09b55f7d36", "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992",
"reference": "4a952bd19dc97879b0620f495552ef09b55f7d36", "reference": "da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -4384,39 +4494,38 @@
"ext-hash": "*", "ext-hash": "*",
"ext-json": "*", "ext-json": "*",
"ext-tokenizer": "*", "ext-tokenizer": "*",
"fidry/cpu-core-counter": "^1.2", "fidry/cpu-core-counter": "^1.3",
"php": "^7.4 || ^8.0", "php": "^7.4 || ^8.0",
"react/child-process": "^0.6.6", "react/child-process": "^0.6.6",
"react/event-loop": "^1.5", "react/event-loop": "^1.5",
"react/promise": "^3.2", "react/promise": "^3.3",
"react/socket": "^1.16", "react/socket": "^1.16",
"react/stream": "^1.4", "react/stream": "^1.4",
"sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0", "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0",
"symfony/console": "^5.4.47 || ^6.4.13 || ^7.0", "symfony/console": "^5.4.47 || ^6.4.24 || ^7.0",
"symfony/event-dispatcher": "^5.4.45 || ^6.4.13 || ^7.0", "symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0",
"symfony/filesystem": "^5.4.45 || ^6.4.13 || ^7.0", "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0",
"symfony/finder": "^5.4.45 || ^6.4.17 || ^7.0", "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0",
"symfony/options-resolver": "^5.4.45 || ^6.4.16 || ^7.0", "symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0",
"symfony/polyfill-mbstring": "^1.32", "symfony/polyfill-mbstring": "^1.33",
"symfony/polyfill-php80": "^1.32", "symfony/polyfill-php80": "^1.33",
"symfony/polyfill-php81": "^1.32", "symfony/polyfill-php81": "^1.33",
"symfony/process": "^5.4.47 || ^6.4.20 || ^7.2", "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2",
"symfony/stopwatch": "^5.4.45 || ^6.4.19 || ^7.0" "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0"
}, },
"require-dev": { "require-dev": {
"facile-it/paraunit": "^1.3.1 || ^2.6", "facile-it/paraunit": "^1.3.1 || ^2.7",
"infection/infection": "^0.29.14", "infection/infection": "^0.29.14",
"justinrainbow/json-schema": "^5.3 || ^6.4", "justinrainbow/json-schema": "^6.5",
"keradus/cli-executor": "^2.2", "keradus/cli-executor": "^2.2",
"mikey179/vfsstream": "^1.6.12", "mikey179/vfsstream": "^1.6.12",
"php-coveralls/php-coveralls": "^2.8", "php-coveralls/php-coveralls": "^2.8",
"php-cs-fixer/accessible-object": "^1.1",
"php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6",
"php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6",
"phpunit/phpunit": "^9.6.23 || ^10.5.47 || ^11.5.25", "phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34",
"symfony/polyfill-php84": "^1.32", "symfony/polyfill-php84": "^1.33",
"symfony/var-dumper": "^5.4.48 || ^6.4.23 || ^7.3.1", "symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2",
"symfony/yaml": "^5.4.45 || ^6.4.23 || ^7.3.1" "symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2"
}, },
"suggest": { "suggest": {
"ext-dom": "For handling output formats in XML", "ext-dom": "For handling output formats in XML",
@ -4457,7 +4566,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.86.0" "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.87.2"
}, },
"funding": [ "funding": [
{ {
@ -4465,7 +4574,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2025-08-13T22:36:21+00:00" "time": "2025-09-10T09:51:40+00:00"
}, },
{ {
"name": "mikey179/vfsstream", "name": "mikey179/vfsstream",
@ -4805,16 +4914,16 @@
}, },
{ {
"name": "phpstan/phpstan", "name": "phpstan/phpstan",
"version": "2.1.22", "version": "2.1.28",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan.git", "url": "https://github.com/phpstan/phpstan.git",
"reference": "41600c8379eb5aee63e9413fe9e97273e25d57e4" "reference": "578fa296a166605d97b94091f724f1257185d278"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/41600c8379eb5aee63e9413fe9e97273e25d57e4", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/578fa296a166605d97b94091f724f1257185d278",
"reference": "41600c8379eb5aee63e9413fe9e97273e25d57e4", "reference": "578fa296a166605d97b94091f724f1257185d278",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -4859,38 +4968,38 @@
"type": "github" "type": "github"
} }
], ],
"time": "2025-08-04T19:17:37+00:00" "time": "2025-09-19T08:58:49+00:00"
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "12.3.4", "version": "12.3.8",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "7ad0e9bdc72b147600badccd694a2e57ffc9297a" "reference": "99e692c6a84708211f7536ba322bbbaef57ac7fc"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7ad0e9bdc72b147600badccd694a2e57ffc9297a", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/99e692c6a84708211f7536ba322bbbaef57ac7fc",
"reference": "7ad0e9bdc72b147600badccd694a2e57ffc9297a", "reference": "99e692c6a84708211f7536ba322bbbaef57ac7fc",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"ext-dom": "*", "ext-dom": "*",
"ext-libxml": "*", "ext-libxml": "*",
"ext-xmlwriter": "*", "ext-xmlwriter": "*",
"nikic/php-parser": "^5.4.0", "nikic/php-parser": "^5.6.1",
"php": ">=8.3", "php": ">=8.3",
"phpunit/php-file-iterator": "^6.0", "phpunit/php-file-iterator": "^6.0",
"phpunit/php-text-template": "^5.0", "phpunit/php-text-template": "^5.0",
"sebastian/complexity": "^5.0", "sebastian/complexity": "^5.0",
"sebastian/environment": "^8.0", "sebastian/environment": "^8.0.3",
"sebastian/lines-of-code": "^4.0", "sebastian/lines-of-code": "^4.0",
"sebastian/version": "^6.0", "sebastian/version": "^6.0",
"theseer/tokenizer": "^1.2.3" "theseer/tokenizer": "^1.2.3"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^12.1" "phpunit/phpunit": "^12.3.7"
}, },
"suggest": { "suggest": {
"ext-pcov": "PHP extension that provides line coverage", "ext-pcov": "PHP extension that provides line coverage",
@ -4928,7 +5037,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.3.4" "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.3.8"
}, },
"funding": [ "funding": [
{ {
@ -4948,7 +5057,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-08-29T11:32:44+00:00" "time": "2025-09-17T11:31:43+00:00"
}, },
{ {
"name": "phpunit/php-file-iterator", "name": "phpunit/php-file-iterator",
@ -5197,16 +5306,16 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "12.3.7", "version": "12.3.12",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "b8fa997c49682979ad6bfaa0d7fb25f54954965e" "reference": "729861f66944204f5b446ee1cb156f02f2a439a6"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b8fa997c49682979ad6bfaa0d7fb25f54954965e", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/729861f66944204f5b446ee1cb156f02f2a439a6",
"reference": "b8fa997c49682979ad6bfaa0d7fb25f54954965e", "reference": "729861f66944204f5b446ee1cb156f02f2a439a6",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -5220,17 +5329,17 @@
"phar-io/manifest": "^2.0.4", "phar-io/manifest": "^2.0.4",
"phar-io/version": "^3.2.1", "phar-io/version": "^3.2.1",
"php": ">=8.3", "php": ">=8.3",
"phpunit/php-code-coverage": "^12.3.3", "phpunit/php-code-coverage": "^12.3.8",
"phpunit/php-file-iterator": "^6.0.0", "phpunit/php-file-iterator": "^6.0.0",
"phpunit/php-invoker": "^6.0.0", "phpunit/php-invoker": "^6.0.0",
"phpunit/php-text-template": "^5.0.0", "phpunit/php-text-template": "^5.0.0",
"phpunit/php-timer": "^8.0.0", "phpunit/php-timer": "^8.0.0",
"sebastian/cli-parser": "^4.0.0", "sebastian/cli-parser": "^4.2.0",
"sebastian/comparator": "^7.1.3", "sebastian/comparator": "^7.1.3",
"sebastian/diff": "^7.0.0", "sebastian/diff": "^7.0.0",
"sebastian/environment": "^8.0.3", "sebastian/environment": "^8.0.3",
"sebastian/exporter": "^7.0.0", "sebastian/exporter": "^7.0.0",
"sebastian/global-state": "^8.0.0", "sebastian/global-state": "^8.0.2",
"sebastian/object-enumerator": "^7.0.0", "sebastian/object-enumerator": "^7.0.0",
"sebastian/type": "^6.0.3", "sebastian/type": "^6.0.3",
"sebastian/version": "^6.0.0", "sebastian/version": "^6.0.0",
@ -5274,7 +5383,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues", "issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy", "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.7" "source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.12"
}, },
"funding": [ "funding": [
{ {
@ -5298,7 +5407,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-08-28T05:15:46+00:00" "time": "2025-09-21T12:23:01+00:00"
}, },
{ {
"name": "psr/container", "name": "psr/container",
@ -5881,16 +5990,16 @@
}, },
{ {
"name": "rector/rector", "name": "rector/rector",
"version": "2.1.4", "version": "2.1.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/rectorphp/rector.git", "url": "https://github.com/rectorphp/rector.git",
"reference": "fe613c528819222f8686a9a037a315ef9d4915b3" "reference": "c34cc07c4698f007a20dc5c99ff820089ae413ce"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/rectorphp/rector/zipball/fe613c528819222f8686a9a037a315ef9d4915b3", "url": "https://api.github.com/repos/rectorphp/rector/zipball/c34cc07c4698f007a20dc5c99ff820089ae413ce",
"reference": "fe613c528819222f8686a9a037a315ef9d4915b3", "reference": "c34cc07c4698f007a20dc5c99ff820089ae413ce",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -5929,7 +6038,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/rectorphp/rector/issues", "issues": "https://github.com/rectorphp/rector/issues",
"source": "https://github.com/rectorphp/rector/tree/2.1.4" "source": "https://github.com/rectorphp/rector/tree/2.1.7"
}, },
"funding": [ "funding": [
{ {
@ -5937,20 +6046,20 @@
"type": "github" "type": "github"
} }
], ],
"time": "2025-08-15T14:41:36+00:00" "time": "2025-09-10T11:13:58+00:00"
}, },
{ {
"name": "sebastian/cli-parser", "name": "sebastian/cli-parser",
"version": "4.0.0", "version": "4.2.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/cli-parser.git", "url": "https://github.com/sebastianbergmann/cli-parser.git",
"reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c" "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/6d584c727d9114bcdc14c86711cd1cad51778e7c", "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/90f41072d220e5c40df6e8635f5dafba2d9d4d04",
"reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c", "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -5962,7 +6071,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "4.0-dev" "dev-main": "4.2-dev"
} }
}, },
"autoload": { "autoload": {
@ -5986,15 +6095,27 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/cli-parser/issues", "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
"security": "https://github.com/sebastianbergmann/cli-parser/security/policy", "security": "https://github.com/sebastianbergmann/cli-parser/security/policy",
"source": "https://github.com/sebastianbergmann/cli-parser/tree/4.0.0" "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.2.0"
}, },
"funding": [ "funding": [
{ {
"url": "https://github.com/sebastianbergmann", "url": "https://github.com/sebastianbergmann",
"type": "github" "type": "github"
},
{
"url": "https://liberapay.com/sebastianbergmann",
"type": "liberapay"
},
{
"url": "https://thanks.dev/u/gh/sebastianbergmann",
"type": "thanks_dev"
},
{
"url": "https://tidelift.com/funding/github/packagist/sebastian/cli-parser",
"type": "tidelift"
} }
], ],
"time": "2025-02-07T04:53:50+00:00" "time": "2025-09-14T09:36:45+00:00"
}, },
{ {
"name": "sebastian/comparator", "name": "sebastian/comparator",
@ -6291,16 +6412,16 @@
}, },
{ {
"name": "sebastian/exporter", "name": "sebastian/exporter",
"version": "7.0.0", "version": "7.0.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git", "url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "76432aafc58d50691a00d86d0632f1217a47b688" "reference": "b759164a8e02263784b662889cc6cbb686077af6"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/76432aafc58d50691a00d86d0632f1217a47b688", "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/b759164a8e02263784b662889cc6cbb686077af6",
"reference": "76432aafc58d50691a00d86d0632f1217a47b688", "reference": "b759164a8e02263784b662889cc6cbb686077af6",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -6357,15 +6478,27 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues", "issues": "https://github.com/sebastianbergmann/exporter/issues",
"security": "https://github.com/sebastianbergmann/exporter/security/policy", "security": "https://github.com/sebastianbergmann/exporter/security/policy",
"source": "https://github.com/sebastianbergmann/exporter/tree/7.0.0" "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.1"
}, },
"funding": [ "funding": [
{ {
"url": "https://github.com/sebastianbergmann", "url": "https://github.com/sebastianbergmann",
"type": "github" "type": "github"
},
{
"url": "https://liberapay.com/sebastianbergmann",
"type": "liberapay"
},
{
"url": "https://thanks.dev/u/gh/sebastianbergmann",
"type": "thanks_dev"
},
{
"url": "https://tidelift.com/funding/github/packagist/sebastian/exporter",
"type": "tidelift"
} }
], ],
"time": "2025-02-07T04:56:42+00:00" "time": "2025-09-22T05:39:29+00:00"
}, },
{ {
"name": "sebastian/global-state", "name": "sebastian/global-state",
@ -6928,16 +7061,16 @@
}, },
{ {
"name": "sebastianfeldmann/git", "name": "sebastianfeldmann/git",
"version": "3.14.3", "version": "3.15.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianfeldmann/git.git", "url": "https://github.com/sebastianfeldmann/git.git",
"reference": "22584df8df01d95b0700000cfd855779fae7d8ea" "reference": "90cb5a32f54dbb0d7dcd87d02e664ec2b50c0c96"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianfeldmann/git/zipball/22584df8df01d95b0700000cfd855779fae7d8ea", "url": "https://api.github.com/repos/sebastianfeldmann/git/zipball/90cb5a32f54dbb0d7dcd87d02e664ec2b50c0c96",
"reference": "22584df8df01d95b0700000cfd855779fae7d8ea", "reference": "90cb5a32f54dbb0d7dcd87d02e664ec2b50c0c96",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -6978,7 +7111,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/sebastianfeldmann/git/issues", "issues": "https://github.com/sebastianfeldmann/git/issues",
"source": "https://github.com/sebastianfeldmann/git/tree/3.14.3" "source": "https://github.com/sebastianfeldmann/git/tree/3.15.1"
}, },
"funding": [ "funding": [
{ {
@ -6986,7 +7119,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2025-06-05T16:05:10+00:00" "time": "2025-09-05T08:07:09+00:00"
}, },
{ {
"name": "staabm/side-effects-detector", "name": "staabm/side-effects-detector",
@ -8114,16 +8247,16 @@
}, },
{ {
"name": "symplify/easy-coding-standard", "name": "symplify/easy-coding-standard",
"version": "12.5.24", "version": "12.6.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/easy-coding-standard/easy-coding-standard.git", "url": "https://github.com/easy-coding-standard/easy-coding-standard.git",
"reference": "4b90f2b6efed9508000968eac2397ac7aff34354" "reference": "781e6124dc7e14768ae999a8f5309566bbe62004"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/easy-coding-standard/easy-coding-standard/zipball/4b90f2b6efed9508000968eac2397ac7aff34354", "url": "https://api.github.com/repos/easy-coding-standard/easy-coding-standard/zipball/781e6124dc7e14768ae999a8f5309566bbe62004",
"reference": "4b90f2b6efed9508000968eac2397ac7aff34354", "reference": "781e6124dc7e14768ae999a8f5309566bbe62004",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -8159,7 +8292,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/easy-coding-standard/easy-coding-standard/issues", "issues": "https://github.com/easy-coding-standard/easy-coding-standard/issues",
"source": "https://github.com/easy-coding-standard/easy-coding-standard/tree/12.5.24" "source": "https://github.com/easy-coding-standard/easy-coding-standard/tree/12.6.0"
}, },
"funding": [ "funding": [
{ {
@ -8171,7 +8304,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2025-08-21T06:57:14+00:00" "time": "2025-09-10T14:21:58+00:00"
}, },
{ {
"name": "theseer/tokenizer", "name": "theseer/tokenizer",
@ -8227,6 +8360,7 @@
"aliases": [], "aliases": [],
"minimum-stability": "dev", "minimum-stability": "dev",
"stability-flags": { "stability-flags": {
"adaures/castopod-plugins-manager": 20,
"codeigniter4/tasks": 20, "codeigniter4/tasks": 20,
"opawg/user-agents-v2-php": 20, "opawg/user-agents-v2-php": 20,
"yassinedoghri/podcast-feed": 20 "yassinedoghri/podcast-feed": 20

4010
docs/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -61,10 +61,6 @@ class Activity extends UuidEntity
public function getActor(): Actor public function getActor(): Actor
{ {
if ($this->actor_id === null) {
throw new RuntimeException('Activity must have an actor_id before getting the actor.');
}
if (! $this->actor instanceof Actor) { if (! $this->actor instanceof Actor) {
$this->actor = model('ActorModel', false) $this->actor = model('ActorModel', false)
->getActorById($this->actor_id); ->getActorById($this->actor_id);

View File

@ -11,7 +11,6 @@ declare(strict_types=1);
namespace Modules\Fediverse\Entities; namespace Modules\Fediverse\Entities;
use CodeIgniter\Entity\Entity; use CodeIgniter\Entity\Entity;
use RuntimeException;
/** /**
* @property int $id * @property int $id
@ -96,10 +95,6 @@ class Actor extends Entity
*/ */
public function getFollowers(): array public function getFollowers(): array
{ {
if ($this->id === null) {
throw new RuntimeException('Actor must be created before getting followers.');
}
if ($this->followers === null) { if ($this->followers === null) {
$this->followers = model('ActorModel', false) $this->followers = model('ActorModel', false)
->getFollowers($this->id); ->getFollowers($this->id);

View File

@ -65,10 +65,6 @@ class Notification extends UuidEntity
public function getActor(): ?Actor public function getActor(): ?Actor
{ {
if ($this->actor_id === null) {
throw new RuntimeException('Notification must have an actor_id before getting actor.');
}
if (! $this->actor instanceof Actor) { if (! $this->actor instanceof Actor) {
$this->actor = new ActorModel() $this->actor = new ActorModel()
->getActorById($this->actor_id); ->getActorById($this->actor_id);
@ -79,10 +75,6 @@ class Notification extends UuidEntity
public function getTargetActor(): ?Actor public function getTargetActor(): ?Actor
{ {
if ($this->target_actor_id === null) {
throw new RuntimeException('Notification must have a target_actor_id before getting target actor.');
}
if (! $this->target_actor instanceof Actor) { if (! $this->target_actor instanceof Actor) {
$this->target_actor = new ActorModel() $this->target_actor = new ActorModel()
->getActorById($this->target_actor_id); ->getActorById($this->target_actor_id);

View File

@ -94,10 +94,6 @@ class Post extends UuidEntity
*/ */
public function getActor(): Actor public function getActor(): Actor
{ {
if ($this->actor_id === null) {
throw new RuntimeException('Post must have an actor_id before getting actor.');
}
if (! $this->actor instanceof Actor) { if (! $this->actor instanceof Actor) {
$this->actor = model('ActorModel', false) $this->actor = model('ActorModel', false)
->getActorById($this->actor_id); ->getActorById($this->actor_id);
@ -108,10 +104,6 @@ class Post extends UuidEntity
public function getPreviewCard(): ?PreviewCard public function getPreviewCard(): ?PreviewCard
{ {
if ($this->id === null) {
throw new RuntimeException('Post must be created before getting preview_card.');
}
if (! $this->preview_card instanceof PreviewCard) { if (! $this->preview_card instanceof PreviewCard) {
$this->preview_card = model('PreviewCardModel', false) $this->preview_card = model('PreviewCardModel', false)
->getPostPreviewCard($this->id); ->getPostPreviewCard($this->id);
@ -125,10 +117,6 @@ class Post extends UuidEntity
*/ */
public function getReplies(): array public function getReplies(): array
{ {
if ($this->id === null) {
throw new RuntimeException('Post must be created before getting replies.');
}
if ($this->replies === null) { if ($this->replies === null) {
$this->replies = model('PostModel', false) $this->replies = model('PostModel', false)
->getPostReplies($this->id); ->getPostReplies($this->id);
@ -161,10 +149,6 @@ class Post extends UuidEntity
*/ */
public function getReblogs(): array public function getReblogs(): array
{ {
if ($this->id === null) {
throw new RuntimeException('Post must be created before getting reblogs.');
}
if ($this->reblogs === null) { if ($this->reblogs === null) {
$this->reblogs = model('PostModel', false) $this->reblogs = model('PostModel', false)
->getPostReblogs($this->id); ->getPostReblogs($this->id);

View File

@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
namespace Modules\Plugins\Commands;
use Castopod\PluginsManager\PluginsManager;
use Override;
class Add extends BaseCommand
{
/**
* The Command's Name
*
* @var string
*/
protected $name = 'plugins:add';
/**
* The Command's Description
*
* @var string
*/
protected $description = 'Add a plugin from the plugins repository given its name and version.';
/**
* The Command's Usage
*
* @var string
*/
protected $usage = 'plugins:add [arguments] [options]';
/**
* The Command's Arguments
*
* @var array<string,string>
*/
protected $arguments = [
'plugin' => 'The pluginKey and an optional version separated by an @. If version is not provided, the latest will be added by default.',
];
/**
* Actually execute a command.
*
* @param array<string> $params
*/
#[Override]
public function run(array $params): void
{
parent::run($params);
$plugins = $this->parsePluginsParams($params);
/** @var PluginsManager $cpm */
$cpm = service('cpm');
$cpm->install($plugins);
}
/**
* @param array<string> $params
* @return array<string,string|null>
*/
private function parsePluginsParams(array $params): array
{
$plugins = [];
foreach ($params as $param) {
preg_match(
'/^(?<pluginKey>[a-z0-9]([_.-]?[a-z0-9]+)*\/[a-z0-9]([_.-]?[a-z0-9]+)*)(@(?<version>\S+))?\s*$/',
$param,
$matches,
);
if (array_key_exists('pluginKey', $matches)) {
$plugins[$matches['pluginKey']] = $matches['version'] ?? null;
}
}
return $plugins;
}
}

View File

@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace Modules\Plugins\Commands;
use Castopod\PluginsManager\Logger\PluginsManagerLogger;
use CodeIgniter\CLI\BaseCommand as CodeIgniterBaseCommand;
use CodeIgniter\CLI\CLI;
class BaseCommand extends CodeIgniterBaseCommand
{
/**
* The Command's Group
*
* @var string
*/
protected $group = 'Plugins';
/**
* The Command's Options
*
* @var array<string,string>
*/
protected $options = [
'--debug' => 'Get log trace to follow what is happening under the hood.',
];
/**
* Actually execute a command.
*
* @param array<int,string> $params
*
* @return int|void
*/
public function run(array $params)
{
if (CLI::getOption('debug')) {
PluginsManagerLogger::$formatter = CpmFormatterDebug::class;
} else {
PluginsManagerLogger::$formatter = CpmFormatter::class;
}
return 0;
}
}

View File

@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace Modules\Plugins\Commands;
use Castopod\PluginsManager\Logger\FormatterInterface;
use Castopod\PluginsManager\Logger\LogLevel;
use CodeIgniter\CLI\CLI;
class CpmFormatter implements FormatterInterface
{
public function format(LogLevel $level, array $log): void
{
match ($log['code']) {
'add.start' => CLI::write(
sprintf('• adding plugin %s%s', CLI::color(
$log['context']['pluginKey'],
'white',
), $log['context']['constraint'] === null ? '' : '@' . CLI::color(
$log['context']['constraint'],
'light_yellow',
)),
),
'add.end' => CLI::write(
sprintf('+ %s@%s added%s', $log['context']['pluginKey'], $log['context']['version'], PHP_EOL),
'light_green',
),
'update.start' => CLI::write(sprintf('• updating plugin %s…', $log['context']['pluginKey'])),
'update.end' => CLI::write(
'✔ ' . sprintf(
'%s updated to version %s%s',
$log['context']['pluginKey'],
$log['context']['version'],
PHP_EOL,
),
'light_green',
),
'remove.start' => CLI::write(sprintf('• removing plugin %s…', $log['context']['pluginKey'])),
'remove.end' => CLI::write(
'- ' . sprintf('%s was removed%s', $log['context']['pluginKey'], PHP_EOL),
'light_red',
),
default => null,
};
if ($level === LogLevel::Warning) {
CLI::write('⚠️ ' . $log['message'], 'light_yellow');
}
if ($level === LogLevel::Error) {
CLI::newLine();
CLI::write(' error ', 'white', 'red');
CLI::error($log['message']);
CLI::newLine();
exit(1); // exit with error, something wrong happened
}
}
}

View File

@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Modules\Plugins\Commands;
use Castopod\PluginsManager\Logger\FormatterInterface;
use Castopod\PluginsManager\Logger\LogLevel;
use CodeIgniter\CLI\CLI;
class CpmFormatterDebug implements FormatterInterface
{
public function format(LogLevel $level, array $log): void
{
match ($level) {
LogLevel::Success => CLI::write(
sprintf('%s %s', CLI::color($level->name, 'white', 'green'), json_encode(
$log,
JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT,
)),
),
LogLevel::Warning => CLI::write(
sprintf('%s %s', CLI::color($level->name, 'white', 'yellow'), json_encode(
$log,
JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT,
)),
),
LogLevel::Error => CLI::write(
sprintf('%s %s', CLI::color($level->name, 'white', 'red'), json_encode(
$log,
JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT,
)),
),
default => CLI::write(
sprintf('%s %s', CLI::color($level->name, 'white', 'blue'), json_encode(
$log,
JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT,
)),
),
};
}
}

View File

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Modules\Plugins\Commands; namespace Modules\Plugins\Commands;
use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\CLI; use CodeIgniter\CLI\CLI;
use Exception; use Exception;
use Modules\Plugins\Config\Plugins as PluginsConfig; use Modules\Plugins\Config\Plugins as PluginsConfig;
@ -45,11 +44,6 @@ class CreatePlugin extends BaseCommand
}', }',
]; ];
/**
* @var string
*/
protected $group = 'Plugins';
/** /**
* @var string * @var string
*/ */

View File

@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace Modules\Plugins\Commands;
use Castopod\PluginsManager\PluginsManager;
use Override;
class Install extends BaseCommand
{
/**
* The Command's Name
*
* @var string
*/
protected $name = 'plugins:install';
/**
* The Command's Description
*
* @var string
*/
protected $description = 'Install all plugins specified in plugins.txt file.';
/**
* The Command's Usage
*
* @var string
*/
protected $usage = 'plugins:install';
/**
* Actually execute a command.
*
* @param array<string> $params
*/
#[Override]
public function run(array $params): void
{
parent::run($params);
/** @var PluginsManager $cpm */
$cpm = service('cpm');
$cpm->installFromPluginsTxt();
}
}

View File

@ -1,20 +0,0 @@
<?php
declare(strict_types=1);
namespace Modules\Fediverse\Commands;
use CodeIgniter\CLI\BaseCommand;
use Override;
class InstallCommand extends BaseCommand
{
/**
* @param array<int|string, string|null> $params
*/
#[Override]
public function run(array $params): void
{
// TODO:
}
}

View File

@ -4,26 +4,19 @@ declare(strict_types=1);
namespace Modules\Plugins\Commands; namespace Modules\Plugins\Commands;
use CodeIgniter\CLI\BaseCommand; use Castopod\PluginsManager\PluginsManager;
use CodeIgniter\CLI\CLI; use CodeIgniter\CLI\CLI;
use Modules\Plugins\Core\Plugins; use Modules\Plugins\Core\Plugins;
use Override; use Override;
class UninstallPlugin extends BaseCommand class Remove extends BaseCommand
{ {
/**
* The Command's Group
*
* @var string
*/
protected $group = 'Plugins';
/** /**
* The Command's Name * The Command's Name
* *
* @var string * @var string
*/ */
protected $name = 'plugins:uninstall'; protected $name = 'plugins:remove';
/** /**
* The Command's Description * The Command's Description
@ -37,7 +30,7 @@ class UninstallPlugin extends BaseCommand
* *
* @var string * @var string
*/ */
protected $usage = 'plugins:uninstall [plugins]'; protected $usage = 'plugins:remove [plugins]';
/** /**
* The Command's Arguments * The Command's Arguments
@ -49,17 +42,22 @@ class UninstallPlugin extends BaseCommand
]; ];
/** /**
* @param list<string> $pluginKeys * @param list<string> $params
*/ */
#[Override] #[Override]
public function run(array $pluginKeys): int public function run(array $params): int
{ {
parent::run($params);
/** @var Plugins $plugins */ /** @var Plugins $plugins */
$plugins = service('plugins'); $plugins = service('plugins');
/** @var PluginsManager $cpm */
$cpm = service('cpm');
/** @var list<string> $errors */ /** @var list<string> $errors */
$errors = []; $errors = [];
foreach ($pluginKeys as $pluginKey) { foreach ($params as $pluginKey) {
$plugin = $plugins->getPluginByKey($pluginKey); $plugin = $plugins->getPluginByKey($pluginKey);
if ($plugin === null) { if ($plugin === null) {
@ -69,11 +67,17 @@ class UninstallPlugin extends BaseCommand
if (! $plugins->uninstall($plugin)) { if (! $plugins->uninstall($plugin)) {
$errors[] = sprintf('Something happened when removing %s', $pluginKey); $errors[] = sprintf('Something happened when removing %s', $pluginKey);
break;
} }
// delete plugin folder
$cpm->remove($pluginKey);
} }
foreach ($errors as $error) { foreach ($errors as $error) {
CLI::error($error . PHP_EOL); CLI::write(' error ', 'white', 'red');
CLI::error($error);
CLI::newLine();
} }
return $errors === [] ? 0 : 1; return $errors === [] ? 0 : 1;

View File

@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace Modules\Plugins\Commands;
use Castopod\PluginsManager\PluginsManager;
use CodeIgniter\CLI\CLI;
use Override;
class Update extends BaseCommand
{
/**
* The Command's Name
*
* @var string
*/
protected $name = 'plugins:update';
/**
* The Command's Description
*
* @var string
*/
protected $description = 'Update a plugin from the plugins repository given its name and version.';
/**
* The Command's Usage
*
* @var string
*/
protected $usage = 'plugins:update [arguments] [options]';
/**
* The Command's Arguments
*
* @var array<string,string>
*/
protected $arguments = [
'plugin' => 'The pluginKey and an optional version separated by an @. If version is not provided, the latest will be added by default.',
];
/**
* Actually execute a command.
*
* @param array<string> $params
*
* @return int|void
*/
#[Override]
public function run(array $params)
{
parent::run($params);
if ($params === []) {
CLI::error('Missing pluginKey argument.');
return 1;
}
/** @var PluginsManager $cpm */
$cpm = service('cpm');
$cpm->update($params[0]);
}
}

View File

@ -10,8 +10,5 @@ class Plugins extends BaseConfig
{ {
public string $folder = PLUGINS_PATH; public string $folder = PLUGINS_PATH;
/** public string $repositoryUrl = 'https://plugins.castopod.org/';
* @var list<string>
*/
public array $repositories = ['https://castopod.org/plugins/repository.json'];
} }

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Modules\Plugins\Config; namespace Modules\Plugins\Config;
use Castopod\PluginsManager\PluginsManager;
use CodeIgniter\Config\BaseService; use CodeIgniter\Config\BaseService;
use Modules\Plugins\Core\Plugins; use Modules\Plugins\Core\Plugins;
@ -19,4 +20,18 @@ class Services extends BaseService
return new Plugins($config); return new Plugins($config);
} }
/**
* Castopod Plugins Manager (cpm)
*/
public static function cpm(bool $getShared = true): PluginsManager
{
if ($getShared) {
return self::getSharedInstance('cpm');
}
$config = config('Plugins');
return new PluginsManager($config->repositoryUrl, WRITEPATH, $config->folder, WRITEPATH . 'temp');
}
} }

View File

@ -99,7 +99,7 @@ class Plugins
/** /**
* @return array<BasePlugin> * @return array<BasePlugin>
*/ */
public function getPlugins(int $page, int $perPage): array public function getPlugins(int $page = 1, int $perPage = 12): array
{ {
return array_slice(static::$plugins, (($page - 1) * $perPage), $perPage); return array_slice(static::$plugins, (($page - 1) * $perPage), $perPage);
} }
@ -180,7 +180,7 @@ class Plugins
public function getPlugin(string $vendor, string $package): ?BasePlugin public function getPlugin(string $vendor, string $package): ?BasePlugin
{ {
foreach ($this->getVendorPlugins($vendor) as $plugin) { foreach ($this->getVendorPlugins($vendor) as $plugin) {
if ($plugin->getKey() === $vendor . '/' . $package) { if ($plugin->getPackage() === $package) {
return $plugin; return $plugin;
} }
} }
@ -190,7 +190,7 @@ class Plugins
public function getPluginByKey(string $key): ?BasePlugin public function getPluginByKey(string $key): ?BasePlugin
{ {
if (! str_contains('/', $key)) { if (! str_contains($key, '/')) {
return null; return null;
} }
@ -265,13 +265,7 @@ class Plugins
return false; return false;
} }
// delete plugin folder return $db->transCommit();
$pluginFolder = $this->config->folder . $plugin->getKey();
$rmdirResult = $this->rrmdir($pluginFolder);
$transResult = $db->transCommit();
return $rmdirResult && $transResult;
} }
protected function registerPlugins(): void protected function registerPlugins(): void
@ -328,38 +322,4 @@ class Plugins
} }
} }
} }
/**
* Adapted from https://stackoverflow.com/a/3338133
*/
private function rrmdir(string $dir): bool
{
if (! is_dir($dir)) {
return false;
}
$objects = scandir($dir);
if (! $objects) {
return false;
}
foreach ($objects as $object) {
if ($object === '.') {
continue;
}
if ($object === '..') {
continue;
}
if (is_dir($dir . DIRECTORY_SEPARATOR . $object) && ! is_link($dir . '/' . $object)) {
$this->rrmdir($dir . DIRECTORY_SEPARATOR . $object);
} else {
unlink($dir . DIRECTORY_SEPARATOR . $object);
}
}
return rmdir($dir);
}
} }

View File

@ -27,7 +27,7 @@ use Exception;
* @property int $episodes_imported * @property int $episodes_imported
* @property ?int $episodes_count * @property ?int $episodes_count
* @property int $progress * @property int $progress
* @property int $duration * @property ?int $duration
* *
* @property ?int $process_id * @property ?int $process_id
* *
@ -100,7 +100,7 @@ class PodcastImportTask extends Entity
public function getDuration(): int public function getDuration(): int
{ {
if ($this->duration === null && $this->started_at && $this->ended_at) { if ($this->duration === null && $this->started_at !== null && $this->ended_at !== null) {
$this->duration = ($this->started_at->difference($this->ended_at)) $this->duration = ($this->started_at->difference($this->ended_at))
->getSeconds(); ->getSeconds();
} }

View File

@ -15,7 +15,6 @@ use App\Models\PodcastModel;
use CodeIgniter\Entity\Entity; use CodeIgniter\Entity\Entity;
use CodeIgniter\I18n\Time; use CodeIgniter\I18n\Time;
use Modules\Analytics\Models\AnalyticsPodcastBySubscriptionModel; use Modules\Analytics\Models\AnalyticsPodcastBySubscriptionModel;
use RuntimeException;
/** /**
* @property int $id * @property int $id
@ -100,10 +99,6 @@ class Subscription extends Entity
*/ */
public function getPodcast(): ?Podcast public function getPodcast(): ?Podcast
{ {
if ($this->podcast_id === null) {
throw new RuntimeException('Subscription must have a podcast_id before getting podcast.');
}
if (! $this->podcast instanceof Podcast) { if (! $this->podcast instanceof Podcast) {
$this->podcast = new PodcastModel() $this->podcast = new PodcastModel()
->getPodcastById($this->podcast_id); ->getPodcastById($this->podcast_id);

View File

@ -20,8 +20,8 @@
"lint:fix": "eslint --fix", "lint:fix": "eslint --fix",
"lint:css": "stylelint -f verbose \"resources/**/*.css\"", "lint:css": "stylelint -f verbose \"resources/**/*.css\"",
"lint:css:fix": "stylelint -f verbose --fix \"resources/**/*.css\"", "lint:css:fix": "stylelint -f verbose --fix \"resources/**/*.css\"",
"prettier": "prettier --check .", "prettier": "prettier --check . --ignore-path ./.gitignore --ignore-path ./docs/.gitignore",
"format": "prettier --write .", "format": "prettier --write . --ignore-path ./.gitignore --ignore-path ./docs/.gitignore",
"typecheck": "tsc", "typecheck": "tsc",
"all-contributors:add": "all-contributors add", "all-contributors:add": "all-contributors add",
"all-contributors:generate": "all-contributors generate", "all-contributors:generate": "all-contributors generate",
@ -33,11 +33,11 @@
"@amcharts/amcharts4": "^4.10.40", "@amcharts/amcharts4": "^4.10.40",
"@amcharts/amcharts4-geodata": "^4.1.31", "@amcharts/amcharts4-geodata": "^4.1.31",
"@codemirror/commands": "^6.8.1", "@codemirror/commands": "^6.8.1",
"@codemirror/lang-html": "^6.4.9", "@codemirror/lang-html": "^6.4.10",
"@codemirror/lang-xml": "^6.1.0", "@codemirror/lang-xml": "^6.1.0",
"@codemirror/language": "^6.11.3", "@codemirror/language": "^6.11.3",
"@codemirror/state": "^6.5.2", "@codemirror/state": "^6.5.2",
"@codemirror/view": "^6.38.1", "@codemirror/view": "^6.38.3",
"@floating-ui/dom": "^1.7.4", "@floating-ui/dom": "^1.7.4",
"@github/clipboard-copy-element": "^1.3.0", "@github/clipboard-copy-element": "^1.3.0",
"@github/hotkey": "^3.1.1", "@github/hotkey": "^3.1.1",
@ -52,22 +52,22 @@
"leaflet": "^1.9.4", "leaflet": "^1.9.4",
"leaflet.markercluster": "^1.5.3", "leaflet.markercluster": "^1.5.3",
"lit": "^3.3.1", "lit": "^3.3.1",
"marked": "^16.2.1", "marked": "^16.3.0",
"wavesurfer.js": "^7.10.1", "wavesurfer.js": "^7.10.1",
"xml-formatter": "^3.6.6" "xml-formatter": "^3.6.7"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^19.8.1", "@commitlint/cli": "^19.8.1",
"@commitlint/config-conventional": "^19.8.1", "@commitlint/config-conventional": "^19.8.1",
"@csstools/css-tokenizer": "^3.0.4", "@csstools/css-tokenizer": "^3.0.4",
"@eslint/eslintrc": "^3.3.1", "@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.34.0", "@eslint/js": "^9.36.0",
"@semantic-release/changelog": "^6.0.3", "@semantic-release/changelog": "^6.0.3",
"@semantic-release/exec": "^7.1.0", "@semantic-release/exec": "^7.1.0",
"@semantic-release/git": "^10.0.1", "@semantic-release/git": "^10.0.1",
"@semantic-release/gitlab": "^13.2.8", "@semantic-release/gitlab": "^13.2.8",
"@tailwindcss/forms": "^0.5.10", "@tailwindcss/forms": "^0.5.10",
"@tailwindcss/typography": "^0.5.16", "@tailwindcss/typography": "^0.5.18",
"@types/leaflet": "^1.9.20", "@types/leaflet": "^1.9.20",
"all-contributors-cli": "^6.26.1", "all-contributors-cli": "^6.26.1",
"commitizen": "^4.3.1", "commitizen": "^4.3.1",
@ -75,30 +75,30 @@
"cross-env": "^10.0.0", "cross-env": "^10.0.0",
"cssnano": "^7.1.1", "cssnano": "^7.1.1",
"cz-conventional-changelog": "^3.3.0", "cz-conventional-changelog": "^3.3.0",
"eslint": "^9.34.0", "eslint": "^9.36.0",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.4", "eslint-plugin-prettier": "^5.5.4",
"glob": "^11.0.3", "glob": "^11.0.3",
"globals": "^16.3.0", "globals": "^16.4.0",
"husky": "^9.1.7", "husky": "^9.1.7",
"is-ci": "^4.1.0", "is-ci": "^4.1.0",
"lint-staged": "^16.1.5", "lint-staged": "^16.2.0",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"postcss-import": "^16.1.1", "postcss-import": "^16.1.1",
"postcss-nesting": "^13.0.2", "postcss-nesting": "^13.0.2",
"postcss-preset-env": "^10.3.1", "postcss-preset-env": "^10.4.0",
"postcss-reporter": "^7.1.0", "postcss-reporter": "^7.1.0",
"prettier": "3.6.2", "prettier": "3.6.2",
"prettier-plugin-organize-imports": "^4.2.0", "prettier-plugin-organize-imports": "^4.3.0",
"semantic-release": "^24.2.7", "semantic-release": "^24.2.9",
"sharp": "^0.34.3", "sharp": "^0.34.4",
"stylelint": "^16.23.1", "stylelint": "^16.24.0",
"stylelint-config-standard": "^39.0.0", "stylelint-config-standard": "^39.0.0",
"svgo": "^4.0.0", "svgo": "^4.0.0",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.17",
"typescript": "~5.9.2", "typescript": "~5.9.2",
"typescript-eslint": "^8.41.0", "typescript-eslint": "^8.44.1",
"vite": "^7.1.3", "vite": "^7.1.7",
"vite-plugin-codeigniter": "^2.0.0", "vite-plugin-codeigniter": "^2.0.0",
"vite-plugin-inspect": "^11.3.3", "vite-plugin-inspect": "^11.3.3",
"vite-plugin-pwa": "^1.0.3", "vite-plugin-pwa": "^1.0.3",

12383
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,15 @@
import path from "path"; import path from "path";
import { defineConfig } from "vite"; import { defineConfig, loadEnv } from "vite";
import { VitePWA } from "vite-plugin-pwa"; import { VitePWA } from "vite-plugin-pwa";
import codeigniter from "vite-plugin-codeigniter"; import codeigniter from "vite-plugin-codeigniter";
export default defineConfig(() => { export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd());
return { return {
server: { server: {
host: true, host: true,
port: 5173, port: env.VITE_PORT || 5173,
strictPort: true, strictPort: true,
}, },
plugins: [ plugins: [