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",
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
"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",
"shutdownAction": "stopCompose",
"features": {

View File

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

2
.gitignore vendored
View File

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

View File

@ -13,7 +13,6 @@ namespace App\Entities;
use App\Models\PodcastModel;
use Modules\Fediverse\Entities\Actor as FediverseActor;
use Override;
use RuntimeException;
/**
* @property Podcast|null $podcast
@ -32,10 +31,6 @@ class Actor extends FediverseActor
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) {
$this->podcast = new PodcastModel()
->getPodcastByActorId($this->id);

View File

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

View File

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

View File

@ -55,10 +55,6 @@ class Credit extends Entity
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) {
$this->person = new PersonModel()
->getPersonById($this->person_id);
@ -69,10 +65,6 @@ class Credit extends Entity
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) {
$this->podcast = new PodcastModel()
->getPodcastById($this->podcast_id);
@ -97,7 +89,7 @@ class Credit extends Entity
public function getGroupLabel(): string
{
if ($this->person_group === null) {
if ($this->person_group === '') {
return '';
}

View File

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

View File

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

View File

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

View File

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

View File

@ -113,9 +113,7 @@ if (! function_exists('get_rss_feed')) {
$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);
@ -145,9 +143,7 @@ if (! function_exists('get_rss_feed')) {
$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') {
$socialSignUpelement = $socialElement->addChild('socialSignUp', null, RssFeed::PODCAST_NAMESPACE);
@ -196,9 +192,7 @@ if (! function_exists('get_rss_feed')) {
RssFeed::PODCAST_NAMESPACE,
);
$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) {

View File

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

View File

@ -8,8 +8,9 @@
"require": {
"php": "^8.4",
"adaures/ipcat-php": "^v1.0.0",
"adaures/castopod-plugins-manager": "dev-main",
"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",
"cocur/slugify": "^v4.6.0",
"codeigniter4/framework": "4.6.3",
@ -27,7 +28,7 @@
"phpseclib/phpseclib": "~2.0.48",
"vlucas/phpdotenv": "5.6.2",
"whichbrowser/parser": "^v2.1.8",
"yassinedoghri/codeigniter-vite": "^v1.1.0",
"yassinedoghri/codeigniter-vite": "^2.1.0",
"yassinedoghri/php-icons": "1.3.0",
"yassinedoghri/podcast-feed": "dev-main"
},
@ -36,11 +37,11 @@
"codeigniter/phpstan-codeigniter": "1.5.4",
"mikey179/vfsstream": "^v1.6.12",
"phpstan/extension-installer": "^1.4.3",
"phpstan/phpstan": "^2.1.22",
"phpunit/phpunit": "^12.3.7",
"rector/rector": "^2.1.4",
"phpstan/phpstan": "^2.1.28",
"phpunit/phpunit": "^12.3.12",
"rector/rector": "^2.1.7",
"symplify/coding-standard": "^12.4.3",
"symplify/easy-coding-standard": "^12.5.24"
"symplify/easy-coding-standard": "^12.6.0"
},
"autoload": {
"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",
"This file is @generated automatically"
],
"content-hash": "656fade414e31429a68d7b3a41b2b8c5",
"content-hash": "62cc52f6536e1b74b94ce8e9baf16cf3",
"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",
"version": "v1.0.0",
@ -206,16 +260,16 @@
},
{
"name": "aws/aws-sdk-php",
"version": "3.356.8",
"version": "3.356.22",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "3efa8c62c11fedb17b90f60b2d3a9f815b406e63"
"reference": "5b3f65a158a0e822586d110192ee78c6565b4f06"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/3efa8c62c11fedb17b90f60b2d3a9f815b406e63",
"reference": "3efa8c62c11fedb17b90f60b2d3a9f815b406e63",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/5b3f65a158a0e822586d110192ee78c6565b4f06",
"reference": "5b3f65a158a0e822586d110192ee78c6565b4f06",
"shasum": ""
},
"require": {
@ -228,7 +282,7 @@
"guzzlehttp/psr7": "^2.4.5",
"mtdowling/jmespath.php": "^2.8.0",
"php": ">=8.1",
"psr/http-message": "^2.0"
"psr/http-message": "^1.0 || ^2.0"
},
"require-dev": {
"andrewsville/php-token-reflection": "^1.4",
@ -297,31 +351,31 @@
"support": {
"forum": "https://github.com/aws/aws-sdk-php/discussions",
"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",
"version": "0.13.1",
"version": "0.14.0",
"source": {
"type": "git",
"url": "https://github.com/brick/math.git",
"reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04"
"reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04",
"reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04",
"url": "https://api.github.com/repos/brick/math/zipball/113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2",
"reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2",
"shasum": ""
},
"require": {
"php": "^8.1"
"php": "^8.2"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.2",
"phpunit/phpunit": "^10.1",
"vimeo/psalm": "6.8.8"
"phpstan/phpstan": "2.1.22",
"phpunit/phpunit": "^11.5"
},
"type": "library",
"autoload": {
@ -351,7 +405,7 @@
],
"support": {
"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": [
{
@ -359,7 +413,7 @@
"type": "github"
}
],
"time": "2025-03-29T13:50:30+00:00"
"time": "2025-08-29T12:40:03+00:00"
},
{
"name": "chrisjean/php-ico",
@ -562,12 +616,12 @@
"source": {
"type": "git",
"url": "https://github.com/codeigniter4/queue.git",
"reference": "0f95012aa9671f4cfcc74af0f62c7e0c29dadd8e"
"reference": "e0cd27943903e26c8fa8004cd57f88115798fc60"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/codeigniter4/queue/zipball/0f95012aa9671f4cfcc74af0f62c7e0c29dadd8e",
"reference": "0f95012aa9671f4cfcc74af0f62c7e0c29dadd8e",
"url": "https://api.github.com/repos/codeigniter4/queue/zipball/e0cd27943903e26c8fa8004cd57f88115798fc60",
"reference": "e0cd27943903e26c8fa8004cd57f88115798fc60",
"shasum": ""
},
"require": {
@ -576,11 +630,13 @@
"require-dev": {
"codeigniter4/devkit": "^1.0",
"codeigniter4/framework": "^4.3",
"php-amqplib/php-amqplib": "^3.7",
"phpstan/phpstan-strict-rules": "^1.5",
"predis/predis": "^2.0"
},
"suggest": {
"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"
},
"default-branch": true,
@ -618,7 +674,7 @@
"issues": "https://github.com/codeigniter4/queue/issues",
"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",
@ -752,12 +808,12 @@
"source": {
"type": "git",
"url": "https://github.com/codeigniter4/tasks.git",
"reference": "04894af5f78b0c8652f87081ef75dd1a030e2972"
"reference": "e5aac87c3645e5173bb1c71f8fec0432f308b816"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/codeigniter4/tasks/zipball/04894af5f78b0c8652f87081ef75dd1a030e2972",
"reference": "04894af5f78b0c8652f87081ef75dd1a030e2972",
"url": "https://api.github.com/repos/codeigniter4/tasks/zipball/e5aac87c3645e5173bb1c71f8fec0432f308b816",
"reference": "e5aac87c3645e5173bb1c71f8fec0432f308b816",
"shasum": ""
},
"require": {
@ -853,7 +909,7 @@
"source": "https://github.com/codeigniter4/tasks/tree/develop",
"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",
@ -3043,20 +3099,20 @@
},
{
"name": "ramsey/uuid",
"version": "4.9.0",
"version": "4.9.1",
"source": {
"type": "git",
"url": "https://github.com/ramsey/uuid.git",
"reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0"
"reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/4e0e23cc785f0724a0e838279a9eb03f28b092a0",
"reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/81f941f6f729b1e3ceea61d9d014f8b6c6800440",
"reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440",
"shasum": ""
},
"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",
"ramsey/collection": "^1.2 || ^2.0"
},
@ -3115,9 +3171,9 @@
],
"support": {
"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",
@ -3587,31 +3643,31 @@
},
{
"name": "yassinedoghri/codeigniter-vite",
"version": "v1.1.0",
"version": "v2.1.0",
"source": {
"type": "git",
"url": "https://github.com/yassinedoghri/codeigniter-vite.git",
"reference": "b96cff850b868b992871ce7a92703567afc9db79"
"reference": "95c1dd30b716e3204ce981aa564202b299f9c8a5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/yassinedoghri/codeigniter-vite/zipball/b96cff850b868b992871ce7a92703567afc9db79",
"reference": "b96cff850b868b992871ce7a92703567afc9db79",
"url": "https://api.github.com/repos/yassinedoghri/codeigniter-vite/zipball/95c1dd30b716e3204ce981aa564202b299f9c8a5",
"reference": "95c1dd30b716e3204ce981aa564202b299f9c8a5",
"shasum": ""
},
"require": {
"php": ">=8.1"
},
"require-dev": {
"codeigniter/phpstan-codeigniter": "v1.5.1",
"codeigniter4/framework": "v4.5.7",
"pestphp/pest": "v3.7.1",
"pestphp/pest-plugin-type-coverage": "v3.2.3",
"codeigniter/phpstan-codeigniter": "^1.5.4",
"codeigniter4/framework": "^v4.6.0",
"pestphp/pest": "^3.8.4",
"pestphp/pest-plugin-type-coverage": "^3.6.1",
"phpstan/extension-installer": "^1.4.3",
"phpstan/phpstan": "^2.1.1",
"rector/rector": "^2.0.6",
"symplify/coding-standard": "^12.2.3",
"symplify/easy-coding-standard": "^12.5.5"
"phpstan/phpstan": "^2.1.22",
"rector/rector": "^2.1.4",
"symplify/coding-standard": "^12.4.3",
"symplify/easy-coding-standard": "^12.5.24"
},
"type": "library",
"autoload": {
@ -3629,7 +3685,7 @@
"homepage": "https://yassinedoghri.com/"
}
],
"description": "A simple ViteJS integration for CodeIgniter4 projects.",
"description": "An opinionated Vite integration for CodeIgniter4 projects.",
"keywords": [
"codeigniter",
"codeigniter4",
@ -3639,9 +3695,9 @@
],
"support": {
"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",
@ -3753,6 +3809,60 @@
"source": "https://github.com/yassinedoghri/podcast-feed/tree/main"
},
"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": [
@ -4364,16 +4474,16 @@
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.86.0",
"version": "v3.87.2",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "4a952bd19dc97879b0620f495552ef09b55f7d36"
"reference": "da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/4a952bd19dc97879b0620f495552ef09b55f7d36",
"reference": "4a952bd19dc97879b0620f495552ef09b55f7d36",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992",
"reference": "da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992",
"shasum": ""
},
"require": {
@ -4384,39 +4494,38 @@
"ext-hash": "*",
"ext-json": "*",
"ext-tokenizer": "*",
"fidry/cpu-core-counter": "^1.2",
"fidry/cpu-core-counter": "^1.3",
"php": "^7.4 || ^8.0",
"react/child-process": "^0.6.6",
"react/event-loop": "^1.5",
"react/promise": "^3.2",
"react/promise": "^3.3",
"react/socket": "^1.16",
"react/stream": "^1.4",
"sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0",
"symfony/console": "^5.4.47 || ^6.4.13 || ^7.0",
"symfony/event-dispatcher": "^5.4.45 || ^6.4.13 || ^7.0",
"symfony/filesystem": "^5.4.45 || ^6.4.13 || ^7.0",
"symfony/finder": "^5.4.45 || ^6.4.17 || ^7.0",
"symfony/options-resolver": "^5.4.45 || ^6.4.16 || ^7.0",
"symfony/polyfill-mbstring": "^1.32",
"symfony/polyfill-php80": "^1.32",
"symfony/polyfill-php81": "^1.32",
"symfony/process": "^5.4.47 || ^6.4.20 || ^7.2",
"symfony/stopwatch": "^5.4.45 || ^6.4.19 || ^7.0"
"symfony/console": "^5.4.47 || ^6.4.24 || ^7.0",
"symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0",
"symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0",
"symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0",
"symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0",
"symfony/polyfill-mbstring": "^1.33",
"symfony/polyfill-php80": "^1.33",
"symfony/polyfill-php81": "^1.33",
"symfony/process": "^5.4.47 || ^6.4.24 || ^7.2",
"symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0"
},
"require-dev": {
"facile-it/paraunit": "^1.3.1 || ^2.6",
"facile-it/paraunit": "^1.3.1 || ^2.7",
"infection/infection": "^0.29.14",
"justinrainbow/json-schema": "^5.3 || ^6.4",
"justinrainbow/json-schema": "^6.5",
"keradus/cli-executor": "^2.2",
"mikey179/vfsstream": "^1.6.12",
"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-xmlmatchesxsd": "^1.6",
"phpunit/phpunit": "^9.6.23 || ^10.5.47 || ^11.5.25",
"symfony/polyfill-php84": "^1.32",
"symfony/var-dumper": "^5.4.48 || ^6.4.23 || ^7.3.1",
"symfony/yaml": "^5.4.45 || ^6.4.23 || ^7.3.1"
"phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34",
"symfony/polyfill-php84": "^1.33",
"symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2",
"symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2"
},
"suggest": {
"ext-dom": "For handling output formats in XML",
@ -4457,7 +4566,7 @@
],
"support": {
"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": [
{
@ -4465,7 +4574,7 @@
"type": "github"
}
],
"time": "2025-08-13T22:36:21+00:00"
"time": "2025-09-10T09:51:40+00:00"
},
{
"name": "mikey179/vfsstream",
@ -4805,16 +4914,16 @@
},
{
"name": "phpstan/phpstan",
"version": "2.1.22",
"version": "2.1.28",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "41600c8379eb5aee63e9413fe9e97273e25d57e4"
"reference": "578fa296a166605d97b94091f724f1257185d278"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/41600c8379eb5aee63e9413fe9e97273e25d57e4",
"reference": "41600c8379eb5aee63e9413fe9e97273e25d57e4",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/578fa296a166605d97b94091f724f1257185d278",
"reference": "578fa296a166605d97b94091f724f1257185d278",
"shasum": ""
},
"require": {
@ -4859,38 +4968,38 @@
"type": "github"
}
],
"time": "2025-08-04T19:17:37+00:00"
"time": "2025-09-19T08:58:49+00:00"
},
{
"name": "phpunit/php-code-coverage",
"version": "12.3.4",
"version": "12.3.8",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "7ad0e9bdc72b147600badccd694a2e57ffc9297a"
"reference": "99e692c6a84708211f7536ba322bbbaef57ac7fc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7ad0e9bdc72b147600badccd694a2e57ffc9297a",
"reference": "7ad0e9bdc72b147600badccd694a2e57ffc9297a",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/99e692c6a84708211f7536ba322bbbaef57ac7fc",
"reference": "99e692c6a84708211f7536ba322bbbaef57ac7fc",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-libxml": "*",
"ext-xmlwriter": "*",
"nikic/php-parser": "^5.4.0",
"nikic/php-parser": "^5.6.1",
"php": ">=8.3",
"phpunit/php-file-iterator": "^6.0",
"phpunit/php-text-template": "^5.0",
"sebastian/complexity": "^5.0",
"sebastian/environment": "^8.0",
"sebastian/environment": "^8.0.3",
"sebastian/lines-of-code": "^4.0",
"sebastian/version": "^6.0",
"theseer/tokenizer": "^1.2.3"
},
"require-dev": {
"phpunit/phpunit": "^12.1"
"phpunit/phpunit": "^12.3.7"
},
"suggest": {
"ext-pcov": "PHP extension that provides line coverage",
@ -4928,7 +5037,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"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": [
{
@ -4948,7 +5057,7 @@
"type": "tidelift"
}
],
"time": "2025-08-29T11:32:44+00:00"
"time": "2025-09-17T11:31:43+00:00"
},
{
"name": "phpunit/php-file-iterator",
@ -5197,16 +5306,16 @@
},
{
"name": "phpunit/phpunit",
"version": "12.3.7",
"version": "12.3.12",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "b8fa997c49682979ad6bfaa0d7fb25f54954965e"
"reference": "729861f66944204f5b446ee1cb156f02f2a439a6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b8fa997c49682979ad6bfaa0d7fb25f54954965e",
"reference": "b8fa997c49682979ad6bfaa0d7fb25f54954965e",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/729861f66944204f5b446ee1cb156f02f2a439a6",
"reference": "729861f66944204f5b446ee1cb156f02f2a439a6",
"shasum": ""
},
"require": {
@ -5220,17 +5329,17 @@
"phar-io/manifest": "^2.0.4",
"phar-io/version": "^3.2.1",
"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-invoker": "^6.0.0",
"phpunit/php-text-template": "^5.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/diff": "^7.0.0",
"sebastian/environment": "^8.0.3",
"sebastian/exporter": "^7.0.0",
"sebastian/global-state": "^8.0.0",
"sebastian/global-state": "^8.0.2",
"sebastian/object-enumerator": "^7.0.0",
"sebastian/type": "^6.0.3",
"sebastian/version": "^6.0.0",
@ -5274,7 +5383,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"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": [
{
@ -5298,7 +5407,7 @@
"type": "tidelift"
}
],
"time": "2025-08-28T05:15:46+00:00"
"time": "2025-09-21T12:23:01+00:00"
},
{
"name": "psr/container",
@ -5881,16 +5990,16 @@
},
{
"name": "rector/rector",
"version": "2.1.4",
"version": "2.1.7",
"source": {
"type": "git",
"url": "https://github.com/rectorphp/rector.git",
"reference": "fe613c528819222f8686a9a037a315ef9d4915b3"
"reference": "c34cc07c4698f007a20dc5c99ff820089ae413ce"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/rectorphp/rector/zipball/fe613c528819222f8686a9a037a315ef9d4915b3",
"reference": "fe613c528819222f8686a9a037a315ef9d4915b3",
"url": "https://api.github.com/repos/rectorphp/rector/zipball/c34cc07c4698f007a20dc5c99ff820089ae413ce",
"reference": "c34cc07c4698f007a20dc5c99ff820089ae413ce",
"shasum": ""
},
"require": {
@ -5929,7 +6038,7 @@
],
"support": {
"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": [
{
@ -5937,20 +6046,20 @@
"type": "github"
}
],
"time": "2025-08-15T14:41:36+00:00"
"time": "2025-09-10T11:13:58+00:00"
},
{
"name": "sebastian/cli-parser",
"version": "4.0.0",
"version": "4.2.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/cli-parser.git",
"reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c"
"reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/6d584c727d9114bcdc14c86711cd1cad51778e7c",
"reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c",
"url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/90f41072d220e5c40df6e8635f5dafba2d9d4d04",
"reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04",
"shasum": ""
},
"require": {
@ -5962,7 +6071,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "4.0-dev"
"dev-main": "4.2-dev"
}
},
"autoload": {
@ -5986,15 +6095,27 @@
"support": {
"issues": "https://github.com/sebastianbergmann/cli-parser/issues",
"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": [
{
"url": "https://github.com/sebastianbergmann",
"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",
@ -6291,16 +6412,16 @@
},
{
"name": "sebastian/exporter",
"version": "7.0.0",
"version": "7.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "76432aafc58d50691a00d86d0632f1217a47b688"
"reference": "b759164a8e02263784b662889cc6cbb686077af6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/76432aafc58d50691a00d86d0632f1217a47b688",
"reference": "76432aafc58d50691a00d86d0632f1217a47b688",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/b759164a8e02263784b662889cc6cbb686077af6",
"reference": "b759164a8e02263784b662889cc6cbb686077af6",
"shasum": ""
},
"require": {
@ -6357,15 +6478,27 @@
"support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues",
"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": [
{
"url": "https://github.com/sebastianbergmann",
"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",
@ -6928,16 +7061,16 @@
},
{
"name": "sebastianfeldmann/git",
"version": "3.14.3",
"version": "3.15.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianfeldmann/git.git",
"reference": "22584df8df01d95b0700000cfd855779fae7d8ea"
"reference": "90cb5a32f54dbb0d7dcd87d02e664ec2b50c0c96"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianfeldmann/git/zipball/22584df8df01d95b0700000cfd855779fae7d8ea",
"reference": "22584df8df01d95b0700000cfd855779fae7d8ea",
"url": "https://api.github.com/repos/sebastianfeldmann/git/zipball/90cb5a32f54dbb0d7dcd87d02e664ec2b50c0c96",
"reference": "90cb5a32f54dbb0d7dcd87d02e664ec2b50c0c96",
"shasum": ""
},
"require": {
@ -6978,7 +7111,7 @@
],
"support": {
"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": [
{
@ -6986,7 +7119,7 @@
"type": "github"
}
],
"time": "2025-06-05T16:05:10+00:00"
"time": "2025-09-05T08:07:09+00:00"
},
{
"name": "staabm/side-effects-detector",
@ -8114,16 +8247,16 @@
},
{
"name": "symplify/easy-coding-standard",
"version": "12.5.24",
"version": "12.6.0",
"source": {
"type": "git",
"url": "https://github.com/easy-coding-standard/easy-coding-standard.git",
"reference": "4b90f2b6efed9508000968eac2397ac7aff34354"
"reference": "781e6124dc7e14768ae999a8f5309566bbe62004"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/easy-coding-standard/easy-coding-standard/zipball/4b90f2b6efed9508000968eac2397ac7aff34354",
"reference": "4b90f2b6efed9508000968eac2397ac7aff34354",
"url": "https://api.github.com/repos/easy-coding-standard/easy-coding-standard/zipball/781e6124dc7e14768ae999a8f5309566bbe62004",
"reference": "781e6124dc7e14768ae999a8f5309566bbe62004",
"shasum": ""
},
"require": {
@ -8159,7 +8292,7 @@
],
"support": {
"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": [
{
@ -8171,7 +8304,7 @@
"type": "github"
}
],
"time": "2025-08-21T06:57:14+00:00"
"time": "2025-09-10T14:21:58+00:00"
},
{
"name": "theseer/tokenizer",
@ -8227,6 +8360,7 @@
"aliases": [],
"minimum-stability": "dev",
"stability-flags": {
"adaures/castopod-plugins-manager": 20,
"codeigniter4/tasks": 20,
"opawg/user-agents-v2-php": 20,
"yassinedoghri/podcast-feed": 20

4088
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
{
if ($this->actor_id === null) {
throw new RuntimeException('Activity must have an actor_id before getting the actor.');
}
if (! $this->actor instanceof Actor) {
$this->actor = model('ActorModel', false)
->getActorById($this->actor_id);

View File

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

View File

@ -65,10 +65,6 @@ class Notification extends UuidEntity
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) {
$this->actor = new ActorModel()
->getActorById($this->actor_id);
@ -79,10 +75,6 @@ class Notification extends UuidEntity
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) {
$this->target_actor = new ActorModel()
->getActorById($this->target_actor_id);

View File

@ -94,10 +94,6 @@ class Post extends UuidEntity
*/
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) {
$this->actor = model('ActorModel', false)
->getActorById($this->actor_id);
@ -108,10 +104,6 @@ class Post extends UuidEntity
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) {
$this->preview_card = model('PreviewCardModel', false)
->getPostPreviewCard($this->id);
@ -125,10 +117,6 @@ class Post extends UuidEntity
*/
public function getReplies(): array
{
if ($this->id === null) {
throw new RuntimeException('Post must be created before getting replies.');
}
if ($this->replies === null) {
$this->replies = model('PostModel', false)
->getPostReplies($this->id);
@ -161,10 +149,6 @@ class Post extends UuidEntity
*/
public function getReblogs(): array
{
if ($this->id === null) {
throw new RuntimeException('Post must be created before getting reblogs.');
}
if ($this->reblogs === null) {
$this->reblogs = model('PostModel', false)
->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;
use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\CLI;
use Exception;
use Modules\Plugins\Config\Plugins as PluginsConfig;
@ -45,11 +44,6 @@ class CreatePlugin extends BaseCommand
}',
];
/**
* @var string
*/
protected $group = 'Plugins';
/**
* @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;
use CodeIgniter\CLI\BaseCommand;
use Castopod\PluginsManager\PluginsManager;
use CodeIgniter\CLI\CLI;
use Modules\Plugins\Core\Plugins;
use Override;
class UninstallPlugin extends BaseCommand
class Remove extends BaseCommand
{
/**
* The Command's Group
*
* @var string
*/
protected $group = 'Plugins';
/**
* The Command's Name
*
* @var string
*/
protected $name = 'plugins:uninstall';
protected $name = 'plugins:remove';
/**
* The Command's Description
@ -37,7 +30,7 @@ class UninstallPlugin extends BaseCommand
*
* @var string
*/
protected $usage = 'plugins:uninstall [plugins]';
protected $usage = 'plugins:remove [plugins]';
/**
* The Command's Arguments
@ -49,17 +42,22 @@ class UninstallPlugin extends BaseCommand
];
/**
* @param list<string> $pluginKeys
* @param list<string> $params
*/
#[Override]
public function run(array $pluginKeys): int
public function run(array $params): int
{
parent::run($params);
/** @var Plugins $plugins */
$plugins = service('plugins');
/** @var PluginsManager $cpm */
$cpm = service('cpm');
/** @var list<string> $errors */
$errors = [];
foreach ($pluginKeys as $pluginKey) {
foreach ($params as $pluginKey) {
$plugin = $plugins->getPluginByKey($pluginKey);
if ($plugin === null) {
@ -69,11 +67,17 @@ class UninstallPlugin extends BaseCommand
if (! $plugins->uninstall($plugin)) {
$errors[] = sprintf('Something happened when removing %s', $pluginKey);
break;
}
// delete plugin folder
$cpm->remove($pluginKey);
}
foreach ($errors as $error) {
CLI::error($error . PHP_EOL);
CLI::write(' error ', 'white', 'red');
CLI::error($error);
CLI::newLine();
}
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;
/**
* @var list<string>
*/
public array $repositories = ['https://castopod.org/plugins/repository.json'];
public string $repositoryUrl = 'https://plugins.castopod.org/';
}

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Modules\Plugins\Config;
use Castopod\PluginsManager\PluginsManager;
use CodeIgniter\Config\BaseService;
use Modules\Plugins\Core\Plugins;
@ -19,4 +20,18 @@ class Services extends BaseService
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>
*/
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);
}
@ -180,7 +180,7 @@ class Plugins
public function getPlugin(string $vendor, string $package): ?BasePlugin
{
foreach ($this->getVendorPlugins($vendor) as $plugin) {
if ($plugin->getKey() === $vendor . '/' . $package) {
if ($plugin->getPackage() === $package) {
return $plugin;
}
}
@ -190,7 +190,7 @@ class Plugins
public function getPluginByKey(string $key): ?BasePlugin
{
if (! str_contains('/', $key)) {
if (! str_contains($key, '/')) {
return null;
}
@ -265,13 +265,7 @@ class Plugins
return false;
}
// delete plugin folder
$pluginFolder = $this->config->folder . $plugin->getKey();
$rmdirResult = $this->rrmdir($pluginFolder);
$transResult = $db->transCommit();
return $rmdirResult && $transResult;
return $db->transCommit();
}
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_count
* @property int $progress
* @property int $duration
* @property ?int $duration
*
* @property ?int $process_id
*
@ -100,7 +100,7 @@ class PodcastImportTask extends Entity
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))
->getSeconds();
}

View File

@ -15,7 +15,6 @@ use App\Models\PodcastModel;
use CodeIgniter\Entity\Entity;
use CodeIgniter\I18n\Time;
use Modules\Analytics\Models\AnalyticsPodcastBySubscriptionModel;
use RuntimeException;
/**
* @property int $id
@ -100,10 +99,6 @@ class Subscription extends Entity
*/
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) {
$this->podcast = new PodcastModel()
->getPodcastById($this->podcast_id);

View File

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

12611
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 { defineConfig } from "vite";
import { defineConfig, loadEnv } from "vite";
import { VitePWA } from "vite-plugin-pwa";
import codeigniter from "vite-plugin-codeigniter";
export default defineConfig(() => {
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd());
return {
server: {
host: true,
port: 5173,
port: env.VITE_PORT || 5173,
strictPort: true,
},
plugins: [