diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index aa74483c..00000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM php:7.3-fpm - -COPY --from=composer /usr/bin/composer /usr/bin/composer - -RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - - -RUN apt-get update && \ - apt-get install -y nodejs - -RUN apt-get update && \ - apt-get upgrade -y && \ - apt-get install -y git vim diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 45fcd1c0..beb4929c 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,8 +1,11 @@ // For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at: // https://github.com/microsoft/vscode-dev-containers/tree/v0.117.1/containers/docker-existing-dockerfile { - "name": "Existing Dockerfile", - "dockerFile": "./Dockerfile", + "name": "Castopod Host dev", + "dockerComposeFile": ["../docker-compose.yml"], + "service": "app", + "workspaceFolder": "/castopod-host", + "postCreateCommand": "cron && php spark serve --host 0.0.0.0", "settings": { "terminal.integrated.shell.linux": "/bin/bash", "editor.formatOnSave": true, @@ -13,18 +16,18 @@ "color-highlight.markerType": "dot-before" }, "extensions": [ - "mikestead.dotenv", - "bmewburn.vscode-intelephense-client", - "streetsidesoftware.code-spell-checker", - "naumovs.color-highlight", - "heybourn.headwind", - "wayou.vscode-todo-highlight", - "esbenp.prettier-vscode", - "bradlc.vscode-tailwindcss", - "jamesbirtles.svelte-vscode", - "dbaeumer.vscode-eslint", - "stylelint.vscode-stylelint", - "wongjn.php-sniffer", - "eamodio.gitlens" -] + "mikestead.dotenv", + "bmewburn.vscode-intelephense-client", + "streetsidesoftware.code-spell-checker", + "naumovs.color-highlight", + "heybourn.headwind", + "wayou.vscode-todo-highlight", + "esbenp.prettier-vscode", + "bradlc.vscode-tailwindcss", + "jamesbirtles.svelte-vscode", + "dbaeumer.vscode-eslint", + "stylelint.vscode-stylelint", + "wongjn.php-sniffer", + "eamodio.gitlens" + ] } diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c295d6fb..f93745e6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,7 @@ image: php:7.3-fpm stages: + - quality - bundle - release @@ -31,16 +32,30 @@ before_script: - curl -sL https://deb.nodesource.com/setup_12.x | bash - - apt-get update && apt-get install -y nodejs - # Install php and js dependencies - - php composer.phar install --no-dev --ignore-platform-reqs + # Install all php and js dependencies + - php composer.phar install --prefer-dist --no-ansi --no-interaction --no-progress --ignore-platform-reqs - npm install - # build all UI assets - - npm run build +tests: + stage: quality + script: + - vendor/bin/phpunit + +code-review: + stage: quality + script: + # run rector + - vendor/bin/rector process --dry-run bundle_app: stage: bundle script: + # remove dev dependencies using the --no-dev option + - php composer.phar install --no-dev --prefer-dist --no-ansi --no-interaction --no-progress --ignore-platform-reqs + + # build all UI assets + - npm run build + # download GeoLite2-City archive and extract it to writable/uploads - wget -c "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=$MAXMIND_LICENCE_KEY&suffix=tar.gz" -O - | tar -xz -C ./writable/uploads/ @@ -65,8 +80,8 @@ release_app: - apt-get install jq -y - apt-get install zip -y - # make prepare-release.sh executable - - chmod +x ./prepare-release.sh + # make scripts/prepare-release.sh executable + - chmod +x ./scripts/prepare-release.sh # IMPORTANT: delete local git tags before release to prevent eventual script failure (ie. tag already exists) - git tag | xargs git tag -d diff --git a/.releaserc.json b/.releaserc.json index 8c76839d..018006c2 100644 --- a/.releaserc.json +++ b/.releaserc.json @@ -11,7 +11,7 @@ [ "@semantic-release/exec", { - "prepareCmd": "./prepare-release.sh ${nextRelease.version}" + "prepareCmd": "./scripts/prepare-release.sh ${nextRelease.version}" } ], "@semantic-release/npm", diff --git a/Dockerfile b/Dockerfile index 8792d21d..fce76183 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,31 @@ +#################################################### +# Castopod Host development Docker file +#################################################### +# NOT optimized for production +# should be used only for development purposes +#################################################### + FROM php:7.3-fpm +LABEL maintainer="Yassine Doghri" + COPY . /castopod-host WORKDIR /castopod-host +# Install composer +COPY --from=composer /usr/bin/composer /usr/bin/composer + +# Install npm +RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - + +RUN apt-get update && \ + apt-get install -y nodejs + +# Install git + vim +RUN apt-get update && \ + apt-get upgrade -y && \ + apt-get install -y git vim + ### Install CodeIgniter's server requirements #-- https://github.com/codeigniter4/appstarter#server-requirements diff --git a/app/Authorization/FlatAuthorization.php b/app/Authorization/FlatAuthorization.php index f96fb2aa..5c4a7f89 100644 --- a/app/Authorization/FlatAuthorization.php +++ b/app/Authorization/FlatAuthorization.php @@ -4,31 +4,13 @@ namespace App\Authorization; class FlatAuthorization extends \Myth\Auth\Authorization\FlatAuthorization { - //-------------------------------------------------------------------- - // Actions - //-------------------------------------------------------------------- - /** * Checks a group to see if they have the specified permission. * * @param int|string $permission - * @param int $groupId - * - * @return mixed */ - public function groupHasPermission($permission, int $groupId) + public function groupHasPermission($permission, int $groupId): bool { - if ( - empty($permission) || - (!is_string($permission) && !is_numeric($permission)) - ) { - return null; - } - - if (empty($groupId) || !is_numeric($groupId)) { - return null; - } - // Get the Permission ID $permissionId = $this->getPermissionID($permission); @@ -36,36 +18,23 @@ class FlatAuthorization extends \Myth\Auth\Authorization\FlatAuthorization return false; } - if ( - $this->permissionModel->doesGroupHavePermission( - $groupId, - (int) $permissionId - ) - ) { - return true; - } - - return false; + return (bool) $this->permissionModel->doesGroupHavePermission( + $groupId, + $permissionId, + ); } /** * Makes user part of given groups. * - * @param $userId - * @param array|null $groups // Either collection of ID or names - * - * @return bool + * @param array $groups Either collection of ID or names */ - public function setUserGroups(int $userId, $groups) + public function setUserGroups(int $userId, array $groups = []): bool { - if (empty($userId) || !is_numeric($userId)) { - return null; - } - // remove user from all groups before resetting it in new groups $this->groupModel->removeUserFromAllGroups($userId); - if (empty($groups)) { + if ($groups = []) { return true; } diff --git a/app/Authorization/GroupModel.php b/app/Authorization/GroupModel.php index 74455708..4c7da3f6 100644 --- a/app/Authorization/GroupModel.php +++ b/app/Authorization/GroupModel.php @@ -4,14 +4,20 @@ namespace App\Authorization; class GroupModel extends \Myth\Auth\Authorization\GroupModel { - public function getContributorRoles() + /** + * @return mixed[] + */ + public function getContributorRoles(): array { return $this->select('auth_groups.*') ->like('name', 'podcast_', 'after') ->findAll(); } - public function getUserRoles() + /** + * @return mixed[] + */ + public function getUserRoles(): array { return $this->select('auth_groups.*') ->notLike('name', 'podcast_', 'after') diff --git a/app/Authorization/PermissionModel.php b/app/Authorization/PermissionModel.php index c15f4412..254265e9 100644 --- a/app/Authorization/PermissionModel.php +++ b/app/Authorization/PermissionModel.php @@ -7,11 +7,6 @@ class PermissionModel extends \Myth\Auth\Authorization\PermissionModel /** * Checks to see if a user, or one of their groups, * has a specific permission. - * - * @param $userId - * @param $permissionId - * - * @return bool */ public function doesGroupHavePermission( int $groupId, @@ -33,9 +28,7 @@ class PermissionModel extends \Myth\Auth\Authorization\PermissionModel * id => name * ] * - * @param int $groupId - * - * @return array + * @return array */ public function getPermissionsForGroup(int $groupId): array { diff --git a/app/Config/Analytics.php b/app/Config/Analytics.php index c3497252..19b75559 100644 --- a/app/Config/Analytics.php +++ b/app/Config/Analytics.php @@ -26,7 +26,10 @@ class Analytics extends AnalyticsBase $this->gateway = config('App')->adminGateway . '/analytics'; } - public function getAudioFileUrl($audioFilePath) + /** + * get the full audio file url + */ + public function getAudioFileUrl(string $audioFilePath): string { helper('media'); diff --git a/app/Config/App.php b/app/Config/App.php index f08c5141..61096007 100644 --- a/app/Config/App.php +++ b/app/Config/App.php @@ -2,6 +2,7 @@ namespace Config; +use CodeIgniter\Session\Handlers\FileHandler; use CodeIgniter\Config\BaseConfig; class App extends BaseConfig @@ -34,6 +35,8 @@ class App extends BaseConfig * WITH a trailing slash: * * http://cdn.example.com/ + * + * @var string */ public $mediaBaseURL = 'http://127.0.0.2:8080/'; @@ -163,7 +166,7 @@ class App extends BaseConfig * * @var string */ - public $sessionDriver = 'CodeIgniter\Session\Handlers\FileHandler'; + public $sessionDriver = FileHandler::class; /** * -------------------------------------------------------------------------- @@ -480,6 +483,8 @@ class App extends BaseConfig * Media root folder * -------------------------------------------------------------------------- * Defines the root folder for media files storage + * + * @var string */ public $mediaRoot = 'media'; @@ -488,6 +493,8 @@ class App extends BaseConfig * Admin gateway * -------------------------------------------------------------------------- * Defines a base route for all admin pages + * + * @var string */ public $adminGateway = 'cp-admin'; @@ -496,6 +503,8 @@ class App extends BaseConfig * Auth gateway * -------------------------------------------------------------------------- * Defines a base route for all authentication related pages + * + * @var string */ public $authGateway = 'cp-auth'; @@ -504,6 +513,8 @@ class App extends BaseConfig * Install gateway * -------------------------------------------------------------------------- * Defines a base route for instance installation + * + * @var string */ public $installGateway = 'cp-install'; } diff --git a/app/Config/Auth.php b/app/Config/Auth.php index 6c5b78f5..db08a27a 100644 --- a/app/Config/Auth.php +++ b/app/Config/Auth.php @@ -4,10 +4,13 @@ namespace Config; class Auth extends \Myth\Auth\Config\Auth { - //-------------------------------------------------------------------- - // Views used by Auth Controllers - //-------------------------------------------------------------------- - + /** + * -------------------------------------------------------------------------- + * Views used by Auth Controllers + * -------------------------------------------------------------------------- + * + * @var array + */ public $views = [ 'login' => 'auth/login', 'register' => 'auth/register', @@ -17,26 +20,35 @@ class Auth extends \Myth\Auth\Config\Auth 'emailActivation' => 'auth/emails/activation', ]; - //-------------------------------------------------------------------- - // Layout for the views to extend - //-------------------------------------------------------------------- - + /** + * -------------------------------------------------------------------------- + * Layout for the views to extend + * -------------------------------------------------------------------------- + * + * @var string + */ public $viewLayout = 'auth/_layout'; - //-------------------------------------------------------------------- - // Allow User Registration - //-------------------------------------------------------------------- - // When enabled (default) any unregistered user may apply for a new - // account. If you disable registration you may need to ensure your - // controllers and views know not to offer registration. - // + /** + * -------------------------------------------------------------------------- + * Allow User Registration + * -------------------------------------------------------------------------- + * When enabled (default) any unregistered user may apply for a new + * account. If you disable registration you may need to ensure your + * controllers and views know not to offer registration. + * + * @var bool + */ public $allowRegistration = false; - //-------------------------------------------------------------------- - // Require confirmation registration via email - //-------------------------------------------------------------------- - // When enabled, every registered user will receive an email message - // with a special link he have to confirm to activate his account. - // + /** + * -------------------------------------------------------------------------- + * Require confirmation registration via email + * -------------------------------------------------------------------------- + * When enabled, every registered user will receive an email message + * with a special link he have to confirm to activate his account. + * + * @var bool + */ public $requireActivation = false; } diff --git a/app/Config/ContentSecurityPolicy.php b/app/Config/ContentSecurityPolicy.php index 7caa5422..05ffa5d7 100644 --- a/app/Config/ContentSecurityPolicy.php +++ b/app/Config/ContentSecurityPolicy.php @@ -32,7 +32,7 @@ class ContentSecurityPolicy extends BaseConfig * * @var string|null */ - public $reportURI = null; + public $reportURI; /** * Instructs user agents to rewrite URL schemes, changing @@ -53,7 +53,7 @@ class ContentSecurityPolicy extends BaseConfig * * @var string|string[]|null */ - public $defaultSrc = null; + public $defaultSrc; /** * Lists allowed scripts' URLs. @@ -83,7 +83,7 @@ class ContentSecurityPolicy extends BaseConfig * * @var string|string[]|null */ - public $baseURI = null; + public $baseURI; /** * Lists the URLs for workers and embedded frame contents @@ -105,7 +105,7 @@ class ContentSecurityPolicy extends BaseConfig * * @var string|string[] */ - public $fontSrc = null; + public $fontSrc; /** * Lists valid endpoints for submission from `
` tags. @@ -122,14 +122,14 @@ class ContentSecurityPolicy extends BaseConfig * * @var string|string[]|null */ - public $frameAncestors = null; + public $frameAncestors; /** * Restricts the origins allowed to deliver video and audio. * * @var string|string[]|null */ - public $mediaSrc = null; + public $mediaSrc; /** * Allows control over Flash and other plugins. @@ -141,19 +141,19 @@ class ContentSecurityPolicy extends BaseConfig /** * @var string|string[]|null */ - public $manifestSrc = null; + public $manifestSrc; /** * Limits the kinds of plugins a page may invoke. * * @var string|string[]|null */ - public $pluginTypes = null; + public $pluginTypes; /** * List of actions allowed. * * @var string|string[]|null */ - public $sandbox = null; + public $sandbox; } diff --git a/app/Config/Database.php b/app/Config/Database.php index f655132c..4ee26d95 100644 --- a/app/Config/Database.php +++ b/app/Config/Database.php @@ -62,6 +62,7 @@ class Database extends Config 'username' => '', 'password' => '', 'database' => ':memory:', + /** @noRector StringClassNameToClassConstantRector */ 'DBDriver' => 'SQLite3', 'DBPrefix' => 'db_', // Needed to ensure we're working correctly with prefixes live. DO NOT REMOVE FOR CI DEVS 'pConnect' => false, diff --git a/app/Config/Events.php b/app/Config/Events.php index da21d27d..6a994450 100644 --- a/app/Config/Events.php +++ b/app/Config/Events.php @@ -52,7 +52,7 @@ Events::on('pre_system', function () { } }); -Events::on('login', function ($user) { +Events::on('login', function ($user): void { helper('auth'); // set interact_as_actor_id value @@ -62,7 +62,7 @@ Events::on('login', function ($user) { } }); -Events::on('logout', function ($user) { +Events::on('logout', function ($user): void { helper('auth'); // remove user's interact_as_actor session @@ -75,7 +75,7 @@ Events::on('logout', function ($user) { * -------------------------------------------------------------------- * Update episode metadata counts */ -Events::on('on_note_add', function ($note) { +Events::on('on_note_add', function ($note): void { if ($note->episode_id) { model('EpisodeModel') ->where('id', $note->episode_id) @@ -87,7 +87,7 @@ Events::on('on_note_add', function ($note) { cache()->deleteMatching("page_podcast#{$note->actor->podcast->id}*"); }); -Events::on('on_note_remove', function ($note) { +Events::on('on_note_remove', function ($note): void { if ($note->episode_id) { model('EpisodeModel') ->where('id', $note->episode_id) @@ -106,7 +106,7 @@ Events::on('on_note_remove', function ($note) { cache()->deleteMatching("page_note#{$note->id}*"); }); -Events::on('on_note_reblog', function ($actor, $note) { +Events::on('on_note_reblog', function ($actor, $note): void { if ($episodeId = $note->episode_id) { model('EpisodeModel') ->where('id', $episodeId) @@ -125,7 +125,7 @@ Events::on('on_note_reblog', function ($actor, $note) { } }); -Events::on('on_note_undo_reblog', function ($reblogNote) { +Events::on('on_note_undo_reblog', function ($reblogNote): void { $note = $reblogNote->reblog_of_note; if ($episodeId = $note->episode_id) { model('EpisodeModel') @@ -147,21 +147,21 @@ Events::on('on_note_undo_reblog', function ($reblogNote) { } }); -Events::on('on_note_reply', function ($reply) { +Events::on('on_note_reply', function ($reply): void { $note = $reply->reply_to_note; cache()->deleteMatching("page_podcast#{$note->actor->podcast->id}*"); cache()->deleteMatching("page_note#{$note->id}*"); }); -Events::on('on_reply_remove', function ($reply) { +Events::on('on_reply_remove', function ($reply): void { $note = $reply->reply_to_note; cache()->deleteMatching("page_podcast#{$note->actor->podcast->id}*"); cache()->deleteMatching("page_note#{$note->id}*"); }); -Events::on('on_note_favourite', function ($actor, $note) { +Events::on('on_note_favourite', function ($actor, $note): void { if ($note->episode_id) { model('EpisodeModel') ->where('id', $note->episode_id) @@ -176,7 +176,7 @@ Events::on('on_note_favourite', function ($actor, $note) { } }); -Events::on('on_note_undo_favourite', function ($actor, $note) { +Events::on('on_note_undo_favourite', function ($actor, $note): void { if ($note->episode_id) { model('EpisodeModel') ->where('id', $note->episode_id) @@ -191,22 +191,22 @@ Events::on('on_note_undo_favourite', function ($actor, $note) { } }); -Events::on('on_block_actor', function ($actorId) { +Events::on('on_block_actor', function ($actorId): void { cache()->deleteMatching('page_podcast*'); cache()->deleteMatching('page_note*'); }); -Events::on('on_unblock_actor', function ($actorId) { +Events::on('on_unblock_actor', function ($actorId): void { cache()->deleteMatching('page_podcast*'); cache()->deleteMatching('page_note*'); }); -Events::on('on_block_domain', function ($domainName) { +Events::on('on_block_domain', function ($domainName): void { cache()->deleteMatching('page_podcast*'); cache()->deleteMatching('page_note*'); }); -Events::on('on_unblock_domain', function ($domainName) { +Events::on('on_unblock_domain', function ($domainName): void { cache()->deleteMatching('page_podcast*'); cache()->deleteMatching('page_note*'); }); diff --git a/app/Config/Filters.php b/app/Config/Filters.php index 45a9ac61..6a709328 100644 --- a/app/Config/Filters.php +++ b/app/Config/Filters.php @@ -2,6 +2,10 @@ namespace Config; +use Myth\Auth\Filters\LoginFilter; +use Myth\Auth\Filters\RoleFilter; +use App\Filters\PermissionFilter; +use ActivityPub\Filters\ActivityPubFilter; use CodeIgniter\Config\BaseConfig; use CodeIgniter\Filters\CSRF; use CodeIgniter\Filters\DebugToolbar; @@ -19,10 +23,10 @@ class Filters extends BaseConfig 'csrf' => CSRF::class, 'toolbar' => DebugToolbar::class, 'honeypot' => Honeypot::class, - 'login' => \Myth\Auth\Filters\LoginFilter::class, - 'role' => \Myth\Auth\Filters\RoleFilter::class, - 'permission' => \App\Filters\PermissionFilter::class, - 'activity-pub' => \ActivityPub\Filters\ActivityPubFilter::class, + 'login' => LoginFilter::class, + 'role' => RoleFilter::class, + 'permission' => PermissionFilter::class, + 'activity-pub' => ActivityPubFilter::class, ]; /** diff --git a/app/Config/Format.php b/app/Config/Format.php index e772f6ad..b3ba4690 100644 --- a/app/Config/Format.php +++ b/app/Config/Format.php @@ -2,6 +2,8 @@ namespace Config; +use CodeIgniter\Format\JSONFormatter; +use CodeIgniter\Format\XMLFormatter; use CodeIgniter\Config\BaseConfig; use CodeIgniter\Format\FormatterInterface; @@ -40,9 +42,9 @@ class Format extends BaseConfig * @var array */ public $formatters = [ - 'application/json' => 'CodeIgniter\Format\JSONFormatter', - 'application/xml' => 'CodeIgniter\Format\XMLFormatter', - 'text/xml' => 'CodeIgniter\Format\XMLFormatter', + 'application/json' => JSONFormatter::class, + 'application/xml' => XMLFormatter::class, + 'text/xml' => XMLFormatter::class, ]; /** @@ -62,17 +64,12 @@ class Format extends BaseConfig ]; //-------------------------------------------------------------------- - /** * A Factory method to return the appropriate formatter for the given mime type. * - * @param string $mime - * - * @return FormatterInterface - * * @deprecated This is an alias of `\CodeIgniter\Format\Format::getFormatter`. Use that instead. */ - public function getFormatter(string $mime) + public function getFormatter(string $mime): FormatterInterface { return Services::format()->getFormatter($mime); } diff --git a/app/Config/Kint.php b/app/Config/Kint.php index 0a4301c9..8d5ed9e0 100644 --- a/app/Config/Kint.php +++ b/app/Config/Kint.php @@ -23,39 +23,70 @@ class Kint extends BaseConfig |-------------------------------------------------------------------------- */ - public $plugins = null; + public $plugins; + /** + * @var int + */ public $maxDepth = 6; + /** + * @var bool + */ public $displayCalledFrom = true; + /** + * @var bool + */ public $expanded = false; /* - |-------------------------------------------------------------------------- - | RichRenderer Settings - |-------------------------------------------------------------------------- - */ + |-------------------------------------------------------------------------- + | RichRenderer Settings + |-------------------------------------------------------------------------- + */ + /** + * @var string + */ public $richTheme = 'aante-light.css'; + /** + * @var bool + */ public $richFolder = false; + /** + * @var int + */ public $richSort = Renderer::SORT_FULL; - public $richObjectPlugins = null; + public $richObjectPlugins; - public $richTabPlugins = null; + public $richTabPlugins; /* - |-------------------------------------------------------------------------- - | CLI Settings - |-------------------------------------------------------------------------- - */ + |-------------------------------------------------------------------------- + | CLI Settings + |-------------------------------------------------------------------------- + */ + + /** + * @var bool + */ public $cliColors = true; + /** + * @var bool + */ public $cliForceUTF8 = false; + /** + * @var bool + */ public $cliDetectWidth = true; + /** + * @var int + */ public $cliMinWidth = 40; } diff --git a/app/Config/Logger.php b/app/Config/Logger.php index a1d60a1d..5607dcfb 100644 --- a/app/Config/Logger.php +++ b/app/Config/Logger.php @@ -2,6 +2,7 @@ namespace Config; +use CodeIgniter\Log\Handlers\FileHandler; use CodeIgniter\Config\BaseConfig; class Logger extends BaseConfig @@ -82,7 +83,7 @@ class Logger extends BaseConfig * File Handler * -------------------------------------------------------------------- */ - 'CodeIgniter\Log\Handlers\FileHandler' => [ + FileHandler::class => [ /* * The log levels that this handler will handle. */ diff --git a/app/Config/Mimes.php b/app/Config/Mimes.php index 574db6d8..39c2aace 100644 --- a/app/Config/Mimes.php +++ b/app/Config/Mimes.php @@ -22,7 +22,7 @@ class Mimes /** * Map of extensions to mime types. * - * @var array + * @var array */ public static $mimes = [ 'hqx' => [ @@ -321,11 +321,9 @@ class Mimes /** * Attempts to determine the best mime type for the given file extension. * - * @param string $extension - * * @return string|null The mime type found, or none if unable to determine. */ - public static function guessTypeFromExtension(string $extension) + public static function guessTypeFromExtension(string $extension): ?string { $extension = trim(strtolower($extension), '. '); @@ -341,15 +339,13 @@ class Mimes /** * Attempts to determine the best file extension for a given mime type. * - * @param string $type * @param string|null $proposedExtension - default extension (in case there is more than one with the same mime type) - * * @return string|null The extension determined, or null if unable to match. */ public static function guessExtensionFromType( string $type, string $proposedExtension = null - ) { + ): ?string { $type = trim(strtolower($type), '. '); $proposedExtension = trim(strtolower($proposedExtension)); diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 3c0064e3..2bc5d9bc 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -54,7 +54,7 @@ $routes->addPlaceholder( $routes->get('/', 'Home::index', ['as' => 'home']); // Install Wizard route -$routes->group(config('App')->installGateway, function ($routes) { +$routes->group(config('App')->installGateway, function ($routes): void { $routes->get('/', 'Install', ['as' => 'install']); $routes->post('instance-config', 'Install::attemptInstanceConfig', [ 'as' => 'instance-config', @@ -76,12 +76,12 @@ $routes->get('.well-known/platforms', 'Platform'); $routes->group( config('App')->adminGateway, ['namespace' => 'App\Controllers\Admin'], - function ($routes) { + function ($routes): void { $routes->get('/', 'Home', [ 'as' => 'admin', ]); - $routes->group('persons', function ($routes) { + $routes->group('persons', function ($routes): void { $routes->get('/', 'Person', [ 'as' => 'person-list', 'filter' => 'permission:person-list', @@ -93,7 +93,7 @@ $routes->group( $routes->post('new', 'Person::attemptCreate', [ 'filter' => 'permission:person-create', ]); - $routes->group('(:num)', function ($routes) { + $routes->group('(:num)', function ($routes): void { $routes->get('/', 'Person::view/$1', [ 'as' => 'person-view', 'filter' => 'permission:person-view', @@ -113,7 +113,7 @@ $routes->group( }); // Podcasts - $routes->group('podcasts', function ($routes) { + $routes->group('podcasts', function ($routes): void { $routes->get('/', 'Podcast::list', [ 'as' => 'podcast-list', ]); @@ -134,7 +134,7 @@ $routes->group( // Podcast // Use ids in admin area to help permission and group lookups - $routes->group('(:num)', function ($routes) { + $routes->group('(:num)', function ($routes): void { $routes->get('/', 'Podcast::view/$1', [ 'as' => 'podcast-view', 'filter' => 'permission:podcasts-view,podcast-view', @@ -151,7 +151,7 @@ $routes->group( 'filter' => 'permission:podcasts-delete', ]); - $routes->group('persons', function ($routes) { + $routes->group('persons', function ($routes): void { $routes->get('/', 'PodcastPerson/$1', [ 'as' => 'podcast-person-manage', 'filter' => 'permission:podcast-edit', @@ -170,7 +170,7 @@ $routes->group( ); }); - $routes->group('analytics', function ($routes) { + $routes->group('analytics', function ($routes): void { $routes->get('/', 'Podcast::viewAnalytics/$1', [ 'as' => 'podcast-analytics', 'filter' => 'permission:podcasts-view,podcast-view', @@ -226,7 +226,7 @@ $routes->group( }); // Podcast episodes - $routes->group('episodes', function ($routes) { + $routes->group('episodes', function ($routes): void { $routes->get('/', 'Episode::list/$1', [ 'as' => 'episode-list', 'filter' => @@ -241,7 +241,7 @@ $routes->group( ]); // Episode - $routes->group('(:num)', function ($routes) { + $routes->group('(:num)', function ($routes): void { $routes->get('/', 'Episode::view/$1/$2', [ 'as' => 'episode-view', 'filter' => @@ -349,7 +349,7 @@ $routes->group( ], ); - $routes->group('persons', function ($routes) { + $routes->group('persons', function ($routes): void { $routes->get('/', 'EpisodePerson/$1/$2', [ 'as' => 'episode-person-manage', 'filter' => 'permission:podcast_episodes-edit', @@ -376,7 +376,7 @@ $routes->group( }); // Podcast contributors - $routes->group('contributors', function ($routes) { + $routes->group('contributors', function ($routes): void { $routes->get('/', 'Contributor::list/$1', [ 'as' => 'contributor-list', 'filter' => @@ -391,7 +391,7 @@ $routes->group( ]); // Contributor - $routes->group('(:num)', function ($routes) { + $routes->group('(:num)', function ($routes): void { $routes->get('/', 'Contributor::view/$1/$2', [ 'as' => 'contributor-view', 'filter' => @@ -418,7 +418,7 @@ $routes->group( }); }); - $routes->group('platforms', function ($routes) { + $routes->group('platforms', function ($routes): void { $routes->get( '/', 'PodcastPlatform::platforms/$1/podcasting', @@ -464,7 +464,7 @@ $routes->group( }); // Instance wide Fediverse config - $routes->group('fediverse', function ($routes) { + $routes->group('fediverse', function ($routes): void { $routes->get('/', 'Fediverse::dashboard', [ 'as' => 'fediverse-dashboard', ]); @@ -479,7 +479,7 @@ $routes->group( }); // Pages - $routes->group('pages', function ($routes) { + $routes->group('pages', function ($routes): void { $routes->get('/', 'Page::list', ['as' => 'page-list']); $routes->get('new', 'Page::create', [ 'as' => 'page-create', @@ -489,7 +489,7 @@ $routes->group( 'filter' => 'permission:pages-manage', ]); - $routes->group('(:num)', function ($routes) { + $routes->group('(:num)', function ($routes): void { $routes->get('/', 'Page::view/$1', ['as' => 'page-view']); $routes->get('edit', 'Page::edit/$1', [ 'as' => 'page-edit', @@ -507,7 +507,7 @@ $routes->group( }); // Users - $routes->group('users', function ($routes) { + $routes->group('users', function ($routes): void { $routes->get('/', 'User::list', [ 'as' => 'user-list', 'filter' => 'permission:users-list', @@ -521,7 +521,7 @@ $routes->group( ]); // User - $routes->group('(:num)', function ($routes) { + $routes->group('(:num)', function ($routes): void { $routes->get('/', 'User::view/$1', [ 'as' => 'user-view', 'filter' => 'permission:users-view', @@ -553,7 +553,7 @@ $routes->group( }); // My account - $routes->group('my-account', function ($routes) { + $routes->group('my-account', function ($routes): void { $routes->get('/', 'MyAccount', [ 'as' => 'my-account', ]); @@ -568,7 +568,7 @@ $routes->group( /** * Overwriting Myth:auth routes file */ -$routes->group(config('App')->authGateway, function ($routes) { +$routes->group(config('App')->authGateway, function ($routes): void { // Login/out $routes->get('login', 'Auth::login', ['as' => 'login']); $routes->post('login', 'Auth::attemptLogin'); @@ -600,7 +600,7 @@ $routes->group(config('App')->authGateway, function ($routes) { }); // Podcast's Public routes -$routes->group('@(:podcastName)', function ($routes) { +$routes->group('@(:podcastName)', function ($routes): void { $routes->get('/', 'Podcast::activity/$1', [ 'as' => 'podcast-activity', ]); @@ -621,7 +621,7 @@ $routes->group('@(:podcastName)', function ($routes) { $routes->get('episodes', 'Podcast::episodes/$1', [ 'as' => 'podcast-episodes', ]); - $routes->group('episodes/(:slug)', function ($routes) { + $routes->group('episodes/(:slug)', function ($routes): void { $routes->get('/', 'Episode/$1/$2', [ 'as' => 'episode', ]); @@ -631,7 +631,7 @@ $routes->group('@(:podcastName)', function ($routes) { $routes->get('oembed.xml', 'Episode::oembedXML/$1/$2', [ 'as' => 'episode-oembed-xml', ]); - $routes->group('embeddable-player', function ($routes) { + $routes->group('embeddable-player', function ($routes): void { $routes->get('/', 'Episode::embeddablePlayer/$1/$2', [ 'as' => 'embeddable-player', ]); @@ -661,13 +661,13 @@ $routes->post('interact-as-actor', 'Auth::attemptInteractAsActor', [ /** * Overwriting ActivityPub routes file */ -$routes->group('@(:podcastName)', function ($routes) { +$routes->group('@(:podcastName)', function ($routes): void { $routes->post('notes/new', 'Note::attemptCreate/$1', [ 'as' => 'note-attempt-create', 'filter' => 'permission:podcast-manage_publications', ]); // Note - $routes->group('notes/(:uuid)', function ($routes) { + $routes->group('notes/(:uuid)', function ($routes): void { $routes->get('/', 'Note/$1/$2', [ 'as' => 'note', 'alternate-content' => [ diff --git a/app/Controllers/Actor.php b/app/Controllers/Actor.php index a063e2b3..16bd68ca 100644 --- a/app/Controllers/Actor.php +++ b/app/Controllers/Actor.php @@ -23,7 +23,7 @@ class Actor extends \ActivityPub\Controllers\ActorController $this->registerPodcastWebpageHit($this->actor->podcast->id); } - $cacheName = "page_podcast@{$this->actor->username}_follow"; + $cacheName = "page_podcast-{$this->actor->username}_follow"; if (!($cachedView = cache($cacheName))) { helper(['form', 'components', 'svg']); $data = [ diff --git a/app/Controllers/Admin/BaseController.php b/app/Controllers/Admin/BaseController.php index a349ac6f..43deabd6 100644 --- a/app/Controllers/Admin/BaseController.php +++ b/app/Controllers/Admin/BaseController.php @@ -31,16 +31,12 @@ class BaseController extends Controller /** * Constructor. - * - * @param RequestInterface $request - * @param ResponseInterface $response - * @param LoggerInterface $logger */ public function initController( RequestInterface $request, ResponseInterface $response, LoggerInterface $logger - ) { + ): void { // Do Not Edit This Line parent::initController($request, $response, $logger); diff --git a/app/Controllers/Admin/Contributor.php b/app/Controllers/Admin/Contributor.php index 01e66ee6..9ae1e21b 100644 --- a/app/Controllers/Admin/Contributor.php +++ b/app/Controllers/Admin/Contributor.php @@ -8,6 +8,10 @@ namespace App\Controllers\Admin; +use App\Entities\Podcast; +use App\Entities\User; +use CodeIgniter\Exceptions\PageNotFoundException; +use Exception; use App\Authorization\GroupModel; use App\Models\PodcastModel; use App\Models\UserModel; @@ -15,12 +19,12 @@ use App\Models\UserModel; class Contributor extends BaseController { /** - * @var \App\Entities\Podcast + * @var Podcast */ protected $podcast; /** - * @var \App\Entities\User|null + * @var User|null */ protected $user; @@ -28,18 +32,20 @@ class Contributor extends BaseController { $this->podcast = (new PodcastModel())->getPodcastById($params[0]); - if (count($params) > 1) { - if ( - !($this->user = (new UserModel())->getPodcastContributor( - $params[1], - $params[0] - )) - ) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); - } + if (count($params) <= 1) { + return $this->$method(); } - return $this->$method(); + if ( + $this->user = (new UserModel())->getPodcastContributor( + $params[1], + $params[0], + ) + ) { + return $this->$method(); + } + + throw PageNotFoundException::forPageNotFound(); } public function list() @@ -57,7 +63,7 @@ class Contributor extends BaseController $data = [ 'contributor' => (new UserModel())->getPodcastContributor( $this->user->id, - $this->podcast->id + $this->podcast->id, ), ]; @@ -79,7 +85,7 @@ class Contributor extends BaseController $result[$user->id] = $user->username; return $result; }, - [] + [], ); $roles = (new GroupModel())->getContributorRoles(); @@ -89,7 +95,7 @@ class Contributor extends BaseController $result[$role->id] = lang('Contributor.roles.' . $role->name); return $result; }, - [] + [], ); $data = [ @@ -108,9 +114,9 @@ class Contributor extends BaseController (new PodcastModel())->addPodcastContributor( $this->request->getPost('user'), $this->podcast->id, - $this->request->getPost('role') + $this->request->getPost('role'), ); - } catch (\Exception $e) { + } catch (Exception $exception) { return redirect() ->back() ->withInput() @@ -133,7 +139,7 @@ class Contributor extends BaseController $result[$role->id] = lang('Contributor.roles.' . $role->name); return $result; }, - [] + [], ); $data = [ @@ -141,7 +147,7 @@ class Contributor extends BaseController 'user' => $this->user, 'contributorGroupId' => (new PodcastModel())->getContributorGroupId( $this->user->id, - $this->podcast->id + $this->podcast->id, ), 'roleOptions' => $roleOptions, ]; @@ -158,7 +164,7 @@ class Contributor extends BaseController (new PodcastModel())->updatePodcastContributor( $this->user->id, $this->podcast->id, - $this->request->getPost('role') + $this->request->getPost('role'), ); return redirect()->route('contributor-list', [$this->podcast->id]); @@ -178,7 +184,7 @@ class Contributor extends BaseController if ( !$podcastModel->removePodcastContributor( $this->user->id, - $this->podcast->id + $this->podcast->id, ) ) { return redirect() @@ -193,7 +199,7 @@ class Contributor extends BaseController lang('Contributor.messages.removeContributorSuccess', [ 'username' => $this->user->username, 'podcastTitle' => $this->podcast->title, - ]) + ]), ); } } diff --git a/app/Controllers/Admin/Episode.php b/app/Controllers/Admin/Episode.php index 619f420e..bbd3318d 100644 --- a/app/Controllers/Admin/Episode.php +++ b/app/Controllers/Admin/Episode.php @@ -8,6 +8,7 @@ namespace App\Controllers\Admin; +use App\Entities\Episode as EpisodeEntity; use App\Entities\Note; use App\Models\EpisodeModel; use App\Models\NoteModel; @@ -18,17 +19,17 @@ use CodeIgniter\I18n\Time; class Episode extends BaseController { /** - * @var \App\Entities\Podcast + * @var Podcast */ protected $podcast; /** - * @var \App\Entities\Episode|null + * @var Episode|null */ protected $episode; /** - * @var \App\Entities\Soundbite|null + * @var Soundbite|null */ protected $soundbites; @@ -123,7 +124,7 @@ class Episode extends BaseController ->with('errors', $this->validator->getErrors()); } - $newEpisode = new \App\Entities\Episode([ + $newEpisode = new EpisodeEntity([ 'podcast_id' => $this->podcast->id, 'title' => $this->request->getPost('title'), 'slug' => $this->request->getPost('slug'), diff --git a/app/Controllers/Admin/EpisodePerson.php b/app/Controllers/Admin/EpisodePerson.php index e6ccca64..420efb8f 100644 --- a/app/Controllers/Admin/EpisodePerson.php +++ b/app/Controllers/Admin/EpisodePerson.php @@ -8,6 +8,9 @@ namespace App\Controllers\Admin; +use App\Entities\Podcast; +use App\Entities\Episode; +use CodeIgniter\Exceptions\PageNotFoundException; use App\Models\EpisodePersonModel; use App\Models\PodcastModel; use App\Models\EpisodeModel; @@ -16,42 +19,39 @@ use App\Models\PersonModel; class EpisodePerson extends BaseController { /** - * @var \App\Entities\Podcast + * @var Podcast */ protected $podcast; /** - * @var \App\Entities\Episode + * @var Episode */ protected $episode; public function _remap($method, ...$params) { - if (count($params) > 1) { - if ( - !($this->podcast = (new PodcastModel())->getPodcastById( - $params[0], - )) - ) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); - } - if ( - !($this->episode = (new EpisodeModel()) - ->where([ - 'id' => $params[1], - 'podcast_id' => $params[0], - ]) - ->first()) - ) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); - } - } else { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); + if (count($params) <= 2) { + throw PageNotFoundException::forPageNotFound(); } - unset($params[1]); - unset($params[0]); - return $this->$method(...$params); + if ( + ($this->podcast = (new PodcastModel())->getPodcastById( + $params[0], + )) && + ($this->episode = (new EpisodeModel()) + ->where([ + 'id' => $params[1], + 'podcast_id' => $params[0], + ]) + ->first()) + ) { + unset($params[1]); + unset($params[0]); + + return $this->$method(...$params); + } + + throw PageNotFoundException::forPageNotFound(); } public function index() diff --git a/app/Controllers/Admin/Fediverse.php b/app/Controllers/Admin/Fediverse.php index 184a52ff..75084575 100644 --- a/app/Controllers/Admin/Fediverse.php +++ b/app/Controllers/Admin/Fediverse.php @@ -8,8 +8,6 @@ namespace App\Controllers\Admin; -use ActivityPub\Models\BlockedDomainModel; - class Fediverse extends BaseController { public function dashboard() diff --git a/app/Controllers/Admin/Page.php b/app/Controllers/Admin/Page.php index f2ce56db..d62855d5 100644 --- a/app/Controllers/Admin/Page.php +++ b/app/Controllers/Admin/Page.php @@ -8,24 +8,28 @@ namespace App\Controllers\Admin; +use App\Entities\Page as EntitiesPage; +use CodeIgniter\Exceptions\PageNotFoundException; use App\Models\PageModel; class Page extends BaseController { /** - * @var \App\Entities\Page|null + * @var Page|null */ protected $page; public function _remap($method, ...$params) { - if (count($params) > 0) { - if (!($this->page = (new PageModel())->find($params[0]))) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); - } + if (count($params) === 0) { + return $this->$method(); } - return $this->$method(); + if ($this->page = (new PageModel())->find($params[0])) { + return $this->$method(); + } + + throw PageNotFoundException::forPageNotFound(); } function list() @@ -51,7 +55,7 @@ class Page extends BaseController function attemptCreate() { - $page = new \App\Entities\Page([ + $page = new EntitiesPage([ 'title' => $this->request->getPost('title'), 'slug' => $this->request->getPost('slug'), 'content' => $this->request->getPost('content'), @@ -72,7 +76,7 @@ class Page extends BaseController 'message', lang('Page.messages.createSuccess', [ 'pageTitle' => $page->title, - ]) + ]), ); } diff --git a/app/Controllers/Admin/Person.php b/app/Controllers/Admin/Person.php index f3dea00e..8a2f33b5 100644 --- a/app/Controllers/Admin/Person.php +++ b/app/Controllers/Admin/Person.php @@ -8,28 +8,28 @@ namespace App\Controllers\Admin; +use App\Entities\Person as EntitiesPerson; +use CodeIgniter\Exceptions\PageNotFoundException; use App\Models\PersonModel; class Person extends BaseController { /** - * @var \App\Entities\Person|null + * @var Person|null */ protected $person; public function _remap($method, ...$params) { - if (count($params) > 0) { - if ( - !($this->person = (new PersonModel())->getPersonById( - $params[0] - )) - ) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); - } + if (count($params) === 0) { + return $this->$method(); } - return $this->$method(); + if ($this->person = (new PersonModel())->getPersonById($params[0])) { + return $this->$method(); + } + + throw PageNotFoundException::forPageNotFound(); } public function index() @@ -68,7 +68,7 @@ class Person extends BaseController ->with('errors', $this->validator->getErrors()); } - $person = new \App\Entities\Person([ + $person = new EntitiesPerson([ 'full_name' => $this->request->getPost('full_name'), 'unique_name' => $this->request->getPost('unique_name'), 'information_url' => $this->request->getPost('information_url'), @@ -118,14 +118,14 @@ class Person extends BaseController $this->person->full_name = $this->request->getPost('full_name'); $this->person->unique_name = $this->request->getPost('unique_name'); $this->person->information_url = $this->request->getPost( - 'information_url' + 'information_url', ); $image = $this->request->getFile('image'); if ($image->isValid()) { $this->person->image = $image; } - $this->updated_by = user()->id; + $this->person->updated_by = user()->id; $personModel = new PersonModel(); if (!$personModel->update($this->person->id, $this->person)) { diff --git a/app/Controllers/Admin/Podcast.php b/app/Controllers/Admin/Podcast.php index fcd75500..7919ae4c 100644 --- a/app/Controllers/Admin/Podcast.php +++ b/app/Controllers/Admin/Podcast.php @@ -8,6 +8,9 @@ namespace App\Controllers\Admin; +use App\Entities\Podcast as EntitiesPodcast; +use CodeIgniter\Exceptions\PageNotFoundException; +use Config\Database; use App\Models\CategoryModel; use App\Models\LanguageModel; use App\Models\PodcastModel; @@ -17,23 +20,21 @@ use Config\Services; class Podcast extends BaseController { /** - * @var \App\Entities\Podcast|null + * @var Podcast|null */ protected $podcast; public function _remap($method, ...$params) { - if (count($params) > 0) { - if ( - !($this->podcast = (new PodcastModel())->getPodcastById( - $params[0], - )) - ) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); - } + if (count($params) === 0) { + return $this->$method(); } - return $this->$method(); + if ($this->podcast = (new PodcastModel())->getPodcastById($params[0])) { + return $this->$method(); + } + + throw PageNotFoundException::forPageNotFound(); } public function list() @@ -145,7 +146,7 @@ class Podcast extends BaseController ->with('errors', $this->validator->getErrors()); } - $podcast = new \App\Entities\Podcast([ + $podcast = new EntitiesPodcast([ 'title' => $this->request->getPost('title'), 'name' => $this->request->getPost('name'), 'description_markdown' => $this->request->getPost('description'), @@ -175,7 +176,7 @@ class Podcast extends BaseController ]); $podcastModel = new PodcastModel(); - $db = \Config\Database::connect(); + $db = Database::connect(); $db->transStart(); @@ -271,18 +272,18 @@ class Podcast extends BaseController ); $this->podcast->partner_id = $this->request->getPost('partner_id'); $this->podcast->partner_link_url = $this->request->getPost( - 'partner_link_url' + 'partner_link_url', ); $this->podcast->partner_image_url = $this->request->getPost( - 'partner_image_url' + 'partner_image_url', ); $this->podcast->is_blocked = $this->request->getPost('block') === 'yes'; $this->podcast->is_completed = $this->request->getPost('complete') === 'yes'; $this->podcast->is_locked = $this->request->getPost('lock') === 'yes'; - $this->updated_by = user()->id; + $this->podcast->updated_by = user()->id; - $db = \Config\Database::connect(); + $db = Database::connect(); $db->transStart(); $podcastModel = new PodcastModel(); diff --git a/app/Controllers/Admin/PodcastImport.php b/app/Controllers/Admin/PodcastImport.php index 6a293261..230e6db5 100644 --- a/app/Controllers/Admin/PodcastImport.php +++ b/app/Controllers/Admin/PodcastImport.php @@ -8,6 +8,13 @@ namespace App\Controllers\Admin; +use App\Entities\Podcast; +use CodeIgniter\Exceptions\PageNotFoundException; +use ErrorException; +use Config\Database; +use Podlibre\PodcastNamespace\ReversedTaxonomy; +use App\Entities\PodcastPerson; +use App\Entities\Episode; use App\Models\CategoryModel; use App\Models\LanguageModel; use App\Models\PodcastModel; @@ -22,23 +29,21 @@ use League\HTMLToMarkdown\HtmlConverter; class PodcastImport extends BaseController { /** - * @var \App\Entities\Podcast|null + * @var Podcast|null */ protected $podcast; public function _remap($method, ...$params) { - if (count($params) > 0) { - if ( - !($this->podcast = (new PodcastModel())->getPodcastById( - $params[0], - )) - ) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); - } + if (count($params) === 0) { + return $this->$method(); } - return $this->$method(); + if ($this->podcast = (new PodcastModel())->getPodcastById($params[0])) { + return $this->$method(); + } + + throw PageNotFoundException::forPageNotFound(); } public function index() @@ -80,12 +85,12 @@ class PodcastImport extends BaseController $feed = simplexml_load_file( $this->request->getPost('imported_feed_url'), ); - } catch (\ErrorException $ex) { + } catch (ErrorException $errorException) { return redirect() ->back() ->withInput() ->with('errors', [ - $ex->getMessage() . + $errorException->getMessage() . ': ' . @@ -115,7 +120,7 @@ class PodcastImport extends BaseController $channelDescriptionHtml = (string) $feed->channel[0]->description; try { - $podcast = new \App\Entities\Podcast([ + $podcast = new Podcast([ 'name' => $this->request->getPost('name'), 'imported_feed_url' => $this->request->getPost( 'imported_feed_url', @@ -157,9 +162,9 @@ class PodcastImport extends BaseController 'is_completed' => empty($nsItunes->complete) ? false : $nsItunes->complete === 'yes', - 'location_name' => !$nsPodcast->location - ? null - : (string) $nsPodcast->location, + 'location_name' => $nsPodcast->location + ? (string) $nsPodcast->location + : null, 'location_geo' => !$nsPodcast->location || empty($nsPodcast->location->attributes()['geo']) @@ -173,7 +178,7 @@ class PodcastImport extends BaseController 'created_by' => user()->id, 'updated_by' => user()->id, ]); - } catch (\ErrorException $ex) { + } catch (ErrorException $ex) { return redirect() ->back() ->withInput() @@ -188,7 +193,7 @@ class PodcastImport extends BaseController } $podcastModel = new PodcastModel(); - $db = \Config\Database::connect(); + $db = Database::connect(); $db->transStart(); @@ -221,13 +226,13 @@ class PodcastImport extends BaseController $platformLabel = $platform->attributes()['platform']; $platformSlug = slugify($platformLabel); if ($platformModel->getPlatform($platformSlug)) { - array_push($podcastsPlatformsData, [ + $podcastsPlatformsData[] = [ 'platform_slug' => $platformSlug, 'podcast_id' => $newPodcastId, 'link_url' => $platform->attributes()['url'], 'link_content' => $platform->attributes()['id'], 'is_visible' => false, - ]); + ]; } } } @@ -243,24 +248,22 @@ class PodcastImport extends BaseController $newPersonId = null; if ($newPerson = $personModel->getPerson($podcastPerson)) { $newPersonId = $newPerson->id; - } else { - if ( - !($newPersonId = $personModel->createPerson( - $podcastPerson, - $podcastPerson->attributes()['href'], - $podcastPerson->attributes()['img'], - )) - ) { - return redirect() - ->back() - ->withInput() - ->with('errors', $personModel->errors()); - } + } elseif ( + !($newPersonId = $personModel->createPerson( + $podcastPerson, + $podcastPerson->attributes()['href'], + $podcastPerson->attributes()['img'], + )) + ) { + return redirect() + ->back() + ->withInput() + ->with('errors', $personModel->errors()); } $personGroup = empty($podcastPerson->attributes()['group']) ? ['slug' => ''] - : \Podlibre\PodcastNamespace\ReversedTaxonomy::$taxonomy[ + : ReversedTaxonomy::$taxonomy[ (string) $podcastPerson->attributes()['group'] ]; $personRole = @@ -270,7 +273,7 @@ class PodcastImport extends BaseController : $personGroup['roles'][ strval($podcastPerson->attributes()['role']) ]; - $newPodcastPerson = new \App\Entities\PodcastPerson([ + $newPodcastPerson = new PodcastPerson([ 'podcast_id' => $newPodcastId, 'person_id' => $newPersonId, 'person_group' => $personGroup['slug'], @@ -297,7 +300,7 @@ class PodcastImport extends BaseController ////////////////////////////////////////////////////////////////// // For each Episode: - for ($itemNumber = 1; $itemNumber <= $lastItem; $itemNumber++) { + for ($itemNumber = 1; $itemNumber <= $lastItem; ++$itemNumber) { $item = $feed->channel[0]->item[$numberItems - $itemNumber]; $nsItunes = $item->children( @@ -318,7 +321,7 @@ class PodcastImport extends BaseController if (in_array($slug, $slugs)) { $slugNumber = 2; while (in_array($slug . '-' . $slugNumber, $slugs)) { - $slugNumber++; + ++$slugNumber; } $slug = $slug . '-' . $slugNumber; } @@ -340,7 +343,7 @@ class PodcastImport extends BaseController $itemDescriptionHtml = $item->description; } - $newEpisode = new \App\Entities\Episode([ + $newEpisode = new Episode([ 'podcast_id' => $newPodcastId, 'guid' => empty($item->guid) ? null : $item->guid, 'title' => $item->title, @@ -366,15 +369,15 @@ class PodcastImport extends BaseController 'number' => $this->request->getPost('force_renumber') === 'yes' ? $itemNumber - : (!empty($nsItunes->episode) - ? $nsItunes->episode - : null), + : (empty($nsItunes->episode) + ? null + : $nsItunes->episode), 'season_number' => empty( $this->request->getPost('season_number') ) - ? (!empty($nsItunes->season) - ? $nsItunes->season - : null) + ? (empty($nsItunes->season) + ? null + : $nsItunes->season) : $this->request->getPost('season_number'), 'type' => empty($nsItunes->episodeType) ? 'full' @@ -382,9 +385,9 @@ class PodcastImport extends BaseController 'is_blocked' => empty($nsItunes->block) ? false : $nsItunes->block === 'yes', - 'location_name' => !$nsPodcast->location - ? null - : $nsPodcast->location, + 'location_name' => $nsPodcast->location + ? $nsPodcast->location + : null, 'location_geo' => !$nsPodcast->location || empty($nsPodcast->location->attributes()['geo']) @@ -415,24 +418,22 @@ class PodcastImport extends BaseController $newPersonId = null; if ($newPerson = $personModel->getPerson($episodePerson)) { $newPersonId = $newPerson->id; - } else { - if ( - !($newPersonId = $personModel->createPerson( - $episodePerson, - $episodePerson->attributes()['href'], - $episodePerson->attributes()['img'], - )) - ) { - return redirect() - ->back() - ->withInput() - ->with('errors', $personModel->errors()); - } + } elseif ( + !($newPersonId = $personModel->createPerson( + $episodePerson, + $episodePerson->attributes()['href'], + $episodePerson->attributes()['img'], + )) + ) { + return redirect() + ->back() + ->withInput() + ->with('errors', $personModel->errors()); } $personGroup = empty($episodePerson->attributes()['group']) ? ['slug' => ''] - : \Podlibre\PodcastNamespace\ReversedTaxonomy::$taxonomy[ + : ReversedTaxonomy::$taxonomy[ strval($episodePerson->attributes()['group']) ]; $personRole = @@ -442,7 +443,7 @@ class PodcastImport extends BaseController : $personGroup['roles'][ strval($episodePerson->attributes()['role']) ]; - $newEpisodePerson = new \App\Entities\PodcastPerson([ + $newEpisodePerson = new PodcastPerson([ 'podcast_id' => $newPodcastId, 'episode_id' => $newEpisodeId, 'person_id' => $newPersonId, diff --git a/app/Controllers/Admin/PodcastPerson.php b/app/Controllers/Admin/PodcastPerson.php index 4da46dbd..9a9e4474 100644 --- a/app/Controllers/Admin/PodcastPerson.php +++ b/app/Controllers/Admin/PodcastPerson.php @@ -8,6 +8,8 @@ namespace App\Controllers\Admin; +use App\Entities\Podcast; +use CodeIgniter\Exceptions\PageNotFoundException; use App\Models\PodcastPersonModel; use App\Models\PodcastModel; use App\Models\PersonModel; @@ -15,26 +17,22 @@ use App\Models\PersonModel; class PodcastPerson extends BaseController { /** - * @var \App\Entities\Podcast + * @var Podcast */ protected $podcast; public function _remap($method, ...$params) { - if (count($params) > 0) { - if ( - !($this->podcast = (new PodcastModel())->getPodcastById( - $params[0], - )) - ) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); - } - } else { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); + if (count($params) === 0) { + throw PageNotFoundException::forPageNotFound(); } - unset($params[0]); - return $this->$method(...$params); + if ($this->podcast = (new PodcastModel())->getPodcastById($params[0])) { + unset($params[0]); + return $this->$method(...$params); + } + + throw PageNotFoundException::forPageNotFound(); } public function index() diff --git a/app/Controllers/Admin/PodcastPlatform.php b/app/Controllers/Admin/PodcastPlatform.php index 6f5fd99f..55d2ab98 100644 --- a/app/Controllers/Admin/PodcastPlatform.php +++ b/app/Controllers/Admin/PodcastPlatform.php @@ -8,6 +8,8 @@ namespace App\Controllers\Admin; +use App\Entities\Podcast; +use CodeIgniter\Exceptions\PageNotFoundException; use App\Models\PlatformModel; use App\Models\PodcastModel; use Config\Services; @@ -15,20 +17,22 @@ use Config\Services; class PodcastPlatform extends BaseController { /** - * @var \App\Entities\Podcast|null + * @var Podcast|null */ protected $podcast; public function _remap($method, ...$params) { - if ( - !($this->podcast = (new PodcastModel())->getPodcastById($params[0])) - ) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); + if (count($params) === 0) { + return $this->$method(); } - unset($params[0]); - return $this->$method(...$params); + if ($this->podcast = (new PodcastModel())->getPodcastById($params[0])) { + unset($params[0]); + return $this->$method(...$params); + } + + throw PageNotFoundException::forPageNotFound(); } public function index() @@ -45,7 +49,7 @@ class PodcastPlatform extends BaseController 'platformType' => $platformType, 'platforms' => (new PlatformModel())->getPlatformsWithLinks( $this->podcast->id, - $platformType + $platformType, ), ]; @@ -65,36 +69,35 @@ class PodcastPlatform extends BaseController as $platformSlug => $podcastPlatform ) { $podcastPlatformUrl = $podcastPlatform['url']; - - if ( - !empty($podcastPlatformUrl) && - $validation->check($podcastPlatformUrl, 'validate_url') - ) { - array_push($podcastsPlatformsData, [ - 'platform_slug' => $platformSlug, - 'podcast_id' => $this->podcast->id, - 'link_url' => $podcastPlatformUrl, - 'link_content' => $podcastPlatform['content'], - 'is_visible' => array_key_exists( - 'visible', - $podcastPlatform - ) - ? $podcastPlatform['visible'] == 'yes' - : false, - 'is_on_embeddable_player' => array_key_exists( - 'on_embeddable_player', - $podcastPlatform - ) - ? $podcastPlatform['on_embeddable_player'] == 'yes' - : false, - ]); + if (empty($podcastPlatformUrl)) { + continue; } + if (!$validation->check($podcastPlatformUrl, 'validate_url')) { + continue; + } + $podcastsPlatformsData[] = [ + 'platform_slug' => $platformSlug, + 'podcast_id' => $this->podcast->id, + 'link_url' => $podcastPlatformUrl, + 'link_content' => $podcastPlatform['content'], + 'is_visible' => + array_key_exists('visible', $podcastPlatform) && + $podcastPlatform['visible'] == 'yes', + 'is_on_embeddable_player' => + array_key_exists( + 'on_embeddable_player', + $podcastPlatform, + ) && $podcastPlatform['on_embeddable_player'] == 'yes', + ]; + return redirect() + ->back() + ->with('message', lang('Platforms.messages.updateSuccess')); } $platformModel->savePodcastPlatforms( $this->podcast->id, $platformType, - $podcastsPlatformsData + $podcastsPlatformsData, ); return redirect() @@ -106,7 +109,7 @@ class PodcastPlatform extends BaseController { (new PlatformModel())->removePodcastPlatform( $this->podcast->id, - $platformSlug + $platformSlug, ); return redirect() diff --git a/app/Controllers/Admin/User.php b/app/Controllers/Admin/User.php index be174529..1b456eac 100644 --- a/app/Controllers/Admin/User.php +++ b/app/Controllers/Admin/User.php @@ -8,26 +8,30 @@ namespace App\Controllers\Admin; +use CodeIgniter\Exceptions\PageNotFoundException; use App\Authorization\GroupModel; +use App\Entities\User as EntitiesUser; use App\Models\UserModel; use Config\Services; class User extends BaseController { /** - * @var \App\Entities\User|null + * @var User|null */ protected $user; public function _remap($method, ...$params) { - if (count($params) > 0) { - if (!($this->user = (new UserModel())->find($params[0]))) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); - } + if (count($params) === 0) { + return $this->$method(); } - return $this->$method(); + if ($this->user = (new UserModel())->find($params[0])) { + return $this->$method(); + } + + throw PageNotFoundException::forPageNotFound(); } public function list() @@ -67,7 +71,7 @@ class User extends BaseController [ 'email' => 'required|valid_email|is_unique[users.email]', 'password' => 'required|strong_password', - ] + ], ); if (!$this->validate($rules)) { @@ -78,7 +82,7 @@ class User extends BaseController } // Save the user - $user = new \App\Entities\User($this->request->getPost()); + $user = new EntitiesUser($this->request->getPost()); // Activate user $user->activate(); @@ -100,7 +104,7 @@ class User extends BaseController 'message', lang('User.messages.createSuccess', [ 'username' => $user->username, - ]) + ]), ); } @@ -115,7 +119,7 @@ class User extends BaseController $result[$role->name] = lang('User.roles.' . $role->name); return $result; }, - [] + [], ); $data = [ @@ -141,7 +145,7 @@ class User extends BaseController 'message', lang('User.messages.rolesEditSuccess', [ 'username' => $this->user->username, - ]) + ]), ); } @@ -163,7 +167,7 @@ class User extends BaseController 'message', lang('User.messages.forcePassResetSuccess', [ 'username' => $this->user->username, - ]) + ]), ); } @@ -196,7 +200,7 @@ class User extends BaseController 'message', lang('User.messages.banSuccess', [ 'username' => $this->user->username, - ]) + ]), ); } @@ -217,7 +221,7 @@ class User extends BaseController 'message', lang('User.messages.unbanSuccess', [ 'username' => $this->user->username, - ]) + ]), ); } @@ -242,7 +246,7 @@ class User extends BaseController 'message', lang('User.messages.deleteSuccess', [ 'username' => $this->user->username, - ]) + ]), ); } } diff --git a/app/Controllers/Auth.php b/app/Controllers/Auth.php index 6dc47754..0ac069f1 100644 --- a/app/Controllers/Auth.php +++ b/app/Controllers/Auth.php @@ -8,9 +8,11 @@ namespace App\Controllers; +use Myth\Auth\Controllers\AuthController; use App\Entities\User; +use CodeIgniter\HTTP\RedirectResponse; -class Auth extends \Myth\Auth\Controllers\AuthController +class Auth extends AuthController { /** * An array of helpers to be automatically loaded @@ -104,10 +106,8 @@ class Auth extends \Myth\Auth\Controllers\AuthController /** * Verifies the code with the email and saves the new password, * if they all pass validation. - * - * @return mixed */ - public function attemptReset() + public function attemptReset(): RedirectResponse { if ($this->config->activeResetter === false) { return redirect() diff --git a/app/Controllers/BaseController.php b/app/Controllers/BaseController.php index df816124..7bfe25bc 100644 --- a/app/Controllers/BaseController.php +++ b/app/Controllers/BaseController.php @@ -30,16 +30,12 @@ class BaseController extends Controller /** * Constructor. - * - * @param RequestInterface $request - * @param ResponseInterface $response - * @param LoggerInterface $logger */ public function initController( RequestInterface $request, ResponseInterface $response, LoggerInterface $logger - ) { + ): void { // Do Not Edit This Line parent::initController($request, $response, $logger); diff --git a/app/Controllers/Episode.php b/app/Controllers/Episode.php index f0533e76..98050652 100644 --- a/app/Controllers/Episode.php +++ b/app/Controllers/Episode.php @@ -11,6 +11,7 @@ namespace App\Controllers; use Analytics\AnalyticsTrait; use App\Models\EpisodeModel; use App\Models\PodcastModel; +use CodeIgniter\Exceptions\PageNotFoundException; use SimpleXMLElement; class Episode extends BaseController @@ -18,31 +19,41 @@ class Episode extends BaseController use AnalyticsTrait; /** - * @var \App\Entities\Podcast + * @var Podcast */ protected $podcast; /** - * @var \App\Entities\Episode|null + * @var Episode */ protected $episode; public function _remap($method, ...$params) { - $this->podcast = (new PodcastModel())->getPodcastByName($params[0]); + if (count($params) < 2) { + throw PageNotFoundException::forPageNotFound(); + } if ( - count($params) > 1 && - !($this->episode = (new EpisodeModel())->getEpisodeBySlug( - $this->podcast->id, - $params[1], + !($this->podcast = (new PodcastModel())->getPodcastByName( + $params[0], )) ) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); + throw PageNotFoundException::forPageNotFound(); } - unset($params[1]); - unset($params[0]); - return $this->$method(...$params); + + if ( + $this->episode = (new EpisodeModel())->getEpisodeBySlug( + $this->podcast->id, + $params[1], + ) + ) { + unset($params[1]); + unset($params[0]); + return $this->$method(...$params); + } + + throw PageNotFoundException::forPageNotFound(); } public function index() diff --git a/app/Controllers/Feed.php b/app/Controllers/Feed.php index 3225e5f2..1844f205 100644 --- a/app/Controllers/Feed.php +++ b/app/Controllers/Feed.php @@ -8,32 +8,34 @@ namespace App\Controllers; +use CodeIgniter\HTTP\ResponseInterface; +use CodeIgniter\Exceptions\PageNotFoundException; +use Opawg\UserAgentsPhp\UserAgentsRSS; +use Exception; use App\Models\EpisodeModel; use App\Models\PodcastModel; use CodeIgniter\Controller; class Feed extends Controller { - public function index($podcastName) + public function index($podcastName): ResponseInterface { helper('rss'); $podcast = (new PodcastModel())->where('name', $podcastName)->first(); if (!$podcast) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); + throw PageNotFoundException::forPageNotFound(); } $serviceSlug = ''; try { - $service = \Opawg\UserAgentsPhp\UserAgentsRSS::find( - $_SERVER['HTTP_USER_AGENT'], - ); + $service = UserAgentsRSS::find($_SERVER['HTTP_USER_AGENT']); if ($service) { $serviceSlug = $service['slug']; } - } catch (\Exception $e) { + } catch (Exception $exception) { // If things go wrong the show must go on and the user must be able to download the file - log_message('critical', $e); + log_message('critical', $exception); } $cacheName = diff --git a/app/Controllers/Home.php b/app/Controllers/Home.php index ef8b0bb0..3e107c79 100644 --- a/app/Controllers/Home.php +++ b/app/Controllers/Home.php @@ -12,6 +12,9 @@ use App\Models\PodcastModel; class Home extends BaseController { + /** + * @return mixed + */ public function index() { $model = new PodcastModel(); diff --git a/app/Controllers/Install.php b/app/Controllers/Install.php index c3e60160..3fbb2ba0 100644 --- a/app/Controllers/Install.php +++ b/app/Controllers/Install.php @@ -8,6 +8,15 @@ namespace App\Controllers; +use CodeIgniter\HTTP\RequestInterface; +use CodeIgniter\HTTP\ResponseInterface; +use Psr\Log\LoggerInterface; +use Throwable; +use Dotenv\Exception\ValidationException; +use CodeIgniter\Exceptions\PageNotFoundException; +use CodeIgniter\Database\Exceptions\DatabaseException; +use Config\Database; +use App\Entities\User; use App\Models\UserModel; use CodeIgniter\Controller; use Config\Services; @@ -15,16 +24,19 @@ use Dotenv\Dotenv; class Install extends Controller { + /** + * @var string[] + */ protected $helpers = ['form', 'components', 'svg']; /** * Constructor. */ public function initController( - \CodeIgniter\HTTP\RequestInterface $request, - \CodeIgniter\HTTP\ResponseInterface $response, - \Psr\Log\LoggerInterface $logger - ) { + RequestInterface $request, + ResponseInterface $response, + LoggerInterface $logger + ): void { // Do Not Edit This Line parent::initController($request, $response, $logger); } @@ -36,14 +48,14 @@ class Install extends Controller * If all required actions have already been performed, * the install route will show a 404 page. */ - public function index() + public function index(): string { try { // Check if .env is created and has all required fields $dotenv = Dotenv::createUnsafeImmutable(ROOTPATH); $dotenv->load(); - } catch (\Throwable $e) { + } catch (Throwable $e) { $this->createEnv(); } @@ -55,7 +67,7 @@ class Install extends Controller 'app.adminGateway', 'app.authGateway', ]); - } catch (\Dotenv\Exception\ValidationException $e) { + } catch (ValidationException $e) { // form to input instance configuration return $this->instanceConfig(); } @@ -68,13 +80,13 @@ class Install extends Controller 'database.default.password', 'database.default.DBPrefix', ]); - } catch (\Dotenv\Exception\ValidationException $e) { + } catch (ValidationException $validationException) { return $this->databaseConfig(); } try { $dotenv->required('cache.handler'); - } catch (\Dotenv\Exception\ValidationException $e) { + } catch (ValidationException $validationException) { return $this->cacheConfig(); } } else { @@ -90,7 +102,7 @@ class Install extends Controller 'database.default.DBPrefix', 'cache.handler', ]); - } catch (\Dotenv\Exception\ValidationException $e) { + } catch (ValidationException $e) { return view('install/manual_config'); } } @@ -104,9 +116,9 @@ class Install extends Controller (new UserModel())->countAll() > 0 ) { // if so, show a 404 page - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); + throw PageNotFoundException::forPageNotFound(); } - } catch (\CodeIgniter\Database\Exceptions\DatabaseException $e) { + } catch (DatabaseException $databaseException) { // Could not connect to the database // show database config view to fix value session()->setFlashdata( @@ -128,6 +140,7 @@ class Install extends Controller /** * Returns the form to generate the .env config file for the instance. + * @return mixed|void */ public function createEnv() { @@ -135,7 +148,7 @@ class Install extends Controller try { $envFile = fopen(ROOTPATH . '.env', 'w'); fclose($envFile); - } catch (\Throwable $e) { + } catch (Throwable $throwable) { // Could not create the .env file, redirect to a view with manual instructions on how to add it return view('install/manual_config'); } @@ -252,22 +265,22 @@ class Install extends Controller /** * Runs all database migrations required for instance. */ - public function migrate() + public function migrate(): void { - $migrations = \Config\Services::migrations(); + $migrations = Services::migrations(); - !$migrations->setNamespace('Myth\Auth')->latest(); - !$migrations->setNamespace('ActivityPub')->latest(); - !$migrations->setNamespace('Analytics')->latest(); - !$migrations->setNamespace(APP_NAMESPACE)->latest(); + $migrations->setNamespace('Myth\Auth')->latest(); + $migrations->setNamespace('ActivityPub')->latest(); + $migrations->setNamespace('Analytics')->latest(); + $migrations->setNamespace(APP_NAMESPACE)->latest(); } /** * Runs all database seeds required for instance. */ - public function seed() + public function seed(): void { - $seeder = \Config\Database::seeder(); + $seeder = Database::seeder(); // Seed database $seeder->call('AppSeeder'); @@ -308,12 +321,12 @@ class Install extends Controller } // Save the user - $user = new \App\Entities\User($this->request->getPost()); + $user = new User($this->request->getPost()); // Activate user $user->activate(); - $db = \Config\Database::connect(); + $db = Database::connect(); $db->transStart(); if (!($userId = $userModel->insert($user, true))) { @@ -345,10 +358,8 @@ class Install extends Controller * overwrites any existing key and appends new ones * * @param array $data key/value config pairs - * - * @return void */ - public static function writeEnv($configData) + public static function writeEnv($configData): void { $envData = file(ROOTPATH . '.env'); // reads an array of lines @@ -360,7 +371,7 @@ class Install extends Controller $keyVal, &$replaced ) { - if (strpos($line, $key) === 0) { + if (strpos($line, (string) $key) === 0) { $replaced = true; return $keyVal; } @@ -369,7 +380,7 @@ class Install extends Controller $envData); if (!$replaced) { - array_push($envData, $keyVal); + $envData[] = $keyVal; } } diff --git a/app/Controllers/Note.php b/app/Controllers/Note.php index 509c7874..6c0ff328 100644 --- a/app/Controllers/Note.php +++ b/app/Controllers/Note.php @@ -8,18 +8,22 @@ namespace App\Controllers; +use ActivityPub\Controllers\NoteController; +use ActivityPub\Entities\Note as ActivityPubNote; use Analytics\AnalyticsTrait; +use App\Entities\Note as CastopodNote; use App\Models\EpisodeModel; use App\Models\PodcastModel; +use CodeIgniter\HTTP\RedirectResponse; use CodeIgniter\HTTP\URI; use CodeIgniter\I18n\Time; -class Note extends \ActivityPub\Controllers\NoteController +class Note extends NoteController { use AnalyticsTrait; /** - * @var \App\Entities\Podcast + * @var Podcast */ protected $podcast; @@ -48,7 +52,7 @@ class Note extends \ActivityPub\Controllers\NoteController return $this->$method(...$params); } - public function index() + public function index(): RedirectResponse { // Prevent analytics hit when authenticated if (!can_user_interact()) { @@ -108,7 +112,7 @@ class Note extends \ActivityPub\Controllers\NoteController $message = $this->request->getPost('message'); - $newNote = new \App\Entities\Note([ + $newNote = new CastopodNote([ 'actor_id' => interact_as_actor_id(), 'published_at' => Time::now(), 'created_by' => user_id(), @@ -162,7 +166,7 @@ class Note extends \ActivityPub\Controllers\NoteController ->with('errors', $this->validator->getErrors()); } - $newNote = new \ActivityPub\Entities\Note([ + $newNote = new ActivityPubNote([ 'actor_id' => interact_as_actor_id(), 'in_reply_to_id' => $this->note->id, 'message' => $this->request->getPost('message'), diff --git a/app/Controllers/Page.php b/app/Controllers/Page.php index 73c452df..b547a4ca 100644 --- a/app/Controllers/Page.php +++ b/app/Controllers/Page.php @@ -8,6 +8,8 @@ namespace App\Controllers; +use App\Entities\Page as PageEntity; +use CodeIgniter\Exceptions\PageNotFoundException; use App\Models\PageModel; use App\Models\CreditModel; use App\Models\PodcastModel; @@ -15,28 +17,28 @@ use App\Models\PodcastModel; class Page extends BaseController { /** - * @var \App\Entities\Page|null + * @var Page|null */ protected $page; public function _remap($method, ...$params) { - if (count($params) > 0) { - if ( - !($this->page = (new PageModel()) - ->where('slug', $params[0]) - ->first()) - ) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); - } + if (count($params) === 0) { + return $this->$method(); } - return $this->$method(); + if ( + $this->page = (new PageModel())->where('slug', $params[0])->first() + ) { + return $this->$method(); + } + + throw PageNotFoundException::forPageNotFound(); } public function index() { - $cacheName = "page@{$this->page->slug}"; + $cacheName = "page-{$this->page->slug}"; if (!($found = cache($cacheName))) { $data = [ 'page' => $this->page, @@ -58,7 +60,7 @@ class Page extends BaseController $cacheName = "page_credits_{$locale}"; if (!($found = cache($cacheName))) { - $page = new \App\Entities\Page([ + $page = new PageEntity([ 'title' => lang('Person.credits', [], $locale), 'slug' => 'credits', 'content' => '', @@ -157,10 +159,10 @@ class Page extends BaseController 'role_label' => $credit->role_label, 'is_in' => [ [ - 'link' => $credit->episode + 'link' => $credit->episode_id ? $credit->episode->link : $credit->podcast->link, - 'title' => $credit->episode + 'title' => $credit->episode_id ? (count($allPodcasts) > 1 ? "{$credit->podcast->title} ā–ø " : '') . @@ -179,10 +181,10 @@ class Page extends BaseController $credits[$person_group]['persons'][$person_id]['roles'][ $person_role ]['is_in'][] = [ - 'link' => $credit->episode + 'link' => $credit->episode_id ? $credit->episode->link : $credit->podcast->link, - 'title' => $credit->episode + 'title' => $credit->episode_id ? (count($allPodcasts) > 1 ? "{$credit->podcast->title} ā–ø " : '') . diff --git a/app/Controllers/Platform.php b/app/Controllers/Platform.php index cca77114..b4ba11de 100644 --- a/app/Controllers/Platform.php +++ b/app/Controllers/Platform.php @@ -8,6 +8,8 @@ namespace App\Controllers; +use CodeIgniter\HTTP\ResponseInterface; +use App\Models\PlatformModel; use CodeIgniter\Controller; /* @@ -15,9 +17,9 @@ use CodeIgniter\Controller; */ class Platform extends Controller { - public function index() + public function index(): ResponseInterface { - $model = new \App\Models\PlatformModel(); + $model = new PlatformModel(); return $this->response->setJSON($model->getPlatforms()); } diff --git a/app/Controllers/Podcast.php b/app/Controllers/Podcast.php index c9e73580..0144e144 100644 --- a/app/Controllers/Podcast.php +++ b/app/Controllers/Podcast.php @@ -18,24 +18,24 @@ class Podcast extends BaseController use AnalyticsTrait; /** - * @var \App\Entities\Podcast|null + * @var Podcast */ protected $podcast; public function _remap($method, ...$params) { - if (count($params) > 0) { - if ( - !($this->podcast = (new PodcastModel())->getPodcastByName( - $params[0], - )) - ) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); - } - unset($params[0]); + if (count($params) === 0) { + throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); } - return $this->$method(...$params); + if ( + $this->podcast = (new PodcastModel())->getPodcastByName($params[0]) + ) { + unset($params[0]); + return $this->$method(...$params); + } + + throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); } public function activity() diff --git a/app/Database/Migrations/2020-05-29-152000_add_categories.php b/app/Database/Migrations/2020-05-29-152000_add_categories.php index 900f5b24..7f56e55b 100644 --- a/app/Database/Migrations/2020-05-29-152000_add_categories.php +++ b/app/Database/Migrations/2020-05-29-152000_add_categories.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddCategories extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'id' => [ @@ -45,7 +45,7 @@ class AddCategories extends Migration $this->forge->createTable('categories'); } - public function down() + public function down(): void { $this->forge->dropTable('categories'); } diff --git a/app/Database/Migrations/2020-05-30-101000_add_languages.php b/app/Database/Migrations/2020-05-30-101000_add_languages.php index 65689906..b56f42bf 100644 --- a/app/Database/Migrations/2020-05-30-101000_add_languages.php +++ b/app/Database/Migrations/2020-05-30-101000_add_languages.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddLanguages extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'code' => [ @@ -32,7 +32,7 @@ class AddLanguages extends Migration $this->forge->createTable('languages'); } - public function down() + public function down(): void { $this->forge->dropTable('languages'); } diff --git a/app/Database/Migrations/2020-05-30-101500_add_podcasts.php b/app/Database/Migrations/2020-05-30-101500_add_podcasts.php index 675fa83c..772d5127 100644 --- a/app/Database/Migrations/2020-05-30-101500_add_podcasts.php +++ b/app/Database/Migrations/2020-05-30-101500_add_podcasts.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddPodcasts extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'id' => [ @@ -204,7 +204,7 @@ class AddPodcasts extends Migration $this->forge->createTable('podcasts'); } - public function down() + public function down(): void { $this->forge->dropTable('podcasts'); } diff --git a/app/Database/Migrations/2020-06-05-170000_add_episodes.php b/app/Database/Migrations/2020-06-05-170000_add_episodes.php index cafca237..c8d55e71 100644 --- a/app/Database/Migrations/2020-06-05-170000_add_episodes.php +++ b/app/Database/Migrations/2020-06-05-170000_add_episodes.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddEpisodes extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'id' => [ @@ -197,7 +197,7 @@ class AddEpisodes extends Migration $this->forge->createTable('episodes'); } - public function down() + public function down(): void { $this->forge->dropTable('episodes'); } diff --git a/app/Database/Migrations/2020-06-05-180000_add_soundbites.php b/app/Database/Migrations/2020-06-05-180000_add_soundbites.php index 1f1aec53..a448d630 100644 --- a/app/Database/Migrations/2020-06-05-180000_add_soundbites.php +++ b/app/Database/Migrations/2020-06-05-180000_add_soundbites.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddSoundbites extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'id' => [ @@ -82,7 +82,7 @@ class AddSoundbites extends Migration $this->forge->createTable('soundbites'); } - public function down() + public function down(): void { $this->forge->dropTable('soundbites'); } diff --git a/app/Database/Migrations/2020-06-05-190000_add_platforms.php b/app/Database/Migrations/2020-06-05-190000_add_platforms.php index fb82e824..e11adbbb 100644 --- a/app/Database/Migrations/2020-06-05-190000_add_platforms.php +++ b/app/Database/Migrations/2020-06-05-190000_add_platforms.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddPlatforms extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'slug' => [ @@ -43,13 +43,13 @@ class AddPlatforms extends Migration ]); $this->forge->addField('`created_at` timestamp NOT NULL DEFAULT NOW()'); $this->forge->addField( - '`updated_at` timestamp NOT NULL DEFAULT NOW() ON UPDATE NOW()' + '`updated_at` timestamp NOT NULL DEFAULT NOW() ON UPDATE NOW()', ); $this->forge->addPrimaryKey('slug'); $this->forge->createTable('platforms'); } - public function down() + public function down(): void { $this->forge->dropTable('platforms'); } diff --git a/app/Database/Migrations/2020-07-03-191500_add_podcasts_users.php b/app/Database/Migrations/2020-07-03-191500_add_podcasts_users.php index 37bb9d56..90d2ab1a 100644 --- a/app/Database/Migrations/2020-07-03-191500_add_podcasts_users.php +++ b/app/Database/Migrations/2020-07-03-191500_add_podcasts_users.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddPodcastsUsers extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'podcast_id' => [ @@ -50,7 +50,7 @@ class AddPodcastsUsers extends Migration $this->forge->createTable('podcasts_users'); } - public function down() + public function down(): void { $this->forge->dropTable('podcasts_users'); } diff --git a/app/Database/Migrations/2020-08-17-150000_add_pages.php b/app/Database/Migrations/2020-08-17-150000_add_pages.php index cd271ab5..fda65e2a 100644 --- a/app/Database/Migrations/2020-08-17-150000_add_pages.php +++ b/app/Database/Migrations/2020-08-17-150000_add_pages.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddPages extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'id' => [ @@ -50,7 +50,7 @@ class AddPages extends Migration $this->forge->createTable('pages'); } - public function down() + public function down(): void { $this->forge->dropTable('pages'); } diff --git a/app/Database/Migrations/2020-09-29-150000_add_podcasts_categories.php b/app/Database/Migrations/2020-09-29-150000_add_podcasts_categories.php index 17c60b71..4d8b73ee 100644 --- a/app/Database/Migrations/2020-09-29-150000_add_podcasts_categories.php +++ b/app/Database/Migrations/2020-09-29-150000_add_podcasts_categories.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddPodcastsCategories extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'podcast_id' => [ @@ -45,7 +45,7 @@ class AddPodcastsCategories extends Migration $this->forge->createTable('podcasts_categories'); } - public function down() + public function down(): void { $this->forge->dropTable('podcasts_categories'); } diff --git a/app/Database/Migrations/2020-12-25-120000_add_persons.php b/app/Database/Migrations/2020-12-25-120000_add_persons.php index 84b34ccf..9a79f711 100644 --- a/app/Database/Migrations/2020-12-25-120000_add_persons.php +++ b/app/Database/Migrations/2020-12-25-120000_add_persons.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddPersons extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'id' => [ @@ -73,7 +73,7 @@ class AddPersons extends Migration $this->forge->createTable('persons'); } - public function down() + public function down(): void { $this->forge->dropTable('persons'); } diff --git a/app/Database/Migrations/2020-12-25-130000_add_podcasts_persons.php b/app/Database/Migrations/2020-12-25-130000_add_podcasts_persons.php index af6c7ee5..5a714efa 100644 --- a/app/Database/Migrations/2020-12-25-130000_add_podcasts_persons.php +++ b/app/Database/Migrations/2020-12-25-130000_add_podcasts_persons.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddPodcastsPersons extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'id' => [ @@ -64,7 +64,7 @@ class AddPodcastsPersons extends Migration $this->forge->createTable('podcasts_persons'); } - public function down() + public function down(): void { $this->forge->dropTable('podcasts_persons'); } diff --git a/app/Database/Migrations/2020-12-25-140000_add_episodes_persons.php b/app/Database/Migrations/2020-12-25-140000_add_episodes_persons.php index 7cc30914..512756b5 100644 --- a/app/Database/Migrations/2020-12-25-140000_add_episodes_persons.php +++ b/app/Database/Migrations/2020-12-25-140000_add_episodes_persons.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddEpisodesPersons extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'id' => [ @@ -76,7 +76,7 @@ class AddEpisodesPersons extends Migration $this->forge->createTable('episodes_persons'); } - public function down() + public function down(): void { $this->forge->dropTable('episodes_persons'); } diff --git a/app/Database/Migrations/2020-12-25-150000_add_credit_view.php b/app/Database/Migrations/2020-12-25-150000_add_credit_view.php index 68dfd05f..8ed0f753 100644 --- a/app/Database/Migrations/2020-12-25-150000_add_credit_view.php +++ b/app/Database/Migrations/2020-12-25-150000_add_credit_view.php @@ -14,30 +14,34 @@ use CodeIgniter\Database\Migration; class AddCreditView extends Migration { - public function up() + public function up(): void { // Creates View for credit UNION query $viewName = $this->db->prefixTable('credits'); - $personTable = $this->db->prefixTable('persons'); - $podcastPersonTable = $this->db->prefixTable('podcasts_persons'); - $episodePersonTable = $this->db->prefixTable('episodes_persons'); + $personsTable = $this->db->prefixTable('persons'); + $podcastPersonsTable = $this->db->prefixTable('podcasts_persons'); + $episodePersonsTable = $this->db->prefixTable('episodes_persons'); + $episodesTable = $this->db->prefixTable('episodes'); $createQuery = <<db->query($createQuery); } - public function down() + public function down(): void { $viewName = $this->db->prefixTable('credits'); - $this->db->query("DROP VIEW IF EXISTS `$viewName`"); + $this->db->query("DROP VIEW IF EXISTS `{$viewName}`"); } } diff --git a/app/Database/Migrations/2021-02-23-100000_add_episode_id_to_notes.php b/app/Database/Migrations/2021-02-23-100000_add_episode_id_to_notes.php index 8278f5a9..e6821fbe 100644 --- a/app/Database/Migrations/2021-02-23-100000_add_episode_id_to_notes.php +++ b/app/Database/Migrations/2021-02-23-100000_add_episode_id_to_notes.php @@ -15,19 +15,19 @@ use CodeIgniter\Database\Migration; class AddEpisodeIdToNotes extends Migration { - public function up() + public function up(): void { $prefix = $this->db->getPrefix(); $createQuery = <<db->query($createQuery); } - public function down() + public function down(): void { $this->forge->dropForeignKey( 'activitypub_notes', diff --git a/app/Database/Migrations/2021-03-09-113000_add_created_by_to_notes.php b/app/Database/Migrations/2021-03-09-113000_add_created_by_to_notes.php index 67512542..4c5ffec5 100644 --- a/app/Database/Migrations/2021-03-09-113000_add_created_by_to_notes.php +++ b/app/Database/Migrations/2021-03-09-113000_add_created_by_to_notes.php @@ -15,19 +15,19 @@ use CodeIgniter\Database\Migration; class AddCreatedByToNotes extends Migration { - public function up() + public function up(): void { $prefix = $this->db->getPrefix(); $createQuery = <<db->query($createQuery); } - public function down() + public function down(): void { $this->forge->dropForeignKey( 'activitypub_notes', diff --git a/app/Database/Seeds/AppSeeder.php b/app/Database/Seeds/AppSeeder.php index 6dac6af4..01262d50 100644 --- a/app/Database/Seeds/AppSeeder.php +++ b/app/Database/Seeds/AppSeeder.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Seeder; class AppSeeder extends Seeder { - public function run() + public function run(): void { $this->call('AuthSeeder'); $this->call('CategorySeeder'); diff --git a/app/Database/Seeds/AuthSeeder.php b/app/Database/Seeds/AuthSeeder.php index 455040f1..0a0bd086 100644 --- a/app/Database/Seeds/AuthSeeder.php +++ b/app/Database/Seeds/AuthSeeder.php @@ -15,6 +15,9 @@ use CodeIgniter\Database\Seeder; class AuthSeeder extends Seeder { + /** + * @var array[] + */ protected $groups = [ [ 'name' => 'superadmin', @@ -37,6 +40,8 @@ class AuthSeeder extends Seeder * ... * ] * ``` + * + * @var array[]> */ protected $permissions = [ 'users' => [ @@ -249,26 +254,16 @@ class AuthSeeder extends Seeder ], ]; - static function getGroupIdByName($name, $dataGroups) - { - foreach ($dataGroups as $group) { - if ($group['name'] === $name) { - return $group['id']; - } - } - return null; - } - - public function run() + public function run(): void { $groupId = 0; $dataGroups = []; foreach ($this->groups as $group) { - array_push($dataGroups, [ + $dataGroups[] = [ 'id' => ++$groupId, 'name' => $group['name'], 'description' => $group['description'], - ]); + ]; } // Map permissions to a format the `auth_permissions` table expects @@ -277,21 +272,21 @@ class AuthSeeder extends Seeder $permissionId = 0; foreach ($this->permissions as $context => $actions) { foreach ($actions as $action) { - array_push($dataPermissions, [ + $dataPermissions[] = [ 'id' => ++$permissionId, 'name' => $context . '-' . $action['name'], 'description' => $action['description'], - ]); + ]; foreach ($action['has_permission'] as $role) { // link permission to specified groups - array_push($dataGroupsPermissions, [ + $dataGroupsPermissions[] = [ 'group_id' => $this->getGroupIdByName( $role, $dataGroups, ), 'permission_id' => $permissionId, - ]); + ]; } } } @@ -309,4 +304,16 @@ class AuthSeeder extends Seeder ->ignore(true) ->insertBatch($dataGroupsPermissions); } + /** + * @param array[] $dataGroups + */ + static function getGroupIdByName(string $name, array $dataGroups): int + { + foreach ($dataGroups as $group) { + if ($group['name'] === $name) { + return $group['id']; + } + } + return null; + } } diff --git a/app/Database/Seeds/CategorySeeder.php b/app/Database/Seeds/CategorySeeder.php index 0fea4e51..483b8fe6 100644 --- a/app/Database/Seeds/CategorySeeder.php +++ b/app/Database/Seeds/CategorySeeder.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Seeder; class CategorySeeder extends Seeder { - public function run() + public function run(): void { $data = [ [ diff --git a/app/Database/Seeds/FakePodcastsAnalyticsSeeder.php b/app/Database/Seeds/FakePodcastsAnalyticsSeeder.php index 7adb67fa..140b61ff 100644 --- a/app/Database/Seeds/FakePodcastsAnalyticsSeeder.php +++ b/app/Database/Seeds/FakePodcastsAnalyticsSeeder.php @@ -11,6 +11,8 @@ namespace App\Database\Seeds; +use GeoIp2\Database\Reader; +use GeoIp2\Exception\AddressNotFoundException; use App\Models\PodcastModel; use App\Models\EpisodeModel; @@ -18,7 +20,7 @@ use CodeIgniter\Database\Seeder; class FakePodcastsAnalyticsSeeder extends Seeder { - public function run() + public function run(): void { $podcast = (new PodcastModel())->first(); @@ -27,6 +29,8 @@ class FakePodcastsAnalyticsSeeder extends Seeder 'https://raw.githubusercontent.com/opawg/user-agents/master/src/user-agents.json', ), true, + 512, + JSON_THROW_ON_ERROR, ); $jsonRSSUserAgents = json_decode( @@ -34,6 +38,8 @@ class FakePodcastsAnalyticsSeeder extends Seeder 'https://raw.githubusercontent.com/opawg/podcast-rss-useragents/master/src/rss-ua.json', ), true, + 512, + JSON_THROW_ON_ERROR, ); if ($podcast) { @@ -68,7 +74,7 @@ class FakePodcastsAnalyticsSeeder extends Seeder for ( $num_line = 0; $num_line < rand(1, $proba1); - $num_line++ + ++$num_line ) { $proba2 = floor(exp(6 - $age / 20)) + 10; @@ -96,7 +102,7 @@ class FakePodcastsAnalyticsSeeder extends Seeder '.' . rand(0, 255); - $cityReader = new \GeoIp2\Database\Reader( + $cityReader = new Reader( WRITEPATH . 'uploads/GeoLite2-City/GeoLite2-City.mmdb', ); @@ -117,7 +123,7 @@ class FakePodcastsAnalyticsSeeder extends Seeder : $city->subdivisions[0]->isoCode; $latitude = round($city->location->latitude, 3); $longitude = round($city->location->longitude, 3); - } catch (\GeoIp2\Exception\AddressNotFoundException $ex) { + } catch (AddressNotFoundException $addressNotFoundException) { //Bad luck, bad IP, nothing to do. } diff --git a/app/Database/Seeds/FakeWebsiteAnalyticsSeeder.php b/app/Database/Seeds/FakeWebsiteAnalyticsSeeder.php index af6661ad..2534d968 100644 --- a/app/Database/Seeds/FakeWebsiteAnalyticsSeeder.php +++ b/app/Database/Seeds/FakeWebsiteAnalyticsSeeder.php @@ -18,6 +18,9 @@ use CodeIgniter\Database\Seeder; class FakeWebsiteAnalyticsSeeder extends Seeder { + /** + * @var string[] + */ protected $keywords = [ 'all the smoke podcast', 'apple podcast', @@ -70,6 +73,10 @@ class FakeWebsiteAnalyticsSeeder extends Seeder 'wind of change podcast', 'your own backyard podcast', ]; + + /** + * @var string[] + */ protected $domains = [ '360.cn ', 'adobe.com ', @@ -123,6 +130,9 @@ class FakeWebsiteAnalyticsSeeder extends Seeder 'zoom.us ', ]; + /** + * @var string[] + */ protected $browsers = [ 'Android Browser', 'Avast Secure Browser', @@ -168,7 +178,7 @@ class FakeWebsiteAnalyticsSeeder extends Seeder 'WOSBrowser', ]; - public function run() + public function run(): void { $podcast = (new PodcastModel())->first(); @@ -201,7 +211,7 @@ class FakeWebsiteAnalyticsSeeder extends Seeder for ( $num_line = 0; $num_line < rand(1, $proba1); - $num_line++ + ++$num_line ) { $proba2 = floor(exp(6 - $age / 20)) + 10; diff --git a/app/Database/Seeds/LanguageSeeder.php b/app/Database/Seeds/LanguageSeeder.php index b2912c3f..0c7189d5 100644 --- a/app/Database/Seeds/LanguageSeeder.php +++ b/app/Database/Seeds/LanguageSeeder.php @@ -21,7 +21,7 @@ use CodeIgniter\Database\Seeder; class LanguageSeeder extends Seeder { - public function run() + public function run(): void { $data = [ ['code' => 'aa', 'native_name' => 'Afaraf'], @@ -163,7 +163,7 @@ class LanguageSeeder extends Seeder 'native_name' => 'GĆ idhlig', ], ['code' => 'gl', 'native_name' => 'Galego'], - ['code' => 'gn', 'native_name' => 'AvaƱe\'įŗ½'], + ['code' => 'gn', 'native_name' => "AvaƱe'įŗ½"], ['code' => 'gu', 'native_name' => 'ąŖ—ą«ąŖœąŖ°ąŖ¾ąŖ¤ą«€'], [ 'code' => 'gv', @@ -436,7 +436,7 @@ class LanguageSeeder extends Seeder ], [ 'code' => 'sm', - 'native_name' => 'gagana fa\'a Samoa', + 'native_name' => "gagana fa'a Samoa", ], ['code' => 'sn', 'native_name' => 'chiShona'], [ diff --git a/app/Database/Seeds/PlatformSeeder.php b/app/Database/Seeds/PlatformSeeder.php index 309b767c..53cf749a 100644 --- a/app/Database/Seeds/PlatformSeeder.php +++ b/app/Database/Seeds/PlatformSeeder.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Seeder; class PlatformSeeder extends Seeder { - public function run() + public function run(): void { $data = [ [ diff --git a/app/Database/Seeds/TestSeeder.php b/app/Database/Seeds/TestSeeder.php index 649b98bb..364feb59 100644 --- a/app/Database/Seeds/TestSeeder.php +++ b/app/Database/Seeds/TestSeeder.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Seeder; class TestSeeder extends Seeder { - public function run() + public function run(): void { /** Inserts an active user with the following credentials: * username: admin @@ -29,6 +29,7 @@ class TestSeeder extends Seeder '$2y$10$TXJEHX/djW8jtzgpDVf7dOOCGo5rv1uqtAYWdwwwkttQcDkAeB2.6', 'active' => 1, ]); + $this->db ->table('auth_groups_users') ->insert(['group_id' => 1, 'user_id' => 1]); diff --git a/app/Entities/Actor.php b/app/Entities/Actor.php index 78515a70..e19c00c1 100644 --- a/app/Entities/Actor.php +++ b/app/Entities/Actor.php @@ -9,6 +9,7 @@ namespace App\Entities; use App\Models\PodcastModel; +use RuntimeException; class Actor extends \ActivityPub\Entities\Actor { @@ -30,7 +31,7 @@ class Actor extends \ActivityPub\Entities\Actor public function getPodcast() { if (empty($this->id)) { - throw new \RuntimeException( + throw new RuntimeException( 'Actor must be created before getting associated podcast.', ); } diff --git a/app/Entities/Category.php b/app/Entities/Category.php index d58e143b..72edbe9e 100644 --- a/app/Entities/Category.php +++ b/app/Entities/Category.php @@ -9,15 +9,18 @@ namespace App\Entities; use App\Models\CategoryModel; -use CodeIgniter\Entity; +use CodeIgniter\Entity\Entity; class Category extends Entity { /** - * @var \App\Entity\Category|null + * @var Category|null */ protected $parent; + /** + * @var array + */ protected $casts = [ 'id' => 'integer', 'parent_id' => 'integer', @@ -26,12 +29,12 @@ class Category extends Entity 'google_category' => 'string', ]; - public function getParent() + public function getParent(): ?Category { - $parentId = $this->attributes['parent_id']; + if (empty($this->parent_id)) { + return null; + } - return $parentId != 0 - ? (new CategoryModel())->getCategoryById($parentId) - : null; + return (new CategoryModel())->getCategoryById($this->parent_id); } } diff --git a/app/Entities/Credit.php b/app/Entities/Credit.php index 94dd5f4a..f323cbb1 100644 --- a/app/Entities/Credit.php +++ b/app/Entities/Credit.php @@ -8,26 +8,26 @@ namespace App\Entities; +use RuntimeException; use App\Models\PersonModel; use App\Models\PodcastModel; use App\Models\EpisodeModel; - -use CodeIgniter\Entity; +use CodeIgniter\Entity\Entity; class Credit extends Entity { /** - * @var \App\Entities\Person + * @var Person */ protected $person; /** - * @var \App\Entities\Podcast + * @var Podcast */ protected $podcast; /** - * @var \App\Entities\Episode|null + * @var Episode|null */ protected $episode; @@ -41,35 +41,47 @@ class Credit extends Entity */ protected $role_label; - public function getPodcast() + /** + * @var array + */ + protected $casts = [ + 'person_group' => 'string', + 'person_role' => 'string', + 'person_id' => 'integer', + 'full_name' => 'integer', + 'podcast_id' => 'integer', + 'episode_id' => '?integer', + ]; + + public function getPodcast(): Podcast { return (new PodcastModel())->getPodcastById( $this->attributes['podcast_id'], ); } - public function getEpisode() + public function getEpisode(): ?Episode { if (empty($this->episode_id)) { - throw new \RuntimeException( + throw new RuntimeException( 'Credit must have episode_id before getting episode.', ); } if (empty($this->episode)) { $this->episode = (new EpisodeModel())->getPublishedEpisodeById( - $this->episode_id, $this->podcast_id, + $this->episode_id, ); } return $this->episode; } - public function getPerson() + public function getPerson(): Person { if (empty($this->person_id)) { - throw new \RuntimeException( + throw new RuntimeException( 'Credit must have person_id before getting person.', ); } @@ -83,23 +95,27 @@ class Credit extends Entity return $this->person; } - public function getGroupLabel() + public function getGroupLabel(): ?string { if (empty($this->person_group)) { return null; - } else { - return lang("PersonsTaxonomy.persons.{$this->person_group}.label"); } + + return lang("PersonsTaxonomy.persons.{$this->person_group}.label"); } - public function getRoleLabel() + public function getRoleLabel(): ?string { - if (empty($this->person_group) || empty($this->person_role)) { + if (empty($this->person_group)) { return null; - } else { - return lang( - "PersonsTaxonomy.persons.{$this->person_group}.roles.{$this->person_role}.label", - ); } + + if (empty($this->person_role)) { + return null; + } + + return lang( + "PersonsTaxonomy.persons.{$this->person_group}.roles.{$this->person_role}.label", + ); } } diff --git a/app/Entities/Episode.php b/app/Entities/Episode.php index 338e9879..d2297421 100644 --- a/app/Entities/Episode.php +++ b/app/Entities/Episode.php @@ -8,20 +8,25 @@ namespace App\Entities; +use App\Libraries\Image; +use App\Libraries\SimpleRSSElement; use App\Models\PodcastModel; use App\Models\SoundbiteModel; use App\Models\EpisodePersonModel; use App\Models\NoteModel; -use CodeIgniter\Entity; +use CodeIgniter\Entity\Entity; use CodeIgniter\Files\Exceptions\FileNotFoundException; +use CodeIgniter\Files\File; use CodeIgniter\HTTP\Exceptions\HTTPException; +use CodeIgniter\HTTP\Files\UploadedFile; use CodeIgniter\I18n\Time; use League\CommonMark\CommonMarkConverter; +use RuntimeException; class Episode extends Entity { /** - * @var \App\Entities\Podcast + * @var Podcast */ protected $podcast; @@ -31,22 +36,22 @@ class Episode extends Entity protected $link; /** - * @var \App\Libraries\Image + * @var Image */ protected $image; /** - * @var \CodeIgniter\Files\File + * @var File */ protected $audioFile; /** - * @var \CodeIgniter\Files\File + * @var File */ protected $transcript_file; /** - * @var \CodeIgniter\Files\File + * @var File */ protected $chapters_file; @@ -71,17 +76,17 @@ class Episode extends Entity protected $audio_file_opengraph_url; /** - * @var \App\Entities\EpisodePerson[] + * @var EpisodePerson[] */ protected $persons; /** - * @var \App\Entities\Soundbite[] + * @var Soundbite[] */ protected $soundbites; /** - * @var \App\Entities\Note[] + * @var Note[] */ protected $notes; @@ -156,15 +161,13 @@ class Episode extends Entity /** * Saves an episode image * - * @param \CodeIgniter\HTTP\Files\UploadedFile|\CodeIgniter\Files\File $image - * + * @param UploadedFile|File $image */ public function setImage($image) { if ( !empty($image) && - (!($image instanceof \CodeIgniter\HTTP\Files\UploadedFile) || - $image->isValid()) + (!($image instanceof UploadedFile) || $image->isValid()) ) { helper('media'); @@ -175,7 +178,7 @@ class Episode extends Entity 'podcasts/' . $this->getPodcast()->name, $this->attributes['slug'], ); - $this->image = new \App\Libraries\Image( + $this->image = new Image( $this->attributes['image_path'], $this->attributes['image_mimetype'], ); @@ -185,13 +188,10 @@ class Episode extends Entity return $this; } - public function getImage(): \App\Libraries\Image + public function getImage(): Image { if ($imagePath = $this->attributes['image_path']) { - return new \App\Libraries\Image( - $imagePath, - $this->attributes['image_mimetype'], - ); + return new Image($imagePath, $this->attributes['image_mimetype']); } return $this->getPodcast()->image; } @@ -199,15 +199,14 @@ class Episode extends Entity /** * Saves an audio file * - * @param \CodeIgniter\HTTP\Files\UploadedFile|\CodeIgniter\Files\File $audioFile + * @param UploadedFile|File $audioFile * */ public function setAudioFile($audioFile = null) { if ( !empty($audioFile) && - (!($audioFile instanceof \CodeIgniter\HTTP\Files\UploadedFile) || - $audioFile->isValid()) + (!($audioFile instanceof UploadedFile) || $audioFile->isValid()) ) { helper(['media', 'id3']); @@ -234,16 +233,14 @@ class Episode extends Entity /** * Saves an episode transcript file * - * @param \CodeIgniter\HTTP\Files\UploadedFile|\CodeIgniter\Files\File $transcriptFile + * @param UploadedFile|File $transcriptFile * */ public function setTranscriptFile($transcriptFile) { if ( !empty($transcriptFile) && - (!( - $transcriptFile instanceof \CodeIgniter\HTTP\Files\UploadedFile - ) || + (!($transcriptFile instanceof UploadedFile) || $transcriptFile->isValid()) ) { helper('media'); @@ -261,14 +258,14 @@ class Episode extends Entity /** * Saves an episode chapters file * - * @param \CodeIgniter\HTTP\Files\UploadedFile|\CodeIgniter\Files\File $chaptersFile + * @param UploadedFile|File $chaptersFile * */ public function setChaptersFile($chaptersFile) { if ( !empty($chaptersFile) && - (!($chaptersFile instanceof \CodeIgniter\HTTP\Files\UploadedFile) || + (!($chaptersFile instanceof UploadedFile) || $chaptersFile->isValid()) ) { helper('media'); @@ -287,7 +284,7 @@ class Episode extends Entity { helper('media'); - return new \CodeIgniter\Files\File(media_path($this->audio_file_path)); + return new File(media_path($this->audio_file_path)); } public function getTranscriptFile() @@ -295,7 +292,7 @@ class Episode extends Entity if ($this->attributes['transcript_file_path']) { helper('media'); - return new \CodeIgniter\Files\File( + return new File( media_path($this->attributes['transcript_file_path']), ); } @@ -308,7 +305,7 @@ class Episode extends Entity if ($this->attributes['chapters_file_path']) { helper('media'); - return new \CodeIgniter\Files\File( + return new File( media_path($this->attributes['chapters_file_path']), ); } @@ -320,7 +317,7 @@ class Episode extends Entity { helper('media'); - return media_url($this->audio_file_path); + return media_base_url($this->audio_file_path); } public function getAudioFileAnalyticsUrl() @@ -359,7 +356,7 @@ class Episode extends Entity public function getTranscriptFileUrl() { if ($this->attributes['transcript_file_path']) { - return media_url($this->attributes['transcript_file_path']); + return media_base_url($this->attributes['transcript_file_path']); } else { return $this->attributes['transcript_file_remote_url']; } @@ -368,17 +365,14 @@ class Episode extends Entity /** * Gets chapters file url from chapters file uri if it exists * or returns the chapters_file_remote_url which can be null. - * - * @return mixed - * @throws HTTPException */ - public function getChaptersFileUrl() + public function getChaptersFileUrl(): ?string { - if ($this->attributes['chapters_file_path']) { - return media_url($this->attributes['chapters_file_path']); - } else { - return $this->attributes['chapters_file_remote_url']; + if ($this->chapters_file_path) { + return media_base_url($this->chapters_file_path); } + + return $this->chapters_file_remote_url; } /** @@ -389,7 +383,7 @@ class Episode extends Entity public function getPersons() { if (empty($this->id)) { - throw new \RuntimeException( + throw new RuntimeException( 'Episode must be created before getting persons.', ); } @@ -412,7 +406,7 @@ class Episode extends Entity public function getSoundbites() { if (empty($this->id)) { - throw new \RuntimeException( + throw new RuntimeException( 'Episode must be created before getting soundbites.', ); } @@ -430,7 +424,7 @@ class Episode extends Entity public function getNotes() { if (empty($this->id)) { - throw new \RuntimeException( + throw new RuntimeException( 'Episode must be created before getting soundbites.', ); } @@ -586,23 +580,24 @@ class Episode extends Entity */ function getCustomRssString() { - helper('rss'); - if (empty($this->attributes['custom_rss'])) { + if (empty($this->custom_rss)) { return ''; - } else { - $xmlNode = (new \App\Libraries\SimpleRSSElement( - '', - )) - ->addChild('channel') - ->addChild('item'); - array_to_rss( - [ - 'elements' => $this->custom_rss, - ], - $xmlNode, - ); - return str_replace(['', ''], '', $xmlNode->asXML()); } + + helper('rss'); + + $xmlNode = (new SimpleRSSElement( + '', + )) + ->addChild('channel') + ->addChild('item'); + array_to_rss( + [ + 'elements' => $this->custom_rss, + ], + $xmlNode, + ); + return str_replace(['', ''], '', $xmlNode->asXML()); } /** @@ -613,6 +608,10 @@ class Episode extends Entity */ function setCustomRssString($customRssString) { + if (empty($customRssString)) { + return $this; + } + helper('rss'); $customRssArray = rss_to_array( simplexml_load_string( @@ -621,6 +620,7 @@ class Episode extends Entity '', ), )['elements'][0]['elements'][0]; + if (array_key_exists('elements', $customRssArray)) { $this->attributes['custom_rss'] = json_encode( $customRssArray['elements'], @@ -628,6 +628,8 @@ class Episode extends Entity } else { $this->attributes['custom_rss'] = null; } + + return $this; } function getPartnerLink($serviceSlug = null) diff --git a/app/Entities/EpisodePerson.php b/app/Entities/EpisodePerson.php index 6c0a6388..dff7be1b 100644 --- a/app/Entities/EpisodePerson.php +++ b/app/Entities/EpisodePerson.php @@ -8,16 +8,19 @@ namespace App\Entities; -use CodeIgniter\Entity; +use CodeIgniter\Entity\Entity; use App\Models\PersonModel; class EpisodePerson extends Entity { /** - * @var \App\Entities\Person + * @var Person */ protected $person; + /** + * @var array + */ protected $casts = [ 'id' => 'integer', 'podcast_id' => 'integer', @@ -30,7 +33,7 @@ class EpisodePerson extends Entity public function getPerson() { return (new PersonModel())->getPersonById( - $this->attributes['person_id'] + $this->attributes['person_id'], ); } } diff --git a/app/Entities/Language.php b/app/Entities/Language.php index aa683fd7..ea9b4a53 100644 --- a/app/Entities/Language.php +++ b/app/Entities/Language.php @@ -8,10 +8,13 @@ namespace App\Entities; -use CodeIgniter\Entity; +use CodeIgniter\Entity\Entity; class Language extends Entity { + /** + * @var array + */ protected $casts = [ 'code' => 'string', 'native_name' => 'string', diff --git a/app/Entities/Note.php b/app/Entities/Note.php index 6ec48029..223a9c20 100644 --- a/app/Entities/Note.php +++ b/app/Entities/Note.php @@ -9,11 +9,12 @@ namespace App\Entities; use App\Models\EpisodeModel; +use RuntimeException; class Note extends \ActivityPub\Entities\Note { /** - * @var \App\Entities\Episode|null + * @var Episode|null */ protected $episode; @@ -40,7 +41,7 @@ class Note extends \ActivityPub\Entities\Note public function getEpisode() { if (empty($this->episode_id)) { - throw new \RuntimeException( + throw new RuntimeException( 'Note must have an episode_id before getting episode.', ); } diff --git a/app/Entities/Page.php b/app/Entities/Page.php index bb40d68a..518edf66 100644 --- a/app/Entities/Page.php +++ b/app/Entities/Page.php @@ -8,7 +8,7 @@ namespace App\Entities; -use CodeIgniter\Entity; +use CodeIgniter\Entity\Entity; use League\CommonMark\CommonMarkConverter; class Page extends Entity @@ -23,6 +23,9 @@ class Page extends Entity */ protected $content_html; + /** + * @var array + */ protected $casts = [ 'id' => 'integer', 'title' => 'string', @@ -32,10 +35,10 @@ class Page extends Entity public function getLink() { - return base_url($this->attributes['slug']); + return url_to('page', $this->attributes['slug']); } - public function getContentHtml() + public function getContentHtml(): string { $converter = new CommonMarkConverter([ 'html_input' => 'strip', diff --git a/app/Entities/Person.php b/app/Entities/Person.php index d6c9c50f..2fdfbc33 100644 --- a/app/Entities/Person.php +++ b/app/Entities/Person.php @@ -8,15 +8,21 @@ namespace App\Entities; -use CodeIgniter\Entity; +use App\Libraries\Image; +use CodeIgniter\HTTP\Files\UploadedFile; +use CodeIgniter\Files\File; +use CodeIgniter\Entity\Entity; class Person extends Entity { /** - * @var \App\Libraries\Image + * @var Image */ protected $image; + /** + * @var array + */ protected $casts = [ 'id' => 'integer', 'full_name' => 'string', @@ -31,12 +37,11 @@ class Person extends Entity /** * Saves a picture in `public/media/persons/` * - * @param \CodeIgniter\HTTP\Files\UploadedFile|\CodeIgniter\Files\File $image - * + * @param UploadedFile|File|null $image */ - public function setImage($image = null) + public function setImage($image = null): self { - if ($image) { + if ($image !== null) { helper('media'); $this->attributes['image_mimetype'] = $image->getMimeType(); @@ -45,7 +50,7 @@ class Person extends Entity 'persons', $this->attributes['unique_name'], ); - $this->image = new \App\Libraries\Image( + $this->image = new Image( $this->attributes['image_path'], $this->attributes['image_mimetype'], ); @@ -55,9 +60,9 @@ class Person extends Entity return $this; } - public function getImage() + public function getImage(): Image { - return new \App\Libraries\Image( + return new Image( $this->attributes['image_path'], $this->attributes['image_mimetype'], ); diff --git a/app/Entities/Platform.php b/app/Entities/Platform.php index a1080da4..9cc5662d 100644 --- a/app/Entities/Platform.php +++ b/app/Entities/Platform.php @@ -8,10 +8,13 @@ namespace App\Entities; -use CodeIgniter\Entity; +use CodeIgniter\Entity\Entity; class Platform extends Entity { + /** + * @var array + */ protected $casts = [ 'slug' => 'string', 'type' => 'string', diff --git a/app/Entities/Podcast.php b/app/Entities/Podcast.php index 740a0134..7bd77a15 100644 --- a/app/Entities/Podcast.php +++ b/app/Entities/Podcast.php @@ -8,14 +8,18 @@ namespace App\Entities; -use ActivityPub\Models\ActorModel; +use App\Libraries\Image; +use App\Libraries\SimpleRSSElement; use App\Models\CategoryModel; use App\Models\EpisodeModel; use App\Models\PlatformModel; use App\Models\PodcastPersonModel; -use CodeIgniter\Entity; +use CodeIgniter\Entity\Entity; use App\Models\UserModel; +use CodeIgniter\Files\File; +use CodeIgniter\HTTP\Files\UploadedFile; use League\CommonMark\CommonMarkConverter; +use RuntimeException; class Podcast extends Entity { @@ -25,32 +29,32 @@ class Podcast extends Entity protected $link; /** - * @var \ActivityPub\Entities\Actor + * @var Actor */ protected $actor; /** - * @var \App\Libraries\Image + * @var Image */ protected $image; /** - * @var \App\Entities\Episode[] + * @var Episode[] */ protected $episodes; /** - * @var \App\Entities\PodcastPerson[] + * @var PodcastPerson[] */ protected $persons; /** - * @var \App\Entities\Category + * @var Category */ protected $category; /** - * @var \App\Entities\Category[] + * @var Category[] */ protected $other_categories; @@ -60,22 +64,22 @@ class Podcast extends Entity protected $other_categories_ids; /** - * @var \App\Entities\User[] + * @var User[] */ protected $contributors; /** - * @var \App\Entities\Platform + * @var Platform */ protected $podcastingPlatforms; /** - * @var \App\Entities\Platform + * @var Platform */ protected $socialPlatforms; /** - * @var \App\Entities\Platform + * @var Platform */ protected $fundingPlatforms; @@ -132,12 +136,12 @@ class Podcast extends Entity /** * Returns the podcast actor * - * @return \App\Entities\Actor + * @return Actor */ - public function getActor() + public function getActor(): Actor { if (!$this->attributes['actor_id']) { - throw new \RuntimeException( + throw new RuntimeException( 'Podcast must have an actor_id before getting actor.', ); } @@ -152,10 +156,9 @@ class Podcast extends Entity /** * Saves a cover image to the corresponding podcast folder in `public/media/podcast_name/` * - * @param \CodeIgniter\HTTP\Files\UploadedFile|\CodeIgniter\Files\File $image - * + * @param UploadedFile|File $image */ - public function setImage($image = null) + public function setImage($image = null): self { if ($image) { helper('media'); @@ -167,7 +170,7 @@ class Podcast extends Entity 'cover', ); - $this->image = new \App\Libraries\Image( + $this->image = new Image( $this->attributes['image_path'], $this->attributes['image_mimetype'], ); @@ -177,20 +180,20 @@ class Podcast extends Entity return $this; } - public function getImage() + public function getImage(): Image { - return new \App\Libraries\Image( + return new Image( $this->attributes['image_path'], $this->attributes['image_mimetype'], ); } - public function getLink() + public function getLink(): string { return url_to('podcast-activity', $this->attributes['name']); } - public function getFeedUrl() + public function getFeedUrl(): string { return url_to('podcast_feed', $this->attributes['name']); } @@ -198,12 +201,12 @@ class Podcast extends Entity /** * Returns the podcast's episodes * - * @return \App\Entities\Episode[] + * @return Episode[] */ - public function getEpisodes() + public function getEpisodes(): array { if (empty($this->id)) { - throw new \RuntimeException( + throw new RuntimeException( 'Podcast must be created before getting episodes.', ); } @@ -221,12 +224,12 @@ class Podcast extends Entity /** * Returns the podcast's persons * - * @return \App\Entities\PodcastPerson[] + * @return PodcastPerson[] */ - public function getPersons() + public function getPersons(): array { if (empty($this->id)) { - throw new \RuntimeException( + throw new RuntimeException( 'Podcast must be created before getting persons.', ); } @@ -243,12 +246,12 @@ class Podcast extends Entity /** * Returns the podcast category entity * - * @return \App\Entities\Category + * @return Category */ - public function getCategory() + public function getCategory(): Category { if (empty($this->id)) { - throw new \RuntimeException( + throw new RuntimeException( 'Podcast must be created before getting category.', ); } @@ -265,12 +268,12 @@ class Podcast extends Entity /** * Returns all podcast contributors * - * @return \App\Entities\User[] + * @return User[] */ - public function getContributors() + public function getContributors(): array { if (empty($this->id)) { - throw new \RuntimeException( + throw new RuntimeException( 'Podcasts must be created before getting contributors.', ); } @@ -284,7 +287,7 @@ class Podcast extends Entity return $this->contributors; } - public function setDescriptionMarkdown(string $descriptionMarkdown) + public function setDescriptionMarkdown(string $descriptionMarkdown): self { $converter = new CommonMarkConverter([ 'html_input' => 'strip', @@ -300,8 +303,8 @@ class Podcast extends Entity } public function setEpisodeDescriptionFooterMarkdown( - string $episodeDescriptionFooterMarkdown = null - ) { + ?string $episodeDescriptionFooterMarkdown = null + ): self { if ($episodeDescriptionFooterMarkdown) { $converter = new CommonMarkConverter([ 'html_input' => 'strip', @@ -319,7 +322,7 @@ class Podcast extends Entity return $this; } - public function getDescription() + public function getDescription(): string { if ($this->description) { return $this->description; @@ -337,12 +340,12 @@ class Podcast extends Entity /** * Returns the podcast's podcasting platform links * - * @return \App\Entities\Platform[] + * @return Platform[] */ - public function getPodcastingPlatforms() + public function getPodcastingPlatforms(): array { if (empty($this->id)) { - throw new \RuntimeException( + throw new RuntimeException( 'Podcast must be created before getting podcasting platform links.', ); } @@ -357,33 +360,15 @@ class Podcast extends Entity return $this->podcastingPlatforms; } - /** - * Returns true if the podcast has podcasting platform links - */ - public function getHasPodcastingPlatforms() - { - if (empty($this->id)) { - throw new \RuntimeException( - 'Podcast must be created before getting podcasting platform.', - ); - } - foreach ($this->getPodcastingPlatforms() as $podcastingPlatform) { - if ($podcastingPlatform->is_on_embeddable_player) { - return true; - } - } - return false; - } - /** * Returns the podcast's social platform links * - * @return \App\Entities\Platform[] + * @return Platform[] */ - public function getSocialPlatforms() + public function getSocialPlatforms(): array { if (empty($this->id)) { - throw new \RuntimeException( + throw new RuntimeException( 'Podcast must be created before getting social platform links.', ); } @@ -398,33 +383,15 @@ class Podcast extends Entity return $this->socialPlatforms; } - /** - * Returns true if the podcast has social platform links - */ - public function getHasSocialPlatforms() - { - if (empty($this->id)) { - throw new \RuntimeException( - 'Podcast must be created before getting social platform.', - ); - } - foreach ($this->getSocialPlatforms() as $socialPlatform) { - if ($socialPlatform->is_on_embeddable_player) { - return true; - } - } - return false; - } - /** * Returns the podcast's funding platform links * - * @return \App\Entities\Platform[] + * @return Platform[] */ - public function getFundingPlatforms() + public function getFundingPlatforms(): array { if (empty($this->id)) { - throw new \RuntimeException( + throw new RuntimeException( 'Podcast must be created before getting funding platform links.', ); } @@ -440,27 +407,12 @@ class Podcast extends Entity } /** - * Returns true if the podcast has social platform links + * @return Category[] */ - public function getHasFundingPlatforms() + public function getOtherCategories(): array { if (empty($this->id)) { - throw new \RuntimeException( - 'Podcast must be created before getting Funding platform.', - ); - } - foreach ($this->getFundingPlatforms() as $fundingPlatform) { - if ($fundingPlatform->is_on_embeddable_player) { - return true; - } - } - return false; - } - - public function getOtherCategories() - { - if (empty($this->id)) { - throw new \RuntimeException( + throw new RuntimeException( 'Podcast must be created before getting other categories.', ); } @@ -474,7 +426,10 @@ class Podcast extends Entity return $this->other_categories; } - public function getOtherCategoriesIds() + /** + * @return array + */ + public function getOtherCategoriesIds(): array { if (empty($this->other_categories_ids)) { $this->other_categories_ids = array_column( @@ -488,11 +443,8 @@ class Podcast extends Entity /** * Saves the location name and fetches OpenStreetMap info - * - * @param string $locationName - * */ - public function setLocation($locationName = null) + public function setLocation(?string $locationName = null): self { helper('location'); @@ -511,6 +463,7 @@ class Podcast extends Entity $this->attributes['location_geo'] = null; $this->attributes['location_osmid'] = null; } + return $this; } @@ -518,39 +471,39 @@ class Podcast extends Entity * Get custom rss tag as XML String * * @return string - * */ - function getCustomRssString() + function getCustomRssString(): string { - helper('rss'); if (empty($this->attributes['custom_rss'])) { return ''; - } else { - $xmlNode = (new \App\Libraries\SimpleRSSElement( - '', - ))->addChild('channel'); - array_to_rss( - [ - 'elements' => $this->custom_rss, - ], - $xmlNode, - ); - return str_replace( - ['', ''], - '', - $xmlNode->asXML(), - ); } + + helper('rss'); + + $xmlNode = (new SimpleRSSElement( + '', + ))->addChild('channel'); + array_to_rss( + [ + 'elements' => $this->custom_rss, + ], + $xmlNode, + ); + + return str_replace(['', ''], '', $xmlNode->asXML()); } /** * Saves custom rss tag into json * * @param string $customRssString - * */ - function setCustomRssString($customRssString) + function setCustomRssString($customRssString): self { + if (empty($customRssString)) { + return $this; + } + helper('rss'); $customRssArray = rss_to_array( simplexml_load_string( @@ -559,6 +512,7 @@ class Podcast extends Entity '', ), )['elements'][0]; + if (array_key_exists('elements', $customRssArray)) { $this->attributes['custom_rss'] = json_encode( $customRssArray['elements'], @@ -566,5 +520,7 @@ class Podcast extends Entity } else { $this->attributes['custom_rss'] = null; } + + return $this; } } diff --git a/app/Entities/PodcastPerson.php b/app/Entities/PodcastPerson.php index 95dec77c..55481910 100644 --- a/app/Entities/PodcastPerson.php +++ b/app/Entities/PodcastPerson.php @@ -8,16 +8,19 @@ namespace App\Entities; -use CodeIgniter\Entity; +use CodeIgniter\Entity\Entity; use App\Models\PersonModel; class PodcastPerson extends Entity { /** - * @var \App\Entities\Person + * @var Person */ protected $person; + /** + * @var array + */ protected $casts = [ 'id' => 'integer', 'podcast_id' => 'integer', @@ -29,7 +32,7 @@ class PodcastPerson extends Entity public function getPerson() { return (new PersonModel())->getPersonById( - $this->attributes['person_id'] + $this->attributes['person_id'], ); } } diff --git a/app/Entities/Soundbite.php b/app/Entities/Soundbite.php index 04d8f293..a12c13ff 100644 --- a/app/Entities/Soundbite.php +++ b/app/Entities/Soundbite.php @@ -8,10 +8,13 @@ namespace App\Entities; -use CodeIgniter\Entity; +use CodeIgniter\Entity\Entity; class Soundbite extends Entity { + /** + * @var array + */ protected $casts = [ 'id' => 'integer', 'podcast_id' => 'integer', @@ -23,7 +26,7 @@ class Soundbite extends Entity 'updated_by' => 'integer', ]; - public function setUpdatedBy(\App\Entities\User $user) + public function setUpdatedBy(User $user): self { $this->attributes['updated_by'] = $user->id; diff --git a/app/Entities/User.php b/app/Entities/User.php index 2c94df54..ca48a273 100644 --- a/app/Entities/User.php +++ b/app/Entities/User.php @@ -8,25 +8,29 @@ namespace App\Entities; +use RuntimeException; use App\Models\PodcastModel; class User extends \Myth\Auth\Entities\User { /** * Per-user podcasts - * @var \App\Entities\Podcast[] + * @var Podcast[] */ protected $podcasts = []; /** * The podcast the user is contributing to - * @var \App\Entities\Podcast|null + * + * @var Podcast|null */ - protected $podcast = null; + protected $podcast; /** * Array of field names and the type of value to cast them as * when they are accessed. + * + * @var array */ protected $casts = [ 'id' => 'integer', @@ -39,13 +43,13 @@ class User extends \Myth\Auth\Entities\User /** * Returns the podcasts the user is contributing to * - * @return \App\Entities\Podcast[] + * @return Podcast[] */ - public function getPodcasts() + public function getPodcasts(): array { if (empty($this->id)) { - throw new \RuntimeException( - 'Users must be created before getting podcasts.' + throw new RuntimeException( + 'Users must be created before getting podcasts.', ); } @@ -58,20 +62,18 @@ class User extends \Myth\Auth\Entities\User /** * Returns a podcast the user is contributing to - * - * @return \App\Entities\Podcast */ - public function getPodcast() + public function getPodcast(): Podcast { if (empty($this->podcast_id)) { - throw new \RuntimeException( - 'Podcast_id must be set before getting podcast.' + throw new RuntimeException( + 'Podcast_id must be set before getting podcast.', ); } if (empty($this->podcast)) { $this->podcast = (new PodcastModel())->getPodcastById( - $this->podcast_id + $this->podcast_id, ); } diff --git a/app/Filters/PermissionFilter.php b/app/Filters/PermissionFilter.php index f40c6a49..929bb954 100644 --- a/app/Filters/PermissionFilter.php +++ b/app/Filters/PermissionFilter.php @@ -21,10 +21,8 @@ class PermissionFilter implements FilterInterface * sent back to the client, allowing for error pages, * redirects, etc. * - * @param \CodeIgniter\HTTP\RequestInterface $request * @param array|null $params - * - * @return mixed + * @return void|mixed */ public function before(RequestInterface $request, $params = null) { @@ -59,15 +57,14 @@ class PermissionFilter implements FilterInterface count($routerParams) > 0 ) { if ( - $groupId = (new PodcastModel())->getContributorGroupId( + ($groupId = (new PodcastModel())->getContributorGroupId( $authenticate->id(), - $routerParams[0] - ) + $routerParams[0], + )) && + $authorize->groupHasPermission($permission, $groupId) ) { - if ($authorize->groupHasPermission($permission, $groupId)) { - $result = true; - break; - } + $result = true; + break; } } elseif ( $authorize->hasPermission($permission, $authenticate->id()) @@ -84,31 +81,25 @@ class PermissionFilter implements FilterInterface return redirect() ->to($redirectURL) ->with('error', lang('Auth.notEnoughPrivilege')); - } else { - throw new PermissionException(lang('Auth.notEnoughPrivilege')); } + throw new PermissionException(lang('Auth.notEnoughPrivilege')); } } //-------------------------------------------------------------------- - /** * Allows After filters to inspect and modify the response * object as needed. This method does not allow any way * to stop execution of other after filters, short of * throwing an Exception or Error. * - * @param \CodeIgniter\HTTP\RequestInterface $request - * @param \CodeIgniter\HTTP\ResponseInterface $response * @param array|null $arguments - * - * @return void */ public function after( RequestInterface $request, ResponseInterface $response, $arguments = null - ) { + ): void { } //-------------------------------------------------------------------- diff --git a/app/Helpers/auth_helper.php b/app/Helpers/auth_helper.php index 4408c0aa..fc07555a 100644 --- a/app/Helpers/auth_helper.php +++ b/app/Helpers/auth_helper.php @@ -6,16 +6,15 @@ * @link https://castopod.org/ */ +use ActivityPub\Entities\Actor; use CodeIgniter\Database\Exceptions\DataException; use Config\Services; if (!function_exists('set_interact_as_actor')) { /** * Sets the actor id of which the user is acting as - * - * @return void */ - function set_interact_as_actor($actorId) + function set_interact_as_actor($actorId): void { $authenticate = Services::authentication(); $authenticate->check(); @@ -28,10 +27,8 @@ if (!function_exists('set_interact_as_actor')) { if (!function_exists('remove_interact_as_actor')) { /** * Removes the actor id of which the user is acting as - * - * @return void */ - function remove_interact_as_actor() + function remove_interact_as_actor(): void { $session = session(); $session->remove('interact_as_actor_id'); @@ -41,10 +38,8 @@ if (!function_exists('remove_interact_as_actor')) { if (!function_exists('interact_as_actor_id')) { /** * Sets the podcast id of which the user is acting as - * - * @return integer */ - function interact_as_actor_id() + function interact_as_actor_id(): int { $authenticate = Services::authentication(); $authenticate->check(); @@ -58,7 +53,7 @@ if (!function_exists('interact_as_actor')) { /** * Get the actor the user is currently interacting as * - * @return \ActivityPub\Entities\Actor|false + * @return Actor|false */ function interact_as_actor() { @@ -78,11 +73,10 @@ if (!function_exists('interact_as_actor')) { if (!function_exists('can_user_interact')) { /** - * @return bool * @throws DataException */ - function can_user_interact() + function can_user_interact(): bool { - return interact_as_actor() ? true : false; + return (bool) interact_as_actor(); } } diff --git a/app/Helpers/breadcrumb_helper.php b/app/Helpers/breadcrumb_helper.php index 03d9c86b..503ee077 100644 --- a/app/Helpers/breadcrumb_helper.php +++ b/app/Helpers/breadcrumb_helper.php @@ -8,20 +8,24 @@ use Config\Services; -/** - * Renders the breadcrumb navigation through the Breadcrumb service - * - * @param string $class to be added to the breadcrumb nav - * @return string html breadcrumb - */ -function render_breadcrumb($class = null) -{ - $breadcrumb = Services::breadcrumb(); - return $breadcrumb->render($class); +if (!function_exists('render_breadcrumb')) { + /** + * Renders the breadcrumb navigation through the Breadcrumb service + * + * @param string $class to be added to the breadcrumb nav + * @return string html breadcrumb + */ + function render_breadcrumb(string $class = null): string + { + $breadcrumb = Services::breadcrumb(); + return $breadcrumb->render($class); + } } -function replace_breadcrumb_params($newParams) -{ - $breadcrumb = Services::breadcrumb(); - $breadcrumb->replaceParams($newParams); +if (!function_exists('replace_breadcrumb_params')) { + function replace_breadcrumb_params($newParams): void + { + $breadcrumb = Services::breadcrumb(); + $breadcrumb->replaceParams($newParams); + } } diff --git a/app/Helpers/components_helper.php b/app/Helpers/components_helper.php index 7c85e951..6b8f96c4 100644 --- a/app/Helpers/components_helper.php +++ b/app/Helpers/components_helper.php @@ -6,24 +6,25 @@ * @link https://castopod.org/ */ +use CodeIgniter\View\Table; +use CodeIgniter\I18n\Time; + if (!function_exists('button')) { /** * Button component * * Creates a stylized button or button like anchor tag if the URL is defined. * - * @param string $label The button label - * @param mixed|null $uri URI string or array of URI segments - * @param array $customOptions button options: variant, size, iconLeft, iconRight - * @param array $customAttributes Additional attributes + * @param array $customOptions button options: variant, size, iconLeft, iconRight + * @param array $customAttributes Additional attributes * * @return string */ function button( string $label = '', - $uri = null, - $customOptions = [], - $customAttributes = [] + string $uri = '', + array $customOptions = [], + array $customAttributes = [] ): string { $defaultOptions = [ 'variant' => 'default', @@ -90,7 +91,7 @@ if (!function_exists('button')) { $label .= icon($options['iconRight'], 'ml-2'); } - if ($uri) { + if ($uri !== '') { return anchor( $uri, $label, @@ -111,8 +112,8 @@ if (!function_exists('button')) { ); return << - $label + HTML; } @@ -126,19 +127,19 @@ if (!function_exists('icon_button')) { * * Abstracts the `button()` helper to create a stylized icon button * - * @param string $label The button label - * @param mixed|null $uri URI string or array of URI segments - * @param array $customOptions button options: variant, size, iconLeft, iconRight - * @param array $customAttributes Additional attributes + * @param string $label The button label + * @param string $uri URI string or array of URI segments + * @param array $customOptions button options: variant, size, iconLeft, iconRight + * @param array $customAttributes Additional attributes * * @return string */ function icon_button( string $icon, string $title, - $uri = null, - $customOptions = [], - $customAttributes = [] + string $uri = '', + array $customOptions = [], + array $customAttributes = [] ): string { $defaultOptions = [ 'isSquared' => true, @@ -197,9 +198,9 @@ if (!function_exists('data_table')) { * * @return string */ - function data_table($columns, $data = [], ...$rest): string + function data_table(array $columns, array $data = [], ...$rest): string { - $table = new \CodeIgniter\View\Table(); + $table = new Table(); $template = [ 'table_open' => '', @@ -219,17 +220,17 @@ if (!function_exists('data_table')) { $tableHeaders = []; foreach ($columns as $column) { - array_push($tableHeaders, $column['header']); + $tableHeaders[] = $column['header']; } $table->setHeading($tableHeaders); - if ($dataCount = count($data)) { - for ($i = 0; $i < $dataCount; $i++) { + if (($dataCount = count($data)) !== 0) { + for ($i = 0; $i < $dataCount; ++$i) { $row = $data[$i]; $rowData = []; foreach ($columns as $column) { - array_push($rowData, $column['cell']($row, ...$rest)); + $rowData[] = $column['cell']($row, ...$rest); } $table->addRow($rowData); } @@ -251,38 +252,39 @@ if (!function_exists('publication_pill')) { * * Shows the stylized publication datetime in regards to current datetime. * - * @param \CodeIgniter\I18n\Time $publicationDate publication datetime of the episode + * @param Time $publicationDate publication datetime of the episode * @param boolean $isPublished whether or not the episode has been published * @param string $customClass css class to add to the component * * @return string */ function publication_pill( - $publicationDate, + ?Time $publicationDate, $publicationStatus, - $customClass = '' + string $customClass = '' ): string { + if ($publicationDate === null) { + return ''; + } + $class = $publicationStatus === 'published' ? 'text-pine-500 border-pine-500' : 'text-red-600 border-red-600'; - $transParam = []; - if ($publicationDate) { - $transParam = [ - '', - ]; - } + $langOptions = [ + '', + ]; $label = lang( 'Episode.publication_status.' . $publicationStatus, - $transParam, + $langOptions, ); return ' '_blank', - 'rel' => 'noreferrer noopener', - ], - ); + ): string { + if (empty($locationName)) { + return ''; } - return $link; + return anchor( + location_url($locationName, $locationGeo, $locationOsmid), + icon('map-pin', 'mr-2') . $locationName, + [ + 'class' => + 'inline-flex items-baseline hover:underline' . + (empty($class) ? '' : " {$class}"), + 'target' => '_blank', + 'rel' => 'noreferrer noopener', + ], + ); } } diff --git a/app/Helpers/form_helper.php b/app/Helpers/form_helper.php index 8e193cfb..afa928f2 100644 --- a/app/Helpers/form_helper.php +++ b/app/Helpers/form_helper.php @@ -1,4 +1,5 @@ $customExtra['class'], 'data-select-text' => lang('Common.forms.multiSelect.selectText'), 'data-loading-text' => lang('Common.forms.multiSelect.loadingText'), 'data-no-results-text' => lang( - 'Common.forms.multiSelect.noResultsText' + 'Common.forms.multiSelect.noResultsText', ), 'data-no-choices-text' => lang( - 'Common.forms.multiSelect.noChoicesText' + 'Common.forms.multiSelect.noChoicesText', ), 'data-max-item-text' => lang( - 'Common.forms.multiSelect.maxItemText' + 'Common.forms.multiSelect.maxItemText', ), ]; $extra = stringify_attributes(array_merge($defaultExtra, $customExtra)); diff --git a/app/Helpers/id3_helper.php b/app/Helpers/id3_helper.php index ab0a567c..06c2af7d 100644 --- a/app/Helpers/id3_helper.php +++ b/app/Helpers/id3_helper.php @@ -6,108 +6,112 @@ * @link https://castopod.org/ */ +use App\Entities\Episode; +use CodeIgniter\Files\File; use JamesHeinrich\GetID3\GetID3; use JamesHeinrich\GetID3\WriteTags; -/** - * Gets audio file metadata and ID3 info - * - * @param UploadedFile $file - * - * @return array - */ -function get_file_tags($file) -{ - $getID3 = new GetID3(); - $FileInfo = $getID3->analyze($file); +if (!function_exists('get_file_tags')) { + /** + * Gets audio file metadata and ID3 info + * + * @param UploadedFile $file + */ + function get_file_tags($file): array + { + $getID3 = new GetID3(); + $FileInfo = $getID3->analyze($file); - return [ - 'filesize' => $FileInfo['filesize'], - 'mime_type' => $FileInfo['mime_type'], - 'avdataoffset' => $FileInfo['avdataoffset'], - 'playtime_seconds' => $FileInfo['playtime_seconds'], - ]; -} - -/** - * Write audio file metadata / ID3 tags - * - * @param App\Entities\Episode $episode - * - * @return UploadedFile - */ -function write_audio_file_tags($episode) -{ - helper('media'); - - $TextEncoding = 'UTF-8'; - - // Initialize getID3 tag-writing module - $tagwriter = new WriteTags(); - $tagwriter->filename = media_path($episode->audio_file_path); - - // set various options (optional) - $tagwriter->tagformats = ['id3v2.4']; - $tagwriter->tag_encoding = $TextEncoding; - - $cover = new \CodeIgniter\Files\File($episode->image->id3_path); - - $APICdata = file_get_contents($cover->getRealPath()); - - // TODO: variables used for podcast specific tags - // $podcast_url = $episode->podcast->link; - // $podcast_feed_url = $episode->podcast->feed_url; - // $episode_media_url = $episode->link; - - // populate data array - $TagData = [ - 'title' => [$episode->title], - 'artist' => [ - empty($episode->podcast->publisher) - ? $episode->podcast->owner_name - : $episode->podcast->publisher, - ], - 'album' => [$episode->podcast->title], - 'year' => [ - $episode->published_at ? $episode->published_at->format('Y') : '', - ], - 'genre' => ['Podcast'], - 'comment' => [$episode->description], - 'track_number' => [strval($episode->number)], - 'copyright_message' => [$episode->podcast->copyright], - 'publisher' => [ - empty($episode->podcast->publisher) - ? $episode->podcast->owner_name - : $episode->podcast->publisher, - ], - 'encoded_by' => ['Castopod'], - - // TODO: find a way to add the remaining tags for podcasts as the library doesn't seem to allow it - // 'website' => [$podcast_url], - // 'podcast' => [], - // 'podcast_identifier' => [$episode_media_url], - // 'podcast_feed' => [$podcast_feed_url], - // 'podcast_description' => [$podcast->description_markdown], - ]; - - $TagData['attached_picture'][] = [ - 'picturetypeid' => 2, // Cover. More: module.tag.id3v2.php - 'data' => $APICdata, - 'description' => 'cover', - 'mime' => $cover->getMimeType(), - ]; - - $tagwriter->tag_data = $TagData; - - // write tags - if ($tagwriter->WriteTags()) { - echo 'Successfully wrote tags
'; - if (!empty($tagwriter->warnings)) { - echo 'There were some warnings:
' . - implode('

', $tagwriter->warnings); - } - } else { - echo 'Failed to write tags!
' . - implode('

', $tagwriter->errors); + return [ + 'filesize' => $FileInfo['filesize'], + 'mime_type' => $FileInfo['mime_type'], + 'avdataoffset' => $FileInfo['avdataoffset'], + 'playtime_seconds' => $FileInfo['playtime_seconds'], + ]; + } +} + +if (!function_exists('write_audio_file_tags')) { + /** + * Write audio file metadata / ID3 tags + * + * @return UploadedFile + */ + function write_audio_file_tags(Episode $episode): void + { + helper('media'); + + $TextEncoding = 'UTF-8'; + + // Initialize getID3 tag-writing module + $tagwriter = new WriteTags(); + $tagwriter->filename = media_path($episode->audio_file_path); + + // set various options (optional) + $tagwriter->tagformats = ['id3v2.4']; + $tagwriter->tag_encoding = $TextEncoding; + + $cover = new File($episode->image->id3_path); + + $APICdata = file_get_contents($cover->getRealPath()); + + // TODO: variables used for podcast specific tags + // $podcast_url = $episode->podcast->link; + // $podcast_feed_url = $episode->podcast->feed_url; + // $episode_media_url = $episode->link; + + // populate data array + $TagData = [ + 'title' => [$episode->title], + 'artist' => [ + empty($episode->podcast->publisher) + ? $episode->podcast->owner_name + : $episode->podcast->publisher, + ], + 'album' => [$episode->podcast->title], + 'year' => [ + $episode->published_at + ? $episode->published_at->format('Y') + : '', + ], + 'genre' => ['Podcast'], + 'comment' => [$episode->description], + 'track_number' => [strval($episode->number)], + 'copyright_message' => [$episode->podcast->copyright], + 'publisher' => [ + empty($episode->podcast->publisher) + ? $episode->podcast->owner_name + : $episode->podcast->publisher, + ], + 'encoded_by' => ['Castopod'], + + // TODO: find a way to add the remaining tags for podcasts as the library doesn't seem to allow it + // 'website' => [$podcast_url], + // 'podcast' => [], + // 'podcast_identifier' => [$episode_media_url], + // 'podcast_feed' => [$podcast_feed_url], + // 'podcast_description' => [$podcast->description_markdown], + ]; + + $TagData['attached_picture'][] = [ + 'picturetypeid' => 2, // Cover. More: module.tag.id3v2.php + 'data' => $APICdata, + 'description' => 'cover', + 'mime' => $cover->getMimeType(), + ]; + + $tagwriter->tag_data = $TagData; + + // write tags + if ($tagwriter->WriteTags()) { + echo 'Successfully wrote tags
'; + if (!empty($tagwriter->warnings)) { + echo 'There were some warnings:
' . + implode('

', $tagwriter->warnings); + } + } else { + echo 'Failed to write tags!
' . + implode('

', $tagwriter->errors); + } } } diff --git a/app/Helpers/location_helper.php b/app/Helpers/location_helper.php index 4b4207c3..11a0498d 100644 --- a/app/Helpers/location_helper.php +++ b/app/Helpers/location_helper.php @@ -6,47 +6,56 @@ * @link https://castopod.org/ */ -/** - * Fetches places from Nominatim OpenStreetMap - * - * @param string $locationName - * - * @return array|null - */ -function fetch_osm_location($locationName) -{ - $osmObject = null; - if (!empty($locationName)) { - try { - $client = \Config\Services::curlrequest(); +use Config\Services; - $response = $client->request( - 'GET', - 'https://nominatim.openstreetmap.org/search.php?q=' . - urlencode($locationName) . - '&polygon_geojson=1&format=jsonv2', - [ - 'headers' => [ - 'User-Agent' => 'Castopod/' . CP_VERSION, - 'Accept' => 'application/json', +if (!function_exists('fetch_osm_location')) { + /** + * Fetches places from Nominatim OpenStreetMap + * + * @return array|null + */ + function fetch_osm_location(string $locationName): ?array + { + $osmObject = null; + + if (!empty($locationName)) { + try { + $client = Services::curlrequest(); + + $response = $client->request( + 'GET', + 'https://nominatim.openstreetmap.org/search.php?q=' . + urlencode($locationName) . + '&polygon_geojson=1&format=jsonv2', + [ + 'headers' => [ + 'User-Agent' => 'Castopod/' . CP_VERSION, + 'Accept' => 'application/json', + ], ], - ] - ); - $places = json_decode($response->getBody(), true); - $osmObject = [ - 'geo' => - empty($places[0]['lat']) || empty($places[0]['lon']) + ); + $places = json_decode( + $response->getBody(), + true, + 512, + JSON_THROW_ON_ERROR, + ); + $osmObject = [ + 'geo' => + empty($places[0]['lat']) || empty($places[0]['lon']) + ? null + : "geo:{$places[0]['lat']},{$places[0]['lon']}", + 'osmid' => empty($places[0]['osm_type']) ? null - : "geo:{$places[0]['lat']},{$places[0]['lon']}", - 'osmid' => empty($places[0]['osm_type']) - ? null - : strtoupper(substr($places[0]['osm_type'], 0, 1)) . - $places[0]['osm_id'], - ]; - } catch (\Exception $e) { - //If things go wrong the show must go on - log_message('critical', $e); + : strtoupper(substr($places[0]['osm_type'], 0, 1)) . + $places[0]['osm_id'], + ]; + } catch (Exception $exception) { + //If things go wrong the show must go on + log_message('critical', $exception); + } } + + return $osmObject; } - return $osmObject; } diff --git a/app/Helpers/media_helper.php b/app/Helpers/media_helper.php index cd71e3e6..7074bcb7 100644 --- a/app/Helpers/media_helper.php +++ b/app/Helpers/media_helper.php @@ -8,125 +8,120 @@ use CodeIgniter\Files\File; use CodeIgniter\HTTP\ResponseInterface; +use CodeIgniter\HTTP\Files\UploadedFile; +use Config\Services; -/** - * Saves a file to the corresponding podcast folder in `public/media` - * - * @param \CodeIgniter\HTTP\Files\UploadedFile|\CodeIgniter\Files\File $filePath - * @param string $folder - * @param string $fileName - * - * @return string The episode's file path in media root - */ -function save_media($filePath, $folder, $mediaName) -{ - $fileName = $mediaName . '.' . $filePath->getExtension(); +if (!function_exists('save_media')) { + /** + * Saves a file to the corresponding podcast folder in `public/media` + * + * @param File|UploadedFile $filePath + */ + function save_media( + File $filePath, + string $folder, + string $mediaName + ): string { + $fileName = $mediaName . '.' . $filePath->getExtension(); - $mediaRoot = config('App')->mediaRoot . '/' . $folder; + $mediaRoot = config('App')->mediaRoot . '/' . $folder; - if (!file_exists($mediaRoot)) { - mkdir($mediaRoot, 0777, true); - touch($mediaRoot . '/index.html'); + if (!file_exists($mediaRoot)) { + mkdir($mediaRoot, 0777, true); + touch($mediaRoot . '/index.html'); + } + + // move to media folder and overwrite file if already existing + $filePath->move($mediaRoot . '/', $fileName, true); + + return $folder . '/' . $fileName; } - - // move to media folder and overwrite file if already existing - $filePath->move($mediaRoot . '/', $fileName, true); - - return $folder . '/' . $fileName; } -/** - * @param string $fileUrl - * @return File - */ -function download_file($fileUrl) -{ - $client = \Config\Services::curlrequest(); +if (!function_exists('download_file')) { + function download_file(string $fileUrl): File + { + $client = Services::curlrequest(); - $response = $client->get($fileUrl, [ - 'headers' => [ - 'User-Agent' => 'Castopod/' . CP_VERSION, - ], - ]); - - // redirect to new file location - $newFileUrl = $fileUrl; - while ( - in_array( - $response->getStatusCode(), - [ - ResponseInterface::HTTP_MOVED_PERMANENTLY, - ResponseInterface::HTTP_FOUND, - ResponseInterface::HTTP_SEE_OTHER, - ResponseInterface::HTTP_NOT_MODIFIED, - ResponseInterface::HTTP_TEMPORARY_REDIRECT, - ResponseInterface::HTTP_PERMANENT_REDIRECT, - ], - true, - ) - ) { - $newFileUrl = (string) trim( - $response->getHeader('location')->getValue(), - ); - $response = $client->get($newFileUrl, [ + $response = $client->get($fileUrl, [ 'headers' => [ 'User-Agent' => 'Castopod/' . CP_VERSION, ], - 'http_errors' => false, ]); + + // redirect to new file location + $newFileUrl = $fileUrl; + while ( + in_array( + $response->getStatusCode(), + [ + ResponseInterface::HTTP_MOVED_PERMANENTLY, + ResponseInterface::HTTP_FOUND, + ResponseInterface::HTTP_SEE_OTHER, + ResponseInterface::HTTP_NOT_MODIFIED, + ResponseInterface::HTTP_TEMPORARY_REDIRECT, + ResponseInterface::HTTP_PERMANENT_REDIRECT, + ], + true, + ) + ) { + $newFileUrl = trim($response->getHeader('location')->getValue()); + $response = $client->get($newFileUrl, [ + 'headers' => [ + 'User-Agent' => 'Castopod/' . CP_VERSION, + ], + 'http_errors' => false, + ]); + } + $tmpFilename = + time() . + '_' . + bin2hex(random_bytes(10)) . + '.' . + pathinfo(parse_url($newFileUrl, PHP_URL_PATH), PATHINFO_EXTENSION); + $tmpFilePath = WRITEPATH . 'uploads/' . $tmpFilename; + file_put_contents($tmpFilePath, $response->getBody()); + + return new File($tmpFilePath); } - $tmpFilename = - time() . - '_' . - bin2hex(random_bytes(10)) . - '.' . - pathinfo(parse_url($newFileUrl, PHP_URL_PATH), PATHINFO_EXTENSION); - $tmpFilePath = WRITEPATH . 'uploads/' . $tmpFilename; - file_put_contents($tmpFilePath, $response->getBody()); - - return new \CodeIgniter\Files\File($tmpFilePath); } +if (!function_exists('media_path')) { + /** + * Prefixes the root media path to a given uri + * + * @param string|array $uri URI string or array of URI segments + */ + function media_path($uri = ''): string + { + // convert segment array to string + if (is_array($uri)) { + $uri = implode('/', $uri); + } + $uri = trim($uri, '/'); -/** - * Prefixes the root media path to a given uri - * - * @param mixed $uri URI string or array of URI segments - * @return string - */ -function media_path($uri = ''): string -{ - // convert segment array to string - if (is_array($uri)) { - $uri = implode('/', $uri); + return config('App')->mediaRoot . '/' . $uri; } - $uri = trim($uri, '/'); - - return config('App')->mediaRoot . '/' . $uri; } -/** - * Return the media base URL to use in views - * - * @param mixed $uri URI string or array of URI segments - * @param string $protocol - * @return string - */ -function media_url($uri = '', string $protocol = null): string -{ - return base_url(config('App')->mediaRoot . '/' . $uri, $protocol); -} +if (!function_exists('media_base_url')) { + /** + * Return the media base URL to use in views + * + * @param string|array $uri URI string or array of URI segments + * @param string $protocol + */ + function media_base_url($uri = ''): string + { + // convert segment array to string + if (is_array($uri)) { + $uri = implode('/', $uri); + } + $uri = trim($uri, '/'); -function media_base_url($uri = '') -{ - // convert segment array to string - if (is_array($uri)) { - $uri = implode('/', $uri); + return rtrim(config('App')->mediaBaseURL, '/') . + '/' . + config('App')->mediaRoot . + '/' . + $uri; } - $uri = trim($uri, '/'); - - return rtrim(config('App')->mediaBaseURL, '/') . - '/' . - config('App')->mediaRoot . - '/' . - $uri; } diff --git a/app/Helpers/misc_helper.php b/app/Helpers/misc_helper.php index 92c16ffa..2eea0972 100644 --- a/app/Helpers/misc_helper.php +++ b/app/Helpers/misc_helper.php @@ -6,142 +6,140 @@ * @link https://castopod.org/ */ -/** - * Gets the browser default language using the request header key `HTTP_ACCEPT_LANGUAGE` - * - * @param mixed $http_accept_language - * - * @return string|null ISO 639-1 language code or null - */ -function get_browser_language($http_accept_language) -{ - $langs = explode(',', $http_accept_language); - if (!empty($langs)) { - return substr($langs[0], 0, 2); - } +if (!function_exists('get_browser_language')) { + /** + * Gets the browser default language using the request header key `HTTP_ACCEPT_LANGUAGE` + * + * @return string|null ISO 639-1 language code or null + */ + function get_browser_language(string $httpAcceptLanguage): ?string + { + $langs = explode(',', $httpAcceptLanguage); + if (!empty($langs)) { + return substr($langs[0], 0, 2); + } - return null; + return null; + } } -/** - * Check if a string starts with some characters - * - * @param string $string - * @param string $query - * - * @return bool - */ -function startsWith($string, $query) -{ - return substr($string, 0, strlen($query)) === $query; +if (!function_exists('startsWith')) { + /** + * Check if a string starts with some characters + */ + function startsWith(string $string, string $query): bool + { + return substr($string, 0, strlen($query)) === $query; + } } -function slugify($text) -{ - if (empty($text)) { - return 'n-a'; +if (!function_exists('slugify')) { + function slugify($text) + { + if (empty($text)) { + return 'n-a'; + } + + // replace non letter or digits by - + $text = preg_replace('~[^\pL\d]+~u', '-', $text); + + $unwanted_array = [ + 'Å ' => 'S', + 'Å”' => 's', + 'Đ' => 'Dj', + 'đ' => 'dj', + 'Ž' => 'Z', + 'ž' => 'z', + 'Č' => 'C', + 'č' => 'c', + 'Ć' => 'C', + 'ć' => 'c', + 'ƀ' => 'A', + 'Ɓ' => 'A', + 'Ƃ' => 'A', + 'ƃ' => 'A', + 'Ƅ' => 'A', + 'ƅ' => 'A', + 'Ɔ' => 'AE', + 'Ƈ' => 'C', + 'ƈ' => 'E', + 'Ɖ' => 'E', + 'Ê' => 'E', + 'Ƌ' => 'E', + 'Ì' => 'I', + 'ƍ' => 'I', + 'Ǝ' => 'I', + 'Ə' => 'I', + 'Ƒ' => 'N', + 'ƒ' => 'O', + 'Ɠ' => 'O', + 'Ɣ' => 'O', + 'ƕ' => 'O', + 'Ɩ' => 'O', + 'Ƙ' => 'O', + 'Œ' => 'OE', + 'ƙ' => 'U', + 'Ú' => 'U', + 'ƛ' => 'U', + 'Ü' => 'U', + 'Ɲ' => 'Y', + 'ƞ' => 'B', + 'ß' => 'Ss', + 'Ć ' => 'a', + 'Ć”' => 'a', + 'Ć¢' => 'a', + 'Ć£' => 'a', + 'Ƥ' => 'a', + 'Ć„' => 'a', + 'Ʀ' => 'ae', + 'Ƨ' => 'c', + 'ĆØ' => 'e', + 'Ć©' => 'e', + 'ĆŖ' => 'e', + 'Ć«' => 'e', + 'Ƭ' => 'i', + 'Ć­' => 'i', + 'Ć®' => 'i', + 'ĆÆ' => 'i', + 'ư' => 'o', + 'Ʊ' => 'n', + 'ò' => 'o', + 'ó' => 'o', + 'Ć“' => 'o', + 'Ƶ' => 'o', + 'ƶ' => 'o', + 'Ćø' => 'o', + 'œ' => 'OE', + 'ù' => 'u', + 'Ćŗ' => 'u', + 'Ć»' => 'u', + 'ý' => 'y', + 'þ' => 'b', + 'Ćæ' => 'y', + 'Ŕ' => 'R', + 'ŕ' => 'r', + '/' => '-', + ' ' => '-', + ]; + $text = strtr($text, $unwanted_array); + + // transliterate + $text = iconv('utf-8', 'us-ascii//TRANSLIT', $text); + + // remove unwanted characters + $text = preg_replace('~[^\\-\w]+~', '', $text); + + // trim + $text = trim($text, '-'); + + // remove duplicate - + $text = preg_replace('~-+~', '-', $text); + + // lowercase + $text = strtolower($text); + + return $text; } - - // replace non letter or digits by - - $text = preg_replace('~[^\pL\d]+~u', '-', $text); - - $unwanted_array = [ - 'Å ' => 'S', - 'Å”' => 's', - 'Đ' => 'Dj', - 'đ' => 'dj', - 'Ž' => 'Z', - 'ž' => 'z', - 'Č' => 'C', - 'č' => 'c', - 'Ć' => 'C', - 'ć' => 'c', - 'ƀ' => 'A', - 'Ɓ' => 'A', - 'Ƃ' => 'A', - 'ƃ' => 'A', - 'Ƅ' => 'A', - 'ƅ' => 'A', - 'Ɔ' => 'AE', - 'Ƈ' => 'C', - 'ƈ' => 'E', - 'Ɖ' => 'E', - 'Ê' => 'E', - 'Ƌ' => 'E', - 'Ì' => 'I', - 'ƍ' => 'I', - 'Ǝ' => 'I', - 'Ə' => 'I', - 'Ƒ' => 'N', - 'ƒ' => 'O', - 'Ɠ' => 'O', - 'Ɣ' => 'O', - 'ƕ' => 'O', - 'Ɩ' => 'O', - 'Ƙ' => 'O', - 'Œ' => 'OE', - 'ƙ' => 'U', - 'Ú' => 'U', - 'ƛ' => 'U', - 'Ü' => 'U', - 'Ɲ' => 'Y', - 'ƞ' => 'B', - 'ß' => 'Ss', - 'Ć ' => 'a', - 'Ć”' => 'a', - 'Ć¢' => 'a', - 'Ć£' => 'a', - 'Ƥ' => 'a', - 'Ć„' => 'a', - 'Ʀ' => 'ae', - 'Ƨ' => 'c', - 'ĆØ' => 'e', - 'Ć©' => 'e', - 'ĆŖ' => 'e', - 'Ć«' => 'e', - 'Ƭ' => 'i', - 'Ć­' => 'i', - 'Ć®' => 'i', - 'ĆÆ' => 'i', - 'ư' => 'o', - 'Ʊ' => 'n', - 'ò' => 'o', - 'ó' => 'o', - 'Ć“' => 'o', - 'Ƶ' => 'o', - 'ƶ' => 'o', - 'Ćø' => 'o', - 'œ' => 'OE', - 'ù' => 'u', - 'Ćŗ' => 'u', - 'Ć»' => 'u', - 'ý' => 'y', - 'ý' => 'y', - 'þ' => 'b', - 'Ćæ' => 'y', - 'Ŕ' => 'R', - 'ŕ' => 'r', - '/' => '-', - ' ' => '-', - ]; - $text = strtr($text, $unwanted_array); - - // transliterate - $text = iconv('utf-8', 'us-ascii//TRANSLIT', $text); - - // remove unwanted characters - $text = preg_replace('~[^-\w]+~', '', $text); - - // trim - $text = trim($text, '-'); - - // remove duplicate - - $text = preg_replace('~-+~', '-', $text); - - // lowercase - $text = strtolower($text); - - return $text; } //-------------------------------------------------------------------- @@ -151,11 +149,8 @@ if (!function_exists('format_duration')) { * Formats duration in seconds to an hh:mm:ss string * * @param int $seconds seconds to format - * @param string $separator - * - * @return string */ - function format_duration($seconds, $separator = ':') + function format_duration(int $seconds, string $separator = ':'): string { return sprintf( '%02d%s%02d%s%02d', @@ -163,7 +158,7 @@ if (!function_exists('format_duration')) { $separator, ($seconds / 60) % 60, $separator, - $seconds % 60 + $seconds % 60, ); } } diff --git a/app/Helpers/page_helper.php b/app/Helpers/page_helper.php index 5dda5b38..ebdf4a28 100644 --- a/app/Helpers/page_helper.php +++ b/app/Helpers/page_helper.php @@ -8,26 +8,27 @@ use App\Models\PageModel; -/** - * Returns instance pages as links inside nav tag - * - * @param string $class - * @return string html pages navigation - */ -function render_page_links($class = null) -{ - $pages = (new PageModel())->findAll(); - $links = anchor(route_to('home'), lang('Common.home'), [ - 'class' => 'px-2 underline hover:no-underline', - ]); - $links .= anchor(route_to('credits'), lang('Person.credits'), [ - 'class' => 'px-2 underline hover:no-underline', - ]); - foreach ($pages as $page) { - $links .= anchor($page->link, $page->title, [ +if (!function_exists('render_page_links')) { + /** + * Returns instance pages as links inside nav tag + * + * @return string html pages navigation + */ + function render_page_links(string $class = null): string + { + $pages = (new PageModel())->findAll(); + $links = anchor(route_to('home'), lang('Common.home'), [ 'class' => 'px-2 underline hover:no-underline', ]); - } + $links .= anchor(route_to('credits'), lang('Person.credits'), [ + 'class' => 'px-2 underline hover:no-underline', + ]); + foreach ($pages as $page) { + $links .= anchor($page->link, $page->title, [ + 'class' => 'px-2 underline hover:no-underline', + ]); + } - return ''; + return ''; + } } diff --git a/app/Helpers/persons_helper.php b/app/Helpers/persons_helper.php index 62dd6b12..29fb9461 100644 --- a/app/Helpers/persons_helper.php +++ b/app/Helpers/persons_helper.php @@ -6,45 +6,47 @@ * @link https://castopod.org/ */ -/** - * Fetches persons from an episode - * - * @param array $persons - * @param array &$personsArray - */ -function construct_person_array($persons, &$personsArray) -{ - foreach ($persons as $person) { - if (array_key_exists($person->person->id, $personsArray)) { - $personsArray[$person->person->id]['roles'] .= - empty($person->person_group) || empty($person->person_role) - ? '' - : (empty($personsArray[$person->person->id]['roles']) - ? '' - : ', ') . - lang( - 'PersonsTaxonomy.persons.' . - $person->person_group . - '.roles.' . - $person->person_role . - '.label', - ); - } else { - $personsArray[$person->person->id] = [ - 'full_name' => $person->person->full_name, - 'information_url' => $person->person->information_url, - 'thumbnail_url' => $person->person->image->thumbnail_url, - 'roles' => +if (!function_exists('construct_person_array')) { + /** + * Fetches persons from an episode + * + * @param array &$personsArray + */ + function construct_person_array(array $persons, &$personsArray): void + { + foreach ($persons as $person) { + if (array_key_exists($person->person->id, $personsArray)) { + $personsArray[$person->person->id]['roles'] .= empty($person->person_group) || empty($person->person_role) ? '' - : lang( - 'PersonsTaxonomy.persons.' . - $person->person_group . - '.roles.' . - $person->person_role . - '.label', - ), - ]; + : (empty($personsArray[$person->person->id]['roles']) + ? '' + : ', ') . + lang( + 'PersonsTaxonomy.persons.' . + $person->person_group . + '.roles.' . + $person->person_role . + '.label', + ); + } else { + $personsArray[$person->person->id] = [ + 'full_name' => $person->person->full_name, + 'information_url' => $person->person->information_url, + 'thumbnail_url' => $person->person->image->thumbnail_url, + 'roles' => + empty($person->person_group) || + empty($person->person_role) + ? '' + : lang( + 'PersonsTaxonomy.persons.' . + $person->person_group . + '.roles.' . + $person->person_role . + '.label', + ), + ]; + } } } } diff --git a/app/Helpers/rss_helper.php b/app/Helpers/rss_helper.php index a49562cc..314f30aa 100644 --- a/app/Helpers/rss_helper.php +++ b/app/Helpers/rss_helper.php @@ -9,517 +9,564 @@ use App\Libraries\SimpleRSSElement; use CodeIgniter\I18n\Time; use Config\Mimes; +use App\Entities\Podcast; +use App\Entities\Category; -/** - * Generates the rss feed for a given podcast entity - * - * @param App\Entities\Podcast $podcast - * @param string $service The name of the service that fetches the RSS feed for future reference when the audio file is eventually downloaded - * @return string rss feed as xml - */ -function get_rss_feed($podcast, $serviceSlug = '') -{ - $episodes = $podcast->episodes; +if (!function_exists('get_rss_feed')) { + /** + * Generates the rss feed for a given podcast entity + * + * @param string $service The name of the service that fetches the RSS feed for future reference when the audio file is eventually downloaded + * @return string rss feed as xml + */ + function get_rss_feed(Podcast $podcast, $serviceSlug = ''): string + { + $episodes = $podcast->episodes; - $itunes_namespace = 'http://www.itunes.com/dtds/podcast-1.0.dtd'; + $itunes_namespace = 'http://www.itunes.com/dtds/podcast-1.0.dtd'; - $podcast_namespace = - 'https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md'; + $podcast_namespace = + 'https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md'; - $rss = new SimpleRSSElement( - "", - ); - - $channel = $rss->addChild('channel'); - - $atom_link = $channel->addChild( - 'atom:link', - null, - 'http://www.w3.org/2005/Atom', - ); - $atom_link->addAttribute('href', $podcast->feed_url); - $atom_link->addAttribute('rel', 'self'); - $atom_link->addAttribute('type', 'application/rss+xml'); - - if (!empty($podcast->new_feed_url)) { - $channel->addChild( - 'new-feed-url', - $podcast->new_feed_url, - $itunes_namespace, + $rss = new SimpleRSSElement( + "", ); - } - // the last build date corresponds to the creation of the feed.xml cache - $channel->addChild( - 'lastBuildDate', - (new Time('now'))->format(DATE_RFC1123), - ); - $channel->addChild('generator', 'Castopod Host - https://castopod.org/'); - $channel->addChild('docs', 'https://cyber.harvard.edu/rss/rss.html'); + $channel = $rss->addChild('channel'); - $channel->addChild('title', $podcast->title); - $channel->addChildWithCDATA('description', $podcast->description_html); - $itunes_image = $channel->addChild('image', null, $itunes_namespace); - $itunes_image->addAttribute('href', $podcast->image->original_url); - $channel->addChild('language', $podcast->language_code); - if (!empty($podcast->location_name)) { - $locationElement = $channel->addChild( - 'location', - htmlspecialchars($podcast->location_name), - $podcast_namespace, - ); - if (!empty($podcast->location_geo)) { - $locationElement->addAttribute('geo', $podcast->location_geo); - } - if (!empty($podcast->location_osmid)) { - $locationElement->addAttribute('osm', $podcast->location_osmid); - } - } - if (!empty($podcast->payment_pointer)) { - $valueElement = $channel->addChild('value', null, $podcast_namespace); - $valueElement->addAttribute('type', 'webmonetization'); - $valueElement->addAttribute('method', ''); - $valueElement->addAttribute('suggested', ''); - $recipientElement = $valueElement->addChild( - 'valueRecipient', + $atom_link = $channel->addChild( + 'atom:link', null, - $podcast_namespace, + 'http://www.w3.org/2005/Atom', ); - $recipientElement->addAttribute('name', $podcast->owner_name); - $recipientElement->addAttribute('type', 'ILP'); - $recipientElement->addAttribute('address', $podcast->payment_pointer); - $recipientElement->addAttribute('split', 100); - } - $channel - ->addChild( - 'locked', - $podcast->is_locked ? 'yes' : 'no', - $podcast_namespace, - ) - ->addAttribute('owner', $podcast->owner_email); - if (!empty($podcast->imported_feed_url)) { + $atom_link->addAttribute('href', $podcast->feed_url); + $atom_link->addAttribute('rel', 'self'); + $atom_link->addAttribute('type', 'application/rss+xml'); + + if (!empty($podcast->new_feed_url)) { + $channel->addChild( + 'new-feed-url', + $podcast->new_feed_url, + $itunes_namespace, + ); + } + + // the last build date corresponds to the creation of the feed.xml cache $channel->addChild( - 'previousUrl', - $podcast->imported_feed_url, - $podcast_namespace, + 'lastBuildDate', + (new Time('now'))->format(DATE_RFC1123), ); - } - - foreach ($podcast->podcastingPlatforms as $podcastingPlatform) { - $podcastingPlatformElement = $channel->addChild( - 'id', - null, - $podcast_namespace, + $channel->addChild( + 'generator', + 'Castopod Host - https://castopod.org/', ); - $podcastingPlatformElement->addAttribute( - 'platform', - $podcastingPlatform->slug, - ); - if (!empty($podcastingPlatform->link_content)) { - $podcastingPlatformElement->addAttribute( - 'id', - $podcastingPlatform->link_content, - ); - } - if (!empty($podcastingPlatform->link_url)) { - $podcastingPlatformElement->addAttribute( - 'url', - htmlspecialchars($podcastingPlatform->link_url), - ); - } - } + $channel->addChild('docs', 'https://cyber.harvard.edu/rss/rss.html'); - foreach ($podcast->socialPlatforms as $socialPlatform) { - $socialPlatformElement = $channel->addChild( - 'social', - $socialPlatform->link_content, - $podcast_namespace, - ); - $socialPlatformElement->addAttribute('platform', $socialPlatform->slug); - if (!empty($socialPlatform->link_url)) { - $socialPlatformElement->addAttribute( - 'url', - htmlspecialchars($socialPlatform->link_url), - ); - } - } + $channel->addChild('title', $podcast->title); + $channel->addChildWithCDATA('description', $podcast->description_html); - foreach ($podcast->fundingPlatforms as $fundingPlatform) { - $fundingPlatformElement = $channel->addChild( - 'funding', - $fundingPlatform->link_content, - $podcast_namespace, - ); - $fundingPlatformElement->addAttribute( - 'platform', - $fundingPlatform->slug, - ); - if (!empty($socialPlatform->link_url)) { - $fundingPlatformElement->addAttribute( - 'url', - htmlspecialchars($fundingPlatform->link_url), - ); - } - } + $itunes_image = $channel->addChild('image', null, $itunes_namespace); - foreach ($podcast->persons as $podcastPerson) { - $podcastPersonElement = $channel->addChild( - 'person', - htmlspecialchars($podcastPerson->person->full_name), - $podcast_namespace, - ); - if ( - !empty($podcastPerson->person_role) && - !empty($podcastPerson->person_group) - ) { - $podcastPersonElement->addAttribute( - 'role', - htmlspecialchars( - lang( - "PersonsTaxonomy.persons.{$podcastPerson->person_group}.roles.{$podcastPerson->person_role}.label", - [], - 'en', - ), - ), - ); - } - if (!empty($podcastPerson->person_group)) { - $podcastPersonElement->addAttribute( - 'group', - htmlspecialchars( - lang( - "PersonsTaxonomy.persons.{$podcastPerson->person_group}.label", - [], - 'en', - ), - ), - ); - } - $podcastPersonElement->addAttribute( - 'img', - $podcastPerson->person->image->large_url, - ); - if (!empty($podcastPerson->person->information_url)) { - $podcastPersonElement->addAttribute( - 'href', - $podcastPerson->person->information_url, - ); - } - } + $itunes_image->addAttribute('href', $podcast->image->original_url); - // set main category first, then other categories as apple - add_category_tag($channel, $podcast->category); - foreach ($podcast->other_categories as $other_category) { - add_category_tag($channel, $other_category); - } - - $channel->addChild( - 'explicit', - $podcast->parental_advisory === 'explicit' ? 'true' : 'false', - $itunes_namespace, - ); - - $channel->addChild( - 'author', - $podcast->publisher ? $podcast->publisher : $podcast->owner_name, - $itunes_namespace, - ); - $channel->addChild('link', $podcast->link); - - $owner = $channel->addChild('owner', null, $itunes_namespace); - $owner->addChild('name', $podcast->owner_name, $itunes_namespace); - $owner->addChild('email', $podcast->owner_email, $itunes_namespace); - - $channel->addChild('type', $podcast->type, $itunes_namespace); - $podcast->copyright && $channel->addChild('copyright', $podcast->copyright); - $podcast->is_blocked && - $channel->addChild('block', 'Yes', $itunes_namespace); - $podcast->is_completed && - $channel->addChild('complete', 'Yes', $itunes_namespace); - - $image = $channel->addChild('image'); - $image->addChild('url', $podcast->image->feed_url); - $image->addChild('title', $podcast->title); - $image->addChild('link', $podcast->link); - - if (!empty($podcast->custom_rss)) { - array_to_rss( - [ - 'elements' => $podcast->custom_rss, - ], - $channel, - ); - } - - foreach ($episodes as $episode) { - $item = $channel->addChild('item'); - $item->addChild('title', $episode->title); - $enclosure = $item->addChild('enclosure'); - - $enclosure->addAttribute( - 'url', - $episode->audio_file_analytics_url . - (empty($serviceSlug) - ? '' - : '?_from=' . urlencode($serviceSlug)), - ); - $enclosure->addAttribute('length', $episode->audio_file_size); - $enclosure->addAttribute('type', $episode->audio_file_mimetype); - - $item->addChild('guid', $episode->guid); - $item->addChild( - 'pubDate', - $episode->published_at->format(DATE_RFC1123), - ); - if (!empty($episode->location_name)) { - $locationElement = $item->addChild( + $channel->addChild('language', $podcast->language_code); + if (!empty($podcast->location_name)) { + $locationElement = $channel->addChild( 'location', - htmlspecialchars($episode->location_name), + htmlspecialchars($podcast->location_name), $podcast_namespace, ); - if (!empty($episode->location_geo)) { - $locationElement->addAttribute('geo', $episode->location_geo); + if (!empty($podcast->location_geo)) { + $locationElement->addAttribute('geo', $podcast->location_geo); } - if (!empty($episode->location_osmid)) { - $locationElement->addAttribute('osm', $episode->location_osmid); + if (!empty($podcast->location_osmid)) { + $locationElement->addAttribute('osm', $podcast->location_osmid); } } - $item->addChildWithCDATA( - 'description', - $episode->getDescriptionHtml($serviceSlug), - ); - $item->addChild( - 'duration', - $episode->audio_file_duration, - $itunes_namespace, - ); - $item->addChild('link', $episode->link); - $episode_itunes_image = $item->addChild( - 'image', - null, - $itunes_namespace, - ); - $episode_itunes_image->addAttribute('href', $episode->image->feed_url); - - $episode->parental_advisory && - $item->addChild( - 'explicit', - $episode->parental_advisory === 'explicit' ? 'true' : 'false', - $itunes_namespace, - ); - - $episode->number && - $item->addChild('episode', $episode->number, $itunes_namespace); - $episode->season_number && - $item->addChild( - 'season', - $episode->season_number, - $itunes_namespace, - ); - $item->addChild('episodeType', $episode->type, $itunes_namespace); - - if ($episode->transcript_file_url) { - $transcriptElement = $item->addChild( - 'transcript', + if (!empty($podcast->payment_pointer)) { + $valueElement = $channel->addChild( + 'value', null, $podcast_namespace, ); - $transcriptElement->addAttribute( - 'url', - $episode->transcript_file_url, - ); - $transcriptElement->addAttribute( - 'type', - Mimes::guessTypeFromExtension( - pathinfo($episode->transcript_file_url, PATHINFO_EXTENSION), - ), - ); - $transcriptElement->addAttribute( - 'language', - $podcast->language_code, - ); - } - - if ($episode->chapters_file_url) { - $chaptersElement = $item->addChild( - 'chapters', + $valueElement->addAttribute('type', 'webmonetization'); + $valueElement->addAttribute('method', ''); + $valueElement->addAttribute('suggested', ''); + $recipientElement = $valueElement->addChild( + 'valueRecipient', null, $podcast_namespace, ); - $chaptersElement->addAttribute('url', $episode->chapters_file_url); - $chaptersElement->addAttribute('type', 'application/json+chapters'); + $recipientElement->addAttribute('name', $podcast->owner_name); + $recipientElement->addAttribute('type', 'ILP'); + $recipientElement->addAttribute( + 'address', + $podcast->payment_pointer, + ); + $recipientElement->addAttribute('split', 100); } - - foreach ($episode->soundbites as $soundbite) { - $soundbiteElement = $item->addChild( - 'soundbite', - empty($soundbite->label) ? null : $soundbite->label, + $channel + ->addChild( + 'locked', + $podcast->is_locked ? 'yes' : 'no', + $podcast_namespace, + ) + ->addAttribute('owner', $podcast->owner_email); + if (!empty($podcast->imported_feed_url)) { + $channel->addChild( + 'previousUrl', + $podcast->imported_feed_url, $podcast_namespace, ); - $soundbiteElement->addAttribute( - 'start_time', - $soundbite->start_time, - ); - $soundbiteElement->addAttribute('duration', $soundbite->duration); } - foreach ($episode->persons as $episodePerson) { - $episodePersonElement = $item->addChild( + foreach ($podcast->podcastingPlatforms as $podcastingPlatform) { + $podcastingPlatformElement = $channel->addChild( + 'id', + null, + $podcast_namespace, + ); + $podcastingPlatformElement->addAttribute( + 'platform', + $podcastingPlatform->slug, + ); + if (!empty($podcastingPlatform->link_content)) { + $podcastingPlatformElement->addAttribute( + 'id', + $podcastingPlatform->link_content, + ); + } + if (!empty($podcastingPlatform->link_url)) { + $podcastingPlatformElement->addAttribute( + 'url', + htmlspecialchars($podcastingPlatform->link_url), + ); + } + } + + foreach ($podcast->socialPlatforms as $socialPlatform) { + $socialPlatformElement = $channel->addChild( + 'social', + $socialPlatform->link_content, + $podcast_namespace, + ); + $socialPlatformElement->addAttribute( + 'platform', + $socialPlatform->slug, + ); + if (!empty($socialPlatform->link_url)) { + $socialPlatformElement->addAttribute( + 'url', + htmlspecialchars($socialPlatform->link_url), + ); + } + } + + foreach ($podcast->fundingPlatforms as $fundingPlatform) { + $fundingPlatformElement = $channel->addChild( + 'funding', + $fundingPlatform->link_content, + $podcast_namespace, + ); + $fundingPlatformElement->addAttribute( + 'platform', + $fundingPlatform->slug, + ); + if (!empty($socialPlatform->link_url)) { + $fundingPlatformElement->addAttribute( + 'url', + htmlspecialchars($fundingPlatform->link_url), + ); + } + } + + foreach ($podcast->persons as $podcastPerson) { + $podcastPersonElement = $channel->addChild( 'person', - htmlspecialchars($episodePerson->person->full_name), + htmlspecialchars($podcastPerson->person->full_name), $podcast_namespace, ); if ( - !empty($episodePerson->person_role) && - !empty($episodePerson->person_group) + !empty($podcastPerson->person_role) && + !empty($podcastPerson->person_group) ) { - $episodePersonElement->addAttribute( + $podcastPersonElement->addAttribute( 'role', htmlspecialchars( lang( - "PersonsTaxonomy.persons.{$episodePerson->person_group}.roles.{$episodePerson->person_role}.label", + "PersonsTaxonomy.persons.{$podcastPerson->person_group}.roles.{$podcastPerson->person_role}.label", [], 'en', ), ), ); } - if (!empty($episodePerson->person_group)) { - $episodePersonElement->addAttribute( + if (!empty($podcastPerson->person_group)) { + $podcastPersonElement->addAttribute( 'group', htmlspecialchars( lang( - "PersonsTaxonomy.persons.{$episodePerson->person_group}.label", + "PersonsTaxonomy.persons.{$podcastPerson->person_group}.label", [], 'en', ), ), ); } - $episodePersonElement->addAttribute( + $podcastPersonElement->addAttribute( 'img', - $episodePerson->person->image->large_url, + $podcastPerson->person->image->large_url, ); - if (!empty($episodePerson->person->information_url)) { - $episodePersonElement->addAttribute( + if (!empty($podcastPerson->person->information_url)) { + $podcastPersonElement->addAttribute( 'href', - $episodePerson->person->information_url, + $podcastPerson->person->information_url, ); } } - $episode->is_blocked && - $item->addChild('block', 'Yes', $itunes_namespace); - - if (!empty($episode->custom_rss)) { - array_to_rss( - [ - 'elements' => $episode->custom_rss, - ], - $item, - ); + // set main category first, then other categories as apple + add_category_tag($channel, $podcast->category); + foreach ($podcast->other_categories as $other_category) { + add_category_tag($channel, $other_category); } - } - return $rss->asXML(); -} - -/** - * Adds and tags to node for a given category - * - * @param \SimpleXMLElement $node - * @param \App\Entities\Category $category - * - * @return void - */ -function add_category_tag($node, $category) -{ - $itunes_namespace = 'http://www.itunes.com/dtds/podcast-1.0.dtd'; - - $itunes_category = $node->addChild('category', null, $itunes_namespace); - $itunes_category->addAttribute( - 'text', - $category->parent - ? $category->parent->apple_category - : $category->apple_category, - ); - - if ($category->parent) { - $itunes_category_child = $itunes_category->addChild( - 'category', - null, + $channel->addChild( + 'explicit', + $podcast->parental_advisory === 'explicit' ? 'true' : 'false', $itunes_namespace, ); - $itunes_category_child->addAttribute('text', $category->apple_category); - $node->addChild('category', $category->parent->apple_category); + + $channel->addChild( + 'author', + $podcast->publisher ? $podcast->publisher : $podcast->owner_name, + $itunes_namespace, + ); + $channel->addChild('link', $podcast->link); + + $owner = $channel->addChild('owner', null, $itunes_namespace); + + $owner->addChild('name', $podcast->owner_name, $itunes_namespace); + + $owner->addChild('email', $podcast->owner_email, $itunes_namespace); + + $channel->addChild('type', $podcast->type, $itunes_namespace); + $podcast->copyright && + $channel->addChild('copyright', $podcast->copyright); + $podcast->is_blocked && + $channel->addChild('block', 'Yes', $itunes_namespace); + $podcast->is_completed && + $channel->addChild('complete', 'Yes', $itunes_namespace); + + $image = $channel->addChild('image'); + $image->addChild('url', $podcast->image->feed_url); + $image->addChild('title', $podcast->title); + $image->addChild('link', $podcast->link); + + if (!empty($podcast->custom_rss)) { + array_to_rss( + [ + 'elements' => $podcast->custom_rss, + ], + $channel, + ); + } + + foreach ($episodes as $episode) { + $item = $channel->addChild('item'); + $item->addChild('title', $episode->title); + $enclosure = $item->addChild('enclosure'); + + $enclosure->addAttribute( + 'url', + $episode->audio_file_analytics_url . + (empty($serviceSlug) + ? '' + : '?_from=' . urlencode($serviceSlug)), + ); + $enclosure->addAttribute('length', $episode->audio_file_size); + $enclosure->addAttribute('type', $episode->audio_file_mimetype); + + $item->addChild('guid', $episode->guid); + $item->addChild( + 'pubDate', + $episode->published_at->format(DATE_RFC1123), + ); + if (!empty($episode->location_name)) { + $locationElement = $item->addChild( + 'location', + htmlspecialchars($episode->location_name), + $podcast_namespace, + ); + if (!empty($episode->location_geo)) { + $locationElement->addAttribute( + 'geo', + $episode->location_geo, + ); + } + if (!empty($episode->location_osmid)) { + $locationElement->addAttribute( + 'osm', + $episode->location_osmid, + ); + } + } + $item->addChildWithCDATA( + 'description', + $episode->getDescriptionHtml($serviceSlug), + ); + $item->addChild( + 'duration', + $episode->audio_file_duration, + $itunes_namespace, + ); + $item->addChild('link', $episode->link); + $episode_itunes_image = $item->addChild( + 'image', + null, + $itunes_namespace, + ); + $episode_itunes_image->addAttribute( + 'href', + $episode->image->feed_url, + ); + + $episode->parental_advisory && + $item->addChild( + 'explicit', + $episode->parental_advisory === 'explicit' + ? 'true' + : 'false', + $itunes_namespace, + ); + + $episode->number && + $item->addChild('episode', $episode->number, $itunes_namespace); + $episode->season_number && + $item->addChild( + 'season', + $episode->season_number, + $itunes_namespace, + ); + $item->addChild('episodeType', $episode->type, $itunes_namespace); + + if ($episode->transcript_file_url) { + $transcriptElement = $item->addChild( + 'transcript', + null, + $podcast_namespace, + ); + $transcriptElement->addAttribute( + 'url', + $episode->transcript_file_url, + ); + $transcriptElement->addAttribute( + 'type', + Mimes::guessTypeFromExtension( + pathinfo( + $episode->transcript_file_url, + PATHINFO_EXTENSION, + ), + ), + ); + $transcriptElement->addAttribute( + 'language', + $podcast->language_code, + ); + } + + if ($episode->chapters_file_url) { + $chaptersElement = $item->addChild( + 'chapters', + null, + $podcast_namespace, + ); + $chaptersElement->addAttribute( + 'url', + $episode->chapters_file_url, + ); + $chaptersElement->addAttribute( + 'type', + 'application/json+chapters', + ); + } + + foreach ($episode->soundbites as $soundbite) { + $soundbiteElement = $item->addChild( + 'soundbite', + empty($soundbite->label) ? null : $soundbite->label, + $podcast_namespace, + ); + $soundbiteElement->addAttribute( + 'start_time', + $soundbite->start_time, + ); + $soundbiteElement->addAttribute( + 'duration', + $soundbite->duration, + ); + } + + foreach ($episode->persons as $episodePerson) { + $episodePersonElement = $item->addChild( + 'person', + htmlspecialchars($episodePerson->person->full_name), + $podcast_namespace, + ); + if ( + !empty($episodePerson->person_role) && + !empty($episodePerson->person_group) + ) { + $episodePersonElement->addAttribute( + 'role', + htmlspecialchars( + lang( + "PersonsTaxonomy.persons.{$episodePerson->person_group}.roles.{$episodePerson->person_role}.label", + [], + 'en', + ), + ), + ); + } + if (!empty($episodePerson->person_group)) { + $episodePersonElement->addAttribute( + 'group', + htmlspecialchars( + lang( + "PersonsTaxonomy.persons.{$episodePerson->person_group}.label", + [], + 'en', + ), + ), + ); + } + $episodePersonElement->addAttribute( + 'img', + $episodePerson->person->image->large_url, + ); + if (!empty($episodePerson->person->information_url)) { + $episodePersonElement->addAttribute( + 'href', + $episodePerson->person->information_url, + ); + } + } + + $episode->is_blocked && + $item->addChild('block', 'Yes', $itunes_namespace); + + if (!empty($episode->custom_rss)) { + array_to_rss( + [ + 'elements' => $episode->custom_rss, + ], + $item, + ); + } + } + + return $rss->asXML(); } - $node->addChild('category', $category->apple_category); } -/** - * Converts XML to array - * - * @param \SimpleRSSElement $xmlNode - * - * @return array - */ -function rss_to_array($xmlNode) -{ - $nameSpaces = [ - '', - 'http://www.itunes.com/dtds/podcast-1.0.dtd', - 'https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md', - ]; - $arrayNode = []; - $arrayNode['name'] = $xmlNode->getName(); - $arrayNode['namespace'] = $xmlNode->getNamespaces(false); - if (count($xmlNode->attributes()) > 0) { +if (!function_exists('add_category_tag')) { + /** + * Adds and tags to node for a given category + */ + function add_category_tag(SimpleXMLElement $node, Category $category): void + { + $itunes_namespace = 'http://www.itunes.com/dtds/podcast-1.0.dtd'; + + $itunes_category = $node->addChild('category', null, $itunes_namespace); + $itunes_category->addAttribute( + 'text', + $category->parent !== null + ? $category->parent->apple_category + : $category->apple_category, + ); + + if ($category->parent !== null) { + $itunes_category_child = $itunes_category->addChild( + 'category', + null, + $itunes_namespace, + ); + $itunes_category_child->addAttribute( + 'text', + $category->apple_category, + ); + $node->addChild('category', $category->parent->apple_category); + } + $node->addChild('category', $category->apple_category); + } +} + +if (!function_exists('rss_to_array')) { + /** + * Converts XML to array + * + * FIXME: should be SimpleRSSElement + * @param SimpleXMLElement $xmlNode + */ + function rss_to_array(SimpleXMLElement $xmlNode): array + { + $nameSpaces = [ + '', + 'http://www.itunes.com/dtds/podcast-1.0.dtd', + 'https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md', + ]; + $arrayNode = []; + $arrayNode['name'] = $xmlNode->getName(); + $arrayNode['namespace'] = $xmlNode->getNamespaces(false); foreach ($xmlNode->attributes() as $key => $value) { $arrayNode['attributes'][$key] = (string) $value; } - } - $textcontent = trim((string) $xmlNode); - if (strlen($textcontent) > 0) { - $arrayNode['content'] = $textcontent; - } - foreach ($nameSpaces as $currentNameSpace) { - foreach ($xmlNode->children($currentNameSpace) as $childXmlNode) { - $arrayNode['elements'][] = rss_to_array($childXmlNode); + $textcontent = trim((string) $xmlNode); + if (strlen($textcontent) > 0) { + $arrayNode['content'] = $textcontent; } + foreach ($nameSpaces as $currentNameSpace) { + foreach ($xmlNode->children($currentNameSpace) as $childXmlNode) { + $arrayNode['elements'][] = rss_to_array($childXmlNode); + } + } + + return $arrayNode; } - return $arrayNode; } -/** - * Inserts array (converted to XML node) in XML node - * - * @param array $arrayNode - * @param \SimpleRSSElement $xmlNode The XML parent node where this arrayNode should be attached - * - */ -function array_to_rss($arrayNode, &$xmlNode) -{ - if (array_key_exists('elements', $arrayNode)) { - foreach ($arrayNode['elements'] as $childArrayNode) { - $childXmlNode = $xmlNode->addChild( - $childArrayNode['name'], - array_key_exists('content', $childArrayNode) - ? $childArrayNode['content'] - : null, - empty($childArrayNode['namespace']) - ? null - : current($childArrayNode['namespace']), - ); - if (array_key_exists('attributes', $childArrayNode)) { - foreach ( - $childArrayNode['attributes'] - as $attributeKey => $attributeValue - ) { - $childXmlNode->addAttribute($attributeKey, $attributeValue); +if (!function_exists('array_to_rss')) { + /** + * Inserts array (converted to XML node) in XML node + * + * @param SimpleRSSElement $xmlNode The XML parent node where this arrayNode should be attached + */ + function array_to_rss(array $arrayNode, SimpleRSSElement &$xmlNode) + { + if (array_key_exists('elements', $arrayNode)) { + foreach ($arrayNode['elements'] as $childArrayNode) { + $childXmlNode = $xmlNode->addChild( + $childArrayNode['name'], + $childArrayNode['content'] ?? null, + empty($childArrayNode['namespace']) + ? null + : current($childArrayNode['namespace']), + ); + if (array_key_exists('attributes', $childArrayNode)) { + foreach ( + $childArrayNode['attributes'] + as $attributeKey => $attributeValue + ) { + $childXmlNode->addAttribute( + $attributeKey, + $attributeValue, + ); + } } + array_to_rss($childArrayNode, $childXmlNode); } - array_to_rss($childArrayNode, $childXmlNode); } + + return $xmlNode; } - return $xmlNode; } diff --git a/app/Helpers/svg_helper.php b/app/Helpers/svg_helper.php index 56e36ccf..b6120169 100644 --- a/app/Helpers/svg_helper.php +++ b/app/Helpers/svg_helper.php @@ -6,43 +6,47 @@ * @link https://castopod.org/ */ -/** - * Returns the inline svg icon - * - * @param string $name name of the icon file without the .svg extension - * @param string $class to be added to the svg string - * @return string svg contents - */ -function icon(string $name, string $class = '') -{ - $svg_contents = file_get_contents('assets/icons/' . $name . '.svg'); - if ($class !== '') { - $svg_contents = str_replace( - ' 'node', 'W' => 'way', 'R' => 'relation'][ substr($locationOsmid, 0, 1) ] . '/' . substr($locationOsmid, 1); - } elseif (!empty($locationGeo)) { - $uri = - 'https://www.openstreetmap.org/#map=17/' . + } + if (!empty($locationGeo)) { + return 'https://www.openstreetmap.org/#map=17/' . str_replace(',', '/', substr($locationGeo, 4)); - } elseif (!empty($locationName)) { - $uri = - 'https://www.openstreetmap.org/search?query=' . - urlencode($locationName); } - return $uri; + return 'https://www.openstreetmap.org/search?query=' . + urlencode($locationName); } } //-------------------------------------------------------------------- @@ -81,27 +76,29 @@ if (!function_exists('extract_params_from_episode_uri')) { * Returns podcast name and episode slug from episode string uri * * @param URI $episodeUri - * @return string|null */ - function extract_params_from_episode_uri($episodeUri) + function extract_params_from_episode_uri($episodeUri): ?array { preg_match( - '/@(?P[a-zA-Z0-9\_]{1,32})\/episodes\/(?P[a-zA-Z0-9\-]{1,191})/', + '~@(?P[a-zA-Z0-9\_]{1,32})\/episodes\/(?P[a-zA-Z0-9\-]{1,191})~', $episodeUri->getPath(), $matches, ); - if ( - $matches && - array_key_exists('podcastName', $matches) && - array_key_exists('episodeSlug', $matches) - ) { - return [ - 'podcastName' => $matches['podcastName'], - 'episodeSlug' => $matches['episodeSlug'], - ]; + if ($matches === []) { + return null; } - return null; + if ( + !array_key_exists('podcastName', $matches) || + !array_key_exists('episodeSlug', $matches) + ) { + return null; + } + + return [ + 'podcastName' => $matches['podcastName'], + 'episodeSlug' => $matches['episodeSlug'], + ]; } } diff --git a/app/Language/en/ActivityPub.php b/app/Language/en/ActivityPub.php index 38b9573d..62a78d81 100644 --- a/app/Language/en/ActivityPub.php +++ b/app/Language/en/ActivityPub.php @@ -17,17 +17,17 @@ return [ 'submit' => 'Proceed to follow', ], 'favourite' => [ - 'title' => 'Favourite {actorDisplayName}\'s note', + 'title' => "Favourite {actorDisplayName}'s note", 'subtitle' => 'You are going to favourite:', 'submit' => 'Proceed to favourite', ], 'reblog' => [ - 'title' => 'Share {actorDisplayName}\'s note', + 'title' => "Share {actorDisplayName}'s note", 'subtitle' => 'You are going to share:', 'submit' => 'Proceed to share', ], 'reply' => [ - 'title' => 'Reply to {actorDisplayName}\'s note', + 'title' => "Reply to {actorDisplayName}'s note", 'subtitle' => 'You are going to reply to:', 'submit' => 'Proceed to reply', ], diff --git a/app/Language/en/Contributor.php b/app/Language/en/Contributor.php index 540dc571..4021ad9d 100644 --- a/app/Language/en/Contributor.php +++ b/app/Language/en/Contributor.php @@ -8,7 +8,7 @@ return [ 'podcast_contributors' => 'Podcast contributors', - 'view' => '{username}\'s contribution to {podcastName}', + 'view' => "{username}'s contribution to {podcastName}", 'add' => 'Add contributor', 'add_contributor' => 'Add a contributor for {0}', 'edit_role' => 'Update role for {0}', @@ -28,10 +28,10 @@ return [ 'podcast_admin' => 'Podcast admin', ], 'messages' => [ - 'removeOwnerContributorError' => 'You can\'t remove the podcast owner!', + 'removeOwnerContributorError' => "You can't remove the podcast owner!", 'removeContributorSuccess' => 'You have successfully removed {username} from {podcastTitle}', 'alreadyAddedError' => - 'The contributor you\'re trying to add has already been added!', + "The contributor you're trying to add has already been added!", ], ]; diff --git a/app/Language/en/Countries.php b/app/Language/en/Countries.php index ba399019..8e915b5d 100644 --- a/app/Language/en/Countries.php +++ b/app/Language/en/Countries.php @@ -51,7 +51,7 @@ return [ 'CF' => 'Central African Republic', 'CG' => 'Congo', 'CH' => 'Switzerland', - 'CI' => 'CĆ“te d\'Ivoire', + 'CI' => "CĆ“te d'Ivoire", 'CK' => 'Cook Islands', 'CL' => 'Chile', 'CM' => 'Cameroon', @@ -128,12 +128,12 @@ return [ 'KI' => 'Kiribati', 'KM' => 'Comoros', 'KN' => 'Saint Kitts and Nevis', - 'KP' => 'Korea, Democratic People\'s Republic of', + 'KP' => "Korea, Democratic People's Republic of", 'KR' => 'Korea, Republic of', 'KW' => 'Kuwait', 'KY' => 'Cayman Islands', 'KZ' => 'Kazakhstan', - 'LA' => 'Lao People\'s Democratic Republic', + 'LA' => "Lao People's Democratic Republic", 'LB' => 'Lebanon', 'LC' => 'Saint Lucia', 'LI' => 'Liechtenstein', diff --git a/app/Language/en/Episode.php b/app/Language/en/Episode.php index 5eaa06ca..44a817fb 100644 --- a/app/Language/en/Episode.php +++ b/app/Language/en/Episode.php @@ -127,7 +127,7 @@ return [ ], 'unpublish_form' => [ 'disclaimer' => - 'Unpublishing the episode will delete all the notes associated with the episode and remove it from the podcast\'s RSS feed.', + "Unpublishing the episode will delete all the notes associated with the episode and remove it from the podcast's RSS feed.", 'understand' => 'I understand, I want to unpublish the episode', 'submit' => 'Unpublish', ], diff --git a/app/Language/en/Install.php b/app/Language/en/Install.php index b4428f8d..df78956d 100644 --- a/app/Language/en/Install.php +++ b/app/Language/en/Install.php @@ -31,7 +31,7 @@ return [ 'db_password' => 'Database password', 'db_prefix' => 'Database prefix', 'db_prefix_hint' => - 'The prefix of the Castopod table names, leave as is if you don\'t know what it means.', + "The prefix of the Castopod table names, leave as is if you don't know what it means.", 'cache_config' => 'Cache configuration', 'cache_config_hint' => 'Choose your preferred cache handler. Leave it as the default value if you have no clue what it means.', @@ -54,6 +54,6 @@ return [ 'databaseConnectError' => 'Castopod could not connect to your database. Edit your database configuration and try again.', 'writeError' => - 'Couldn\'t create/write the `.env` file. You must create it manually by following the `.env.example` file template in the Castopod package.', + "Couldn't create/write the `.env` file. You must create it manually by following the `.env.example` file template in the Castopod package.", ], ]; diff --git a/app/Language/en/MyAccount.php b/app/Language/en/MyAccount.php index b9b001d8..ccdced98 100644 --- a/app/Language/en/MyAccount.php +++ b/app/Language/en/MyAccount.php @@ -10,8 +10,7 @@ return [ 'info' => 'My account info', 'changePassword' => 'Change my password', 'messages' => [ - 'wrongPasswordError' => - 'You\'ve entered the wrong password, try again.', + 'wrongPasswordError' => "You've entered the wrong password, try again.", 'passwordChangeSuccess' => 'Password has been successfully changed!', ], ]; diff --git a/app/Language/en/Note.php b/app/Language/en/Note.php index ba759032..7c4daaa8 100644 --- a/app/Language/en/Note.php +++ b/app/Language/en/Note.php @@ -7,7 +7,7 @@ */ return [ - 'title' => '{actorDisplayName}\'s Note', + 'title' => "{actorDisplayName}'s Note", 'back_to_actor_notes' => 'Back to {actor} notes', 'actor_shared' => '{actor} shared', 'reply_to' => 'Reply to @{actorUsername}', diff --git a/app/Language/en/User.php b/app/Language/en/User.php index 3c84d274..493b3fe6 100644 --- a/app/Language/en/User.php +++ b/app/Language/en/User.php @@ -7,13 +7,13 @@ */ return [ - 'edit_roles' => 'Edit {username}\'s roles', + 'edit_roles' => "Edit {username}'s roles", 'forcePassReset' => 'Force pass reset', 'ban' => 'Ban', 'unban' => 'Unban', 'delete' => 'Delete', 'create' => 'New user', - 'view' => '{username}\'s info', + 'view' => "{username}'s info", 'all_users' => 'All users', 'list' => [ 'user' => 'User', @@ -38,7 +38,7 @@ return [ 'createSuccess' => 'User created successfully! {username} will be prompted with a password reset upon first authentication.', 'rolesEditSuccess' => - '{username}\'s roles have been successfully updated.', + "{username}'s roles have been successfully updated.", 'forcePassResetSuccess' => '{username} will be prompted with a password reset upon next visit.', 'banSuccess' => '{username} has been banned.', diff --git a/app/Language/fr/Countries.php b/app/Language/fr/Countries.php index 48b36541..f37fbb0a 100644 --- a/app/Language/fr/Countries.php +++ b/app/Language/fr/Countries.php @@ -116,7 +116,7 @@ return [ 'VI ' => 'Ǝles Vierges Des Ɖtats-Unis', 'IN ' => 'Inde', 'ID ' => 'IndonĆ©sie', - 'IR ' => 'Iran, RĆ©publique Islamique D\'', + 'IR ' => "Iran, RĆ©publique Islamique D'", 'IQ ' => 'Iraq', 'IE ' => 'Irlande', 'IS ' => 'Islande', @@ -176,7 +176,7 @@ return [ 'NO ' => 'NorvĆØge', 'NC ' => 'Nouvelle-CalĆ©donie', 'NZ ' => 'Nouvelle-ZĆ©lande', - 'IO ' => 'OcĆ©an Indien, Territoire Britannique De L\'', + 'IO ' => "OcĆ©an Indien, Territoire Britannique De L'", 'OM ' => 'Oman', 'UG ' => 'Ouganda', 'UZ ' => 'OuzbĆ©kistan', diff --git a/app/Language/fr/Episode.php b/app/Language/fr/Episode.php index df4a5d90..39e860e5 100644 --- a/app/Language/fr/Episode.php +++ b/app/Language/fr/Episode.php @@ -112,17 +112,14 @@ return [ 'submit_create' => 'CrĆ©er l’épisode', 'submit_edit' => 'Enregistrer l’épisode', ], - 'publish_form' => [ - 'publication_date' => 'Date de publication', - 'publication_date_clear' => 'Effacer la date de publication', - 'publication_date_hint' => - 'Vous pouvez planifier la sortie de l’épisode en saisissant une date de publication future. Ce champ doit ĆŖtre au format YYYY-MM-DD HH:mm', - ], 'publish_form' => [ 'note' => 'Votre note', 'note_hint' => 'Le message que vous Ć©crirez sera diffusĆ© Ć  toutes les personnes qui vous suivent dans le fĆ©diverse.', 'publication_date' => 'Date de publication', + 'publication_date_clear' => 'Effacer la date de publication', + 'publication_date_hint' => + 'Vous pouvez planifier la sortie de l’épisode en saisissant une date de publication future. Ce champ doit ĆŖtre au format YYYY-MM-DD HH:mm', 'publication_method' => [ 'now' => 'Maintenant', 'schedule' => 'Planifier', diff --git a/app/Language/fr/PodcastNavigation.php b/app/Language/fr/PodcastNavigation.php index 77bf5041..cc8bea64 100644 --- a/app/Language/fr/PodcastNavigation.php +++ b/app/Language/fr/PodcastNavigation.php @@ -26,7 +26,6 @@ return [ 'platforms-podcasting' => 'Podcasts', 'platforms-social' => 'RĆ©seaux Sociaux', 'platforms-funding' => 'Financement', - 'platforms' => 'Plateformes du podcast', 'podcast-analytics' => 'Vue d’ensemble', 'podcast-analytics-webpages' => 'Visites des pages web', 'podcast-analytics-locations' => 'Localisations', diff --git a/app/Libraries/ActivityPub/ActivityRequest.php b/app/Libraries/ActivityPub/ActivityRequest.php index 3386dc77..9f8925a7 100644 --- a/app/Libraries/ActivityPub/ActivityRequest.php +++ b/app/Libraries/ActivityPub/ActivityRequest.php @@ -8,23 +8,28 @@ namespace ActivityPub; +use CodeIgniter\HTTP\ResponseInterface; +use CodeIgniter\HTTP\CURLRequest; +use CodeIgniter\HTTP\URI; +use ActivityPub\Core\Activity; +use Config\Services; use CodeIgniter\I18n\Time; use phpseclib\Crypt\RSA; class ActivityRequest { /** - * @var \CodeIgniter\HTTP\CURLRequest + * @var CURLRequest */ protected $request; /** - * @var \CodeIgniter\HTTP\URI + * @var URI */ protected $uri; /** - * @var \ActivityPub\Core\Activity|null + * @var Activity|null */ protected $activity; @@ -44,33 +49,33 @@ class ActivityRequest */ public function __construct($uri, $activityPayload = null) { - $this->request = \Config\Services::curlrequest(); + $this->request = Services::curlrequest(); if ($activityPayload) { $this->request->setBody($activityPayload); } - $this->uri = new \CodeIgniter\HTTP\URI($uri); + $this->uri = new URI($uri); } - public function post() + public function post(): void { // send Message to Fediverse instance $this->request->post($this->uri, $this->options); } - public function get() + public function get(): ResponseInterface { return $this->request->get($this->uri, $this->options); } - public function getDomain() + public function getDomain(): string { return $this->uri->getHost() . ($this->uri->getPort() ? ':' . $this->uri->getPort() : ''); } - public function sign($keyId, $privateKey) + public function sign($keyId, $privateKey): void { $rsa = new RSA(); $rsa->loadKey($privateKey); // private key @@ -79,7 +84,7 @@ class ActivityRequest $path = $this->uri->getPath() . - ($this->uri->getQuery() ? "?{$this->uri->getQuery()}" : ''); + ($this->uri->getQuery() !== '' ? "?{$this->uri->getQuery()}" : ''); $host = $this->uri->getHost(); $date = Time::now('GMT')->format('D, d M Y H:i:s T'); $digest = 'SHA-256=' . base64_encode($this->getBodyDigest()); @@ -87,7 +92,7 @@ class ActivityRequest $contentLength = strval(strlen($this->request->getBody())); $userAgent = 'Castopod'; - $plainText = "(request-target): post $path\nhost: $host\ndate: $date\ndigest: $digest\ncontent-type: $contentType\ncontent-length: $contentLength\nuser-agent: $userAgent"; + $plainText = "(request-target): post {$path}\nhost: {$host}\ndate: {$date}\ndigest: {$digest}\ncontent-type: {$contentType}\ncontent-length: {$contentLength}\nuser-agent: {$userAgent}"; $signature = $rsa->sign($plainText); @@ -102,7 +107,7 @@ class ActivityRequest 'headers' => [ 'Content-Type' => $contentType, 'Content-Length' => $contentLength, - 'Authorization' => "Signature $signatureHeader", + 'Authorization' => "Signature {$signatureHeader}", 'Signature' => $signatureHeader, 'Host' => $host, 'Date' => $date, @@ -112,7 +117,7 @@ class ActivityRequest ]; } - protected function getBodyDigest() + protected function getBodyDigest(): string { return hash('sha256', $this->request->getBody(), true); } diff --git a/app/Libraries/ActivityPub/Config/ActivityPub.php b/app/Libraries/ActivityPub/Config/ActivityPub.php index e96460d4..3ac4e4fc 100644 --- a/app/Libraries/ActivityPub/Config/ActivityPub.php +++ b/app/Libraries/ActivityPub/Config/ActivityPub.php @@ -16,25 +16,43 @@ class ActivityPub extends BaseConfig * -------------------------------------------------------------------- * ActivityPub Objects * -------------------------------------------------------------------- + * @var string */ public $actorObject = 'ActivityPub\Objects\ActorObject'; + + /** + * @var string + */ public $noteObject = 'ActivityPub\Objects\NoteObject'; /** * -------------------------------------------------------------------- * Default avatar and cover images * -------------------------------------------------------------------- + * @var string */ public $defaultAvatarImagePath = 'assets/images/avatar-default.jpg'; + + /** + * @var string + */ public $defaultAvatarImageMimetype = 'image/jpeg'; + /** + * @var string + */ public $defaultCoverImagePath = 'assets/images/cover-default.jpg'; + + /** + * @var string + */ public $defaultCoverImageMimetype = 'image/jpeg'; /** * -------------------------------------------------------------------- * Cache options * -------------------------------------------------------------------- + * @var string */ public $cachePrefix = 'ap_'; } diff --git a/app/Libraries/ActivityPub/Config/Routes.php b/app/Libraries/ActivityPub/Config/Routes.php index 9f68c1a9..01199135 100644 --- a/app/Libraries/ActivityPub/Config/Routes.php +++ b/app/Libraries/ActivityPub/Config/Routes.php @@ -19,14 +19,14 @@ $routes->addPlaceholder('noteAction', '\bfavourite|\breblog|\breply'); $routes->group('', ['namespace' => 'ActivityPub\Controllers'], function ( $routes -) { +): void { // webfinger $routes->get('.well-known/webfinger', 'WebFingerController', [ 'as' => 'webfinger', ]); // Actor - $routes->group('@(:actorUsername)', function ($routes) { + $routes->group('@(:actorUsername)', function ($routes): void { // Actor $routes->get('/', 'ActorController/$1', [ 'as' => 'actor', diff --git a/app/Libraries/ActivityPub/Controllers/ActorController.php b/app/Libraries/ActivityPub/Controllers/ActorController.php index e6cdc3a2..bdaa53df 100644 --- a/app/Libraries/ActivityPub/Controllers/ActorController.php +++ b/app/Libraries/ActivityPub/Controllers/ActorController.php @@ -8,6 +8,13 @@ namespace ActivityPub\Controllers; +use ActivityPub\Entities\Actor; +use ActivityPub\Config\ActivityPub; +use CodeIgniter\HTTP\RedirectResponse; +use CodeIgniter\HTTP\ResponseInterface; +use CodeIgniter\Exceptions\PageNotFoundException; +use ActivityPub\Entities\Note; +use CodeIgniter\HTTP\Exceptions\HTTPException; use ActivityPub\Objects\OrderedCollectionObject; use ActivityPub\Objects\OrderedCollectionPage; use CodeIgniter\Controller; @@ -15,15 +22,18 @@ use CodeIgniter\I18n\Time; class ActorController extends Controller { + /** + * @var string[] + */ protected $helpers = ['activitypub']; /** - * @var \ActivityPub\Entities\Actor + * @var Actor */ protected $actor; /** - * @var \ActivityPub\Config\ActivityPub + * @var ActivityPub */ protected $config; @@ -34,21 +44,20 @@ class ActorController extends Controller public function _remap($method, ...$params) { - if (count($params) > 0) { - if ( - !($this->actor = model('ActorModel')->getActorByUsername( - $params[0], - )) - ) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); - } + if ( + count($params) > 0 && + !($this->actor = model('ActorModel')->getActorByUsername( + $params[0], + )) + ) { + throw PageNotFoundException::forPageNotFound(); } unset($params[0]); return $this->$method(...$params); } - public function index() + public function index(): RedirectResponse { $actorObjectClass = $this->config->actorObject; $actorObject = new $actorObjectClass($this->actor); @@ -61,7 +70,7 @@ class ActorController extends Controller /** * Handles incoming requests from fediverse servers */ - public function inbox() + public function inbox(): ResponseInterface { // get json body and parse it $payload = $this->request->getJSON(); @@ -75,56 +84,41 @@ class ActorController extends Controller $payloadActor->id, $this->actor->id, null, - json_encode($payload), + json_encode($payload, JSON_THROW_ON_ERROR), ); // switch/case on activity type switch ($payload->type) { case 'Create': - switch ($payload->object->type) { - case 'Note': - if (!$payload->object->inReplyTo) { - return $this->response - ->setStatusCode(501) - ->setJSON([]); - } - - $replyToNote = model('NoteModel')->getNoteByUri( - $payload->object->inReplyTo, - ); - - // TODO: strip content from html to retrieve message - // remove all html tags and reconstruct message with mentions? - extract_text_from_html($payload->object->content); - - $reply = new \ActivityPub\Entities\Note([ - 'uri' => $payload->object->id, - 'actor_id' => $payloadActor->id, - 'in_reply_to_id' => $replyToNote->id, - 'message' => $payload->object->content, - 'published_at' => Time::parse( - $payload->object->published, - ), - ]); - - $noteId = model('NoteModel')->addReply( - $reply, - true, - false, - ); - - model('ActivityModel')->update($activityId, [ - 'note_id' => service('uuid') - ->fromBytes($noteId) - ->getString(), - ]); - - return $this->response->setStatusCode(200)->setJSON([]); - default: - // return not handled undo error (501 = not implemented) + if ($payload->object->type == 'Note') { + if (!$payload->object->inReplyTo) { return $this->response->setStatusCode(501)->setJSON([]); + } + $replyToNote = model('NoteModel')->getNoteByUri( + $payload->object->inReplyTo, + ); + // TODO: strip content from html to retrieve message + // remove all html tags and reconstruct message with mentions? + extract_text_from_html($payload->object->content); + $reply = new Note([ + 'uri' => $payload->object->id, + 'actor_id' => $payloadActor->id, + 'in_reply_to_id' => $replyToNote->id, + 'message' => $payload->object->content, + 'published_at' => Time::parse( + $payload->object->published, + ), + ]); + $noteId = model('NoteModel')->addReply($reply, true, false); + model('ActivityModel')->update($activityId, [ + 'note_id' => service('uuid') + ->fromBytes($noteId) + ->getString(), + ]); + return $this->response->setStatusCode(200)->setJSON([]); } - break; + // return not handled undo error (501 = not implemented) + return $this->response->setStatusCode(501)->setJSON([]); case 'Delete': $noteToDelete = model('NoteModel')->getNoteByUri( $payload->object->id, @@ -234,7 +228,7 @@ class ActorController extends Controller } } - public function outbox() + public function outbox(): RedirectResponse { // get published activities by publication date $actorActivity = model('ActivityModel') @@ -257,7 +251,7 @@ class ActorController extends Controller $pager = $actorActivity->pager; $orderedItems = []; foreach ($paginatedActivity as $activity) { - array_push($orderedItems, $activity->payload); + $orderedItems[] = $activity->payload; } $collection = new OrderedCollectionPage($pager, $orderedItems); } @@ -267,7 +261,7 @@ class ActorController extends Controller ->setBody($collection->toJSON()); } - public function followers() + public function followers(): RedirectResponse { // get followers for a specific actor $followers = model('ActorModel') @@ -295,7 +289,7 @@ class ActorController extends Controller $orderedItems = []; foreach ($paginatedFollowers as $follower) { - array_push($orderedItems, $follower->uri); + $orderedItems[] = $follower->uri; } $followersCollection = new OrderedCollectionPage( $pager, @@ -308,6 +302,9 @@ class ActorController extends Controller ->setBody($followersCollection->toJSON()); } + /** + * @return mixed|ResponseInterface + */ public function attemptFollow() { $rules = [ @@ -334,7 +331,7 @@ class ActorController extends Controller $data = get_webfinger_data($username, $domain); } - } catch (\CodeIgniter\HTTP\Exceptions\HTTPException $e) { + } catch (HTTPException $httpException) { return redirect() ->back() ->withInput() @@ -361,16 +358,16 @@ class ActorController extends Controller ); } - public function activity($activityId) + public function activity($activityId): RedirectResponse { if ( !($activity = model('ActivityModel')->getActivityById($activityId)) ) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); + throw PageNotFoundException::forPageNotFound(); } return $this->response ->setContentType('application/activity+json') - ->setBody(json_encode($activity->payload)); + ->setBody(json_encode($activity->payload, JSON_THROW_ON_ERROR)); } } diff --git a/app/Libraries/ActivityPub/Controllers/BlockController.php b/app/Libraries/ActivityPub/Controllers/BlockController.php index 87ffe0bb..f38d84e1 100644 --- a/app/Libraries/ActivityPub/Controllers/BlockController.php +++ b/app/Libraries/ActivityPub/Controllers/BlockController.php @@ -12,6 +12,9 @@ use CodeIgniter\Controller; class BlockController extends Controller { + /** + * @var string[] + */ protected $helpers = ['activitypub']; public function attemptBlockActor() @@ -32,7 +35,7 @@ class BlockController extends Controller if ($parts = split_handle($handle)) { extract($parts); - if (!($actor = get_or_create_actor($username, $domain))) { + if (($actor = get_or_create_actor($username, $domain)) === null) { return redirect() ->back() ->withInput() diff --git a/app/Libraries/ActivityPub/Controllers/NoteController.php b/app/Libraries/ActivityPub/Controllers/NoteController.php index 048beaa0..708e92d7 100644 --- a/app/Libraries/ActivityPub/Controllers/NoteController.php +++ b/app/Libraries/ActivityPub/Controllers/NoteController.php @@ -8,6 +8,11 @@ namespace ActivityPub\Controllers; +use CodeIgniter\HTTP\RedirectResponse; +use CodeIgniter\HTTP\ResponseInterface; +use CodeIgniter\Exceptions\PageNotFoundException; +use ActivityPub\Entities\Note; +use CodeIgniter\HTTP\Exceptions\HTTPException; use ActivityPub\Config\ActivityPub; use ActivityPub\Objects\OrderedCollectionObject; use ActivityPub\Objects\OrderedCollectionPage; @@ -16,15 +21,18 @@ use CodeIgniter\I18n\Time; class NoteController extends Controller { + /** + * @var string[] + */ protected $helpers = ['activitypub']; /** - * @var \ActivityPub\Entities\Note|null + * @var Note|null */ protected $note; /** - * @var \ActivityPub\Config\ActivityPub + * @var ActivityPub */ protected $config; @@ -36,14 +44,14 @@ class NoteController extends Controller public function _remap($method, ...$params) { if (!($this->note = model('NoteModel')->getNoteById($params[0]))) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); + throw PageNotFoundException::forPageNotFound(); } unset($params[0]); return $this->$method(...$params); } - public function index() + public function index(): RedirectResponse { $noteObjectClass = $this->config->noteObject; $noteObject = new $noteObjectClass($this->note); @@ -53,7 +61,7 @@ class NoteController extends Controller ->setBody($noteObject->toJSON()); } - public function replies() + public function replies(): RedirectResponse { // get note replies $noteReplies = model('NoteModel') @@ -84,7 +92,7 @@ class NoteController extends Controller $noteObjectClass = $this->config->noteObject; foreach ($paginatedReplies as $reply) { $replyObject = new $noteObjectClass($reply); - array_push($orderedItems, $replyObject->toJSON()); + $orderedItems[] = $replyObject->toJSON(); } $collection = new OrderedCollectionPage($pager, $orderedItems); } @@ -108,7 +116,7 @@ class NoteController extends Controller ->with('errors', $this->validator->getErrors()); } - $newNote = new \ActivityPub\Entities\Note([ + $newNote = new Note([ 'actor_id' => $this->request->getPost('actor_id'), 'message' => $this->request->getPost('message'), 'published_at' => Time::now(), @@ -119,7 +127,7 @@ class NoteController extends Controller ->back() ->withInput() // TODO: translate - ->with('error', 'Couldn\'t create Note'); + ->with('error', "Couldn't create Note"); } // Note without preview card has been successfully created @@ -184,7 +192,7 @@ class NoteController extends Controller ->with('errors', $this->validator->getErrors()); } - $newReplyNote = new \ActivityPub\Entities\Note([ + $newReplyNote = new Note([ 'actor_id' => $this->request->getPost('actor_id'), 'in_reply_to_id' => $this->note->id, 'message' => $this->request->getPost('message'), @@ -196,14 +204,17 @@ class NoteController extends Controller ->back() ->withInput() // TODO: translate - ->with('error', 'Couldn\'t create Reply'); + ->with('error', "Couldn't create Reply"); } // Reply note without preview card has been successfully created return redirect()->back(); } - public function attemptRemoteAction($action) + /** + * @return mixed|ResponseInterface + */ + public function attemptRemoteAction(string $action) { $rules = [ 'handle' => @@ -228,7 +239,7 @@ class NoteController extends Controller $data = get_webfinger_data($username, $domain); } - } catch (\CodeIgniter\HTTP\Exceptions\HTTPException $e) { + } catch (HTTPException $httpException) { return redirect() ->back() ->withInput() diff --git a/app/Libraries/ActivityPub/Controllers/SchedulerController.php b/app/Libraries/ActivityPub/Controllers/SchedulerController.php index 617290ae..16ff7fdf 100644 --- a/app/Libraries/ActivityPub/Controllers/SchedulerController.php +++ b/app/Libraries/ActivityPub/Controllers/SchedulerController.php @@ -12,9 +12,12 @@ use CodeIgniter\Controller; class SchedulerController extends Controller { + /** + * @var string[] + */ protected $helpers = ['activitypub']; - public function activity() + public function activity(): void { // retrieve scheduled activities from database $scheduledActivities = model('ActivityModel')->getScheduledActivities(); @@ -24,7 +27,7 @@ class SchedulerController extends Controller // send activity to all actor followers send_activity_to_followers( $scheduledActivity->actor, - json_encode($scheduledActivity->payload), + json_encode($scheduledActivity->payload, JSON_THROW_ON_ERROR), ); // set activity status to delivered diff --git a/app/Libraries/ActivityPub/Controllers/WebFingerController.php b/app/Libraries/ActivityPub/Controllers/WebFingerController.php index df671370..0ec6f92a 100644 --- a/app/Libraries/ActivityPub/Controllers/WebFingerController.php +++ b/app/Libraries/ActivityPub/Controllers/WebFingerController.php @@ -8,19 +8,21 @@ namespace ActivityPub\Controllers; +use CodeIgniter\HTTP\ResponseInterface; +use CodeIgniter\Exceptions\PageNotFoundException; use ActivityPub\WebFinger; use CodeIgniter\Controller; use Exception; class WebFingerController extends Controller { - public function index() + public function index(): ResponseInterface { try { $webfinger = new WebFinger($this->request->getGet('resource')); - } catch (Exception $e) { + } catch (Exception $exception) { // return 404, actor not found - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); + throw PageNotFoundException::forPageNotFound(); } return $this->response->setJSON($webfinger->toArray()); diff --git a/app/Libraries/ActivityPub/Core/AbstractObject.php b/app/Libraries/ActivityPub/Core/AbstractObject.php index d25c9db6..4e4de802 100644 --- a/app/Libraries/ActivityPub/Core/AbstractObject.php +++ b/app/Libraries/ActivityPub/Core/AbstractObject.php @@ -15,14 +15,17 @@ namespace ActivityPub\Core; abstract class AbstractObject { - public function set($property, $value) + public function set($property, $value): self { $this->$property = $value; return $this; } - public function toArray() + /** + * @return array + */ + public function toArray(): array { $objectVars = get_object_vars($this); $array = []; @@ -30,19 +33,22 @@ abstract class AbstractObject if ($key === 'context') { $key = '@context'; } - if (is_object($value) && $value instanceof self) { - $array[$key] = $value->toArray(); - } else { - $array[$key] = $value; - } + + $array[$key] = + is_object($value) && $value instanceof self + ? $value->toArray() + : $value; } // removes all NULL, FALSE and Empty Strings but leaves 0 (zero) values - return array_filter($array, function ($value) { + return array_filter($array, function ($value): bool { return $value !== null && $value !== false && $value !== ''; }); } + /** + * @return string|bool + */ public function toJSON() { return json_encode($this->toArray(), JSON_UNESCAPED_UNICODE); diff --git a/app/Libraries/ActivityPub/Core/Activity.php b/app/Libraries/ActivityPub/Core/Activity.php index 5219bbfc..ece7239c 100644 --- a/app/Libraries/ActivityPub/Core/Activity.php +++ b/app/Libraries/ActivityPub/Core/Activity.php @@ -26,7 +26,7 @@ class Activity extends ObjectType protected $actor; /** - * @var string|\ActivityPub\Core\ObjectType + * @var string|ObjectType */ protected $object; } diff --git a/app/Libraries/ActivityPub/Core/ObjectType.php b/app/Libraries/ActivityPub/Core/ObjectType.php index 53b865ac..5ef6ddcd 100644 --- a/app/Libraries/ActivityPub/Core/ObjectType.php +++ b/app/Libraries/ActivityPub/Core/ObjectType.php @@ -48,5 +48,5 @@ class ObjectType extends AbstractObject /** * @var array */ - protected $cc; + protected $cc = []; } diff --git a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-010000_add_actors.php b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-010000_add_actors.php index 9e2b2f5c..06cf447b 100644 --- a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-010000_add_actors.php +++ b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-010000_add_actors.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddActors extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'id' => [ @@ -115,7 +115,7 @@ class AddActors extends Migration $this->forge->createTable('activitypub_actors'); } - public function down() + public function down(): void { $this->forge->dropTable('activitypub_actors'); } diff --git a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-020000_add_notes.php b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-020000_add_notes.php index 22f42938..f6cc7cf6 100644 --- a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-020000_add_notes.php +++ b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-020000_add_notes.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddNotes extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'id' => [ @@ -101,7 +101,7 @@ class AddNotes extends Migration $this->forge->createTable('activitypub_notes'); } - public function down() + public function down(): void { $this->forge->dropTable('activitypub_notes'); } diff --git a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_activities.php b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_activities.php index 11555bf1..de475d92 100644 --- a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_activities.php +++ b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_activities.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddActivities extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'id' => [ @@ -83,7 +83,7 @@ class AddActivities extends Migration $this->forge->createTable('activitypub_activities'); } - public function down() + public function down(): void { $this->forge->dropTable('activitypub_activities'); } diff --git a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_favourites.php b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_favourites.php index d76d5452..c7ecddcf 100644 --- a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_favourites.php +++ b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_favourites.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddFavourites extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'actor_id' => [ @@ -48,7 +48,7 @@ class AddFavourites extends Migration $this->forge->createTable('activitypub_favourites'); } - public function down() + public function down(): void { $this->forge->dropTable('activitypub_favourites'); } diff --git a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_follows.php b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_follows.php index f3145f4f..b7b8a809 100644 --- a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_follows.php +++ b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_follows.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddFollowers extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'actor_id' => [ @@ -50,7 +50,7 @@ class AddFollowers extends Migration $this->forge->createTable('activitypub_follows'); } - public function down() + public function down(): void { $this->forge->dropTable('activitypub_follows'); } diff --git a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_preview_cards.php b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_preview_cards.php index eaa07eb4..978572ff 100644 --- a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_preview_cards.php +++ b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-100000_add_preview_cards.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddPreviewCards extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'id' => [ @@ -75,7 +75,7 @@ class AddPreviewCards extends Migration $this->forge->createTable('activitypub_preview_cards'); } - public function down() + public function down(): void { $this->forge->dropTable('activitypub_preview_cards'); } diff --git a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-110000_add_notes_preview_cards.php b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-110000_add_notes_preview_cards.php index 25fd22f9..74ae7dbc 100644 --- a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-110000_add_notes_preview_cards.php +++ b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-110000_add_notes_preview_cards.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddNotesPreviewCards extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'note_id' => [ @@ -46,7 +46,7 @@ class AddNotesPreviewCards extends Migration $this->forge->createTable('activitypub_notes_preview_cards'); } - public function down() + public function down(): void { $this->forge->dropTable('activitypub_notes_preview_cards'); } diff --git a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-120000_add_blocked_domains.php b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-120000_add_blocked_domains.php index 3b136dca..7f7488d2 100644 --- a/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-120000_add_blocked_domains.php +++ b/app/Libraries/ActivityPub/Database/Migrations/2018-01-01-120000_add_blocked_domains.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddBlockedDomains extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'name' => [ @@ -30,7 +30,7 @@ class AddBlockedDomains extends Migration $this->forge->createTable('activitypub_blocked_domains'); } - public function down() + public function down(): void { $this->forge->dropTable('activitypub_blocked_domains'); } diff --git a/app/Libraries/ActivityPub/Entities/Activity.php b/app/Libraries/ActivityPub/Entities/Activity.php index 14a7ed0d..fa595cec 100644 --- a/app/Libraries/ActivityPub/Entities/Activity.php +++ b/app/Libraries/ActivityPub/Entities/Activity.php @@ -8,29 +8,39 @@ namespace ActivityPub\Entities; +use RuntimeException; use Michalsn\Uuid\UuidEntity; class Activity extends UuidEntity { + /** + * @var string[] + */ protected $uuids = ['id', 'note_id']; /** - * @var \ActivityPub\Entities\Actor + * @var Actor */ protected $actor; /** - * @var \ActivityPub\Entities\Actor + * @var Actor */ protected $target_actor; /** - * @var \ActivityPub\Entities\Note + * @var Note */ protected $note; + /** + * @var string[] + */ protected $dates = ['scheduled_at', 'created_at']; + /** + * @var array + */ protected $casts = [ 'id' => 'string', 'actor_id' => 'integer', @@ -41,13 +51,10 @@ class Activity extends UuidEntity 'status' => '?string', ]; - /** - * @return \ActivityPub\Entities\Actor - */ - public function getActor() + public function getActor(): Actor { if (empty($this->actor_id)) { - throw new \RuntimeException( + throw new RuntimeException( 'Activity must have an actor_id before getting the actor.', ); } @@ -59,13 +66,10 @@ class Activity extends UuidEntity return $this->actor; } - /** - * @return \ActivityPub\Entities\Actor - */ - public function getTargetActor() + public function getTargetActor(): Actor { if (empty($this->target_actor_id)) { - throw new \RuntimeException( + throw new RuntimeException( 'Activity must have a target_actor_id before getting the target actor.', ); } @@ -79,13 +83,10 @@ class Activity extends UuidEntity return $this->target_actor; } - /** - * @return \ActivityPub\Entities\Note - */ - public function getNote() + public function getNote(): Note { if (empty($this->note_id)) { - throw new \RuntimeException( + throw new RuntimeException( 'Activity must have a note_id before getting note.', ); } diff --git a/app/Libraries/ActivityPub/Entities/Actor.php b/app/Libraries/ActivityPub/Entities/Actor.php index acf73aa3..db64f837 100644 --- a/app/Libraries/ActivityPub/Entities/Actor.php +++ b/app/Libraries/ActivityPub/Entities/Actor.php @@ -8,7 +8,8 @@ namespace ActivityPub\Entities; -use CodeIgniter\Entity; +use RuntimeException; +use CodeIgniter\Entity\Entity; class Actor extends Entity { @@ -18,15 +19,18 @@ class Actor extends Entity protected $key_id; /** - * @var \ActivityPub\Entities\Actor[] + * @var Actor[] */ - protected $followers; + protected $followers = []; /** * @var boolean */ - protected $is_local; + protected $is_local = false; + /** + * @var array + */ protected $casts = [ 'id' => 'integer', 'uri' => 'string', @@ -48,12 +52,12 @@ class Actor extends Entity 'is_blocked' => 'boolean', ]; - public function getKeyId() + public function getKeyId(): string { return $this->uri . '#main-key'; } - public function getIsLocal() + public function getIsLocal(): bool { if (!$this->is_local) { $uri = current_url(true); @@ -67,22 +71,27 @@ class Actor extends Entity return $this->is_local; } - public function getFollowers() + /** + * @return Follower[] + */ + public function getFollowers(): array { if (empty($this->id)) { - throw new \RuntimeException( + throw new RuntimeException( 'Actor must be created before getting followers.', ); } if (empty($this->followers)) { - $this->followers = model('ActorModel')->getFollowers($this->id); + $this->followers = (array) model('ActorModel')->getFollowers( + $this->id, + ); } return $this->followers; } - public function getAvatarImageUrl() + public function getAvatarImageUrl(): string { if (empty($this->attributes['avatar_image_url'])) { return base_url(config('ActivityPub')->defaultAvatarImagePath); @@ -91,7 +100,7 @@ class Actor extends Entity return $this->attributes['avatar_image_url']; } - public function getAvatarImageMimetype() + public function getAvatarImageMimetype(): string { if (empty($this->attributes['avatar_image_mimetype'])) { return config('ActivityPub')->defaultAvatarImageMimetype; @@ -100,7 +109,7 @@ class Actor extends Entity return $this->attributes['avatar_image_mimetype']; } - public function getCoverImageUrl() + public function getCoverImageUrl(): string { if (empty($this->attributes['cover_image_url'])) { return base_url(config('ActivityPub')->defaultCoverImagePath); @@ -109,7 +118,7 @@ class Actor extends Entity return $this->attributes['cover_image_url']; } - public function getCoverImageMimetype() + public function getCoverImageMimetype(): string { if (empty($this->attributes['cover_image_mimetype'])) { return config('ActivityPub')->defaultCoverImageMimetype; diff --git a/app/Libraries/ActivityPub/Entities/BlockedDomain.php b/app/Libraries/ActivityPub/Entities/BlockedDomain.php index bf609e7c..8405d883 100644 --- a/app/Libraries/ActivityPub/Entities/BlockedDomain.php +++ b/app/Libraries/ActivityPub/Entities/BlockedDomain.php @@ -8,10 +8,13 @@ namespace ActivityPub\Entities; -use CodeIgniter\Entity; +use CodeIgniter\Entity\Entity; class BlockedDomain extends Entity { + /** + * @var array + */ protected $casts = [ 'name' => 'string', ]; diff --git a/app/Libraries/ActivityPub/Entities/Favourite.php b/app/Libraries/ActivityPub/Entities/Favourite.php index 759448d4..ea4381b2 100644 --- a/app/Libraries/ActivityPub/Entities/Favourite.php +++ b/app/Libraries/ActivityPub/Entities/Favourite.php @@ -12,8 +12,14 @@ use Michalsn\Uuid\UuidEntity; class Favourite extends UuidEntity { + /** + * @var string[] + */ protected $uuids = ['note_id']; + /** + * @var array + */ protected $casts = [ 'actor_id' => 'integer', 'note_id' => 'integer', diff --git a/app/Libraries/ActivityPub/Entities/Follow.php b/app/Libraries/ActivityPub/Entities/Follow.php index dea45eed..fd966a17 100644 --- a/app/Libraries/ActivityPub/Entities/Follow.php +++ b/app/Libraries/ActivityPub/Entities/Follow.php @@ -8,10 +8,13 @@ namespace ActivityPub\Entities; -use CodeIgniter\Entity; +use CodeIgniter\Entity\Entity; class Follow extends Entity { + /** + * @var array + */ protected $casts = [ 'actor_id' => 'integer', 'target_actor_id' => 'integer', diff --git a/app/Libraries/ActivityPub/Entities/Note.php b/app/Libraries/ActivityPub/Entities/Note.php index af278d4b..b6b6d858 100644 --- a/app/Libraries/ActivityPub/Entities/Note.php +++ b/app/Libraries/ActivityPub/Entities/Note.php @@ -8,64 +8,74 @@ namespace ActivityPub\Entities; +use RuntimeException; use Michalsn\Uuid\UuidEntity; class Note extends UuidEntity { + /** + * @var string[] + */ protected $uuids = ['id', 'in_reply_to_id', 'reblog_of_id']; /** - * @var \ActivityPub\Entities\Actor + * @var Actor */ protected $actor; /** * @var boolean */ - protected $is_reply; + protected $is_reply = false; /** - * @var \ActivityPub\Entities\Note + * @var Note */ protected $reply_to_note; /** * @var boolean */ - protected $is_reblog; + protected $is_reblog = false; /** - * @var \ActivityPub\Entities\Note + * @var Note */ protected $reblog_of_note; /** - * @var \ActivityPub\Entities\PreviewCard + * @var PreviewCard|null */ protected $preview_card; /** * @var boolean */ - protected $has_preview_card; + protected $has_preview_card = false; /** - * @var \ActivityPub\Entities\Note[] + * @var Note[] */ - protected $replies; + protected $replies = []; /** * @var boolean */ - protected $has_replies; + protected $has_replies = false; /** - * @var \ActivityPub\Entities\Note[] + * @var Note[] */ - protected $reblogs; + protected $reblogs = []; + /** + * @var string[] + */ protected $dates = ['published_at', 'created_at']; + /** + * @var array + */ protected $casts = [ 'id' => 'string', 'uri' => 'string', @@ -81,13 +91,11 @@ class Note extends UuidEntity /** * Returns the note's actor - * - * @return \ActivityPub\Entities\Actor */ - public function getActor() + public function getActor(): Actor { if (empty($this->actor_id)) { - throw new \RuntimeException( + throw new RuntimeException( 'Note must have an actor_id before getting actor.', ); } @@ -99,10 +107,10 @@ class Note extends UuidEntity return $this->actor; } - public function getPreviewCard() + public function getPreviewCard(): ?PreviewCard { if (empty($this->id)) { - throw new \RuntimeException( + throw new RuntimeException( 'Note must be created before getting preview_card.', ); } @@ -116,42 +124,47 @@ class Note extends UuidEntity return $this->preview_card; } - public function getHasPreviewCard() + public function getHasPreviewCard(): bool { - return !empty($this->getPreviewCard()) ? true : false; + return !empty($this->getPreviewCard()); } - public function getIsReply() + public function getIsReply(): bool { $this->is_reply = $this->in_reply_to_id !== null; return $this->is_reply; } - public function getReplies() + /** + * @return Note[] + */ + public function getReplies(): array { if (empty($this->id)) { - throw new \RuntimeException( + throw new RuntimeException( 'Note must be created before getting replies.', ); } if (empty($this->replies)) { - $this->replies = model('NoteModel')->getNoteReplies($this->id); + $this->replies = (array) model('NoteModel')->getNoteReplies( + $this->id, + ); } return $this->replies; } - public function getHasReplies() + public function getHasReplies(): bool { - return !empty($this->getReplies()) ? true : false; + return !empty($this->getReplies()); } - public function getReplyToNote() + public function getReplyToNote(): Note { if (empty($this->in_reply_to_id)) { - throw new \RuntimeException('Note is not a reply.'); + throw new RuntimeException('Note is not a reply.'); } if (empty($this->reply_to_note)) { @@ -163,30 +176,35 @@ class Note extends UuidEntity return $this->reply_to_note; } - public function getReblogs() + /** + * @return Note[] + */ + public function getReblogs(): array { if (empty($this->id)) { - throw new \RuntimeException( + throw new RuntimeException( 'Note must be created before getting reblogs.', ); } if (empty($this->reblogs)) { - $this->reblogs = model('NoteModel')->getNoteReblogs($this->id); + $this->reblogs = (array) model('NoteModel')->getNoteReblogs( + $this->id, + ); } return $this->reblogs; } - public function getIsReblog() + public function getIsReblog(): bool { return $this->reblog_of_id != null; } - public function getReblogOfNote() + public function getReblogOfNote(): Note { if (empty($this->reblog_of_id)) { - throw new \RuntimeException('Note is not a reblog.'); + throw new RuntimeException('Note is not a reblog.'); } if (empty($this->reblog_of_note)) { @@ -198,7 +216,7 @@ class Note extends UuidEntity return $this->reblog_of_note; } - public function setMessage(string $message) + public function setMessage(string $message): self { helper('activitypub'); diff --git a/app/Libraries/ActivityPub/Entities/PreviewCard.php b/app/Libraries/ActivityPub/Entities/PreviewCard.php index cd3523c4..62cbcd62 100644 --- a/app/Libraries/ActivityPub/Entities/PreviewCard.php +++ b/app/Libraries/ActivityPub/Entities/PreviewCard.php @@ -8,10 +8,13 @@ namespace ActivityPub\Entities; -use CodeIgniter\Entity; +use CodeIgniter\Entity\Entity; class PreviewCard extends Entity { + /** + * @var array + */ protected $casts = [ 'id' => 'integer', 'note_id' => 'string', diff --git a/app/Libraries/ActivityPub/Filters/ActivityPubFilter.php b/app/Libraries/ActivityPub/Filters/ActivityPubFilter.php index bf9d8708..32503543 100644 --- a/app/Libraries/ActivityPub/Filters/ActivityPubFilter.php +++ b/app/Libraries/ActivityPub/Filters/ActivityPubFilter.php @@ -2,6 +2,9 @@ namespace ActivityPub\Filters; +use Config\Services; +use CodeIgniter\Exceptions\PageNotFoundException; +use Exception; use ActivityPub\HttpSignature; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; @@ -20,10 +23,8 @@ class ActivityPubFilter implements FilterInterface * sent back to the client, allowing for error pages, * redirects, etc. * - * @param \CodeIgniter\HTTP\RequestInterface $request * @param array|null $params - * - * @return mixed + * @return void|mixed */ public function before(RequestInterface $request, $params = null) { @@ -32,7 +33,7 @@ class ActivityPubFilter implements FilterInterface } if (in_array('verify-activitystream', $params)) { - $negotiate = \Config\Services::negotiator(); + $negotiate = Services::negotiator(); $allowedContentTypes = [ 'application/ld+json; profile="https://www.w3.org/ns/activitystreams', @@ -41,7 +42,7 @@ class ActivityPubFilter implements FilterInterface if (empty($negotiate->media($allowedContentTypes))) { // return $this->response->setStatusCode(415)->setJSON([]); - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); + throw PageNotFoundException::forPageNotFound(); } } @@ -53,12 +54,12 @@ class ActivityPubFilter implements FilterInterface // check first if domain is blocked if (model('BlockedDomainModel')->isDomainBlocked($domain)) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); + throw PageNotFoundException::forPageNotFound(); } // check if actor is blocked if (model('ActorModel')->isActorBlocked($actorUri)) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); + throw PageNotFoundException::forPageNotFound(); } } @@ -66,7 +67,7 @@ class ActivityPubFilter implements FilterInterface try { // securityCheck: check activity signature before handling it (new HttpSignature())->verify(); - } catch (\Exception $e) { + } catch (Exception $exception) { // Invalid HttpSignature (401 = unauthorized) // TODO: show error message? return service('response')->setStatusCode(401); @@ -75,24 +76,19 @@ class ActivityPubFilter implements FilterInterface } //-------------------------------------------------------------------- - /** * Allows After filters to inspect and modify the response * object as needed. This method does not allow any way * to stop execution of other after filters, short of * throwing an Exception or Error. * - * @param \CodeIgniter\HTTP\RequestInterface $request - * @param \CodeIgniter\HTTP\ResponseInterface $response * @param array|null $arguments - * - * @return void */ public function after( RequestInterface $request, ResponseInterface $response, $arguments = null - ) { + ): void { } //-------------------------------------------------------------------- diff --git a/app/Libraries/ActivityPub/Helpers/activitypub_helper.php b/app/Libraries/ActivityPub/Helpers/activitypub_helper.php index b4227371..8e6e6d6e 100644 --- a/app/Libraries/ActivityPub/Helpers/activitypub_helper.php +++ b/app/Libraries/ActivityPub/Helpers/activitypub_helper.php @@ -6,6 +6,11 @@ * @link https://castopod.org/ */ +use CodeIgniter\HTTP\URI; +use Config\Database; +use Essence\Essence; +use ActivityPub\Entities\PreviewCard; +use ActivityPub\Entities\Actor; use ActivityPub\Activities\AcceptActivity; use ActivityPub\ActivityRequest; use CodeIgniter\HTTP\Exceptions\HTTPException; @@ -14,15 +19,11 @@ if (!function_exists('get_webfinger_data')) { /** * Retrieve actor webfinger data from username and domain * - * @param string $username - * @param string $domain - * @return mixed - * @throws HTTPException - * @throws InvalidArgumentException + * @return object|null */ - function get_webfinger_data($username, $domain) + function get_webfinger_data(string $username, string $domain): ?object { - $webfingerUri = new \CodeIgniter\HTTP\URI(); + $webfingerUri = new URI(); $webfingerUri->setScheme('https'); $webfingerUri->setHost($domain); isset($port) && $webfingerUri->setPort((int) $port); @@ -32,7 +33,12 @@ if (!function_exists('get_webfinger_data')) { $webfingerRequest = new ActivityRequest($webfingerUri); $webfingerResponse = $webfingerRequest->get(); - return json_decode($webfingerResponse->getBody()); + return json_decode( + $webfingerResponse->getBody(), + null, + 512, + JSON_THROW_ON_ERROR, + ); } } @@ -47,7 +53,7 @@ if (!function_exists('split_handle')) { { if ( !preg_match( - '/^@?(?P[\w\.\-]+)@(?P[\w\.\-]+)(?P:[\d]+)?$/', + '~^@?(?P[\w\.\-]+)@(?P[\w\.\-]+)(?P:[\d]+)?$~', $handle, $matches, ) @@ -63,17 +69,18 @@ if (!function_exists('accept_follow')) { /** * Sends an accept activity to the targetActor's inbox * - * @param \ActivityPub\Entities\Actor $actor Actor which accepts the follow - * @param \ActivityPub\Entities\Actor $targetActor Actor which receives the accept follow - * @param string $objectId - * @return void + * @param Actor $actor Actor which accepts the follow + * @param Actor $targetActor Actor which receives the accept follow */ - function accept_follow($actor, $targetActor, $objectId) - { + function accept_follow( + Actor $actor, + Actor $targetActor, + string $objectId + ): void { $acceptActivity = new AcceptActivity(); $acceptActivity->set('actor', $actor->uri)->set('object', $objectId); - $db = \Config\Database::connect(); + $db = Database::connect(); $db->transStart(); $activityModel = model('ActivityModel'); @@ -101,7 +108,7 @@ if (!function_exists('accept_follow')) { ); $acceptRequest->sign($actor->key_id, $actor->private_key); $acceptRequest->post(); - } catch (\Exception $e) { + } catch (Exception $exception) { $db->transRollback(); } @@ -113,11 +120,9 @@ if (!function_exists('send_activity_to_followers')) { /** * Sends an activity to all actor followers * - * @param \ActivityPub\Entities\Actor $actor * @param string $activity - * @return void */ - function send_activity_to_followers($actor, $activityPayload) + function send_activity_to_followers(Actor $actor, $activityPayload): void { foreach ($actor->followers as $follower) { try { @@ -127,7 +132,7 @@ if (!function_exists('send_activity_to_followers')) { ); $acceptRequest->sign($actor->key_id, $actor->private_key); $acceptRequest->post(); - } catch (\Exception $e) { + } catch (Exception $e) { // log error log_message('critical', $e); } @@ -139,10 +144,9 @@ if (!function_exists('extract_urls_from_message')) { /** * Returns an array of all urls from a string * - * @param mixed $message * @return string[] */ - function extract_urls_from_message($message) + function extract_urls_from_message(string $message): array { preg_match_all( '~(?:(https?)://([^\s<]+)|(www\.[^\s<]+?\.[^\s<]+))(? [ 'OEmbedProvider' => '//', 'OpenGraphProvider' => '//', @@ -182,7 +185,7 @@ if (!function_exists('create_preview_card_from_url')) { // Check that, at least, the url and title are set if ($media->url && $media->title) { - $preview_card = new \ActivityPub\Entities\PreviewCard([ + $preview_card = new PreviewCard([ 'url' => (string) $url, 'title' => $media->title, 'description' => $media->description, @@ -219,10 +222,9 @@ if (!function_exists('get_or_create_preview_card_from_url')) { /** * Extract open graph metadata from given url and create preview card * - * @param \CodeIgniter\HTTP\URI $url - * @return \ActivityPub\Entities\PreviewCard|null + * @return PreviewCard|null */ - function get_or_create_preview_card_from_url($url) + function get_or_create_preview_card_from_url(URI $url): ?PreviewCard { // check if preview card has already been generated if ( @@ -243,10 +245,9 @@ if (!function_exists('get_or_create_actor_from_uri')) { * Retrieves actor from database using the actor uri * If Actor is not present, it creates the record in the database and returns it. * - * @param string $actorUri - * @return \ActivityPub\Entities\Actor|null + * @return Actor|null */ - function get_or_create_actor_from_uri($actorUri) + function get_or_create_actor_from_uri(string $actorUri): ?Actor { // check if actor exists in database already and return it if ($actor = model('ActorModel')->getActorByUri($actorUri)) { @@ -263,11 +264,9 @@ if (!function_exists('get_or_create_actor')) { * Retrieves actor from database using the actor username and domain * If actor is not present, it creates the record in the database and returns it. * - * @param string $username - * @param string $domain - * @return \ActivityPub\Entities\Actor|null + * @return Actor|null */ - function get_or_create_actor($username, $domain) + function get_or_create_actor(string $username, string $domain): ?Actor { // check if actor exists in database already and return it if ( @@ -292,16 +291,20 @@ if (!function_exists('create_actor_from_uri')) { * Creates actor record in database using * the info gathered from the actorUri parameter * - * @param string $actorUri - * @return \ActivityPub\Entities\Actor|null + * @return Actor|null */ - function create_actor_from_uri($actorUri) + function create_actor_from_uri(string $actorUri): ?Actor { $activityRequest = new ActivityRequest($actorUri); $actorResponse = $activityRequest->get(); - $actorPayload = json_decode($actorResponse->getBody()); + $actorPayload = json_decode( + $actorResponse->getBody(), + null, + 512, + JSON_THROW_ON_ERROR, + ); - $newActor = new \ActivityPub\Entities\Actor(); + $newActor = new Actor(); $newActor->uri = $actorUri; $newActor->username = $actorPayload->preferredUsername; $newActor->domain = $activityRequest->getDomain(); @@ -335,10 +338,9 @@ if (!function_exists('get_current_domain')) { /** * Returns instance's domain name * - * @return string * @throws HTTPException */ - function get_current_domain() + function get_current_domain(): string { $uri = current_url(true); return $uri->getHost() . ($uri->getPort() ? ':' . $uri->getPort() : ''); @@ -349,12 +351,11 @@ if (!function_exists('extract_text_from_html')) { /** * Extracts the text from html content * - * @param mixed $content * @return string|string[]|null */ - function extract_text_from_html($content) + function extract_text_from_html(string $content) { - return preg_replace('/\s+/', ' ', strip_tags($content)); + return preg_replace('~\s+~', ' ', strip_tags($content)); } } @@ -366,14 +367,13 @@ if (!function_exists('linkify')) { * @param string $value * @param array $protocols http/https, ftp, mail, twitter * @param array $attributes - * @return string */ - function linkify($text, $protocols = ['http', 'handle']) + function linkify($text, array $protocols = ['http', 'handle']): string { $links = []; // Extract text links for each protocol - foreach ((array) $protocols as $protocol) { + foreach ($protocols as $protocol) { switch ($protocol) { case 'http': case 'https': @@ -388,7 +388,7 @@ if (!function_exists('linkify')) { helper('text'); $link = preg_replace( - '#^www\.(.+\.)#i', + '~^www\.(.+\.)~i', '$1', $link, ); @@ -397,7 +397,7 @@ if (!function_exists('linkify')) { array_push( $links, anchor( - "$protocol://$link", + "{$protocol}://{$link}", ellipsize(rtrim($link, '/'), 30), [ 'target' => '_blank', @@ -434,28 +434,27 @@ if (!function_exists('linkify')) { ]), ) . '>'; - } else { - try { - $actor = get_or_create_actor( - $match['username'], - $match['domain'], - ); - return '<' . - array_push( - $links, - anchor($actor->uri, $match[0], [ - 'target' => '_blank', - 'rel' => - 'noopener noreferrer', - ]), - ) . - '>'; - } catch (\CodeIgniter\HTTP\Exceptions\HTTPException $e) { - // Couldn't retrieve actor, do not wrap the text in link - return '<' . - array_push($links, $match[0]) . - '>'; - } + } + + try { + $actor = get_or_create_actor( + $match['username'], + $match['domain'], + ); + return '<' . + array_push( + $links, + anchor($actor->uri, $match[0], [ + 'target' => '_blank', + 'rel' => 'noopener noreferrer', + ]), + ) . + '>'; + } catch (\CodeIgniter\HTTP\Exceptions\HTTPException $httpException) { + // Couldn't retrieve actor, do not wrap the text in link + return '<' . + array_push($links, $match[0]) . + '>'; } } else { if ( @@ -488,10 +487,14 @@ if (!function_exists('linkify')) { return '<' . array_push( $links, - anchor("$protocol://$match[1]", $match[1], [ - 'target' => '_blank', - 'rel' => 'noopener noreferrer', - ]), + anchor( + "{$protocol}://$match[1]", + $match[1], + [ + 'target' => '_blank', + 'rel' => 'noopener noreferrer', + ], + ), ) . '>'; }, @@ -503,7 +506,7 @@ if (!function_exists('linkify')) { // Insert all links return preg_replace_callback( - '/<(\d+)>/', + '~<(\d+)>~', function ($match) use (&$links) { return $links[$match[1] - 1]; }, diff --git a/app/Libraries/ActivityPub/HttpSignature.php b/app/Libraries/ActivityPub/HttpSignature.php index 8b1bde6d..077233fd 100644 --- a/app/Libraries/ActivityPub/HttpSignature.php +++ b/app/Libraries/ActivityPub/HttpSignature.php @@ -24,6 +24,9 @@ use phpseclib\Crypt\RSA; */ class HttpSignature { + /** + * @var string + */ const SIGNATURE_PATTERN = '/^ keyId="(?P (https?:\/\/[\w\-\.]+[\w]+) @@ -31,18 +34,15 @@ class HttpSignature ([\w\-\.#\/@]+) )", algorithm="(?P[\w\-]+)", - (headers="\(request-target\) (?P[\w\-\s]+)",)? + (headers="\(request-target\) (?P[\w\\-\s]+)",)? signature="(?P[\w+\/]+={0,2})" /x'; /** - * @var \CodeIgniter\HTTP\IncomingRequest + * @var IncomingRequest */ protected $request; - /** - * @param \CodeIgniter\HTTP\IncomingRequest $request - */ public function __construct(IncomingRequest $request = null) { if (is_null($request)) { @@ -57,7 +57,7 @@ class HttpSignature * * @return bool True if signature has been verified. Otherwise false */ - public function verify() + public function verify(): bool { if (!($dateHeader = $this->request->header('date'))) { throw new Exception('Request must include a date header.'); @@ -87,7 +87,7 @@ class HttpSignature } // read the Signature header - if (!($signature = $this->request->getHeaderLine('signature'))) { + if (($signature = $this->request->getHeaderLine('signature')) === '') { // Signature header not found throw new Exception('Request must include a signature header'); } @@ -103,7 +103,12 @@ class HttpSignature // Fetch the public key linked from keyId $actorRequest = new ActivityRequest($keyId); $actorResponse = $actorRequest->get(); - $actor = json_decode($actorResponse->getBody()); + $actor = json_decode( + $actorResponse->getBody(), + null, + 512, + JSON_THROW_ON_ERROR, + ); $publicKeyPem = $actor->publicKey->publicKeyPem; @@ -123,8 +128,7 @@ class HttpSignature /** * Split HTTP signature into its parts (keyId, headers and signature) * - * @param string $signature - * @return bool|array + * @return bool|mixed */ private function splitSignature(string $signature) { @@ -145,23 +149,22 @@ class HttpSignature * Get plain text that has been originally signed * * @param array $headers HTTP header keys - * @return string */ - private function getPlainText(array $headers) + private function getPlainText(array $headers): string { $strings = []; $strings[] = sprintf( '(request-target): %s %s%s', $this->request->getMethod(), '/' . $this->request->uri->getPath(), - $this->request->uri->getQuery() + $this->request->uri->getQuery() !== '' ? '?' . $this->request->uri->getQuery() : '', ); foreach ($headers as $key) { if ($this->request->hasHeader($key)) { - $strings[] = "$key: {$this->request->getHeaderLine($key)}"; + $strings[] = "{$key}: {$this->request->getHeaderLine($key)}"; } } diff --git a/app/Libraries/ActivityPub/Models/ActivityModel.php b/app/Libraries/ActivityPub/Models/ActivityModel.php index 0d7a82ad..cdd9020e 100644 --- a/app/Libraries/ActivityPub/Models/ActivityModel.php +++ b/app/Libraries/ActivityPub/Models/ActivityModel.php @@ -8,15 +8,30 @@ namespace ActivityPub\Models; +use ActivityPub\Entities\Activity; +use CodeIgniter\I18n\Time; +use DateTimeInterface; use Michalsn\Uuid\UuidModel; class ActivityModel extends UuidModel { + /** + * @var string + */ protected $table = 'activitypub_activities'; + /** + * @var string + */ protected $primaryKey = 'id'; + /** + * @var string[] + */ protected $uuidFields = ['id', 'note_id']; + /** + * @var string[] + */ protected $allowedFields = [ 'id', 'actor_id', @@ -28,11 +43,20 @@ class ActivityModel extends UuidModel 'scheduled_at', ]; - protected $returnType = \ActivityPub\Entities\Activity::class; + /** + * @var string + */ + protected $returnType = Activity::class; + /** + * @var bool + */ protected $useSoftDeletes = false; + /** + * @var bool + */ protected $useTimestamps = true; - protected $updatedField = null; + protected $updatedField; public function getActivityById($activityId) { @@ -50,24 +74,18 @@ class ActivityModel extends UuidModel /** * Inserts a new activity record in the database * - * @param string $type - * @param integer $actorId - * @param integer $targetActorId - * @param integer $noteId - * @param string $payload - * @param \CodeIgniter\I18n\Time $scheduledAt - * @param string $status + * @param Time $scheduledAt * * @return Michalsn\Uuid\BaseResult|int|string|false */ public function newActivity( - $type, - $actorId, - $targetActorId, - $noteId, - $payload, - $scheduledAt = null, - $status = null + string $type, + int $actorId, + ?int $targetActorId, + ?string $noteId, + string $payload, + DateTimeInterface $scheduledAt = null, + ?string $status = null ) { return $this->insert( [ diff --git a/app/Libraries/ActivityPub/Models/ActorModel.php b/app/Libraries/ActivityPub/Models/ActorModel.php index a82ee94e..c7548d8f 100644 --- a/app/Libraries/ActivityPub/Models/ActorModel.php +++ b/app/Libraries/ActivityPub/Models/ActorModel.php @@ -8,13 +8,20 @@ namespace ActivityPub\Models; +use ActivityPub\Entities\Actor; use CodeIgniter\Events\Events; use CodeIgniter\Model; class ActorModel extends Model { + /** + * @var string + */ protected $table = 'activitypub_actors'; + /** + * @var string[] + */ protected $allowedFields = [ 'id', 'uri', @@ -36,9 +43,18 @@ class ActorModel extends Model 'is_blocked', ]; - protected $returnType = \ActivityPub\Entities\Actor::class; + /** + * @var string + */ + protected $returnType = Actor::class; + /** + * @var bool + */ protected $useSoftDeletes = false; + /** + * @var bool + */ protected $useTimestamps = true; public function getActorById($id) @@ -56,13 +72,11 @@ class ActorModel extends Model /** * Looks for actor with username and domain, * if no domain has been specified, the current host will be used - * - * @param mixed $username - * @param mixed|null $domain - * @return mixed */ - public function getActorByUsername($username, $domain = null) - { + public function getActorByUsername( + string $username, + ?string $domain = null + ): ?Actor { // TODO: is there a better way? helper('activitypub'); @@ -70,13 +84,14 @@ class ActorModel extends Model $domain = get_current_domain(); } - if (!($found = cache("actor@{$username}@{$domain}"))) { + $cacheName = "actor-{$username}-{$domain}"; + if (!($found = cache($cacheName))) { $found = $this->where([ 'username' => $username, 'domain' => $domain, ])->first(); - cache()->save("actor@{$username}@{$domain}", $found, DECADE); + cache()->save($cacheName, $found, DECADE); } return $found; @@ -86,7 +101,7 @@ class ActorModel extends Model { $hashedActorUri = md5($actorUri); $cacheName = - config('ActivityPub')->cachePrefix . "actor@{$hashedActorUri}"; + config('ActivityPub')->cachePrefix . "actor-{$hashedActorUri}"; if (!($found = cache($cacheName))) { $found = $this->where('uri', $actorUri)->first(); @@ -118,11 +133,8 @@ class ActorModel extends Model /** * Check if an existing actor is blocked using its uri. * Returns FALSE if the actor doesn't exist - * - * @param mixed $actorUri - * @return boolean */ - public function isActorBlocked($actorUri) + public function isActorBlocked(string $actorUri): bool { if ($actor = $this->getActorByUri($actorUri)) { return $actor->is_blocked; @@ -134,9 +146,9 @@ class ActorModel extends Model /** * Retrieves all blocked actors. * - * @return \ActivityPub\Entities\Actor[] + * @return Actor[] */ - public function getBlockedActors() + public function getBlockedActors(): array { $cacheName = config('ActivityPub')->cachePrefix . 'blocked_actors'; if (!($found = cache($cacheName))) { @@ -148,7 +160,7 @@ class ActorModel extends Model return $found; } - public function blockActor($actorId) + public function blockActor($actorId): void { $prefix = config('ActivityPub')->cachePrefix; cache()->delete($prefix . 'blocked_actors'); @@ -159,7 +171,7 @@ class ActorModel extends Model $this->update($actorId, ['is_blocked' => 1]); } - public function unblockActor($actorId) + public function unblockActor($actorId): void { $prefix = config('ActivityPub')->cachePrefix; cache()->delete($prefix . 'blocked_actors'); diff --git a/app/Libraries/ActivityPub/Models/BlockedDomainModel.php b/app/Libraries/ActivityPub/Models/BlockedDomainModel.php index d87e1157..60b0f203 100644 --- a/app/Libraries/ActivityPub/Models/BlockedDomainModel.php +++ b/app/Libraries/ActivityPub/Models/BlockedDomainModel.php @@ -8,21 +8,44 @@ namespace ActivityPub\Models; +use CodeIgniter\Database\BaseResult; +use ActivityPub\Entities\BlockedDomain; use CodeIgniter\Events\Events; use CodeIgniter\Model; class BlockedDomainModel extends Model { + /** + * @var string + */ protected $table = 'activitypub_blocked_domains'; + + /** + * @var string + */ protected $primaryKey = 'name'; + /** + * @var string[] + */ protected $allowedFields = ['name']; - protected $returnType = \ActivityPub\Entities\BlockedDomain::class; + /** + * @var string + */ + protected $returnType = BlockedDomain::class; + + /** + * @var bool + */ protected $useSoftDeletes = false; + /** + * @var bool + */ protected $useTimestamps = true; - protected $updatedField = null; + + protected $updatedField; /** * Retrieves instance or podcast domain blocks depending on whether or not $podcastId param is set. @@ -47,7 +70,7 @@ class BlockedDomainModel extends Model config('ActivityPub')->cachePrefix . "domain#{$hashedDomain}_isBlocked"; if (!($found = cache($cacheName))) { - $found = $this->find($domain) ? true : false; + $found = (bool) $this->find($domain); cache()->save($cacheName, $found, DECADE); } @@ -83,6 +106,9 @@ class BlockedDomainModel extends Model return $result; } + /** + * @return bool|BaseResult + */ public function unblockDomain($name) { $hashedDomain = md5($name); diff --git a/app/Libraries/ActivityPub/Models/FavouriteModel.php b/app/Libraries/ActivityPub/Models/FavouriteModel.php index 4aacdc9a..8dbb292a 100644 --- a/app/Libraries/ActivityPub/Models/FavouriteModel.php +++ b/app/Libraries/ActivityPub/Models/FavouriteModel.php @@ -8,6 +8,9 @@ namespace ActivityPub\Models; +use ActivityPub\Entities\Actor; +use ActivityPub\Entities\Note; +use ActivityPub\Entities\Favourite; use ActivityPub\Activities\LikeActivity; use ActivityPub\Activities\UndoActivity; use CodeIgniter\Events\Events; @@ -15,25 +18,38 @@ use Michalsn\Uuid\UuidModel; class FavouriteModel extends UuidModel { + /** + * @var string + */ protected $table = 'activitypub_favourites'; - protected $uuidFields = ['note_id']; - - protected $allowedFields = ['actor_id', 'note_id']; - - protected $returnType = \ActivityPub\Entities\Favourite::class; - - protected $useTimestamps = true; - protected $updatedField = null; /** - * - * @param \ActivityPub\Entities\Actor $actor - * @param \ActivityPub\Entities\Note $note - * @param bool $registerActivity - * @return void + * @var string[] */ - public function addFavourite($actor, $note, $registerActivity = true) - { + protected $uuidFields = ['note_id']; + + /** + * @var string[] + */ + protected $allowedFields = ['actor_id', 'note_id']; + + /** + * @var string + */ + protected $returnType = Favourite::class; + + /** + * @var bool + */ + protected $useTimestamps = true; + + protected $updatedField; + + public function addFavourite( + Actor $actor, + Note $note, + bool $registerActivity = true + ): void { $this->db->transStart(); $this->insert([ @@ -53,7 +69,7 @@ class FavouriteModel extends UuidModel $prefix = config('ActivityPub')->cachePrefix; $hashedNoteUri = md5($note->uri); cache()->delete($prefix . "note#{$note->id}"); - cache()->delete($prefix . "note@{$hashedNoteUri}"); + cache()->delete($prefix . "note-{$hashedNoteUri}"); cache()->delete($prefix . "actor#{$actor->id}_published_notes"); if ($note->in_reply_to_id) { @@ -92,8 +108,11 @@ class FavouriteModel extends UuidModel $this->db->transComplete(); } - public function removeFavourite($actor, $note, $registerActivity = true) - { + public function removeFavourite( + $actor, + $note, + $registerActivity = true + ): void { $this->db->transStart(); model('NoteModel') @@ -108,7 +127,7 @@ class FavouriteModel extends UuidModel $prefix = config('ActivityPub')->cachePrefix; $hashedNoteUri = md5($note->uri); cache()->delete($prefix . "note#{$note->id}"); - cache()->delete($prefix . "note@{$hashedNoteUri}"); + cache()->delete($prefix . "note-{$hashedNoteUri}"); cache()->delete($prefix . "actor#{$actor->id}_published_notes"); if ($note->in_reply_to_id) { @@ -182,12 +201,8 @@ class FavouriteModel extends UuidModel /** * Adds or removes favourite from database and increments count - * - * @param \ActivityPub\Entities\Actor $actor - * @param \ActivityPub\Entities\Note $note - * @return void */ - public function toggleFavourite($actor, $note) + public function toggleFavourite(Actor $actor, Note $note): void { if ( $this->where([ diff --git a/app/Libraries/ActivityPub/Models/FollowModel.php b/app/Libraries/ActivityPub/Models/FollowModel.php index ced37815..4bd0031b 100644 --- a/app/Libraries/ActivityPub/Models/FollowModel.php +++ b/app/Libraries/ActivityPub/Models/FollowModel.php @@ -8,6 +8,9 @@ namespace ActivityPub\Models; +use ActivityPub\Entities\Actor; +use ActivityPub\Entities\Follow; +use Exception; use ActivityPub\Activities\FollowActivity; use ActivityPub\Activities\UndoActivity; use CodeIgniter\Database\Exceptions\DatabaseException; @@ -17,24 +20,38 @@ use InvalidArgumentException; class FollowModel extends Model { + /** + * @var string + */ protected $table = 'activitypub_follows'; + /** + * @var string[] + */ protected $allowedFields = ['actor_id', 'target_actor_id']; - protected $returnType = \ActivityPub\Entities\Follow::class; - - protected $useTimestamps = true; - protected $updatedField = null; + /** + * @var string + */ + protected $returnType = Follow::class; /** - * @param \ActivityPub\Entities\Actor $actor Actor that is following - * @param \ActivityPub\Entities\Actor $targetActor Actor that is being followed - * @param bool $registerActivity - * @return void + * @var bool + */ + protected $useTimestamps = true; + + protected $updatedField; + + /** + * @param Actor $actor Actor that is following + * @param Actor $targetActor Actor that is being followed * @throws DatabaseException */ - public function addFollower($actor, $targetActor, $registerActivity = true) - { + public function addFollower( + Actor $actor, + Actor $targetActor, + bool $registerActivity = true + ): void { try { $this->db->transStart(); @@ -86,23 +103,22 @@ class FollowModel extends Model } $this->db->transComplete(); - } catch (\Exception $e) { + } catch (Exception $exception) { // follow already exists, do nothing } } /** - * @param \ActivityPub\Entities\Actor $actor Actor that is unfollowing - * @param \ActivityPub\Entities\Actor $targetActor Actor that is being unfollowed - * @return void + * @param Actor $actor Actor that is unfollowing + * @param Actor $targetActor Actor that is being unfollowed * @throws InvalidArgumentException * @throws DatabaseException */ public function removeFollower( - $actor, - $targetActor, + Actor $actor, + Actor $targetActor, $registerActivity = true - ) { + ): void { $this->db->transStart(); $this->where([ diff --git a/app/Libraries/ActivityPub/Models/NoteModel.php b/app/Libraries/ActivityPub/Models/NoteModel.php index ed1600f3..fe33f0df 100644 --- a/app/Libraries/ActivityPub/Models/NoteModel.php +++ b/app/Libraries/ActivityPub/Models/NoteModel.php @@ -8,6 +8,9 @@ namespace ActivityPub\Models; +use ActivityPub\Entities\Actor; +use CodeIgniter\Database\Query; +use Exception; use ActivityPub\Entities\Note; use ActivityPub\Activities\AnnounceActivity; use ActivityPub\Activities\CreateActivity; @@ -21,11 +24,23 @@ use Michalsn\Uuid\UuidModel; class NoteModel extends UuidModel { + /** + * @var string + */ protected $table = 'activitypub_notes'; + /** + * @var string + */ protected $primaryKey = 'id'; + /** + * @var string[] + */ protected $uuidFields = ['id', 'in_reply_to_id', 'reblog_of_id']; + /** + * @var string[] + */ protected $allowedFields = [ 'id', 'uri', @@ -40,17 +55,34 @@ class NoteModel extends UuidModel 'published_at', ]; - protected $returnType = \ActivityPub\Entities\Note::class; + /** + * @var string + */ + protected $returnType = Note::class; + + /** + * @var bool + */ protected $useSoftDeletes = false; + /** + * @var bool + */ protected $useTimestamps = true; - protected $updatedField = null; + protected $updatedField; + + /** + * @var array + */ protected $validationRules = [ 'actor_id' => 'required', 'message_html' => 'required_without[reblog_of_id]|max_length[500]', ]; + /** + * @var string[] + */ protected $beforeInsert = ['setNoteId']; public function getNoteById($noteId) @@ -69,7 +101,7 @@ class NoteModel extends UuidModel { $hashedNoteUri = md5($noteUri); $cacheName = - config('ActivityPub')->cachePrefix . "note@{$hashedNoteUri}"; + config('ActivityPub')->cachePrefix . "note-{$hashedNoteUri}"; if (!($found = cache($cacheName))) { $found = $this->where('uri', $noteUri)->first(); @@ -82,9 +114,9 @@ class NoteModel extends UuidModel /** * Retrieves all published notes for a given actor ordered by publication date * - * @return \ActivityPub\Entities\Note[] + * @return Note[] */ - public function getActorPublishedNotes($actorId) + public function getActorPublishedNotes($actorId): array { $cacheName = config('ActivityPub')->cachePrefix . @@ -108,12 +140,12 @@ class NoteModel extends UuidModel * Retrieves all published replies for a given note. * By default, it does not get replies from blocked actors. * - * @param mixed $noteId - * @param boolean $withBlocked false by default - * @return array + * @return Note[] */ - public function getNoteReplies($noteId, $withBlocked = false) - { + public function getNoteReplies( + string $noteId, + bool $withBlocked = false + ): array { $cacheName = config('ActivityPub')->cachePrefix . "note#{$noteId}_replies" . @@ -167,6 +199,9 @@ class NoteModel extends UuidModel return $found; } + /** + * @return bool|Query + */ public function addPreviewCard($noteId, $previewCardId) { return $this->db->table('activitypub_notes_preview_cards')->insert([ @@ -178,15 +213,12 @@ class NoteModel extends UuidModel /** * Adds note in database along preview card if relevant * - * @param \ActivityPub\Entities\Note $note - * @param boolean $registerActivity - * @param boolean $createPreviewCard * @return string|false returns the new note id if success or false otherwise */ public function addNote( - $note, - $createPreviewCard = true, - $registerActivity = true + Note $note, + bool $createPreviewCard = true, + bool $registerActivity = true ) { helper('activitypub'); @@ -207,14 +239,12 @@ class NoteModel extends UuidModel !empty($messageUrls) && ($previewCard = get_or_create_preview_card_from_url( new URI($messageUrls[0]), - )) + )) && + !$this->addPreviewCard($newNoteId, $previewCard->id) ) { - if (!$this->addPreviewCard($newNoteId, $previewCard->id)) { - $this->db->transRollback(); - - // problem when linking note to preview card - return false; - } + $this->db->transRollback(); + // problem when linking note to preview card + return false; } } @@ -270,7 +300,7 @@ class NoteModel extends UuidModel return $newNoteId; } - public function editNote($updatedNote) + public function editNote($updatedNote): bool { $this->db->transStart(); @@ -290,7 +320,7 @@ class NoteModel extends UuidModel DATE_W3C, ); model('ActivityModel')->update($scheduledActivity->id, [ - 'payload' => json_encode($newPayload), + 'payload' => json_encode($newPayload, JSON_THROW_ON_ERROR), 'scheduled_at' => $updatedNote->published_at, ]); @@ -301,7 +331,7 @@ class NoteModel extends UuidModel $prefix = config('ActivityPub')->cachePrefix; $hashedNoteUri = md5($updatedNote->uri); cache()->delete($prefix . "note#{$updatedNote->id}"); - cache()->delete($prefix . "note@{$hashedNoteUri}"); + cache()->delete($prefix . "note-{$hashedNoteUri}"); $this->db->transComplete(); @@ -311,10 +341,9 @@ class NoteModel extends UuidModel /** * Removes a note from the database and decrements meta data * - * @param \ActivityPub\Entities\Note $note - * @return mixed + * @return BaseResult|bool */ - public function removeNote($note, $registerActivity = true) + public function removeNote(Note $note, bool $registerActivity = true) { $this->db->transStart(); @@ -339,7 +368,7 @@ class NoteModel extends UuidModel $replyToNote = $note->reply_to_note; cache()->delete($cachePrefix . "note#{$replyToNote->id}"); - cache()->delete($cachePrefix . "note@{$replyToNote->uri}"); + cache()->delete($cachePrefix . "note-{$replyToNote->uri}"); cache()->delete($cachePrefix . "note#{$replyToNote->id}_replies"); cache()->delete( $cachePrefix . "note#{$replyToNote->id}_replies_withBlocked", @@ -358,19 +387,18 @@ class NoteModel extends UuidModel $this->removeNote($reply); } - if ($note->preview_card) { - // check that preview card in no longer used elsewhere before deleting it - if ( - $this->db - ->table('activitypub_notes_preview_cards') - ->where('preview_card_id', $note->preview_card->id) - ->countAll() <= 1 - ) { - model('PreviewCardModel')->deletePreviewCard( - $note->preview_card->id, - $note->preview_card->url, - ); - } + // check that preview card in no longer used elsewhere before deleting it + if ( + $note->preview_card && + $this->db + ->table('activitypub_notes_preview_cards') + ->where('preview_card_id', $note->preview_card->id) + ->countAll() <= 1 + ) { + model('PreviewCardModel')->deletePreviewCard( + $note->preview_card->id, + $note->preview_card->url, + ); } Events::trigger('on_note_remove', $note); @@ -408,7 +436,7 @@ class NoteModel extends UuidModel // clear note + replies / reblogs + actor and its published notes $hashedNoteUri = md5($note->uri); cache()->delete($cachePrefix . "note#{$note->id}"); - cache()->delete($cachePrefix . "note@{$hashedNoteUri}"); + cache()->delete($cachePrefix . "note-{$hashedNoteUri}"); cache()->delete($cachePrefix . "note#{$note->id}_replies"); cache()->delete($cachePrefix . "note#{$note->id}_replies_withBlocked"); cache()->delete($cachePrefix . "note#{$note->id}_reblogs"); @@ -421,13 +449,16 @@ class NoteModel extends UuidModel return $result; } + /** + * @return string|bool + */ public function addReply( $reply, $createPreviewCard = true, $registerActivity = true ) { if (!$reply->in_reply_to_id) { - throw new \Exception('Passed note is not a reply!'); + throw new Exception('Passed note is not a reply!'); } $this->db->transStart(); @@ -444,7 +475,7 @@ class NoteModel extends UuidModel $prefix = config('ActivityPub')->cachePrefix; $hashedNoteUri = md5($reply->reply_to_note->uri); cache()->delete($prefix . "note#{$reply->in_reply_to_id}"); - cache()->delete($prefix . "note@{$hashedNoteUri}"); + cache()->delete($prefix . "note-{$hashedNoteUri}"); cache()->delete($prefix . "note#{$reply->in_reply_to_id}_replies"); cache()->delete( $prefix . "note#{$reply->in_reply_to_id}_replies_withBlocked", @@ -458,12 +489,9 @@ class NoteModel extends UuidModel } /** - * - * @param \ActivityPub\Entities\Actor $actor - * @param \ActivityPub\Entities\Note $note * @return ActivityPub\Models\BaseResult|int|string|false */ - public function reblog($actor, $note, $registerActivity = true) + public function reblog(Actor $actor, Note $note, $registerActivity = true) { $this->db->transStart(); @@ -490,7 +518,7 @@ class NoteModel extends UuidModel $hashedNoteUri = md5($note->uri); cache()->delete($prefix . "note#{$note->id}"); - cache()->delete($prefix . "note@{$hashedNoteUri}"); + cache()->delete($prefix . "note-{$hashedNoteUri}"); cache()->delete($prefix . "note#{$note->id}_reblogs"); Events::trigger('on_note_reblog', $actor, $note); @@ -526,10 +554,9 @@ class NoteModel extends UuidModel } /** - * @param \ActivityPub\Entities\Note $reblogNote - * @return mixed + * @return BaseResult|bool */ - public function undoReblog($reblogNote, $registerActivity = true) + public function undoReblog(Note $reblogNote, bool $registerActivity = true) { $this->db->transStart(); @@ -553,9 +580,9 @@ class NoteModel extends UuidModel $hashedReblogNoteUri = md5($reblogNote->uri); $hashedNoteUri = md5($reblogNote->reblog_of_note->uri); cache()->delete($cachePrefix . "note#{$reblogNote->id}"); - cache()->delete($cachePrefix . "note@{$hashedReblogNoteUri}"); + cache()->delete($cachePrefix . "note-{$hashedReblogNoteUri}"); cache()->delete($cachePrefix . "note#{$reblogNote->reblog_of_id}"); - cache()->delete($cachePrefix . "note@{$hashedNoteUri}"); + cache()->delete($cachePrefix . "note-{$hashedNoteUri}"); Events::trigger('on_note_undo_reblog', $reblogNote); @@ -621,7 +648,7 @@ class NoteModel extends UuidModel return $result; } - public function toggleReblog($actor, $note) + public function toggleReblog($actor, $note): void { if ( !($reblogNote = $this->where([ diff --git a/app/Libraries/ActivityPub/Models/PreviewCardModel.php b/app/Libraries/ActivityPub/Models/PreviewCardModel.php index f7dc02ae..5580e7bb 100644 --- a/app/Libraries/ActivityPub/Models/PreviewCardModel.php +++ b/app/Libraries/ActivityPub/Models/PreviewCardModel.php @@ -8,12 +8,20 @@ namespace ActivityPub\Models; +use CodeIgniter\Database\BaseResult; +use ActivityPub\Entities\PreviewCard; use CodeIgniter\Model; class PreviewCardModel extends Model { + /** + * @var string + */ protected $table = 'activitypub_preview_cards'; + /** + * @var string[] + */ protected $allowedFields = [ 'id', 'url', @@ -28,9 +36,19 @@ class PreviewCardModel extends Model 'html', ]; - protected $returnType = \ActivityPub\Entities\PreviewCard::class; + /** + * @var string + */ + protected $returnType = PreviewCard::class; + + /** + * @var bool + */ protected $useSoftDeletes = false; + /** + * @var bool + */ protected $useTimestamps = true; public function getPreviewCardFromUrl($url) @@ -38,7 +56,7 @@ class PreviewCardModel extends Model $hashedPreviewCardUrl = md5($url); $cacheName = config('ActivityPub')->cachePrefix . - "preview_card@{$hashedPreviewCardUrl}"; + "preview_card-{$hashedPreviewCardUrl}"; if (!($found = cache($cacheName))) { $found = $this->where('url', $url)->first(); cache()->save($cacheName, $found, DECADE); @@ -71,12 +89,15 @@ class PreviewCardModel extends Model return $found; } + /** + * @return bool|BaseResult + */ public function deletePreviewCard($id, $url) { $hashedPreviewCardUrl = md5($url); cache()->delete( config('ActivityPub')->cachePrefix . - "preview_card@{$hashedPreviewCardUrl}", + "preview_card-{$hashedPreviewCardUrl}", ); return $this->delete($id); diff --git a/app/Libraries/ActivityPub/Objects/ActorObject.php b/app/Libraries/ActivityPub/Objects/ActorObject.php index 4fa0b806..819d2991 100644 --- a/app/Libraries/ActivityPub/Objects/ActorObject.php +++ b/app/Libraries/ActivityPub/Objects/ActorObject.php @@ -8,6 +8,7 @@ namespace ActivityPub\Objects; +use ActivityPub\Entities\Actor; use ActivityPub\Core\ObjectType; class ActorObject extends ObjectType @@ -68,17 +69,14 @@ class ActorObject extends ObjectType /** * @var array */ - protected $icon; + protected $icon = []; /** * @var object */ protected $publicKey; - /** - * @param \ActivityPub\Entities\Actor $podcast - */ - public function __construct($actor) + public function __construct(Actor $actor) { $this->id = $actor->uri; diff --git a/app/Libraries/ActivityPub/Objects/NoteObject.php b/app/Libraries/ActivityPub/Objects/NoteObject.php index 92a83c53..2c2f6c36 100644 --- a/app/Libraries/ActivityPub/Objects/NoteObject.php +++ b/app/Libraries/ActivityPub/Objects/NoteObject.php @@ -13,6 +13,7 @@ namespace ActivityPub\Objects; +use ActivityPub\Entities\Note; use ActivityPub\Core\ObjectType; class NoteObject extends ObjectType @@ -35,10 +36,10 @@ class NoteObject extends ObjectType /** * @var array */ - protected $replies; + protected $replies = []; /** - * @param \ActivityPub\Entities\Note $note + * @param Note $note */ public function __construct($note) { diff --git a/app/Libraries/ActivityPub/Objects/OrderedCollectionObject.php b/app/Libraries/ActivityPub/Objects/OrderedCollectionObject.php index faabb78b..639bbc97 100644 --- a/app/Libraries/ActivityPub/Objects/OrderedCollectionObject.php +++ b/app/Libraries/ActivityPub/Objects/OrderedCollectionObject.php @@ -11,10 +11,15 @@ namespace ActivityPub\Objects; +use ActivityPub\Core\Activity; +use CodeIgniter\Pager\Pager; use ActivityPub\Core\ObjectType; class OrderedCollectionObject extends ObjectType { + /** + * @var string + */ protected $type = 'OrderedCollection'; /** @@ -43,18 +48,19 @@ class OrderedCollectionObject extends ObjectType protected $orderedItems; /** - * @param \ActivityPub\Libraries\ActivityPub\Activity[] $orderedItems - * @param \CodeIgniter\Pager\Pager $pager + * @param array $orderedItems */ - public function __construct($orderedItems, $pager = null) - { + public function __construct( + ?array $orderedItems = null, + ?Pager $pager = null + ) { $this->id = current_url(); - if ($pager) { + if ($pager !== null) { $totalItems = $pager->getTotal(); $this->totalItems = $totalItems; - if ($totalItems) { + if ($totalItems !== 0) { $this->first = $pager->getPageURI($pager->getFirstPage()); $this->current = $pager->getPageURI(); $this->last = $pager->getPageURI($pager->getLastPage()); diff --git a/app/Libraries/ActivityPub/Objects/OrderedCollectionPage.php b/app/Libraries/ActivityPub/Objects/OrderedCollectionPage.php index 98eba8f2..a5a5dde4 100644 --- a/app/Libraries/ActivityPub/Objects/OrderedCollectionPage.php +++ b/app/Libraries/ActivityPub/Objects/OrderedCollectionPage.php @@ -11,6 +11,7 @@ namespace ActivityPub\Objects; +use CodeIgniter\Pager\Pager; class OrderedCollectionPage extends OrderedCollectionObject { /** @@ -33,18 +34,18 @@ class OrderedCollectionPage extends OrderedCollectionObject */ protected $next; - /** - * @param \CodeIgniter\Pager\Pager $pager - * @param \ActivityPub\Libraries\ActivityPub\Activity[] $orderedItems - */ - public function __construct($pager, $orderedItems) + public function __construct(Pager $pager, ?array $orderedItems = null) { parent::__construct($orderedItems, $pager); $isFirstPage = $pager->getCurrentPage() === $pager->getFirstPage(); $isLastPage = $pager->getCurrentPage() === $pager->getLastPage(); - $isFirstPage && ($this->first = null); - $isLastPage && ($this->last = null); + if ($isFirstPage) { + $this->first = null; + } + if ($isLastPage) { + $this->last = null; + } $this->id = $pager->getPageURI($pager->getCurrentPage()); $this->partOf = $pager->getPageURI(); diff --git a/app/Libraries/ActivityPub/WebFinger.php b/app/Libraries/ActivityPub/WebFinger.php index 58f6ce4b..c273a075 100644 --- a/app/Libraries/ActivityPub/WebFinger.php +++ b/app/Libraries/ActivityPub/WebFinger.php @@ -12,6 +12,9 @@ use Exception; class WebFinger { + /** + * @var string + */ const RESOURCE_PATTERN = '/^acct:(?P([\w_]+))@(?P([\w\-\.]+[\w]+)(:[\d]+)?)$/x'; /** @@ -19,6 +22,11 @@ class WebFinger */ protected $username; + /** + * @var string + */ + protected $domain; + /** * @var string */ @@ -37,12 +45,12 @@ class WebFinger /** * @var array */ - protected $aliases; + protected $aliases = []; /** - * @var string + * @var array */ - protected $links; + protected $links = []; /** * @param string $resource @@ -94,11 +102,23 @@ class WebFinger ]; } + /** + * Get WebFinger response as an array + * + * @return array + */ + public function toArray(): array + { + return [ + 'subject' => $this->subject, + 'aliases' => $this->aliases, + 'links' => $this->links, + ]; + } /** * Split resource into its parts (username, domain) * - * @param string $resource - * @return bool|array + * @return bool|mixed */ private function splitResource(string $resource) { @@ -109,18 +129,4 @@ class WebFinger return $matches; } - - /** - * Get WebFinger response as an array - * - * @return array - */ - public function toArray() - { - return [ - 'subject' => $this->subject, - 'aliases' => $this->aliases, - 'links' => $this->links, - ]; - } } diff --git a/app/Libraries/Analytics/AnalyticsTrait.php b/app/Libraries/Analytics/AnalyticsTrait.php index 2b0a69e7..4f9a374c 100644 --- a/app/Libraries/Analytics/AnalyticsTrait.php +++ b/app/Libraries/Analytics/AnalyticsTrait.php @@ -8,14 +8,11 @@ namespace Analytics; +use Config\Services; +use Config\Database; trait AnalyticsTrait { - /** - * - * @param integer $podcastId - * @return void - */ - protected function registerPodcastWebpageHit($podcastId) + protected function registerPodcastWebpageHit(int $podcastId): void { helper('analytics'); @@ -24,11 +21,11 @@ trait AnalyticsTrait set_user_session_referer(); set_user_session_entry_page(); - $session = \Config\Services::session(); + $session = Services::session(); $session->start(); if (!$session->get('denyListIp')) { - $db = \Config\Database::connect(); + $db = Database::connect(); $referer = $session->get('referer'); $domain = empty(parse_url($referer, PHP_URL_HOST)) @@ -38,7 +35,7 @@ trait AnalyticsTrait $keywords = empty($queries['q']) ? null : $queries['q']; $procedureName = $db->prefixTable('analytics_website'); - $db->query("call $procedureName(?,?,?,?,?,?)", [ + $db->query("call {$procedureName}(?,?,?,?,?,?)", [ $podcastId, $session->get('browser'), $session->get('entryPage'), diff --git a/app/Libraries/Analytics/Config/Analytics.php b/app/Libraries/Analytics/Config/Analytics.php index 64a85b5d..28cf7379 100644 --- a/app/Libraries/Analytics/Config/Analytics.php +++ b/app/Libraries/Analytics/Config/Analytics.php @@ -18,6 +18,7 @@ class Analytics extends BaseConfig * -------------------------------------------------------------------- * Route filters options * -------------------------------------------------------------------- + * @var array */ public $routeFilters = [ 'analytics-full-data' => '', @@ -27,11 +28,8 @@ class Analytics extends BaseConfig /** * get the full audio file url - * - * @param string $filename - * @return string */ - public function getAudioFileUrl(string $audioFilePath) + public function getAudioFileUrl(string $audioFilePath): string { return base_url($audioFilePath); } diff --git a/app/Libraries/Analytics/Config/Routes.php b/app/Libraries/Analytics/Config/Routes.php index 62de878c..15b11a69 100644 --- a/app/Libraries/Analytics/Config/Routes.php +++ b/app/Libraries/Analytics/Config/Routes.php @@ -21,10 +21,10 @@ $routes->addPlaceholder( $routes->group('', ['namespace' => 'Analytics\Controllers'], function ( $routes -) { +): void { $routes->group(config('Analytics')->gateway . '/(:num)/(:class)', function ( $routes - ) { + ): void { $routes->get('/', 'AnalyticsController::getData/$1/$2', [ 'as' => 'analytics-full-data', 'filter' => config('Analytics')->routeFilters[ diff --git a/app/Libraries/Analytics/Controllers/AnalyticsController.php b/app/Libraries/Analytics/Controllers/AnalyticsController.php index d80f03d5..139892b3 100644 --- a/app/Libraries/Analytics/Controllers/AnalyticsController.php +++ b/app/Libraries/Analytics/Controllers/AnalyticsController.php @@ -8,6 +8,8 @@ namespace Analytics\Controllers; +use CodeIgniter\HTTP\ResponseInterface; +use CodeIgniter\Exceptions\PageNotFoundException; use CodeIgniter\Controller; class AnalyticsController extends Controller @@ -25,7 +27,7 @@ class AnalyticsController extends Controller public function _remap($method, ...$params) { if (!isset($params[1])) { - throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); + throw PageNotFoundException::forPageNotFound(); } $this->className = model('Analytics' . $params[1] . 'Model'); @@ -37,7 +39,7 @@ class AnalyticsController extends Controller ); } - public function getData($podcastId, $episodeId) + public function getData($podcastId, $episodeId): ResponseInterface { $analytics_model = new $this->className(); $methodName = $this->methodName; @@ -45,10 +47,10 @@ class AnalyticsController extends Controller return $this->response->setJSON( $analytics_model->$methodName($podcastId, $episodeId), ); - } else { - return $this->response->setJSON( - $analytics_model->$methodName($podcastId), - ); } + + return $this->response->setJSON( + $analytics_model->$methodName($podcastId), + ); } } diff --git a/app/Libraries/Analytics/Controllers/EpisodeAnalyticsController.php b/app/Libraries/Analytics/Controllers/EpisodeAnalyticsController.php index b9a2cb43..f2fa71f7 100644 --- a/app/Libraries/Analytics/Controllers/EpisodeAnalyticsController.php +++ b/app/Libraries/Analytics/Controllers/EpisodeAnalyticsController.php @@ -8,6 +8,11 @@ namespace Analytics\Controllers; +use Analytics\Config\Analytics; +use CodeIgniter\HTTP\RequestInterface; +use CodeIgniter\HTTP\ResponseInterface; +use Psr\Log\LoggerInterface; +use Config\Services; use CodeIgniter\Controller; class EpisodeAnalyticsController extends Controller @@ -22,17 +27,17 @@ class EpisodeAnalyticsController extends Controller protected $helpers = ['analytics']; /** - * @var \Analytics\Config\Analytics + * @var Analytics */ protected $config; /** * Constructor. */ public function initController( - \CodeIgniter\HTTP\RequestInterface $request, - \CodeIgniter\HTTP\ResponseInterface $response, - \Psr\Log\LoggerInterface $logger - ) { + RequestInterface $request, + ResponseInterface $response, + LoggerInterface $logger + ): void { // Do Not Edit This Line parent::initController($request, $response, $logger); @@ -52,8 +57,9 @@ class EpisodeAnalyticsController extends Controller // Add one hit to this episode: public function hit($base64EpisodeData, ...$audioFilePath) { - $session = \Config\Services::session(); + $session = Services::session(); $session->start(); + $serviceName = ''; if (isset($_GET['_from'])) { $serviceName = $_GET['_from']; diff --git a/app/Libraries/Analytics/Controllers/UnknownUserAgentsController.php b/app/Libraries/Analytics/Controllers/UnknownUserAgentsController.php index c2664818..7d5549a0 100644 --- a/app/Libraries/Analytics/Controllers/UnknownUserAgentsController.php +++ b/app/Libraries/Analytics/Controllers/UnknownUserAgentsController.php @@ -8,11 +8,12 @@ namespace Analytics\Controllers; +use CodeIgniter\HTTP\ResponseInterface; use CodeIgniter\Controller; class UnknownUserAgentsController extends Controller { - public function index($lastKnownId = 0) + public function index($lastKnownId = 0): ResponseInterface { $model = model('UnknownUserAgentsModel'); diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-120000_add_analytics_podcasts.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-120000_add_analytics_podcasts.php index e89b1d19..7db412ae 100644 --- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-120000_add_analytics_podcasts.php +++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-120000_add_analytics_podcasts.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddAnalyticsPodcasts extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'podcast_id' => [ @@ -54,7 +54,7 @@ class AddAnalyticsPodcasts extends Migration $this->forge->createTable('analytics_podcasts'); } - public function down() + public function down(): void { $this->forge->dropTable('analytics_podcasts'); } diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-130000_add_analytics_podcasts_by_episode.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-130000_add_analytics_podcasts_by_episode.php index 72d9b861..ebd78f3f 100644 --- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-130000_add_analytics_podcasts_by_episode.php +++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-130000_add_analytics_podcasts_by_episode.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddAnalyticsPodcastsByEpisode extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'podcast_id' => [ @@ -50,7 +50,7 @@ class AddAnalyticsPodcastsByEpisode extends Migration $this->forge->createTable('analytics_podcasts_by_episode'); } - public function down() + public function down(): void { $this->forge->dropTable('analytics_podcasts_by_episode'); } diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-130000_add_analytics_podcasts_by_hour.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-130000_add_analytics_podcasts_by_hour.php index 4e898f41..31f2ed72 100644 --- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-130000_add_analytics_podcasts_by_hour.php +++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-130000_add_analytics_podcasts_by_hour.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddAnalyticsPodcastsByHour extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'podcast_id' => [ @@ -45,7 +45,7 @@ class AddAnalyticsPodcastsByHour extends Migration $this->forge->createTable('analytics_podcasts_by_hour'); } - public function down() + public function down(): void { $this->forge->dropTable('analytics_podcasts_by_hour'); } diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-140000_add_analytics_podcasts_by_player.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-140000_add_analytics_podcasts_by_player.php index 424ede82..a2429dd5 100644 --- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-140000_add_analytics_podcasts_by_player.php +++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-140000_add_analytics_podcasts_by_player.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddAnalyticsPodcastsByPlayer extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'podcast_id' => [ @@ -70,7 +70,7 @@ class AddAnalyticsPodcastsByPlayer extends Migration $this->forge->createTable('analytics_podcasts_by_player'); } - public function down() + public function down(): void { $this->forge->dropTable('analytics_podcasts_by_player'); } diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-150000_add_analytics_podcasts_by_country.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-150000_add_analytics_podcasts_by_country.php index d2a14f22..4fe827ff 100644 --- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-150000_add_analytics_podcasts_by_country.php +++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-150000_add_analytics_podcasts_by_country.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddAnalyticsPodcastsByCountry extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'podcast_id' => [ @@ -46,7 +46,7 @@ class AddAnalyticsPodcastsByCountry extends Migration $this->forge->createTable('analytics_podcasts_by_country'); } - public function down() + public function down(): void { $this->forge->dropTable('analytics_podcasts_by_country'); } diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-160000_add_analytics_podcasts_by_region.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-160000_add_analytics_podcasts_by_region.php index ee55120d..51a60486 100644 --- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-160000_add_analytics_podcasts_by_region.php +++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-160000_add_analytics_podcasts_by_region.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddAnalyticsPodcastsByRegion extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'podcast_id' => [ @@ -64,7 +64,7 @@ class AddAnalyticsPodcastsByRegion extends Migration $this->forge->createTable('analytics_podcasts_by_region'); } - public function down() + public function down(): void { $this->forge->dropTable('analytics_podcasts_by_region'); } diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-160000_add_podcasts_platforms.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-160000_add_podcasts_platforms.php index dbfd70e6..3bd16c3a 100644 --- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-160000_add_podcasts_platforms.php +++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-160000_add_podcasts_platforms.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddPodcastsPlatforms extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'podcast_id' => [ @@ -51,7 +51,7 @@ class AddPodcastsPlatforms extends Migration $this->forge->createTable('podcasts_platforms'); } - public function down() + public function down(): void { $this->forge->dropTable('podcasts_platforms'); } diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-170000_add_analytics_website_by_browser.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-170000_add_analytics_website_by_browser.php index 5a542c49..80cf8dcb 100644 --- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-170000_add_analytics_website_by_browser.php +++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-170000_add_analytics_website_by_browser.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddAnalyticsWebsiteByBrowser extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'podcast_id' => [ @@ -46,7 +46,7 @@ class AddAnalyticsWebsiteByBrowser extends Migration $this->forge->createTable('analytics_website_by_browser'); } - public function down() + public function down(): void { $this->forge->dropTable('analytics_website_by_browser'); } diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-180000_add_analytics_website_by_referer.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-180000_add_analytics_website_by_referer.php index 45711429..9bbfd9ed 100644 --- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-180000_add_analytics_website_by_referer.php +++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-180000_add_analytics_website_by_referer.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddAnalyticsWebsiteByReferer extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'podcast_id' => [ @@ -55,7 +55,7 @@ class AddAnalyticsWebsiteByReferer extends Migration $this->forge->createTable('analytics_website_by_referer'); } - public function down() + public function down(): void { $this->forge->dropTable('analytics_website_by_referer'); } diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-190000_add_analytics_website_by_entry_page.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-190000_add_analytics_website_by_entry_page.php index 8e331a6e..d422dad3 100644 --- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-190000_add_analytics_website_by_entry_page.php +++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-190000_add_analytics_website_by_entry_page.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddAnalyticsWebsiteByEntryPage extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'podcast_id' => [ @@ -45,7 +45,7 @@ class AddAnalyticsWebsiteByEntryPage extends Migration $this->forge->createTable('analytics_website_by_entry_page'); } - public function down() + public function down(): void { $this->forge->dropTable('analytics_website_by_entry_page'); } diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-200000_add_analytics_unknown_useragents.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-200000_add_analytics_unknown_useragents.php index 2ff7fcea..60db8159 100644 --- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-200000_add_analytics_unknown_useragents.php +++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-200000_add_analytics_unknown_useragents.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddAnalyticsUnknownUseragents extends Migration { - public function up() + public function up(): void { $this->forge->addField([ 'id' => [ @@ -46,7 +46,7 @@ class AddAnalyticsUnknownUseragents extends Migration $this->forge->createTable('analytics_unknown_useragents'); } - public function down() + public function down(): void { $this->forge->dropTable('analytics_unknown_useragents'); } diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_podcasts_procedure.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_podcasts_procedure.php index ca579b3a..7e2052d4 100644 --- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_podcasts_procedure.php +++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_podcasts_procedure.php @@ -15,7 +15,7 @@ use CodeIgniter\Database\Migration; class AddAnalyticsPodcastsProcedure extends Migration { - public function up() + public function up(): void { // Creates Procedure for data insertion // Example: CALL analytics_podcasts(1, 2, 'FR', 'IDF', 48.853, 2.349, PodcastAddict, 'phone', 'android', 0, 1); @@ -77,7 +77,7 @@ class AddAnalyticsPodcastsProcedure extends Migration $this->db->query($createQuery); } - public function down() + public function down(): void { $prefix = $this->db->getPrefix(); $this->db->query( diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_unknown_useragents_procedure.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_unknown_useragents_procedure.php index 5f6a65a9..67bb5813 100644 --- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_unknown_useragents_procedure.php +++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_unknown_useragents_procedure.php @@ -15,26 +15,26 @@ use CodeIgniter\Database\Migration; class AddAnalyticsUnknownUseragentsProcedure extends Migration { - public function up() + public function up(): void { // Creates Procedure for data insertion // Example: CALL analytics_unknown_useragents('Podcasts/1430.46 CFNetwork/1125.2 Darwin/19.4.0'); $procedureName = $this->db->prefixTable('analytics_unknown_useragents'); $createQuery = <<db->query($createQuery); } - public function down() + public function down(): void { $procedureName = $this->db->prefixTable('analytics_unknown_useragents'); - $this->db->query("DROP PROCEDURE IF EXISTS `$procedureName`"); + $this->db->query("DROP PROCEDURE IF EXISTS `{$procedureName}`"); } } diff --git a/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_website_procedure.php b/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_website_procedure.php index c4e5ac8c..3817fcfc 100644 --- a/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_website_procedure.php +++ b/app/Libraries/Analytics/Database/Migrations/2017-12-01-210000_add_analytics_website_procedure.php @@ -15,13 +15,13 @@ use CodeIgniter\Database\Migration; class AddAnalyticsWebsiteProcedure extends Migration { - public function up() + public function up(): void { // Creates Procedure for data insertion // Example: CALL analytics_website(1,'FR','Firefox'); $procedureName = $this->db->prefixTable('analytics_website'); $createQuery = <<db->query($createQuery); } - public function down() + public function down(): void { $procedureName = $this->db->prefixTable('analytics_website'); - $this->db->query("DROP PROCEDURE IF EXISTS `$procedureName`"); + $this->db->query("DROP PROCEDURE IF EXISTS `{$procedureName}`"); } } diff --git a/app/Libraries/Analytics/Entities/AnalyticsPodcasts.php b/app/Libraries/Analytics/Entities/AnalyticsPodcasts.php index 1c05c275..ef0df9a9 100644 --- a/app/Libraries/Analytics/Entities/AnalyticsPodcasts.php +++ b/app/Libraries/Analytics/Entities/AnalyticsPodcasts.php @@ -10,10 +10,14 @@ namespace Analytics\Entities; -use CodeIgniter\Entity; +use datetime; +use CodeIgniter\Entity\Entity; class AnalyticsPodcasts extends Entity { + /** + * @var array + */ protected $casts = [ 'podcast_id' => 'integer', 'date' => 'datetime', diff --git a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByCountry.php b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByCountry.php index 0f03599a..b02876dc 100644 --- a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByCountry.php +++ b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByCountry.php @@ -10,7 +10,8 @@ namespace Analytics\Entities; -use CodeIgniter\Entity; +use datetime; +use CodeIgniter\Entity\Entity; class AnalyticsPodcastsByCountry extends Entity { @@ -19,6 +20,9 @@ class AnalyticsPodcastsByCountry extends Entity */ protected $labels; + /** + * @var array + */ protected $casts = [ 'podcast_id' => 'integer', 'country_code' => 'string', diff --git a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByEpisode.php b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByEpisode.php index d2e570da..b512a945 100644 --- a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByEpisode.php +++ b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByEpisode.php @@ -10,10 +10,14 @@ namespace Analytics\Entities; -use CodeIgniter\Entity; +use datetime; +use CodeIgniter\Entity\Entity; class AnalyticsPodcastsByEpisode extends Entity { + /** + * @var array + */ protected $casts = [ 'podcast_id' => 'integer', 'episode_id' => 'integer', diff --git a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByHour.php b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByHour.php index 6dc44330..7794e5a3 100644 --- a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByHour.php +++ b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByHour.php @@ -10,10 +10,14 @@ namespace Analytics\Entities; -use CodeIgniter\Entity; +use datetime; +use CodeIgniter\Entity\Entity; class AnalyticsPodcastsByHour extends Entity { + /** + * @var array + */ protected $casts = [ 'podcast_id' => 'integer', 'date' => 'datetime', diff --git a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByPlayer.php b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByPlayer.php index 1bd636ee..7bec2f40 100644 --- a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByPlayer.php +++ b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByPlayer.php @@ -10,10 +10,14 @@ namespace Analytics\Entities; -use CodeIgniter\Entity; +use datetime; +use CodeIgniter\Entity\Entity; class AnalyticsPodcastsByPlayer extends Entity { + /** + * @var array + */ protected $casts = [ 'podcast_id' => 'integer', 'app' => '?string', diff --git a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByRegion.php b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByRegion.php index 7f62f570..6afa1944 100644 --- a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByRegion.php +++ b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByRegion.php @@ -10,10 +10,14 @@ namespace Analytics\Entities; -use CodeIgniter\Entity; +use datetime; +use CodeIgniter\Entity\Entity; class AnalyticsPodcastsByRegion extends Entity { + /** + * @var array + */ protected $casts = [ 'podcast_id' => 'integer', 'country_code' => 'string', diff --git a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByService.php b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByService.php index d73eaeff..e17fe504 100644 --- a/app/Libraries/Analytics/Entities/AnalyticsPodcastsByService.php +++ b/app/Libraries/Analytics/Entities/AnalyticsPodcastsByService.php @@ -10,7 +10,9 @@ namespace Analytics\Entities; -use CodeIgniter\Entity; +use datetime; +use Opawg\UserAgentsPhp\UserAgentsRSS; +use CodeIgniter\Entity\Entity; class AnalyticsPodcastsByService extends Entity { @@ -19,6 +21,9 @@ class AnalyticsPodcastsByService extends Entity */ protected $labels; + /** + * @var array + */ protected $casts = [ 'podcast_id' => 'integer', 'app' => '?string', @@ -31,8 +36,7 @@ class AnalyticsPodcastsByService extends Entity public function getLabels() { - return \Opawg\UserAgentsPhp\UserAgentsRSS::getName( - $this->attributes['labels'], - ) ?? $this->attributes['labels']; + return UserAgentsRSS::getName($this->attributes['labels']) ?? + $this->attributes['labels']; } } diff --git a/app/Libraries/Analytics/Entities/AnalyticsUnknownUseragents.php b/app/Libraries/Analytics/Entities/AnalyticsUnknownUseragents.php index b5a9dd3e..4a843ab2 100644 --- a/app/Libraries/Analytics/Entities/AnalyticsUnknownUseragents.php +++ b/app/Libraries/Analytics/Entities/AnalyticsUnknownUseragents.php @@ -10,10 +10,13 @@ namespace Analytics\Entities; -use CodeIgniter\Entity; +use CodeIgniter\Entity\Entity; class AnalyticsUnknownUseragents extends Entity { + /** + * @var array + */ protected $casts = [ 'useragent' => 'integer', 'hits' => 'integer', diff --git a/app/Libraries/Analytics/Entities/AnalyticsWebsiteByBrowser.php b/app/Libraries/Analytics/Entities/AnalyticsWebsiteByBrowser.php index d753e9e7..0ca6734c 100644 --- a/app/Libraries/Analytics/Entities/AnalyticsWebsiteByBrowser.php +++ b/app/Libraries/Analytics/Entities/AnalyticsWebsiteByBrowser.php @@ -10,10 +10,14 @@ namespace Analytics\Entities; -use CodeIgniter\Entity; +use datetime; +use CodeIgniter\Entity\Entity; class AnalyticsWebsiteByBrowser extends Entity { + /** + * @var array + */ protected $casts = [ 'podcast_id' => 'integer', 'browser' => 'string', diff --git a/app/Libraries/Analytics/Entities/AnalyticsWebsiteByEntryPage.php b/app/Libraries/Analytics/Entities/AnalyticsWebsiteByEntryPage.php index 1e1c6d98..f70fcbb3 100644 --- a/app/Libraries/Analytics/Entities/AnalyticsWebsiteByEntryPage.php +++ b/app/Libraries/Analytics/Entities/AnalyticsWebsiteByEntryPage.php @@ -10,10 +10,14 @@ namespace Analytics\Entities; -use CodeIgniter\Entity; +use datetime; +use CodeIgniter\Entity\Entity; class AnalyticsWebsiteByEntryPage extends Entity { + /** + * @var array + */ protected $casts = [ 'podcast_id' => 'integer', 'entry_page_url' => 'string', diff --git a/app/Libraries/Analytics/Entities/AnalyticsWebsiteByReferer.php b/app/Libraries/Analytics/Entities/AnalyticsWebsiteByReferer.php index 4fdf50b1..980fe575 100644 --- a/app/Libraries/Analytics/Entities/AnalyticsWebsiteByReferer.php +++ b/app/Libraries/Analytics/Entities/AnalyticsWebsiteByReferer.php @@ -10,10 +10,14 @@ namespace Analytics\Entities; -use CodeIgniter\Entity; +use datetime; +use CodeIgniter\Entity\Entity; class AnalyticsWebsiteByReferer extends Entity { + /** + * @var array + */ protected $casts = [ 'podcast_id' => 'integer', 'referer_url' => 'string', diff --git a/app/Libraries/Analytics/Helpers/analytics_helper.php b/app/Libraries/Analytics/Helpers/analytics_helper.php index e18806a8..d5d44f94 100644 --- a/app/Libraries/Analytics/Helpers/analytics_helper.php +++ b/app/Libraries/Analytics/Helpers/analytics_helper.php @@ -1,5 +1,11 @@ start(); if (!$session->has('denyListIp')) { $session->set( 'denyListIp', - \Podlibre\Ipcat\IpDb::find($_SERVER['REMOTE_ADDR']) != null, + IpDb::find($_SERVER['REMOTE_ADDR']) != null, ); } } @@ -103,9 +100,9 @@ if (!function_exists('set_user_session_location')) { /** * Set user country in session variable, for analytic purposes */ - function set_user_session_location() + function set_user_session_location(): void { - $session = \Config\Services::session(); + $session = Services::session(); $session->start(); $location = [ @@ -118,7 +115,7 @@ if (!function_exists('set_user_session_location')) { // Finds location: if (!$session->has('location')) { try { - $cityReader = new \GeoIp2\Database\Reader( + $cityReader = new Reader( WRITEPATH . 'uploads/GeoLite2-City/GeoLite2-City.mmdb', ); $city = $cityReader->city($_SERVER['REMOTE_ADDR']); @@ -133,8 +130,8 @@ if (!function_exists('set_user_session_location')) { 'latitude' => round($city->location->latitude, 3), 'longitude' => round($city->location->longitude, 3), ]; - } catch (\Exception $e) { // If things go wrong the show must go on and the user must be able to download the file + } catch (Exception $exception) { } $session->set('location', $location); } @@ -145,9 +142,9 @@ if (!function_exists('set_user_session_player')) { /** * Set user player in session variable, for analytic purposes */ - function set_user_session_player() + function set_user_session_player(): void { - $session = \Config\Services::session(); + $session = Services::session(); $session->start(); if (!$session->has('player')) { @@ -155,11 +152,9 @@ if (!function_exists('set_user_session_player')) { $userAgent = $_SERVER['HTTP_USER_AGENT']; try { - $playerFound = \Opawg\UserAgentsPhp\UserAgents::find( - $userAgent, - ); - } catch (\Exception $e) { + $playerFound = UserAgents::find($userAgent); // If things go wrong the show must go on and the user must be able to download the file + } catch (Exception $exception) { } if ($playerFound) { $session->set('player', $playerFound); @@ -172,16 +167,16 @@ if (!function_exists('set_user_session_player')) { ]); // Add to unknown list try { - $db = \Config\Database::connect(); + $db = Database::connect(); $procedureNameAnalyticsUnknownUseragents = $db->prefixTable( 'analytics_unknown_useragents', ); $db->query( - "CALL $procedureNameAnalyticsUnknownUseragents(?)", + "CALL {$procedureNameAnalyticsUnknownUseragents}(?)", [$userAgent], ); - } catch (\Exception $e) { // If things go wrong the show must go on and the user must be able to download the file + } catch (Exception $exception) { } } } @@ -191,20 +186,18 @@ if (!function_exists('set_user_session_player')) { if (!function_exists('set_user_session_browser')) { /** * Set user browser in session variable, for analytic purposes - * - * @return void */ - function set_user_session_browser() + function set_user_session_browser(): void { - $session = \Config\Services::session(); + $session = Services::session(); $session->start(); if (!$session->has('browser')) { $browserName = '- Other -'; try { - $whichbrowser = new \WhichBrowser\Parser(getallheaders()); + $whichbrowser = new Parser(getallheaders()); $browserName = $whichbrowser->browser->name; - } catch (\Exception $e) { + } catch (Exception $exception) { $browserName = '- Could not get browser name -'; } if ($browserName == null) { @@ -218,23 +211,21 @@ if (!function_exists('set_user_session_browser')) { if (!function_exists('set_user_session_referer')) { /** * Set user referer in session variable, for analytic purposes - * - * @return void */ - function set_user_session_referer() + function set_user_session_referer(): void { - $session = \Config\Services::session(); + $session = Services::session(); $session->start(); $newreferer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '- Direct -'; $newreferer = - parse_url($newreferer, PHP_URL_HOST) == + parse_url($newreferer, PHP_URL_HOST) === parse_url(current_url(false), PHP_URL_HOST) ? '- Direct -' : $newreferer; - if (!$session->has('referer') or $newreferer != '- Direct -') { + if (!$session->has('referer') || $newreferer != '- Direct -') { $session->set('referer', $newreferer); } } @@ -243,12 +234,10 @@ if (!function_exists('set_user_session_referer')) { if (!function_exists('set_user_session_entry_page')) { /** * Set user entry page in session variable, for analytic purposes - * - * @return void */ - function set_user_session_entry_page() + function set_user_session_entry_page(): void { - $session = \Config\Services::session(); + $session = Services::session(); $session->start(); $entryPage = $_SERVER['REQUEST_URI']; @@ -279,19 +268,17 @@ if (!function_exists('podcast_hit')) { * @param integer $bytesThreshold The minimum total number of bytes that must be downloaded so that an episode is counted (>1mn) * @param integer $fileSize The podcast complete file size * @param string $serviceName The name of the service that had fetched the RSS feed - * - * @return void */ function podcast_hit( - $podcastId, - $episodeId, - $bytesThreshold, - $fileSize, + int $podcastId, + int $episodeId, + int $bytesThreshold, + int $fileSize, $duration, $publicationDate, - $serviceName - ) { - $session = \Config\Services::session(); + string $serviceName + ): void { + $session = Services::session(); $session->start(); // We try to count (but if things went wrong the show should go on and the user should be able to download the file): @@ -333,19 +320,16 @@ if (!function_exists('podcast_hit')) { // If HTTP_RANGE is null we are downloading the complete file: if (!$httpRange) { $downloadedBytes = $fileSize; - } else { + } elseif ($httpRange != 'bytes=0-1') { // [0-1] bytes range requests are used (by Apple) to check that file exists and that 206 partial content is working. - // We don't count these requests: - if ($httpRange != 'bytes=0-1') { - // We calculate how many bytes are being downloaded based on HTTP_RANGE values: - $ranges = explode(',', substr($httpRange, 6)); - foreach ($ranges as $range) { - $parts = explode('-', $range); - $downloadedBytes += empty($parts[1]) - ? $fileSize - : $parts[1] - - (empty($parts[0]) ? 0 : $parts[0]); - } + // We don't count these requests. + // We calculate how many bytes are being downloaded based on HTTP_RANGE values: + $ranges = explode(',', substr($httpRange, 6)); + foreach ($ranges as $range) { + $parts = explode('-', $range); + $downloadedBytes += empty($parts[1]) + ? $fileSize + : $parts[1] - (empty($parts[0]) ? 0 : $parts[0]); } } // We save the number of downloaded bytes for this user and this episode: @@ -353,7 +337,7 @@ if (!function_exists('podcast_hit')) { // If more that 1mn was downloaded, that's a hit, we send that to the database: if ($downloadedBytes >= $bytesThreshold) { - $db = \Config\Database::connect(); + $db = Database::connect(); $procedureName = $db->prefixTable('analytics_podcasts'); $age = intdiv(time() - $publicationDate, 86400); @@ -374,7 +358,7 @@ if (!function_exists('podcast_hit')) { // We add one download if ($downloadsByUser) { $newListener = 0; - $downloadsByUser++; + ++$downloadsByUser; } else { $downloadsByUser = 1; } @@ -388,7 +372,7 @@ if (!function_exists('podcast_hit')) { ); $db->query( - "CALL $procedureName(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);", + "CALL {$procedureName}(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);", [ $podcastId, $episodeId, @@ -409,9 +393,9 @@ if (!function_exists('podcast_hit')) { ); } } - } catch (\Exception $e) { + } catch (Exception $exception) { // If things go wrong the show must go on and the user must be able to download the file - log_message('critical', $e); + log_message('critical', $exception); } } } diff --git a/app/Libraries/Analytics/Models/AnalyticsPodcastByCountryModel.php b/app/Libraries/Analytics/Models/AnalyticsPodcastByCountryModel.php index 13b9fdf8..d5661deb 100644 --- a/app/Libraries/Analytics/Models/AnalyticsPodcastByCountryModel.php +++ b/app/Libraries/Analytics/Models/AnalyticsPodcastByCountryModel.php @@ -10,25 +10,34 @@ namespace Analytics\Models; +use Analytics\Entities\AnalyticsPodcastsByCountry; use CodeIgniter\Model; class AnalyticsPodcastByCountryModel extends Model { + /** + * @var string + */ protected $table = 'analytics_podcasts_by_country'; - protected $allowedFields = []; - - protected $returnType = \Analytics\Entities\AnalyticsPodcastsByCountry::class; + /** + * @var string + */ + protected $returnType = AnalyticsPodcastsByCountry::class; + /** + * @var bool + */ protected $useSoftDeletes = false; + /** + * @var bool + */ protected $useTimestamps = false; /** * Gets country data for a podcast * - * @param int $podcastId - * - * @return array + * @return AnalyticsPodcastsByCountry[] */ public function getDataWeekly(int $podcastId): array { @@ -54,15 +63,14 @@ class AnalyticsPodcastByCountryModel extends Model 600, ); } + return $found; } /** * Gets country data for a podcast * - * @param int $podcastId - * - * @return array + * @return AnalyticsPodcastsByCountry[] */ public function getDataYearly(int $podcastId): array { diff --git a/app/Libraries/Analytics/Models/AnalyticsPodcastByEpisodeModel.php b/app/Libraries/Analytics/Models/AnalyticsPodcastByEpisodeModel.php index ed53ce22..07564287 100644 --- a/app/Libraries/Analytics/Models/AnalyticsPodcastByEpisodeModel.php +++ b/app/Libraries/Analytics/Models/AnalyticsPodcastByEpisodeModel.php @@ -10,24 +10,32 @@ namespace Analytics\Models; +use Analytics\Entities\AnalyticsPodcastsByEpisode; use CodeIgniter\Model; class AnalyticsPodcastByEpisodeModel extends Model { + /** + * @var string + */ protected $table = 'analytics_podcasts_by_episode'; - protected $allowedFields = []; - - protected $returnType = \Analytics\Entities\AnalyticsPodcastsByEpisode::class; + /** + * @var string + */ + protected $returnType = AnalyticsPodcastsByEpisode::class; + /** + * @var bool + */ protected $useSoftDeletes = false; + /** + * @var bool + */ protected $useTimestamps = false; /** - * @param int $podcastId - * @param int $episodeId - * - * @return array + * @return AnalyticsPodcastsByEpisode[] */ public function getDataByDay(int $podcastId, int $episodeId): array { @@ -53,14 +61,12 @@ class AnalyticsPodcastByEpisodeModel extends Model 600, ); } + return $found; } /** - * @param int $podcastId - * @param int $episodeId - * - * @return array + * @return AnalyticsPodcastsByEpisode[] */ public function getDataByMonth(int $podcastId, int $episodeId = null): array { @@ -85,6 +91,7 @@ class AnalyticsPodcastByEpisodeModel extends Model 600, ); } + return $found; } } diff --git a/app/Libraries/Analytics/Models/AnalyticsPodcastByHourModel.php b/app/Libraries/Analytics/Models/AnalyticsPodcastByHourModel.php index 4b5df270..34a5d18f 100644 --- a/app/Libraries/Analytics/Models/AnalyticsPodcastByHourModel.php +++ b/app/Libraries/Analytics/Models/AnalyticsPodcastByHourModel.php @@ -10,32 +10,39 @@ namespace Analytics\Models; +use Analytics\Entities\AnalyticsPodcastsByHour; use CodeIgniter\Model; class AnalyticsPodcastByHourModel extends Model { + /** + * @var string + */ protected $table = 'analytics_podcasts_by_hour'; - protected $allowedFields = []; - - protected $returnType = \Analytics\Entities\AnalyticsPodcastsByHour::class; + /** + * @var string + */ + protected $returnType = AnalyticsPodcastsByHour::class; + /** + * @var bool + */ protected $useSoftDeletes = false; + /** + * @var bool + */ protected $useTimestamps = false; /** * Gets hits data for a podcast * - * @param int $podcastId - * - * @return array + * @return AnalyticsPodcastsByHour[] */ public function getData(int $podcastId): array { if (!($found = cache("{$podcastId}_analytics_podcasts_by_hour"))) { - $found = $this->select( - 'right(concat(\'0\',hour,\'h\'),3) as labels', - ) + $found = $this->select("right(concat('0',hour,'h'),3) as labels") ->selectSum('hits', 'values') ->where([ 'podcast_id' => $podcastId, @@ -51,6 +58,7 @@ class AnalyticsPodcastByHourModel extends Model 600, ); } + return $found; } } diff --git a/app/Libraries/Analytics/Models/AnalyticsPodcastByPlayerModel.php b/app/Libraries/Analytics/Models/AnalyticsPodcastByPlayerModel.php index 23784344..636f2cbb 100644 --- a/app/Libraries/Analytics/Models/AnalyticsPodcastByPlayerModel.php +++ b/app/Libraries/Analytics/Models/AnalyticsPodcastByPlayerModel.php @@ -10,25 +10,34 @@ namespace Analytics\Models; +use Analytics\Entities\AnalyticsPodcastsByPlayer; use CodeIgniter\Model; class AnalyticsPodcastByPlayerModel extends Model { + /** + * @var string + */ protected $table = 'analytics_podcasts_by_player'; - protected $allowedFields = []; - - protected $returnType = \Analytics\Entities\AnalyticsPodcastsByPlayer::class; + /** + * @var string + */ + protected $returnType = AnalyticsPodcastsByPlayer::class; + /** + * @var bool + */ protected $useSoftDeletes = false; + /** + * @var bool + */ protected $useTimestamps = false; /** * Gets player data for a podcast * - * @param int $podcastId - * - * @return array + * @return AnalyticsPodcastsByPlayer[] */ public function getDataByAppWeekly(int $podcastId): array { @@ -61,9 +70,7 @@ class AnalyticsPodcastByPlayerModel extends Model /** * Gets player data for a podcast * - * @param int $podcastId - * - * @return array + * @return AnalyticsPodcastsByPlayer[] */ public function getDataByAppYearly(int $podcastId): array { @@ -96,9 +103,7 @@ class AnalyticsPodcastByPlayerModel extends Model /** * Gets os data for a podcast * - * @param int $podcastId - * - * @return array + * @return AnalyticsPodcastsByPlayer[] */ public function getDataByOsWeekly(int $podcastId): array { @@ -132,9 +137,7 @@ class AnalyticsPodcastByPlayerModel extends Model /** * Gets player data for a podcast * - * @param int $podcastId - * - * @return array + * @return AnalyticsPodcastsByPlayer[] */ public function getDataByDeviceWeekly(int $podcastId): array { @@ -167,9 +170,7 @@ class AnalyticsPodcastByPlayerModel extends Model /** * Gets bots data for a podcast * - * @param int $podcastId - * - * @return array + * @return AnalyticsPodcastsByPlayer[] */ public function getDataBots(int $podcastId): array { diff --git a/app/Libraries/Analytics/Models/AnalyticsPodcastByRegionModel.php b/app/Libraries/Analytics/Models/AnalyticsPodcastByRegionModel.php index 76a2f2dc..b40d606d 100644 --- a/app/Libraries/Analytics/Models/AnalyticsPodcastByRegionModel.php +++ b/app/Libraries/Analytics/Models/AnalyticsPodcastByRegionModel.php @@ -10,25 +10,34 @@ namespace Analytics\Models; +use Analytics\Entities\AnalyticsPodcastsByRegion; use CodeIgniter\Model; class AnalyticsPodcastByRegionModel extends Model { + /** + * @var string + */ protected $table = 'analytics_podcasts_by_region'; - protected $allowedFields = []; - - protected $returnType = \Analytics\Entities\AnalyticsPodcastsByRegion::class; + /** + * @var string + */ + protected $returnType = AnalyticsPodcastsByRegion::class; + /** + * @var bool + */ protected $useSoftDeletes = false; + /** + * @var bool + */ protected $useTimestamps = false; /** * Gets region data for a podcast * - * @param int $podcastId - * - * @return array + * @return AnalyticsPodcastsByRegion[] */ public function getData(int $podcastId): array { diff --git a/app/Libraries/Analytics/Models/AnalyticsPodcastByServiceModel.php b/app/Libraries/Analytics/Models/AnalyticsPodcastByServiceModel.php index a3039ae9..6bd889c4 100644 --- a/app/Libraries/Analytics/Models/AnalyticsPodcastByServiceModel.php +++ b/app/Libraries/Analytics/Models/AnalyticsPodcastByServiceModel.php @@ -10,25 +10,34 @@ namespace Analytics\Models; +use Analytics\Entities\AnalyticsPodcastsByService; use CodeIgniter\Model; class AnalyticsPodcastByServiceModel extends Model { + /** + * @var string + */ protected $table = 'analytics_podcasts_by_player'; - protected $allowedFields = []; - - protected $returnType = \Analytics\Entities\AnalyticsPodcastsByService::class; + /** + * @var string + */ + protected $returnType = AnalyticsPodcastsByService::class; + /** + * @var bool + */ protected $useSoftDeletes = false; + /** + * @var bool + */ protected $useTimestamps = false; /** * Gets service data for a podcast * - * @param int $podcastId - * - * @return array + * @return AnalyticsPodcastsByService[] */ public function getDataByServiceWeekly(int $podcastId): array { diff --git a/app/Libraries/Analytics/Models/AnalyticsPodcastModel.php b/app/Libraries/Analytics/Models/AnalyticsPodcastModel.php index a621bae2..dccf6842 100644 --- a/app/Libraries/Analytics/Models/AnalyticsPodcastModel.php +++ b/app/Libraries/Analytics/Models/AnalyticsPodcastModel.php @@ -10,25 +10,34 @@ namespace Analytics\Models; +use Analytics\Entities\AnalyticsPodcasts; use CodeIgniter\Model; class AnalyticsPodcastModel extends Model { + /** + * @var string + */ protected $table = 'analytics_podcasts'; - protected $allowedFields = []; - - protected $returnType = \Analytics\Entities\AnalyticsPodcasts::class; + /** + * @var string + */ + protected $returnType = AnalyticsPodcasts::class; + /** + * @var bool + */ protected $useSoftDeletes = false; + /** + * @var bool + */ protected $useTimestamps = false; /** * Gets hits data for a podcast * - * @param int $podcastId - * - * @return array + * @return AnalyticsPodcasts[] */ public function getDataByDay(int $podcastId): array { @@ -49,9 +58,7 @@ class AnalyticsPodcastModel extends Model /** * Gets hits data for a podcast * - * @param int $podcastId - * - * @return array + * @return AnalyticsPodcasts[] */ public function getDataByWeekday(int $podcastId): array { @@ -80,9 +87,7 @@ class AnalyticsPodcastModel extends Model /** * Gets bandwidth data for a podcast * - * @param int $podcastId - * - * @return array + * @return AnalyticsPodcasts[] */ public function getDataBandwidthByDay(int $podcastId): array { @@ -109,9 +114,7 @@ class AnalyticsPodcastModel extends Model /** * Gets hits data for a podcast * - * @param int $podcastId - * - * @return array + * @return AnalyticsPodcasts[] */ public function getDataByMonth(int $podcastId): array { @@ -137,9 +140,7 @@ class AnalyticsPodcastModel extends Model /** * Gets unique listeners data for a podcast * - * @param int $podcastId - * - * @return array + * @return AnalyticsPodcasts[] */ public function getDataUniqueListenersByDay(int $podcastId): array { @@ -168,9 +169,7 @@ class AnalyticsPodcastModel extends Model /** * Gets unique listeners data for a podcast * - * @param int $podcastId - * - * @return array + * @return AnalyticsPodcasts[] */ public function getDataUniqueListenersByMonth(int $podcastId): array { @@ -200,9 +199,7 @@ class AnalyticsPodcastModel extends Model /** * Gets listening-time data for a podcast * - * @param int $podcastId - * - * @return array + * @return AnalyticsPodcasts[] */ public function getDataTotalListeningTimeByDay(int $podcastId): array { @@ -233,9 +230,7 @@ class AnalyticsPodcastModel extends Model /** * Gets listening-time data for a podcast * - * @param int $podcastId - * - * @return array + * @return AnalyticsPodcasts[] */ public function getDataTotalListeningTimeByMonth(int $podcastId): array { diff --git a/app/Libraries/Analytics/Models/AnalyticsUnknownUseragentsModel.php b/app/Libraries/Analytics/Models/AnalyticsUnknownUseragentsModel.php index c002668b..c76772f6 100644 --- a/app/Libraries/Analytics/Models/AnalyticsUnknownUseragentsModel.php +++ b/app/Libraries/Analytics/Models/AnalyticsUnknownUseragentsModel.php @@ -10,17 +10,31 @@ namespace Analytics\Models; +use Analytics\Entities\AnalyticsUnknownUseragents; use CodeIgniter\Model; class AnalyticsUnknownUseragentsModel extends Model { + /** + * @var string + */ protected $table = 'analytics_unknown_useragents'; + /** + * @var string + */ protected $primaryKey = 'id'; - protected $allowedFields = []; - - protected $returnType = \Analytics\Entities\AnalyticsUnknownUseragents::class; + /** + * @var string + */ + protected $returnType = AnalyticsUnknownUseragents::class; + /** + * @var bool + */ protected $useSoftDeletes = false; + /** + * @var bool + */ protected $useTimestamps = false; } diff --git a/app/Libraries/Analytics/Models/AnalyticsWebsiteByBrowserModel.php b/app/Libraries/Analytics/Models/AnalyticsWebsiteByBrowserModel.php index efdfc95f..aadfb01b 100644 --- a/app/Libraries/Analytics/Models/AnalyticsWebsiteByBrowserModel.php +++ b/app/Libraries/Analytics/Models/AnalyticsWebsiteByBrowserModel.php @@ -10,25 +10,34 @@ namespace Analytics\Models; +use Analytics\Entities\AnalyticsWebsiteByBrowser; use CodeIgniter\Model; class AnalyticsWebsiteByBrowserModel extends Model { + /** + * @var string + */ protected $table = 'analytics_website_by_browser'; - protected $allowedFields = []; - - protected $returnType = \Analytics\Entities\AnalyticsWebsiteByBrowser::class; + /** + * @var string + */ + protected $returnType = AnalyticsWebsiteByBrowser::class; + /** + * @var bool + */ protected $useSoftDeletes = false; + /** + * @var bool + */ protected $useTimestamps = false; /** * Gets browser data for a podcast * - * @param int $podcastId - * - * @return array + * @return AnalyticsWebsiteByBrowser[] */ public function getData(int $podcastId): array { diff --git a/app/Libraries/Analytics/Models/AnalyticsWebsiteByEntryPageModel.php b/app/Libraries/Analytics/Models/AnalyticsWebsiteByEntryPageModel.php index 95931b42..8981e809 100644 --- a/app/Libraries/Analytics/Models/AnalyticsWebsiteByEntryPageModel.php +++ b/app/Libraries/Analytics/Models/AnalyticsWebsiteByEntryPageModel.php @@ -10,32 +10,41 @@ namespace Analytics\Models; +use Analytics\Entities\AnalyticsWebsiteByEntryPage; use CodeIgniter\Model; class AnalyticsWebsiteByEntryPageModel extends Model { + /** + * @var string + */ protected $table = 'analytics_website_by_entry_page'; - protected $allowedFields = []; - - protected $returnType = \Analytics\Entities\AnalyticsWebsiteByEntryPage::class; + /** + * @var string + */ + protected $returnType = AnalyticsWebsiteByEntryPage::class; + /** + * @var bool + */ protected $useSoftDeletes = false; + /** + * @var bool + */ protected $useTimestamps = false; /** * Gets entry pages data for a podcast * - * @param int $podcastId - * - * @return array + * @return AnalyticsWebsiteByEntryPage[] */ public function getData(int $podcastId): array { if (!($found = cache("{$podcastId}_analytics_website_by_entry_page"))) { $oneWeekAgo = date('Y-m-d', strtotime('-1 week')); $found = $this->select( - 'IF(entry_page_url=\'/\',\'/\',SUBSTRING_INDEX(entry_page_url,\'/\',-1)) as labels', + "IF(entry_page_url='/','/',SUBSTRING_INDEX(entry_page_url,'/',-1)) as labels", ) ->selectSum('hits', 'values') ->where([ diff --git a/app/Libraries/Analytics/Models/AnalyticsWebsiteByRefererModel.php b/app/Libraries/Analytics/Models/AnalyticsWebsiteByRefererModel.php index 65ee8dd2..507ee56c 100644 --- a/app/Libraries/Analytics/Models/AnalyticsWebsiteByRefererModel.php +++ b/app/Libraries/Analytics/Models/AnalyticsWebsiteByRefererModel.php @@ -10,25 +10,34 @@ namespace Analytics\Models; +use Analytics\Entities\AnalyticsWebsiteByReferer; use CodeIgniter\Model; class AnalyticsWebsiteByRefererModel extends Model { + /** + * @var string + */ protected $table = 'analytics_website_by_referer'; - protected $allowedFields = []; - - protected $returnType = \Analytics\Entities\AnalyticsWebsiteByReferer::class; + /** + * @var string + */ + protected $returnType = AnalyticsWebsiteByReferer::class; + /** + * @var bool + */ protected $useSoftDeletes = false; + /** + * @var bool + */ protected $useTimestamps = false; /** * Gets referer data for a podcast * - * @param int $podcastId - * - * @return array + * @return AnalyticsWebsiteByReferer[] */ public function getData(int $podcastId): array { @@ -55,9 +64,7 @@ class AnalyticsWebsiteByRefererModel extends Model /** * Gets domain data for a podcast * - * @param int $podcastId - * - * @return array + * @return AnalyticsWebsiteByReferer[] */ public function getDataByDomainWeekly(int $podcastId): array { @@ -65,9 +72,7 @@ class AnalyticsWebsiteByRefererModel extends Model !($found = cache("{$podcastId}_analytics_website_by_domain_weekly")) ) { $oneWeekAgo = date('Y-m-d', strtotime('-1 week')); - $found = $this->select( - 'SUBSTRING_INDEX(domain, \'.\', -2) as labels', - ) + $found = $this->select("SUBSTRING_INDEX(domain, '.', -2) as labels") ->selectSum('hits', 'values') ->where([ 'podcast_id' => $podcastId, @@ -88,9 +93,7 @@ class AnalyticsWebsiteByRefererModel extends Model /** * Gets domain data for a podcast * - * @param int $podcastId - * - * @return array + * @return AnalyticsWebsiteByReferer[] */ public function getDataByDomainYearly(int $podcastId): array { @@ -98,9 +101,7 @@ class AnalyticsWebsiteByRefererModel extends Model !($found = cache("{$podcastId}_analytics_website_by_domain_yearly")) ) { $oneYearAgo = date('Y-m-d', strtotime('-1 year')); - $found = $this->select( - 'SUBSTRING_INDEX(domain, \'.\', -2) as labels', - ) + $found = $this->select("SUBSTRING_INDEX(domain, '.', -2) as labels") ->selectSum('hits', 'values') ->where([ 'podcast_id' => $podcastId, diff --git a/app/Libraries/Analytics/Models/UnknownUserAgentsModel.php b/app/Libraries/Analytics/Models/UnknownUserAgentsModel.php index 1f531f45..b0173a58 100644 --- a/app/Libraries/Analytics/Models/UnknownUserAgentsModel.php +++ b/app/Libraries/Analytics/Models/UnknownUserAgentsModel.php @@ -14,10 +14,11 @@ use CodeIgniter\Model; class UnknownUserAgentsModel extends Model { + /** + * @var string + */ protected $table = 'analytics_unknown_useragents'; - protected $allowedFields = []; - public function getUserAgents($last_known_id = 0) { return $this->where('id >', $last_known_id)->findAll(); diff --git a/app/Libraries/Breadcrumb.php b/app/Libraries/Breadcrumb.php index 5f5bea27..e520b398 100644 --- a/app/Libraries/Breadcrumb.php +++ b/app/Libraries/Breadcrumb.php @@ -32,12 +32,12 @@ class Breadcrumb $uri = ''; foreach (current_url(true)->getSegments() as $segment) { $uri .= '/' . $segment; - array_push($this->links, [ + $this->links[] = [ 'text' => is_numeric($segment) ? $segment : lang('Breadcrumb.' . $segment), 'href' => base_url($uri), - ]); + ]; } } @@ -57,10 +57,8 @@ class Breadcrumb * replaceParams($newParams); * * The breadcrumb is now `Home / podcasts / foo / episodes / bar` - * - * @param array $newParams */ - public function replaceParams($newParams) + public function replaceParams(array $newParams): void { foreach ($this->links as $key => $link) { if (is_numeric($link['text'])) { @@ -72,10 +70,8 @@ class Breadcrumb /** * Renders the breadcrumb object as an accessible html breadcrumb nav - * - * @return string */ - public function render($class = null) + public function render($class = null): string { $listItems = ''; $keys = array_keys($this->links); diff --git a/app/Libraries/Image.php b/app/Libraries/Image.php index 9204aa38..572c1564 100644 --- a/app/Libraries/Image.php +++ b/app/Libraries/Image.php @@ -8,18 +8,10 @@ namespace App\Libraries; +use Config\Images; +use Config\Services; class Image { - /** - * @var \Config\Images - */ - protected $config; - - /** - * @var string - */ - protected $original_path; - /** * @var string */ @@ -28,28 +20,18 @@ class Image /** * @var string */ - protected $thumbnail_path; + public $mimetype; /** * @var string */ public $thumbnail_url; - /** - * @var string - */ - protected $medium_path; - /** * @var string */ public $medium_url; - /** - * @var string - */ - protected $large_path; - /** * @var string */ @@ -69,6 +51,26 @@ class Image * @var string */ public $id3_path; + /** + * @var Images + */ + protected $config; + /** + * @var string + */ + protected $original_path; + /** + * @var string + */ + protected $thumbnail_path; + /** + * @var string + */ + protected $medium_path; + /** + * @var string + */ + protected $large_path; public function __construct($originalPath, $mimetype) { @@ -115,7 +117,7 @@ class Image $this->mimetype = $mimetype; } - public function saveSizes() + public function saveSizes(): void { $thumbnailSize = $this->config->thumbnailSize; $mediumSize = $this->config->mediumSize; @@ -123,7 +125,7 @@ class Image $feedSize = $this->config->feedSize; $id3Size = $this->config->id3Size; - $imageService = \Config\Services::image(); + $imageService = Services::image(); $imageService ->withFile($this->original_path) diff --git a/app/Libraries/NoteObject.php b/app/Libraries/NoteObject.php index 4602784e..f02c9275 100644 --- a/app/Libraries/NoteObject.php +++ b/app/Libraries/NoteObject.php @@ -11,7 +11,7 @@ namespace App\Libraries; class NoteObject extends \ActivityPub\Objects\NoteObject { /** - * @param \App\Entities\Note $note + * @param Note $note */ public function __construct($note) { diff --git a/app/Libraries/PodcastActor.php b/app/Libraries/PodcastActor.php index acc55e2c..5f6351e1 100644 --- a/app/Libraries/PodcastActor.php +++ b/app/Libraries/PodcastActor.php @@ -8,6 +8,7 @@ namespace App\Libraries; +use ActivityPub\Entities\Actor; use App\Models\PodcastModel; class PodcastActor extends \ActivityPub\Objects\ActorObject @@ -17,10 +18,7 @@ class PodcastActor extends \ActivityPub\Objects\ActorObject */ protected $rss; - /** - * @param \App\Entities\Actor $actor - */ - public function __construct($actor) + public function __construct(Actor $actor) { parent::__construct($actor); diff --git a/app/Libraries/Router.php b/app/Libraries/Router.php index fddaa2ba..d8202b5c 100644 --- a/app/Libraries/Router.php +++ b/app/Libraries/Router.php @@ -29,6 +29,7 @@ class Router extends \CodeIgniter\Router\Router */ protected function checkRoutes(string $uri): bool { + /** @noRector RemoveExtraParametersRector */ $routes = $this->collection->getRoutes( $this->collection->getHTTPVerb(), ); @@ -54,7 +55,7 @@ class Router extends \CodeIgniter\Router\Router $localeSegment = array_search( '{locale}', preg_split( - '/[\/]*((^[a-zA-Z0-9])|\(([^()]*)\))*[\/]+/m', + '~[\/]*((^[a-zA-Z0-9])|\(([^()]*)\))*[\/]+~m', $key, ), true, diff --git a/app/Libraries/SimpleRSSElement.php b/app/Libraries/SimpleRSSElement.php index f37453c1..e39c0359 100644 --- a/app/Libraries/SimpleRSSElement.php +++ b/app/Libraries/SimpleRSSElement.php @@ -16,11 +16,16 @@ class SimpleRSSElement extends SimpleXMLElement * Adds a child with $value inside CDATA * * @param string $name — The name of the child element to add. - * @param string $value — [optional] If specified, the value of the child element. - * @param string $namespace [optional] If specified, the namespace to which the child element belongs. + * @param string|null $value — [optional] If specified, the value of the child element. + * @param string|null $namespace [optional] If specified, the namespace to which the child element belongs. + * + * @return $this */ - public function addChildWithCDATA($name, $value = null, $namespace = null) - { + public function addChildWithCDATA( + string $name, + ?string $value = null, + ?string $namespace = null + ) { $new_child = parent::addChild($name, null, $namespace); if ($new_child !== null) { @@ -39,6 +44,7 @@ class SimpleRSSElement extends SimpleXMLElement * @param string $name — The name of the child element to add. * @param string $value — [optional] If specified, the value of the child element. * @param string $namespace [optional] If specified, the namespace to which the child element belongs. + * @return $this */ public function addChild($name, $value = null, $namespace = null) { diff --git a/app/Models/CategoryModel.php b/app/Models/CategoryModel.php index 1116116b..0bf50436 100644 --- a/app/Models/CategoryModel.php +++ b/app/Models/CategoryModel.php @@ -8,13 +8,23 @@ namespace App\Models; +use App\Entities\Category; use CodeIgniter\Model; class CategoryModel extends Model { + /** + * @var string + */ protected $table = 'categories'; + /** + * @var string + */ protected $primaryKey = 'id'; + /** + * @var string[] + */ protected $allowedFields = [ 'parent_id', 'code', @@ -22,12 +32,21 @@ class CategoryModel extends Model 'google_category', ]; - protected $returnType = \App\Entities\Category::class; + /** + * @var string + */ + protected $returnType = Category::class; + /** + * @var bool + */ protected $useSoftDeletes = false; + /** + * @var bool + */ protected $useTimestamps = false; - public function getCategoryById($id) + public function getCategoryById($id): ?Category { return $this->find($id); } @@ -60,12 +79,9 @@ class CategoryModel extends Model /** * Sets categories for a given podcast * - * @param int $podcastId - * @param array $categories - * - * @return integer|false Number of rows inserted or FALSE on failure + * @return int|bool Number of rows inserted or FALSE on failure */ - public function setPodcastCategories($podcastId, $categories) + public function setPodcastCategories(int $podcastId, ?array $categories) { cache()->delete("podcast#{$podcastId}_categories"); @@ -74,37 +90,35 @@ class CategoryModel extends Model ->table('podcasts_categories') ->delete(['podcast_id' => $podcastId]); - if (!empty($categories)) { - // prepare data for `podcasts_categories` table - $data = array_reduce( - $categories, - function ($result, $categoryId) use ($podcastId) { - $result[] = [ - 'podcast_id' => $podcastId, - 'category_id' => $categoryId, - ]; - - return $result; - }, - [], - ); - - // Set podcast categories - return $this->db->table('podcasts_categories')->insertBatch($data); + if (empty($categories)) { + // no row has been inserted after deletion + return 0; } - // no row has been inserted after deletion - return 0; + // prepare data for `podcasts_categories` table + $data = array_reduce( + $categories, + function ($result, $categoryId) use ($podcastId) { + $result[] = [ + 'podcast_id' => $podcastId, + 'category_id' => $categoryId, + ]; + + return $result; + }, + [], + ); + + // Set podcast categories + return $this->db->table('podcasts_categories')->insertBatch($data); } /** * Gets all the podcast categories * - * @param int $podcastId - * - * @return \App\Entities\Category[] + * @return Category[] */ - public function getPodcastCategories($podcastId) + public function getPodcastCategories(int $podcastId): array { $cacheName = "podcast#{$podcastId}_categories"; if (!($categories = cache($cacheName))) { diff --git a/app/Models/CreditModel.php b/app/Models/CreditModel.php index 00121757..dfded4b2 100644 --- a/app/Models/CreditModel.php +++ b/app/Models/CreditModel.php @@ -8,13 +8,18 @@ namespace App\Models; +use App\Entities\Credit; use CodeIgniter\Model; class CreditModel extends Model { + /** + * @var string + */ protected $table = 'credits'; - protected $allowedFields = []; - - protected $returnType = \App\Entities\Credit::class; + /** + * @var string + */ + protected $returnType = Credit::class; } diff --git a/app/Models/EpisodeModel.php b/app/Models/EpisodeModel.php index 9679d717..c76aaa20 100644 --- a/app/Models/EpisodeModel.php +++ b/app/Models/EpisodeModel.php @@ -8,13 +8,55 @@ namespace App\Models; +use App\Entities\Episode; use CodeIgniter\Model; class EpisodeModel extends Model { + // TODO: remove + /** + * @var array> + */ + public static $themes = [ + 'light-transparent' => [ + 'style' => + 'background-color: #fff; background-image: linear-gradient(45deg, #ccc 12.5%, transparent 12.5%, transparent 50%, #ccc 50%, #ccc 62.5%, transparent 62.5%, transparent 100%); background-size: 5.66px 5.66px;', + 'background' => 'transparent', + 'text' => '#000', + 'inverted' => '#fff', + ], + 'light' => [ + 'style' => 'background-color: #fff;', + 'background' => '#fff', + 'text' => '#000', + 'inverted' => '#fff', + ], + 'dark-transparent' => [ + 'style' => + 'background-color: #001f1a; background-image: linear-gradient(45deg, #888 12.5%, transparent 12.5%, transparent 50%, #888 50%, #888 62.5%, transparent 62.5%, transparent 100%); background-size: 5.66px 5.66px;', + 'background' => 'transparent', + 'text' => '#fff', + 'inverted' => '#000', + ], + 'dark' => [ + 'style' => 'background-color: #001f1a;', + 'background' => '#001f1a', + 'text' => '#fff', + 'inverted' => '#000', + ], + ]; + /** + * @var string + */ protected $table = 'episodes'; + /** + * @var string + */ protected $primaryKey = 'id'; + /** + * @var string[] + */ protected $allowedFields = [ 'id', 'podcast_id', @@ -51,11 +93,23 @@ class EpisodeModel extends Model 'updated_by', ]; - protected $returnType = \App\Entities\Episode::class; + /** + * @var string + */ + protected $returnType = Episode::class; + /** + * @var bool + */ protected $useSoftDeletes = true; + /** + * @var bool + */ protected $useTimestamps = true; + /** + * @var array + */ protected $validationRules = [ 'podcast_id' => 'required', 'title' => 'required', @@ -71,53 +125,32 @@ class EpisodeModel extends Model 'created_by' => 'required', 'updated_by' => 'required', ]; - protected $validationMessages = []; - - protected $afterInsert = ['writeEnclosureMetadata', 'clearCache']; - // clear cache beforeUpdate because if slug changes, so will the episode link - protected $beforeUpdate = ['clearCache']; - protected $afterUpdate = ['writeEnclosureMetadata']; - protected $beforeDelete = ['clearCache']; - - // TODO: remove - public static $themes = [ - 'light-transparent' => [ - 'style' => - 'background-color: #fff; background-image: linear-gradient(45deg, #ccc 12.5%, transparent 12.5%, transparent 50%, #ccc 50%, #ccc 62.5%, transparent 62.5%, transparent 100%); background-size: 5.66px 5.66px;', - 'background' => 'transparent', - 'text' => '#000', - 'inverted' => '#fff', - ], - 'light' => [ - 'style' => 'background-color: #fff;', - 'background' => '#fff', - 'text' => '#000', - 'inverted' => '#fff', - ], - 'dark-transparent' => [ - 'style' => - 'background-color: #001f1a; background-image: linear-gradient(45deg, #888 12.5%, transparent 12.5%, transparent 50%, #888 50%, #888 62.5%, transparent 62.5%, transparent 100%); background-size: 5.66px 5.66px;', - 'background' => 'transparent', - 'text' => '#fff', - 'inverted' => '#000', - ], - 'dark' => [ - 'style' => 'background-color: #001f1a;', - 'background' => '#001f1a', - 'text' => '#fff', - 'inverted' => '#000', - ], - ]; /** - * - * @param int|string $podcastId Podcast Id or name - * @param mixed $episodeSlug - * @return mixed + * @var string[] */ - public function getEpisodeBySlug($podcastId, $episodeSlug) - { - $cacheName = "podcast#{$podcastId}_episode@{$episodeSlug}"; + protected $afterInsert = ['writeEnclosureMetadata', 'clearCache']; + // clear cache beforeUpdate because if slug changes, so will the episode link + /** + * @var string[] + */ + protected $beforeUpdate = ['clearCache']; + + /** + * @var string[] + */ + protected $afterUpdate = ['writeEnclosureMetadata']; + + /** + * @var string[] + */ + protected $beforeDelete = ['clearCache']; + + public function getEpisodeBySlug( + int $podcastId, + string $episodeSlug + ): ?Episode { + $cacheName = "podcast#{$podcastId}_episode-{$episodeSlug}"; if (!($found = cache($cacheName))) { $builder = $this->select('episodes.*') ->where('slug', $episodeSlug) @@ -179,9 +212,7 @@ class EpisodeModel extends Model * Gets all episodes for a podcast ordered according to podcast type * Filtered depending on year or season * - * @param int $podcastId - * - * @return \App\Entities\Episode[] + * @return Episode[] */ public function getPodcastEpisodes( int $podcastId, @@ -244,8 +275,6 @@ class EpisodeModel extends Model * Returns the timestamp difference in seconds between the next episode to publish and the current timestamp * Returns false if there's no episode to publish * - * @param int $podcastId - * * @return int|false seconds */ public function getSecondsToNextUnpublishedEpisode(int $podcastId) @@ -261,23 +290,13 @@ class EpisodeModel extends Model ->get() ->getResultArray(); - return (int) $result ? $result[0]['timestamp_diff'] : false; + return (int) $result !== 0 ? $result[0]['timestamp_diff'] : false; } - protected function writeEnclosureMetadata(array $data) - { - helper('id3'); - - $episode = (new EpisodeModel())->find( - is_array($data['id']) ? $data['id'][0] : $data['id'], - ); - - write_audio_file_tags($episode); - - return $data; - } - - public function clearCache(array $data) + /** + * @return array> + */ + public function clearCache($data): array { $episode = (new EpisodeModel())->find( is_array($data['id']) ? $data['id'][0] : $data['id'], @@ -294,7 +313,7 @@ class EpisodeModel extends Model "podcast#{$episode->podcast_id}_episode#{$episode->id}*", ); cache()->delete( - "podcast#{$episode->podcast_id}_episode@{$episode->slug}", + "podcast#{$episode->podcast_id}_episode-{$episode->slug}", ); cache()->deleteMatching( @@ -324,4 +343,20 @@ class EpisodeModel extends Model return $data; } + + /** + * @return array> + */ + protected function writeEnclosureMetadata(array $data): array + { + helper('id3'); + + $episode = (new EpisodeModel())->find( + is_array($data['id']) ? $data['id'][0] : $data['id'], + ); + + write_audio_file_tags($episode); + + return $data; + } } diff --git a/app/Models/EpisodePersonModel.php b/app/Models/EpisodePersonModel.php index 5e96171b..1fd56a53 100644 --- a/app/Models/EpisodePersonModel.php +++ b/app/Models/EpisodePersonModel.php @@ -8,13 +8,24 @@ namespace App\Models; +use CodeIgniter\Database\BaseResult; +use App\Entities\EpisodePerson; use CodeIgniter\Model; class EpisodePersonModel extends Model { + /** + * @var string + */ protected $table = 'episodes_persons'; + /** + * @var string + */ protected $primaryKey = 'id'; + /** + * @var string[] + */ protected $allowedFields = [ 'id', 'podcast_id', @@ -24,18 +35,35 @@ class EpisodePersonModel extends Model 'person_role', ]; - protected $returnType = \App\Entities\EpisodePerson::class; + /** + * @var string + */ + protected $returnType = EpisodePerson::class; + /** + * @var bool + */ protected $useSoftDeletes = false; + /** + * @var bool + */ protected $useTimestamps = false; + /** + * @var array + */ protected $validationRules = [ 'episode_id' => 'required', 'person_id' => 'required', ]; - protected $validationMessages = []; + /** + * @var string[] + */ protected $afterInsert = ['clearCache']; + /** + * @var string[] + */ protected $beforeDelete = ['clearCache']; public function getEpisodePersons($podcastId, $episodeId) @@ -57,17 +85,14 @@ class EpisodePersonModel extends Model * Add persons to episode * * @param int podcastId - * @param int $episodeId - * @param array $persons - * @param array $groups_roles * - * @return integer|false Number of rows inserted or FALSE on failure + * @return bool|int Number of rows inserted or FALSE on failure */ public function addEpisodePersons( $podcastId, - $episodeId, - $persons, - $groups_roles + int $episodeId, + array $persons, + array $groups_roles ) { if (!empty($persons)) { $this->clearCache([ @@ -76,7 +101,7 @@ class EpisodePersonModel extends Model $data = []; foreach ($persons as $person) { - if ($groups_roles) { + if ($groups_roles !== []) { foreach ($groups_roles as $group_role) { $group_role = explode(',', $group_role); $data[] = [ @@ -100,6 +125,9 @@ class EpisodePersonModel extends Model return 0; } + /** + * @return bool|BaseResult + */ public function removeEpisodePersons( $podcastId, $episodeId, @@ -112,9 +140,11 @@ class EpisodePersonModel extends Model ]); } - protected function clearCache(array $data) + /** + * @return array> + */ + protected function clearCache(array $data): array { - $episodeId = null; if (isset($data['episode_id'])) { $episodeId = $data['episode_id']; } else { diff --git a/app/Models/LanguageModel.php b/app/Models/LanguageModel.php index 5eb780f3..09c52dfb 100644 --- a/app/Models/LanguageModel.php +++ b/app/Models/LanguageModel.php @@ -8,18 +8,37 @@ namespace App\Models; +use App\Entities\Language; use CodeIgniter\Model; class LanguageModel extends Model { + /** + * @var string + */ protected $table = 'languages'; + /** + * @var string + */ protected $primaryKey = 'id'; + /** + * @var string[] + */ protected $allowedFields = ['code', 'native_name']; - protected $returnType = \App\Entities\Language::class; + /** + * @var string + */ + protected $returnType = Language::class; + /** + * @var bool + */ protected $useSoftDeletes = false; + /** + * @var bool + */ protected $useTimestamps = false; public function getLanguageOptions() @@ -33,7 +52,7 @@ class LanguageModel extends Model $result[$language->code] = $language->native_name; return $result; }, - [] + [], ); cache()->save('language_options', $options, DECADE); diff --git a/app/Models/PageModel.php b/app/Models/PageModel.php index c045ca0c..f52eaf94 100644 --- a/app/Models/PageModel.php +++ b/app/Models/PageModel.php @@ -8,34 +8,70 @@ namespace App\Models; +use App\Entities\Page; use CodeIgniter\Model; class PageModel extends Model { + /** + * @var string + */ protected $table = 'pages'; + /** + * @var string + */ protected $primaryKey = 'id'; + /** + * @var string[] + */ protected $allowedFields = ['id', 'title', 'slug', 'content']; - protected $returnType = \App\Entities\Page::class; + /** + * @var string + */ + protected $returnType = Page::class; + /** + * @var bool + */ protected $useSoftDeletes = true; + /** + * @var bool + */ protected $useTimestamps = true; + /** + * @var array + */ protected $validationRules = [ 'title' => 'required', 'slug' => 'required|regex_match[/^[a-zA-Z0-9\-]{1,191}$/]|is_unique[pages.slug,id,{id}]', 'content' => 'required', ]; - protected $validationMessages = []; - // Before update because slug or title might change + /** + * @var string[] + */ protected $afterInsert = ['clearCache']; + + /** + * Before update because slug or title might change + * + * @var string[] + */ protected $beforeUpdate = ['clearCache']; + + /** + * @var string[] + */ protected $beforeDelete = ['clearCache']; - protected function clearCache(array $data) + /** + * @return array> + */ + protected function clearCache(array $data): array { // Clear the cache of all pages cache()->deleteMatching('page*'); diff --git a/app/Models/PersonModel.php b/app/Models/PersonModel.php index 41dee4f2..94054790 100644 --- a/app/Models/PersonModel.php +++ b/app/Models/PersonModel.php @@ -8,13 +8,23 @@ namespace App\Models; +use App\Entities\Person; use CodeIgniter\Model; class PersonModel extends Model { + /** + * @var string + */ protected $table = 'persons'; + /** + * @var string + */ protected $primaryKey = 'id'; + /** + * @var string[] + */ protected $allowedFields = [ 'id', 'full_name', @@ -26,11 +36,23 @@ class PersonModel extends Model 'updated_by', ]; - protected $returnType = \App\Entities\Person::class; + /** + * @var string + */ + protected $returnType = Person::class; + /** + * @var bool + */ protected $useSoftDeletes = false; + /** + * @var bool + */ protected $useTimestamps = true; + /** + * @var array + */ protected $validationRules = [ 'full_name' => 'required', 'unique_name' => @@ -39,11 +61,22 @@ class PersonModel extends Model 'created_by' => 'required', 'updated_by' => 'required', ]; - protected $validationMessages = []; - // clear cache before update if by any chance, the person name changes, so will the person link + /** + * @var string[] + */ protected $afterInsert = ['clearCache']; + + /** + * clear cache before update if by any chance, the person name changes, so will the person link + * + * @var string[] + */ protected $beforeUpdate = ['clearCache']; + + /** + * @var string[] + */ protected $beforeDelete = ['clearCache']; public function getPersonById($personId) @@ -65,7 +98,7 @@ class PersonModel extends Model public function createPerson($fullName, $informationUrl, $image) { - $person = new \App\Entities\Person([ + $person = new Person([ 'full_name' => $fullName, 'unique_name' => slugify($fullName), 'information_url' => $informationUrl, @@ -103,10 +136,13 @@ class PersonModel extends Model $locale = service('request')->getLocale(); $cacheName = "taxonomy_options_{$locale}"; if (!($options = cache($cacheName))) { - foreach (lang('PersonsTaxonomy.persons') as $group_key => $group) { + foreach ( + (array) lang('PersonsTaxonomy.persons') + as $group_key => $group + ) { foreach ($group['roles'] as $role_key => $role) { $options[ - "$group_key,$role_key" + "{$group_key},{$role_key}" ] = "{$group['label']} ā–ø {$role['label']}"; } } @@ -117,7 +153,10 @@ class PersonModel extends Model return $options; } - protected function clearCache(array $data) + /** + * @return array> + */ + protected function clearCache(array $data): array { $person = (new PersonModel())->find( is_array($data['id']) ? $data['id'][0] : $data['id'], diff --git a/app/Models/PlatformModel.php b/app/Models/PlatformModel.php index 45d84bb9..9ce8e241 100644 --- a/app/Models/PlatformModel.php +++ b/app/Models/PlatformModel.php @@ -11,13 +11,23 @@ namespace App\Models; +use App\Entities\Platform; use CodeIgniter\Model; class PlatformModel extends Model { + /** + * @var string + */ protected $table = 'platforms'; + /** + * @var string + */ protected $primaryKey = 'slug'; + /** + * @var string[] + */ protected $allowedFields = [ 'slug', 'type', @@ -26,9 +36,18 @@ class PlatformModel extends Model 'submit_url', ]; - protected $returnType = \App\Entities\Platform::class; + /** + * @var string + */ + protected $returnType = Platform::class; + /** + * @var bool + */ protected $useSoftDeletes = false; + /** + * @var bool + */ protected $useTimestamps = false; public function getPlatforms() @@ -45,7 +64,7 @@ class PlatformModel extends Model public function getPlatform($slug) { - $cacheName = "platform@{$slug}"; + $cacheName = "platform-{$slug}"; if (!($found = cache($cacheName))) { $found = $this->where('slug', $slug)->first(); cache()->save($cacheName, $found, DECADE); @@ -82,7 +101,7 @@ class PlatformModel extends Model ) ->join( 'podcasts_platforms', - "podcasts_platforms.platform_slug = platforms.slug AND podcasts_platforms.podcast_id = $podcastId", + "podcasts_platforms.platform_slug = platforms.slug AND podcasts_platforms.podcast_id = {$podcastId}", 'left', ) ->where('platforms.type', $platformType) @@ -119,6 +138,9 @@ class PlatformModel extends Model return $found; } + /** + * @return int|bool + */ public function savePodcastPlatforms( $podcastId, $platformType, @@ -130,9 +152,9 @@ class PlatformModel extends Model $platformsTable = $this->db->prefixTable('platforms'); $deleteJoinQuery = <<insertBatch($podcastsPlatformsData); } + /** + * @return int|bool + */ public function createPodcastPlatforms($podcastId, $podcastsPlatformsData) { $this->clearCache($podcastId); @@ -154,6 +179,9 @@ class PlatformModel extends Model ->insertBatch($podcastsPlatformsData); } + /** + * @return bool|string + */ public function removePodcastPlatform($podcastId, $platformSlug) { $this->clearCache($podcastId); @@ -164,7 +192,7 @@ class PlatformModel extends Model ]); } - public function clearCache($podcastId) + public function clearCache($podcastId): void { cache()->deleteMatching("podcast#{$podcastId}_platforms_*"); diff --git a/app/Models/PodcastModel.php b/app/Models/PodcastModel.php index f8d79e00..98a7d939 100644 --- a/app/Models/PodcastModel.php +++ b/app/Models/PodcastModel.php @@ -82,10 +82,10 @@ class PodcastModel extends Model public function getPodcastByName($podcastName) { - $cacheName = "podcast@{$podcastName}"; + $cacheName = "podcast-{$podcastName}"; if (!($found = cache($cacheName))) { $found = $this->where('name', $podcastName)->first(); - cache()->save("podcast@{$podcastName}", $found, DECADE); + cache()->save("podcast-{$podcastName}", $found, DECADE); } return $found; @@ -405,7 +405,7 @@ class PodcastModel extends Model // delete model requests cache, includes feed / query / episode lists, etc. cache()->deleteMatching("podcast#{$podcast->id}*"); - cache()->delete("podcast@{$podcast->name}"); + cache()->delete("podcast-{$podcast->name}"); // clear cache for every credit page cache()->deleteMatching('page_credits_*'); diff --git a/app/Models/PodcastPersonModel.php b/app/Models/PodcastPersonModel.php index c111fe98..eedb85c9 100644 --- a/app/Models/PodcastPersonModel.php +++ b/app/Models/PodcastPersonModel.php @@ -8,13 +8,24 @@ namespace App\Models; +use CodeIgniter\Database\BaseResult; +use App\Entities\PodcastPerson; use CodeIgniter\Model; class PodcastPersonModel extends Model { + /** + * @var string + */ protected $table = 'podcasts_persons'; + /** + * @var string + */ protected $primaryKey = 'id'; + /** + * @var string[] + */ protected $allowedFields = [ 'id', 'podcast_id', @@ -23,18 +34,36 @@ class PodcastPersonModel extends Model 'person_role', ]; - protected $returnType = \App\Entities\PodcastPerson::class; + /** + * @var string + */ + protected $returnType = PodcastPerson::class; + /** + * @var bool + */ protected $useSoftDeletes = false; + /** + * @var bool + */ protected $useTimestamps = false; + /** + * @var array + */ protected $validationRules = [ 'podcast_id' => 'required', 'person_id' => 'required', ]; - protected $validationMessages = []; + /** + * @var string[] + */ protected $afterInsert = ['clearCache']; + + /** + * @var string[] + */ protected $beforeDelete = ['clearCache']; public function getPodcastPersons($podcastId) @@ -56,19 +85,18 @@ class PodcastPersonModel extends Model /** * Add persons to podcast * - * @param int $podcastId - * @param array $persons - * @param array $groups_roles - * - * @return integer Number of rows inserted or FALSE on failure + * @return bool|int Number of rows inserted or FALSE on failure */ - public function addPodcastPersons($podcastId, $persons, $groups_roles) - { + public function addPodcastPersons( + int $podcastId, + array $persons, + array $groups_roles + ) { if (!empty($persons)) { $this->clearCache(['podcast_id' => $podcastId]); $data = []; foreach ($persons as $person) { - if ($groups_roles) { + if ($groups_roles !== []) { foreach ($groups_roles as $group_role) { $group_role = explode(',', $group_role); $data[] = [ @@ -90,6 +118,9 @@ class PodcastPersonModel extends Model return 0; } + /** + * @return bool|BaseResult + */ public function removePodcastPersons($podcastId, $podcastPersonId) { return $this->delete([ @@ -98,9 +129,11 @@ class PodcastPersonModel extends Model ]); } - protected function clearCache(array $data) + /** + * @return array> + */ + protected function clearCache(array $data): array { - $podcastId = null; if (isset($data['podcast_id'])) { $podcastId = $data['podcast_id']; } else { diff --git a/app/Models/SoundbiteModel.php b/app/Models/SoundbiteModel.php index 9f7bcbc6..8bdf04ea 100644 --- a/app/Models/SoundbiteModel.php +++ b/app/Models/SoundbiteModel.php @@ -11,13 +11,24 @@ namespace App\Models; +use CodeIgniter\Database\BaseResult; +use App\Entities\Soundbite; use CodeIgniter\Model; class SoundbiteModel extends Model { + /** + * @var string + */ protected $table = 'soundbites'; + /** + * @var string + */ protected $primaryKey = 'id'; + /** + * @var string[] + */ protected $allowedFields = [ 'podcast_id', 'episode_id', @@ -28,15 +39,36 @@ class SoundbiteModel extends Model 'updated_by', ]; - protected $returnType = \App\Entities\Soundbite::class; + /** + * @var string + */ + protected $returnType = Soundbite::class; + /** + * @var bool + */ protected $useSoftDeletes = false; + /** + * @var bool + */ protected $useTimestamps = true; + /** + * @var string[] + */ protected $afterInsert = ['clearCache']; + /** + * @var string[] + */ protected $afterUpdate = ['clearCache']; + /** + * @var string[] + */ protected $beforeDelete = ['clearCache']; + /** + * @return bool|BaseResult + */ public function deleteSoundbite($podcastId, $episodeId, $soundbiteId) { return $this->delete([ @@ -49,10 +81,7 @@ class SoundbiteModel extends Model /** * Gets all soundbites for an episode * - * @param int $podcastId - * @param int $episodeId - * - * @return \App\Entities\Soundbite[] + * @return Soundbite[] */ public function getEpisodeSoundbites(int $podcastId, int $episodeId): array { @@ -69,7 +98,10 @@ class SoundbiteModel extends Model return $found; } - public function clearCache(array $data) + /** + * @return array> + */ + public function clearCache(array $data): array { $episode = (new EpisodeModel())->find( isset($data['data']) diff --git a/app/Models/UserModel.php b/app/Models/UserModel.php index e726b132..db7a3074 100644 --- a/app/Models/UserModel.php +++ b/app/Models/UserModel.php @@ -8,9 +8,13 @@ namespace App\Models; +use App\Entities\User; class UserModel extends \Myth\Auth\Models\UserModel { - protected $returnType = \App\Entities\User::class; + /** + * @var string + */ + protected $returnType = User::class; public function getPodcastContributors($podcastId) { diff --git a/app/Validation/FileRules.php b/app/Validation/FileRules.php index 3e855260..05a7284a 100644 --- a/app/Validation/FileRules.php +++ b/app/Validation/FileRules.php @@ -17,9 +17,7 @@ class FileRules extends ValidationFileRules * a specified allowable dimension. * * @param string|null $blank - * @param string $params * - * @return boolean */ public function min_dims(string $blank = null, string $params): bool { @@ -59,14 +57,11 @@ class FileRules extends ValidationFileRules } //-------------------------------------------------------------------- - /** * Checks an uploaded file to verify that the image ratio is of 1:1 * * @param string|null $blank - * @param string $params * - * @return boolean */ public function is_image_squared(string $blank = null, string $params): bool { diff --git a/app/Validation/Rules.php b/app/Validation/Rules.php index 94f11774..ac352ba0 100644 --- a/app/Validation/Rules.php +++ b/app/Validation/Rules.php @@ -13,9 +13,6 @@ class Rules /** * Checks a URL to ensure it's formed correctly. * - * @param string $str - * - * @return boolean */ public function validate_url(string $str = null): bool { diff --git a/app/Views/_assets/modules/Charts.ts b/app/Views/_assets/modules/Charts.ts index 08014db1..cf37414e 100644 --- a/app/Views/_assets/modules/Charts.ts +++ b/app/Views/_assets/modules/Charts.ts @@ -13,20 +13,17 @@ const drawPieChart = (chartDivId: string, dataUrl: string | null): void => { chart.exporting.menu = new am4core.ExportMenu(); chart.exporting.menu.align = "left"; chart.exporting.menu.verticalAlign = "top"; + // Set theme am4core.useTheme(am4themes_material); chart.innerRadius = am4core.percent(10); + // Add data chart.dataSource.url = dataUrl || ""; chart.dataSource.parser.options.emptyAs = 0; + // Add and configure Series const pieSeries = chart.series.push(new am4charts.PieSeries()); - const grouper = pieSeries.plugins.push( - new am4plugins_sliceGrouper.SliceGrouper() - ); - grouper.limit = 9; - grouper.groupName = "- Other -"; - grouper.clickBehavior = "break"; pieSeries.dataFields.value = "values"; pieSeries.dataFields.category = "labels"; pieSeries.slices.template.stroke = am4core.color("#ffffff"); @@ -34,6 +31,14 @@ const drawPieChart = (chartDivId: string, dataUrl: string | null): void => { pieSeries.slices.template.strokeOpacity = 1; pieSeries.labels.template.disabled = true; pieSeries.ticks.template.disabled = true; + + const grouper = pieSeries.plugins.push( + new am4plugins_sliceGrouper.SliceGrouper() + ); + grouper.limit = 9; + grouper.groupName = "- Other -"; + grouper.clickBehavior = "break"; + chart.legend = new am4charts.Legend(); chart.legend.position = "right"; chart.legend.scrollable = true; @@ -46,29 +51,38 @@ const drawXYChart = (chartDivId: string, dataUrl: string | null): void => { chart.exporting.menu = new am4core.ExportMenu(); chart.exporting.menu.align = "right"; chart.exporting.menu.verticalAlign = "bottom"; + // Set theme am4core.useTheme(am4themes_material); + // Create axes const dateAxis = chart.xAxes.push(new am4charts.DateAxis()); dateAxis.renderer.minGridDistance = 60; chart.yAxes.push(new am4charts.ValueAxis()); + // Add data chart.dataSource.url = dataUrl || ""; chart.dataSource.parser.options.emptyAs = 0; + // Create series const series = chart.series.push(new am4charts.LineSeries()); series.dataFields.valueY = "values"; series.dataFields.dateX = "labels"; series.tooltipText = "{valueY}"; series.strokeWidth = 2; + if (series.tooltip) { + series.tooltip.pointerOrientation = "vertical"; + } + // Make bullets grow on hover const bullet = series.bullets.push(new am4charts.CircleBullet()); bullet.circle.strokeWidth = 2; bullet.circle.radius = 4; bullet.circle.fill = am4core.color("#fff"); + const bullethover = bullet.states.create("hover"); bullethover.properties.scale = 1.3; - series.tooltip.pointerOrientation = "vertical"; + chart.cursor = new am4charts.XYCursor(); chart.cursor.snapToSeries = series; chart.cursor.xAxis = dateAxis; @@ -82,15 +96,18 @@ const drawBarChart = (chartDivId: string, dataUrl: string | null): void => { chart.exporting.menu = new am4core.ExportMenu(); chart.exporting.menu.align = "right"; chart.exporting.menu.verticalAlign = "bottom"; + // Set theme am4core.useTheme(am4themes_material); chart.dataSource.url = dataUrl || ""; chart.dataSource.parser.options.emptyAs = 0; + const categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis()); categoryAxis.dataFields.category = "labels"; categoryAxis.renderer.grid.template.location = 0; categoryAxis.renderer.minGridDistance = 30; chart.yAxes.push(new am4charts.ValueAxis()); + // Create series const series = chart.series.push(new am4charts.ColumnSeries()); series.dataFields.valueY = "values"; @@ -98,6 +115,7 @@ const drawBarChart = (chartDivId: string, dataUrl: string | null): void => { series.name = "Hits"; series.columns.template.tooltipText = "{valueY} hits"; series.columns.template.fillOpacity = 0.8; + const columnTemplate = series.columns.template; columnTemplate.strokeWidth = 2; columnTemplate.strokeOpacity = 1; @@ -113,73 +131,47 @@ const drawXYDurationChart = ( chart.exporting.menu = new am4core.ExportMenu(); chart.exporting.menu.align = "right"; chart.exporting.menu.verticalAlign = "bottom"; + // Set theme am4core.useTheme(am4themes_material); + // Create axes const dateAxis = chart.xAxes.push(new am4charts.DateAxis()); dateAxis.renderer.minGridDistance = 60; + const yAxis = chart.yAxes.push(new am4charts.DurationAxis()); yAxis.baseUnit = "second"; chart.durationFormatter.durationFormat = "hh'h,' mm'mn'"; + // Add data chart.dataSource.url = dataUrl || ""; chart.dataSource.parser.options.emptyAs = 0; + // Create series const series = chart.series.push(new am4charts.LineSeries()); series.dataFields.valueY = "values"; series.dataFields.dateX = "labels"; series.tooltipText = "{valueY.formatDuration()}"; series.strokeWidth = 2; + if (series.tooltip) { + series.tooltip.pointerOrientation = "vertical"; + } + // Make bullets grow on hover const bullet = series.bullets.push(new am4charts.CircleBullet()); bullet.circle.strokeWidth = 2; bullet.circle.radius = 4; bullet.circle.fill = am4core.color("#fff"); + const bullethover = bullet.states.create("hover"); bullethover.properties.scale = 1.3; - series.tooltip.pointerOrientation = "vertical"; + chart.cursor = new am4charts.XYCursor(); chart.cursor.snapToSeries = series; chart.cursor.xAxis = dateAxis; chart.scrollbarX = new am4core.Scrollbar(); }; -const drawXYSeriesChart = ( - chartDivId: string, - dataUrl: string | null -): void => { - // Create chart instance - const chart = am4core.create(chartDivId, am4charts.XYChart); - am4core.percent(100); - chart.exporting.menu = new am4core.ExportMenu(); - chart.exporting.menu.align = "right"; - chart.exporting.menu.verticalAlign = "bottom"; - // Set theme - am4core.useTheme(am4themes_material); - // Create axes - chart.xAxes.push(new am4charts.ValueAxis()); - chart.yAxes.push(new am4charts.ValueAxis()); - // Add data - chart.dataSource.url = dataUrl || ""; - chart.dataSource.parser.options.emptyAs = 0; - // Create series - const series1 = chart.series.push(new am4charts.LineSeries()); - series1.dataFields.valueX = "X"; - series1.dataFields.valueY = "aY"; - const series2 = chart.series.push(new am4charts.LineSeries()); - series2.dataFields.valueX = "X"; - series2.dataFields.valueY = "bY"; - const series3 = chart.series.push(new am4charts.LineSeries()); - series3.dataFields.valueX = "X"; - series3.dataFields.valueY = "cY"; - const series4 = chart.series.push(new am4charts.LineSeries()); - series4.dataFields.valueX = "X"; - series4.dataFields.valueY = "dY"; - const series5 = chart.series.push(new am4charts.LineSeries()); - series5.dataFields.valueX = "X"; - series5.dataFields.valueY = "eY"; -}; - const drawMapChart = (chartDivId: string, dataUrl: string | null): void => { // Create map instance const chart = am4core.create(chartDivId, am4maps.MapChart); @@ -187,25 +179,34 @@ const drawMapChart = (chartDivId: string, dataUrl: string | null): void => { chart.exporting.menu = new am4core.ExportMenu(); chart.exporting.menu.align = "left"; chart.exporting.menu.verticalAlign = "top"; + // Set theme am4core.useTheme(am4themes_material); + // Set map definition chart.geodata = am4geodata_worldLow; + // Set projection chart.projection = new am4maps.projections.Miller(); + // Create map polygon series const polygonSeries = chart.series.push(new am4maps.MapPolygonSeries()); + // Exclude Antartica polygonSeries.exclude = ["AQ"]; + // Make map load polygon (like country names) data from GeoJSON polygonSeries.useGeodata = true; + // Configure series const polygonTemplate = polygonSeries.mapPolygons.template; polygonTemplate.tooltipText = "{name}"; polygonTemplate.polygon.fillOpacity = 0.6; + // Create hover state and set alternative fill color const hs = polygonTemplate.states.create("hover"); hs.properties.fill = chart.colors.getIndex(0); + // Add image series const imageSeries = chart.series.push(new am4maps.MapImageSeries()); imageSeries.dataSource.url = dataUrl || ""; @@ -213,9 +214,11 @@ const drawMapChart = (chartDivId: string, dataUrl: string | null): void => { imageSeries.mapImages.template.propertyFields.latitude = "latitude"; imageSeries.mapImages.template.tooltipText = "{country_code}, {region_code}:\n[bold]{value}[/] hits"; + const circle = imageSeries.mapImages.template.createChild(am4core.Circle); circle.radius = 1; circle.fill = am4core.color("#60f"); + imageSeries.heatRules.push({ target: circle, property: "radius", @@ -233,6 +236,7 @@ const DrawCharts = (): void => { for (let i = 0; i < chartDivs.length; i++) { const chartDiv: HTMLDivElement = chartDivs[i]; const chartType = chartDiv.dataset.chartType; + switch (chartType) { case "pie-chart": drawPieChart(chartDiv.id, chartDiv.getAttribute("data-chart-url")); @@ -249,9 +253,6 @@ const DrawCharts = (): void => { chartDiv.getAttribute("data-chart-url") ); break; - case "xy-series-chart": - drawXYSeriesChart(chartDiv.id, chartDiv.getAttribute("data-chart-url")); - break; case "map-chart": drawMapChart(chartDiv.id, chartDiv.getAttribute("data-chart-url")); break; diff --git a/app/Views/_assets/modules/Soundbites.ts b/app/Views/_assets/modules/Soundbites.ts index 601b2b70..7c2009fa 100644 --- a/app/Views/_assets/modules/Soundbites.ts +++ b/app/Views/_assets/modules/Soundbites.ts @@ -1,3 +1,6 @@ +/** + * TODO: refactor file + */ let timeout: number | null = null; const playSoundbite = ( diff --git a/app/Views/admin/_partials/_user_info.php b/app/Views/admin/_partials/_user_info.php index e116b04e..2b2d2eda 100644 --- a/app/Views/admin/_partials/_user_info.php +++ b/app/Views/admin/_partials/_user_info.php @@ -29,4 +29,4 @@
[permissions) ?>]
- \ No newline at end of file + diff --git a/app/Views/admin/contributor/add.php b/app/Views/admin/contributor/add.php index 8d8530d8..4bd2381c 100644 --- a/app/Views/admin/contributor/add.php +++ b/app/Views/admin/contributor/add.php @@ -32,9 +32,9 @@ 'primary'], - ['type' => 'submit', 'class' => 'self-end'] + ['type' => 'submit', 'class' => 'self-end'], ) ?> diff --git a/app/Views/admin/contributor/edit.php b/app/Views/admin/contributor/edit.php index cc995aee..33398d99 100644 --- a/app/Views/admin/contributor/edit.php +++ b/app/Views/admin/contributor/edit.php @@ -25,9 +25,9 @@ 'primary'], - ['type' => 'submit', 'class' => 'self-end'] + ['type' => 'submit', 'class' => 'self-end'], ) ?> diff --git a/app/Views/admin/contributor/list.php b/app/Views/admin/contributor/list.php index e2b3178e..f83064d9 100644 --- a/app/Views/admin/contributor/list.php +++ b/app/Views/admin/contributor/list.php @@ -40,29 +40,32 @@ route_to( 'contributor-edit', $podcast->id, - $contributor->id + $contributor->id, ), [ 'variant' => 'info', 'size' => 'small', ], - ['class' => 'mr-2'] + ['class' => 'mr-2'], ) . button( lang('Contributor.remove'), route_to( 'contributor-remove', $podcast->id, - $contributor->id + $contributor->id, ), - ['variant' => 'danger', 'size' => 'small'], - ['class' => 'mr-2'] + [ + 'variant' => 'danger', + 'size' => 'small', + ], + ['class' => 'mr-2'], ); }, ], ], $podcast->contributors, - $podcast + $podcast, ) ?> endSection() ?> diff --git a/app/Views/admin/episode/create.php b/app/Views/admin/episode/create.php index 883af9f7..77501a20 100644 --- a/app/Views/admin/episode/create.php +++ b/app/Views/admin/episode/create.php @@ -131,7 +131,7 @@ 'trailer', 'name' => 'type', 'class' => 'form-radio-btn'], 'trailer', - old('type') ? old('type') == 'trailer' : false, + old('type') && old('type') == 'trailer', ) ?>
- $value): ?> - - - - - + $value): ?> + + + + +
'primary'], [ 'class' => 'mb-1 mr-1', @@ -187,7 +187,7 @@ 'primary'], ['type' => 'submit', 'class' => 'self-end'], ) ?> diff --git a/app/Views/admin/episode/unpublish.php b/app/Views/admin/episode/unpublish.php index 82980813..64f1c465 100644 --- a/app/Views/admin/episode/unpublish.php +++ b/app/Views/admin/episode/unpublish.php @@ -41,7 +41,7 @@ 'danger'], ['type' => 'submit'], ) ?> diff --git a/app/Views/admin/episode/view.php b/app/Views/admin/episode/view.php index c5a0a883..2f2ca3a7 100644 --- a/app/Views/admin/episode/view.php +++ b/app/Views/admin/episode/view.php @@ -21,10 +21,6 @@ $podcast->id, $episode->id, $episode->publication_status, - lang('Episode.publish'), - route_to('episode-publish', $podcast->id, $episode->id), - ['variant' => 'accent', 'iconLeft' => 'upload'], - ['class' => 'mr-2'], ) ?> endSection() ?> @@ -97,11 +93,11 @@ [ [ 'header' => 'Play', - 'cell' => function ($soundbite) { + 'cell' => function ($soundbite): string { return icon_button( 'play', lang('Episode.soundbites_form.play'), - null, + '', ['variant' => 'primary'], [ 'class' => 'mb-1 mr-1', @@ -115,13 +111,13 @@ ], [ 'header' => lang('Episode.soundbites_form.start_time'), - 'cell' => function ($soundbite) { + 'cell' => function ($soundbite): string { return format_duration($soundbite->start_time); }, ], [ 'header' => lang('Episode.soundbites_form.duration'), - 'cell' => function ($soundbite) { + 'cell' => function ($soundbite): string { return format_duration($soundbite->duration); }, ], diff --git a/app/Views/admin/fediverse/blocked_actors.php b/app/Views/admin/fediverse/blocked_actors.php index 40505748..0cc8ba5a 100644 --- a/app/Views/admin/fediverse/blocked_actors.php +++ b/app/Views/admin/fediverse/blocked_actors.php @@ -34,7 +34,7 @@ 'primary'], ['type' => 'submit', 'class' => 'self-end'], ) ?> diff --git a/app/Views/admin/fediverse/blocked_domains.php b/app/Views/admin/fediverse/blocked_domains.php index c6a440dd..713b737b 100644 --- a/app/Views/admin/fediverse/blocked_domains.php +++ b/app/Views/admin/fediverse/blocked_domains.php @@ -33,7 +33,7 @@ 'primary'], ['type' => 'submit', 'class' => 'self-end'], ) ?> diff --git a/app/Views/admin/my_account/change_password.php b/app/Views/admin/my_account/change_password.php index 0ebf4994..6af3e6d5 100644 --- a/app/Views/admin/my_account/change_password.php +++ b/app/Views/admin/my_account/change_password.php @@ -32,14 +32,15 @@ 'class' => 'form-input mb-4', 'required' => 'required', 'type' => 'password', + 'autocomplete' => 'new-password', ]) ?> 'primary'], - ['type' => 'submit', 'class' => 'self-end'] + ['type' => 'submit', 'class' => 'self-end'], ) ?> diff --git a/app/Views/admin/page/create.php b/app/Views/admin/page/create.php index 1147f9d9..94b00916 100644 --- a/app/Views/admin/page/create.php +++ b/app/Views/admin/page/create.php @@ -46,16 +46,16 @@ 'required' => 'required', ], old('content', '', false), - 'data-editor="markdown"' + 'data-editor="markdown"', ) ?> 'primary'], - ['type' => 'submit', 'class' => 'self-end'] + ['type' => 'submit', 'class' => 'self-end'], ) ?> diff --git a/app/Views/admin/page/edit.php b/app/Views/admin/page/edit.php index 9d23cddf..e12bbaad 100644 --- a/app/Views/admin/page/edit.php +++ b/app/Views/admin/page/edit.php @@ -46,15 +46,15 @@ 'required' => 'required', ], old('content', $page->content, false), - 'data-editor="markdown"' + 'data-editor="markdown"', ) ?> 'primary'], - ['type' => 'submit', 'class' => 'self-end'] + ['type' => 'submit', 'class' => 'self-end'], ) ?> diff --git a/app/Views/admin/page/list.php b/app/Views/admin/page/list.php index 745ddc5e..2a113a21 100644 --- a/app/Views/admin/page/list.php +++ b/app/Views/admin/page/list.php @@ -40,23 +40,23 @@ 'variant' => 'secondary', 'size' => 'small', ], - ['class' => 'mr-2'] + ['class' => 'mr-2'], ) . button( lang('Page.edit'), route_to('page-edit', $page->id), ['variant' => 'info', 'size' => 'small'], - ['class' => 'mr-2'] + ['class' => 'mr-2'], ) . button( lang('Page.delete'), route_to('page-delete', $page->id), - ['variant' => 'danger', 'size' => 'small'] + ['variant' => 'danger', 'size' => 'small'], ); }, ], ], - $pages + $pages, ) ?> endSection() ?> diff --git a/app/Views/admin/person/create.php b/app/Views/admin/person/create.php index 5ec2c7ee..b89ae188 100644 --- a/app/Views/admin/person/create.php +++ b/app/Views/admin/person/create.php @@ -19,14 +19,14 @@ 'full_name', @@ -41,7 +41,7 @@ lang('Person.form.unique_name'), 'unique_name', [], - lang('Person.form.unique_name_hint') + lang('Person.form.unique_name_hint'), ) ?> 'unique_name', @@ -57,7 +57,7 @@ 'information_url', [], lang('Person.form.information_url_hint'), - true + true, ) ?> 'information_url', @@ -76,16 +76,16 @@ 'accept' => '.jpg,.jpeg,.png', ]) ?> 'primary'], - ['type' => 'submit', 'class' => 'self-end'] + ['type' => 'submit', 'class' => 'self-end'], ) ?> diff --git a/app/Views/admin/person/edit.php b/app/Views/admin/person/edit.php index 98a1d629..c76f3c54 100644 --- a/app/Views/admin/person/edit.php +++ b/app/Views/admin/person/edit.php @@ -20,14 +20,14 @@ image->thumbnail_url}\" alt=\"{$person->full_name}\" class=\"object-cover w-32 h-32 mt-3 rounded\" />" + "image->thumbnail_url}\" alt=\"{$person->full_name}\" class=\"object-cover w-32 h-32 mt-3 rounded\" />", ) ?> 'full_name', @@ -42,7 +42,7 @@ lang('Person.form.unique_name'), 'unique_name', [], - lang('Person.form.unique_name_hint') + lang('Person.form.unique_name_hint'), ) ?> 'unique_name', @@ -58,7 +58,7 @@ 'information_url', [], lang('Person.form.information_url_hint'), - true + true, ) ?> 'information_url', @@ -76,16 +76,16 @@ 'accept' => '.jpg,.jpeg,.png', ]) ?> 'primary'], - ['type' => 'submit', 'class' => 'self-end'] + ['type' => 'submit', 'class' => 'self-end'], ) ?> diff --git a/app/Views/admin/person/list.php b/app/Views/admin/person/list.php index de4040fd..193eb7e8 100644 --- a/app/Views/admin/person/list.php +++ b/app/Views/admin/person/list.php @@ -13,7 +13,7 @@ lang('Person.create'), route_to('person-create'), ['variant' => 'primary', 'iconLeft' => 'add'], - ['class' => 'mr-2'] + ['class' => 'mr-2'], ) ?> endSection() ?> @@ -26,11 +26,11 @@ <?= $person->full_name ?> + ->thumbnail_url ?>" class="object-cover w-full" />

full_name ?>

@@ -38,21 +38,21 @@
diff --git a/app/Views/admin/person/view.php b/app/Views/admin/person/view.php index 99446eb4..21916064 100644 --- a/app/Views/admin/person/view.php +++ b/app/Views/admin/person/view.php @@ -14,7 +14,7 @@ lang('Person.edit'), route_to('person-edit', $person->id), ['variant' => 'secondary', 'iconLeft' => 'edit'], - ['class' => 'mr-2'] + ['class' => 'mr-2'], ) ?> endSection() ?> diff --git a/app/Views/admin/podcast/analytics/listening_time.php b/app/Views/admin/podcast/analytics/listening_time.php index 2285f376..d7c1a557 100644 --- a/app/Views/admin/podcast/analytics/listening_time.php +++ b/app/Views/admin/podcast/analytics/listening_time.php @@ -16,7 +16,7 @@ 'analytics-data', $podcast->id, 'Podcast', - 'TotalListeningTimeByDay' + 'TotalListeningTimeByDay', ) ?>">
@@ -26,7 +26,7 @@ 'analytics-data', $podcast->id, 'Podcast', - 'TotalListeningTimeByMonth' + 'TotalListeningTimeByMonth', ) ?>"> diff --git a/app/Views/admin/podcast/analytics/locations.php b/app/Views/admin/podcast/analytics/locations.php index 3504529b..b2bbb832 100644 --- a/app/Views/admin/podcast/analytics/locations.php +++ b/app/Views/admin/podcast/analytics/locations.php @@ -17,7 +17,7 @@ 'analytics-data', $podcast->id, 'PodcastByCountry', - 'Weekly' + 'Weekly', ) ?>"> @@ -27,7 +27,7 @@ 'analytics-data', $podcast->id, 'PodcastByCountry', - 'Yearly' + 'Yearly', ) ?>"> @@ -37,7 +37,7 @@
diff --git a/app/Views/admin/podcast/analytics/players.php b/app/Views/admin/podcast/analytics/players.php index 667918a1..a30e9fee 100644 --- a/app/Views/admin/podcast/analytics/players.php +++ b/app/Views/admin/podcast/analytics/players.php @@ -17,7 +17,7 @@ 'analytics-data', $podcast->id, 'PodcastByPlayer', - 'ByAppWeekly' + 'ByAppWeekly', ) ?>"> @@ -27,7 +27,7 @@ 'analytics-data', $podcast->id, 'PodcastByService', - 'ByServiceWeekly' + 'ByServiceWeekly', ) ?>"> @@ -37,7 +37,7 @@ 'analytics-data', $podcast->id, 'PodcastByPlayer', - 'ByDeviceWeekly' + 'ByDeviceWeekly', ) ?>"> @@ -47,7 +47,7 @@ 'analytics-data', $podcast->id, 'PodcastByPlayer', - 'ByOsWeekly' + 'ByOsWeekly', ) ?>"> @@ -58,7 +58,7 @@ 'analytics-data', $podcast->id, 'PodcastByPlayer', - 'Bots' + 'Bots', ) ?>"> diff --git a/app/Views/admin/podcast/analytics/time_periods.php b/app/Views/admin/podcast/analytics/time_periods.php index 8b568730..66264a03 100644 --- a/app/Views/admin/podcast/analytics/time_periods.php +++ b/app/Views/admin/podcast/analytics/time_periods.php @@ -17,7 +17,7 @@ 'analytics-data', $podcast->id, 'Podcast', - 'ByWeekday' + 'ByWeekday', ) ?>"> @@ -26,7 +26,7 @@
diff --git a/app/Views/admin/podcast/analytics/unique_listeners.php b/app/Views/admin/podcast/analytics/unique_listeners.php index 8803a54a..698c33e2 100644 --- a/app/Views/admin/podcast/analytics/unique_listeners.php +++ b/app/Views/admin/podcast/analytics/unique_listeners.php @@ -16,7 +16,7 @@ 'analytics-data', $podcast->id, 'Podcast', - 'UniqueListenersByDay' + 'UniqueListenersByDay', ) ?>"> @@ -26,7 +26,7 @@ 'analytics-data', $podcast->id, 'Podcast', - 'UniqueListenersByMonth' + 'UniqueListenersByMonth', ) ?>"> diff --git a/app/Views/admin/podcast/analytics/webpages.php b/app/Views/admin/podcast/analytics/webpages.php index 5ec0f99a..f6c79001 100644 --- a/app/Views/admin/podcast/analytics/webpages.php +++ b/app/Views/admin/podcast/analytics/webpages.php @@ -18,7 +18,7 @@ 'analytics-data', $podcast->id, 'WebsiteByReferer', - 'ByDomainWeekly' + 'ByDomainWeekly', ) ?>"> @@ -29,7 +29,7 @@ 'analytics-data', $podcast->id, 'WebsiteByReferer', - 'ByDomainYearly' + 'ByDomainYearly', ) ?>"> @@ -39,7 +39,7 @@
@@ -49,7 +49,7 @@
diff --git a/app/Views/admin/podcast/create.php b/app/Views/admin/podcast/create.php index b30e6abf..1686104e 100644 --- a/app/Views/admin/podcast/create.php +++ b/app/Views/admin/podcast/create.php @@ -19,7 +19,7 @@ @@ -32,7 +32,7 @@ 'accept' => '.jpg,.jpeg,.png', ]) ?> @@ -48,7 +48,7 @@ lang('Podcast.form.name'), 'name', [], - lang('Podcast.form.name_hint') + lang('Podcast.form.name_hint'), ) ?> 'name', @@ -58,9 +58,7 @@ 'required' => 'required', ]) ?> - 'mb-4', -]) ?> + 'mb-4']) ?> @@ -68,13 +66,13 @@ 'episodic', 'name' => 'type', 'class' => 'form-radio-btn'], 'episodic', - old('type') ? old('type') == 'episodic' : true + old('type') ? old('type') == 'episodic' : true, ) ?> 'serial', 'name' => 'type', 'class' => 'form-radio-btn'], 'serial', - old('type') ? old('type') == 'serial' : false + old('type') && old('type') == 'serial', ) ?> @@ -89,7 +87,7 @@ 'required' => 'required', ], old('description', '', false), - 'data-editor="markdown"' + 'data-editor="markdown"', ) ?> @@ -98,7 +96,7 @@ @@ -120,7 +118,7 @@ 'other_categories', [], '', - true + true, ) ?> 'other_categories', 'class' => 'mb-4', 'data-max-item-count' => '2', - ] + ], ) ?> 'mb-4']) ?> @@ -147,10 +145,10 @@ 'undefined', old('parental_advisory') ? old('parental_advisory') === 'undefined' - : true + : true, ) ?> 'form-radio-btn', ], 'clean', - old('parental_advisory') ? old('parental_advisory') === 'clean' : false + old('parental_advisory') && old('parental_advisory') === 'clean', ) ?> 'form-radio-btn', ], 'explicit', - old('parental_advisory') - ? old('parental_advisory') === 'explicit' - : false + old('parental_advisory') && old('parental_advisory') === 'explicit', ) ?> @@ -185,14 +181,14 @@ 'owner_name', @@ -206,7 +202,7 @@ lang('Podcast.form.owner_email'), 'owner_email', [], - lang('Podcast.form.owner_email_hint') + lang('Podcast.form.owner_email_hint'), ) ?> 'owner_email', @@ -222,7 +218,7 @@ 'publisher', [], lang('Podcast.form.publisher_hint'), - true + true, ) ?> 'publisher', @@ -243,7 +239,7 @@ 'location_name', @@ -263,7 +259,7 @@ 'payment_pointer', @@ -288,7 +284,7 @@ 'partner_id', [], lang('Podcast.form.partner_id_hint'), - true + true, ) ?> 'partner_id', @@ -303,7 +299,7 @@ 'partner_link_url', [], lang('Podcast.form.partner_link_url_hint'), - true + true, ) ?> 'partner_link_url', @@ -318,8 +314,7 @@ 'partner_image_url', [], lang('Podcast.form.partner_image_url_hint'), - - true + true, ) ?> 'partner_image_url', @@ -334,14 +329,14 @@ 'custom_rss', @@ -353,7 +348,7 @@ 'block', 'name' => 'block'], 'yes', old('block', false), - 'mb-2' + 'mb-2', ) ?> 'complete', 'name' => 'complete'], 'yes', old('complete', false), - 'mb-2' + 'mb-2', ) ?> 'lock', 'name' => 'lock'], 'yes', - old('lock', true) + old('lock', true), ) ?> 'primary'], - ['type' => 'submit', 'class' => 'self-end'] + ['type' => 'submit', 'class' => 'self-end'], ) ?> diff --git a/app/Views/admin/podcast/edit.php b/app/Views/admin/podcast/edit.php index cd520c10..c0ca9e64 100644 --- a/app/Views/admin/podcast/edit.php +++ b/app/Views/admin/podcast/edit.php @@ -379,7 +379,7 @@ 'primary'], ['type' => 'submit', 'class' => 'self-end'], ) ?> diff --git a/app/Views/admin/podcast/import.php b/app/Views/admin/podcast/import.php index c7a30c88..258ba52f 100644 --- a/app/Views/admin/podcast/import.php +++ b/app/Views/admin/podcast/import.php @@ -108,7 +108,7 @@ 'class' => 'form-radio text-pine-700', ], 'title', - old('slug_field') ? old('slug_field') == 'title' : false, + old('slug_field') && old('slug_field') == 'title', ) ?> @@ -138,9 +138,7 @@ 'class' => 'form-radio text-pine-600', ], 'summary', - old('description_field') - ? old('description_field') == 'summary' - : false, + old('description_field') && old('description_field') == 'summary', ) ?> <itunes:summary> @@ -152,9 +150,8 @@ 'class' => 'form-radio text-pine-700', ], 'subtitle_summary', - old('description_field') - ? old('description_field') == 'subtitle_summary' - : false, + old('description_field') && + old('description_field') == 'subtitle_summary', ) ?> <itunes:subtitle> + <itunes:summary> @@ -166,9 +163,7 @@ 'class' => 'form-radio text-pine-700', ], 'content', - old('description_field') - ? old('description_field') == 'content' - : false, + old('description_field') && old('description_field') == 'content', ) ?> <content:encoded> @@ -221,7 +216,7 @@ 'primary'], ['type' => 'submit', 'class' => 'self-end'], ) ?> diff --git a/app/Views/admin/podcast/person.php b/app/Views/admin/podcast/person.php index 1338f2ad..f0a5b99c 100644 --- a/app/Views/admin/podcast/person.php +++ b/app/Views/admin/podcast/person.php @@ -13,7 +13,7 @@ lang('Person.create'), route_to('person-create'), ['variant' => 'primary', 'iconLeft' => 'add'], - ['class' => 'mr-2'] + ['class' => 'mr-2'], ) ?> endSection() ?> @@ -27,75 +27,78 @@ - + - lang('Person.podcast_form.person'), - 'cell' => function ($podcastPerson) { - return '
' . - 'person->image->thumbnail_url}\" alt=\"{$podcastPerson->person->full_name}\" class=\"object-cover w-16 h-16 rounded-full\" />" . - '
' . - $podcastPerson->person->full_name . - ($podcastPerson->person_group && $podcastPerson->person_role - ? '' . - lang( - "PersonsTaxonomy.persons.{$podcastPerson->person_group}.label" - ) . - ' ā–ø ' . - lang( - "PersonsTaxonomy.persons.{$podcastPerson->person_group}.roles.{$podcastPerson->person_role}.label" - ) . - '' - : '') . - (empty($podcastPerson->person->information_url) - ? '' - : "person->information_url}\" target=\"_blank\" rel=\"noreferrer noopener\" class=\"text-sm text-blue-800 hover:underline\">" . - $podcastPerson->person->information_url . - '') . - '
'; - }, + [ + 'header' => lang('Person.podcast_form.person'), + 'cell' => function ($podcastPerson) { + return '
' . + 'person->image->thumbnail_url}\" alt=\"{$podcastPerson->person->full_name}\" class=\"object-cover w-16 h-16 rounded-full\" />" . + '
' . + $podcastPerson->person->full_name . + ($podcastPerson->person_group && + $podcastPerson->person_role + ? '' . + lang( + "PersonsTaxonomy.persons.{$podcastPerson->person_group}.label", + ) . + ' ā–ø ' . + lang( + "PersonsTaxonomy.persons.{$podcastPerson->person_group}.roles.{$podcastPerson->person_role}.label", + ) . + '' + : '') . + (empty($podcastPerson->person->information_url) + ? '' + : "person->information_url}\" target=\"_blank\" rel=\"noreferrer noopener\" class=\"text-sm text-blue-800 hover:underline\">" . + $podcastPerson->person->information_url . + '') . + '
'; + }, + ], + [ + 'header' => lang('Common.actions'), + 'cell' => function ($podcastPerson): string { + return button( + lang('Person.podcast_form.remove'), + route_to( + 'podcast-person-remove', + $podcastPerson->podcast_id, + $podcastPerson->id, + ), + [ + 'variant' => 'danger', + 'size' => 'small', + ], + ); + }, + ], ], - [ - 'header' => lang('Common.actions'), - 'cell' => function ($podcastPerson) { - return button( - lang('Person.podcast_form.remove'), - route_to( - 'podcast-person-remove', - $podcastPerson->podcast_id, - $podcastPerson->id - ), + $podcastPersons, + ) ?> - ['variant' => 'danger', 'size' => 'small'] - ); - }, - ], - ], - $podcastPersons -) ?> - - + 'person', @@ -107,25 +110,26 @@ lang('Person.podcast_form.group_role'), 'group_role', [], - lang('Person.podcast_form.group_role_hint'), - true + true, ) ?> 'person_group_role', 'class' => 'form-select mb-4'] + [ + 'id' => 'person_group_role', + 'class' => 'form-select mb-4', + ], ) ?> - - + 'primary'], - ['type' => 'submit', 'class' => 'self-end'] -) ?> + ['type' => 'submit', 'class' => 'self-end'], +) ?> endSection() ?> diff --git a/app/Views/admin/podcast/platforms.php b/app/Views/admin/podcast/platforms.php index b41f8d51..6cd06182 100644 --- a/app/Views/admin/podcast/platforms.php +++ b/app/Views/admin/podcast/platforms.php @@ -139,7 +139,7 @@ 'primary'], ['type' => 'submit', 'class' => 'self-end'], ) ?> diff --git a/app/Views/admin/user/create.php b/app/Views/admin/user/create.php index aaf8369b..afc93cb8 100644 --- a/app/Views/admin/user/create.php +++ b/app/Views/admin/user/create.php @@ -35,7 +35,6 @@ 'password', 'name' => 'password', - 'class' => 'form-input mb-4', 'type' => 'password', 'autocomplete' => 'new-password', @@ -43,9 +42,9 @@ 'primary'], - ['type' => 'submit', 'class' => 'self-end'] + ['type' => 'submit', 'class' => 'self-end'], ) ?> diff --git a/app/Views/admin/user/edit.php b/app/Views/admin/user/edit.php index a2693143..4fac32d9 100644 --- a/app/Views/admin/user/edit.php +++ b/app/Views/admin/user/edit.php @@ -24,9 +24,9 @@ 'primary'], - ['type' => 'submit', 'class' => 'self-end'] + ['type' => 'submit', 'class' => 'self-end'], ) ?> diff --git a/app/Views/admin/user/list.php b/app/Views/admin/user/list.php index ebaed87d..a7e34bea 100644 --- a/app/Views/admin/user/list.php +++ b/app/Views/admin/user/list.php @@ -41,7 +41,7 @@ ]), route_to('user-edit', $user->id), ['variant' => 'info'], - ['class' => 'ml-2'] + ['class' => 'ml-2'], ); }, ], @@ -63,26 +63,26 @@ 'variant' => 'secondary', 'size' => 'small', ], - ['class' => 'mr-2'] + ['class' => 'mr-2'], ) . button( lang('User.' . ($user->isBanned() ? 'unban' : 'ban')), route_to( $user->isBanned() ? 'user-unban' : 'user-ban', - $user->id + $user->id, ), ['variant' => 'warning', 'size' => 'small'], - ['class' => 'mr-2'] + ['class' => 'mr-2'], ) . button( lang('User.delete'), route_to('user-delete', $user->id), - ['variant' => 'danger', 'size' => 'small'] + ['variant' => 'danger', 'size' => 'small'], ); }, ], ], - $users + $users, ) ?> endSection() ?> diff --git a/app/Views/auth/emails/activation.php b/app/Views/auth/emails/activation.php index d76eb17f..82a2032f 100644 --- a/app/Views/auth/emails/activation.php +++ b/app/Views/auth/emails/activation.php @@ -8,4 +8,4 @@
-

If you did not registered on this website, you can safely ignore this email.

\ No newline at end of file +

If you did not registered on this website, you can safely ignore this email.

diff --git a/app/Views/auth/forgot.php b/app/Views/auth/forgot.php index 376fa93d..5f046c51 100644 --- a/app/Views/auth/forgot.php +++ b/app/Views/auth/forgot.php @@ -24,9 +24,9 @@ 'primary'], - ['type' => 'submit', 'class' => 'self-end'] + ['type' => 'submit', 'class' => 'self-end'], ) ?> diff --git a/app/Views/auth/login.php b/app/Views/auth/login.php index 4faf1785..1a6e8108 100644 --- a/app/Views/auth/login.php +++ b/app/Views/auth/login.php @@ -31,9 +31,9 @@ 'primary'], - ['type' => 'submit', 'class' => 'self-end'] + ['type' => 'submit', 'class' => 'self-end'], ) ?> @@ -46,11 +46,11 @@
allowRegistration): ?>
diff --git a/app/Views/auth/register.php b/app/Views/auth/register.php index de58efd4..cf81efa4 100644 --- a/app/Views/auth/register.php +++ b/app/Views/auth/register.php @@ -46,9 +46,9 @@ 'primary'], - ['type' => 'submit', 'class' => 'self-end'] + ['type' => 'submit', 'class' => 'self-end'], ) ?> @@ -60,9 +60,9 @@

diff --git a/app/Views/auth/reset.php b/app/Views/auth/reset.php index a89577ae..429aba78 100644 --- a/app/Views/auth/reset.php +++ b/app/Views/auth/reset.php @@ -44,9 +44,9 @@ 'primary'], - ['type' => 'submit', 'class' => 'self-end'] + ['type' => 'submit', 'class' => 'self-end'], ) ?> diff --git a/app/Views/credits.php b/app/Views/credits.php index f85ddb07..34344e76 100644 --- a/app/Views/credits.php +++ b/app/Views/credits.php @@ -7,45 +7,45 @@ section('content') ?>
- $groups): ?> - -
- - $persons): ?> -
- <?= $persons['full_name'] ?> -
- - - - - - + $groups): ?> + +
+ + +
+ <?= $persons[
+    'full_name'
+] ?> +
+ + + + + + +
+
+
+ + + + + + +
-
-
- $role_array): ?> - - - - - -
-
endSection(); ?> diff --git a/app/Views/embeddable_player.php b/app/Views/embeddable_player.php index 59fd7759..e878a237 100644 --- a/app/Views/embeddable_player.php +++ b/app/Views/embeddable_player.php @@ -60,4 +60,4 @@
- \ No newline at end of file + diff --git a/app/Views/errors/cli/error_exception.php b/app/Views/errors/cli/error_exception.php index f9ebc35a..8fe9a97a 100644 --- a/app/Views/errors/cli/error_exception.php +++ b/app/Views/errors/cli/error_exception.php @@ -12,8 +12,8 @@ CLI::write( 'at ' . CLI::color( clean_path($exception->getFile()) . ':' . $exception->getLine(), - 'green' - ) + 'green', + ), ); CLI::newLine(); @@ -36,7 +36,7 @@ if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) { CLI::write($c . $padFile . CLI::color($filepath, 'yellow')); } else { CLI::write( - $c . $padFile . CLI::color('[internal function]', 'yellow') + $c . $padFile . CLI::color('[internal function]', 'yellow'), ); } @@ -60,13 +60,13 @@ if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) { case is_object($value): return 'Object(' . get_class($value) . ')'; case is_array($value): - return count($value) ? '[...]' : '[]'; + return count($value) > 0 ? '[...]' : '[]'; case is_null($value): return 'null'; // return the lowercased version default: return var_export($value, true); } - }, array_values($error['args'] ?? [])) + }, array_values($error['args'] ?? [])), ); $function .= '(' . $args . ')'; diff --git a/app/Views/errors/html/error_404.php b/app/Views/errors/html/error_404.php index ae4d553d..bee5dc32 100644 --- a/app/Views/errors/html/error_404.php +++ b/app/Views/errors/html/error_404.php @@ -5,25 +5,25 @@ 404 Page Not Found - + - -

404 - File Not Found

+ +

404 - File Not Found

-

- - - - Sorry! Cannot seem to find the page you were looking for. - -

+

+ + + + Sorry! Cannot seem to find the page you were looking for. + +

- 'primary', - 'iconLeft' => 'arrow-left', - ]) ?> + 'primary', + 'iconLeft' => 'arrow-left', + ]) ?> - \ No newline at end of file + diff --git a/app/Views/errors/html/error_exception.php b/app/Views/errors/html/error_exception.php index 7a1c053c..5ec36809 100644 --- a/app/Views/errors/html/error_exception.php +++ b/app/Views/errors/html/error_exception.php @@ -1,6 +1,13 @@ - + + @@ -8,9 +15,9 @@ <?= esc($title) ?> @@ -18,6 +25,7 @@ + @@ -30,9 +38,8 @@ getMessage()) - ) ?>" - rel="noreferrer" target="_blank">search → + preg_replace('~\'.*\'|".*"~Us', '', $exception->getMessage()), + ) ?>" rel="noreferrer" target="_blank">search →

@@ -40,7 +47,7 @@

at line

@@ -54,11 +61,11 @@ @@ -68,86 +75,88 @@
    - $row): ?> + $row): ?> -
  1. -

    - - - - - {PHP internal code} - - - - -   —   - - - ( arguments ) -

    - - - getParameters(); - } - foreach ($row['args'] as $key => $value): ?> - - - - - - -
    name : "#$key" - ) ?>
    -
    +
  2. +

    + + + - () + {PHP internal code} - - -   —   () - -

    + + +   —   + + + ( arguments ) +
    + - - -
    - + getParameters(); + } + foreach ($row['args'] as $key => $value): ?> +
    + + + + + +
    name : "#{$key}", + ) ?> +
    +
    + + () -
  3. + - + +   —   () + +

    + + + +
    + +
    + + + +
@@ -169,18 +178,18 @@
- - - -
- -
+ + + +
+ +
@@ -199,18 +208,18 @@ - $value): ?> - - - - - - -
- - - - + $value): ?> + + + + + + +
+ + + + @@ -218,7 +227,7 @@
- + @@ -257,9 +266,13 @@ - + + if (empty($GLOBALS[$var])) { + continue; + } + if (!is_array($GLOBALS[$var])) { + continue; + } ?> @@ -273,18 +286,18 @@ - $value): ?> - - - - - + $value): ?> + + + + +
- - - -
- -
+ + + +
+ +
@@ -311,20 +324,20 @@ - - - - - - getName(), 'html') ?> - getValueLine(), 'html') ?> - + + + + + + getName(), 'html') ?> + getValueLine(), 'html') ?> + + - @@ -333,7 +346,7 @@ setStatusCode(http_response_code()); ?>
@@ -358,12 +371,12 @@ - $value): ?> - - - getHeaderLine($name), 'html') ?> - - + + + + getHeaderLine($name), 'html') ?> + + @@ -375,9 +388,9 @@
    - -
  1. - + +
  2. +
@@ -403,7 +416,7 @@
- + @@ -412,12 +425,13 @@

Displayed at — - PHP: — - CodeIgniter: + PHP: — + CodeIgniter:

- \ No newline at end of file + + diff --git a/app/Views/errors/html/production.php b/app/Views/errors/html/production.php index 4125e0c9..f7bdb022 100644 --- a/app/Views/errors/html/production.php +++ b/app/Views/errors/html/production.php @@ -7,7 +7,7 @@ Whoops! - + @@ -16,4 +16,4 @@

We seem to have hit a snag. Please try again later...

- \ No newline at end of file + diff --git a/app/Views/install/cache_config.php b/app/Views/install/cache_config.php index 0cb1fc80..3a254303 100644 --- a/app/Views/install/cache_config.php +++ b/app/Views/install/cache_config.php @@ -34,7 +34,7 @@ 'primary'], ['type' => 'submit', 'class' => 'self-end'], ) ?> diff --git a/app/Views/install/create_superadmin.php b/app/Views/install/create_superadmin.php index 9bf89683..4363cfd9 100644 --- a/app/Views/install/create_superadmin.php +++ b/app/Views/install/create_superadmin.php @@ -42,7 +42,7 @@ 'primary'], ['type' => 'submit', 'class' => 'self-end'], ) ?> diff --git a/app/Views/install/database_config.php b/app/Views/install/database_config.php index e50d584e..5a887dd6 100644 --- a/app/Views/install/database_config.php +++ b/app/Views/install/database_config.php @@ -70,7 +70,7 @@ 'primary'], ['type' => 'submit', 'class' => 'self-end'], ) ?> diff --git a/app/Views/install/instance_config.php b/app/Views/install/instance_config.php index 4c7ff578..f9d1bc95 100644 --- a/app/Views/install/instance_config.php +++ b/app/Views/install/instance_config.php @@ -67,7 +67,7 @@ 'primary'], ['type' => 'submit', 'class' => 'self-end'], ) ?> diff --git a/app/Views/page.php b/app/Views/page.php index d8aef557..77a04ba8 100644 --- a/app/Views/page.php +++ b/app/Views/page.php @@ -16,7 +16,7 @@

title ?>

diff --git a/app/Views/pager/default_full.php b/app/Views/pager/default_full.php index ab90a46f..7c4c64cb 100644 --- a/app/Views/pager/default_full.php +++ b/app/Views/pager/default_full.php @@ -1,31 +1,32 @@ setSurroundCount(2); ?> +$pager->setSurroundCount(2); +?> - \ No newline at end of file + diff --git a/app/Views/podcast/_partials/note_authenticated.php b/app/Views/podcast/_partials/note_authenticated.php index baeb8794..c60f5942 100644 --- a/app/Views/podcast/_partials/note_authenticated.php +++ b/app/Views/podcast/_partials/note_authenticated.php @@ -4,17 +4,17 @@ ->avatar_image_url ?>" alt="display_name ?>" class="w-12 h-12 mr-4 rounded-full" />
uri ?>" class="flex items-baseline hover:underline" actor->is_local - ? 'target="_blank" rel="noopener noreferrer"' - : '' ?>> + ? '' + : 'target="_blank" rel="noopener noreferrer"' ?>> actor ->display_name ?> @actor ->username . - (!$note->actor->is_local - ? '@' . $note->actor->domain - : '') ?> + ($note->actor->is_local + ? '' + : '@' . $note->actor->domain) ?> diff --git a/app/Views/podcast/_partials/note_with_replies_authenticated.php b/app/Views/podcast/_partials/note_with_replies_authenticated.php index 95e7fe72..f91ef540 100644 --- a/app/Views/podcast/_partials/note_with_replies_authenticated.php +++ b/app/Views/podcast/_partials/note_with_replies_authenticated.php @@ -27,7 +27,7 @@ ) ?> 'primary', 'size' => 'small'], [ 'type' => 'submit', diff --git a/app/Views/podcast/_partials/reblog.php b/app/Views/podcast/_partials/reblog.php index 461c76ca..65d6d9c1 100644 --- a/app/Views/podcast/_partials/reblog.php +++ b/app/Views/podcast/_partials/reblog.php @@ -11,17 +11,17 @@ ->avatar_image_url ?>" alt="display_name ?>" class="w-12 h-12 mr-4 rounded-full" />
uri ?>" class="flex items-baseline hover:underline" actor->is_local - ? 'target="_blank" rel="noopener noreferrer"' - : '' ?>> + ? '' + : 'target="_blank" rel="noopener noreferrer"' ?>> actor ->display_name ?> @actor ->username . - (!$note->actor->is_local - ? '@' . $note->actor->domain - : '') ?> + ($note->actor->is_local + ? '' + : '@' . $note->actor->domain) ?> diff --git a/app/Views/podcast/_partials/reblog_authenticated.php b/app/Views/podcast/_partials/reblog_authenticated.php index e9251e25..80c2431e 100644 --- a/app/Views/podcast/_partials/reblog_authenticated.php +++ b/app/Views/podcast/_partials/reblog_authenticated.php @@ -11,17 +11,17 @@ ->avatar_image_url ?>" alt="display_name ?>" class="w-12 h-12 mr-4 rounded-full" />
uri ?>" class="flex items-baseline hover:underline" actor->is_local - ? 'target="_blank" rel="noopener noreferrer"' - : '' ?>> + ? '' + : 'target="_blank" rel="noopener noreferrer"' ?>> actor ->display_name ?> @actor ->username . - (!$note->actor->is_local - ? '@' . $note->actor->domain - : '') ?> + ($note->actor->is_local + ? '' + : '@' . $note->actor->domain) ?> diff --git a/app/Views/podcast/_partials/reply.php b/app/Views/podcast/_partials/reply.php index 2a9018ed..8476bfd4 100644 --- a/app/Views/podcast/_partials/reply.php +++ b/app/Views/podcast/_partials/reply.php @@ -4,13 +4,13 @@
uri ?>" class="mr-2 text-base font-semibold truncate hover:underline" actor->is_local - ? 'target="_blank" rel="noopener noreferrer"' - : '' ?>>actor + ? '' + : 'target="_blank" rel="noopener noreferrer"' ?>>actor ->display_name ?>@actor->username . - (!$reply->actor->is_local ? '@' . $reply->actor->domain : '') ?> + ($reply->actor->is_local ? '' : '@' . $reply->actor->domain) ?>