refactor: add rector to enforce type declarations, code quality + style and remove dead code
- update CI process to include quality stage (tests + code review) - add captainhook to install git pre-commit & pre-push hooks - remove .devcontainer Dockerfile to use project's docker-compose services: all services can now be started automatically using vscode - update docs/setup-development.md
This commit is contained in:
parent
a54a5964c3
commit
5c5c6da4be
|
@ -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
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
[
|
||||
"@semantic-release/exec",
|
||||
{
|
||||
"prepareCmd": "./prepare-release.sh ${nextRelease.version}"
|
||||
"prepareCmd": "./scripts/prepare-release.sh ${nextRelease.version}"
|
||||
}
|
||||
],
|
||||
"@semantic-release/npm",
|
||||
|
|
23
Dockerfile
23
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<yassine@podlibre.org>"
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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<int, string>
|
||||
*/
|
||||
public function getPermissionsForGroup(int $groupId): array
|
||||
{
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -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';
|
||||
}
|
||||
|
|
|
@ -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<string, string>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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 `<form>` 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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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*');
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
@ -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<string, string>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -22,7 +22,7 @@ class Mimes
|
|||
/**
|
||||
* Map of extensions to mime types.
|
||||
*
|
||||
* @var array
|
||||
* @var array<string, string>
|
||||
*/
|
||||
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));
|
||||
|
|
|
@ -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' => [
|
||||
|
|
|
@ -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 = [
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
])
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
|
||||
namespace App\Controllers\Admin;
|
||||
|
||||
use ActivityPub\Models\BlockedDomainModel;
|
||||
|
||||
class Fediverse extends BaseController
|
||||
{
|
||||
public function dashboard()
|
||||
|
|
|
@ -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,
|
||||
])
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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() .
|
||||
': <a href="' .
|
||||
$this->request->getPost('imported_feed_url') .
|
||||
'" rel="noreferrer noopener" target="_blank">' .
|
||||
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
])
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -12,6 +12,9 @@ use App\Models\PodcastModel;
|
|||
|
||||
class Home extends BaseController
|
||||
{
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$model = new PodcastModel();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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} ▸ "
|
||||
: '') .
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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 = <<<EOD
|
||||
CREATE VIEW `$viewName` AS
|
||||
SELECT `person_group`, `person_id`, `full_name`, `person_role`, `podcast_id`, NULL AS `episode_id` FROM `$podcastPersonTable`
|
||||
INNER JOIN `$personTable`
|
||||
ON (`person_id`=`$personTable`.`id`)
|
||||
CREATE VIEW `{$viewName}` AS
|
||||
SELECT `person_group`, `person_id`, `full_name`, `person_role`, `podcast_id`, NULL AS `episode_id` FROM `{$podcastPersonsTable}`
|
||||
INNER JOIN `{$personsTable}`
|
||||
ON (`person_id`=`{$personsTable}`.`id`)
|
||||
UNION
|
||||
SELECT `person_group`, `person_id`, `full_name`, `person_role`, `podcast_id`, `episode_id` FROM `$episodePersonTable`
|
||||
INNER JOIN `$personTable`
|
||||
ON (`person_id`=`$personTable`.`id`)
|
||||
SELECT `person_group`, `person_id`, `full_name`, `person_role`, {$episodePersonsTable}.`podcast_id`, `episode_id` FROM `{$episodePersonsTable}`
|
||||
INNER JOIN `{$personsTable}`
|
||||
ON (`person_id`=`{$personsTable}`.`id`)
|
||||
INNER JOIN `{$episodesTable}`
|
||||
ON (`episode_id`=`{$episodesTable}`.`id`)
|
||||
WHERE `{$episodesTable}`.published_at <= NOW()
|
||||
ORDER BY `person_group`, `full_name`, `person_role`, `podcast_id`, `episode_id`;
|
||||
EOD;
|
||||
$this->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}`");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,19 +15,19 @@ use CodeIgniter\Database\Migration;
|
|||
|
||||
class AddEpisodeIdToNotes extends Migration
|
||||
{
|
||||
public function up()
|
||||
public function up(): void
|
||||
{
|
||||
$prefix = $this->db->getPrefix();
|
||||
|
||||
$createQuery = <<<SQL
|
||||
ALTER TABLE ${prefix}activitypub_notes
|
||||
ALTER TABLE {$prefix}activitypub_notes
|
||||
ADD COLUMN `episode_id` INT UNSIGNED NULL AFTER `replies_count`,
|
||||
ADD FOREIGN KEY ${prefix}activitypub_notes_episode_id_foreign(episode_id) REFERENCES ${prefix}episodes(id) ON DELETE CASCADE;
|
||||
ADD FOREIGN KEY {$prefix}activitypub_notes_episode_id_foreign(episode_id) REFERENCES {$prefix}episodes(id) ON DELETE CASCADE;
|
||||
SQL;
|
||||
$this->db->query($createQuery);
|
||||
}
|
||||
|
||||
public function down()
|
||||
public function down(): void
|
||||
{
|
||||
$this->forge->dropForeignKey(
|
||||
'activitypub_notes',
|
||||
|
|
|
@ -15,19 +15,19 @@ use CodeIgniter\Database\Migration;
|
|||
|
||||
class AddCreatedByToNotes extends Migration
|
||||
{
|
||||
public function up()
|
||||
public function up(): void
|
||||
{
|
||||
$prefix = $this->db->getPrefix();
|
||||
|
||||
$createQuery = <<<SQL
|
||||
ALTER TABLE ${prefix}activitypub_notes
|
||||
ALTER TABLE {$prefix}activitypub_notes
|
||||
ADD COLUMN `created_by` INT UNSIGNED AFTER `episode_id`,
|
||||
ADD FOREIGN KEY ${prefix}activitypub_notes_created_by_foreign(created_by) REFERENCES ${prefix}users(id) ON DELETE CASCADE;
|
||||
ADD FOREIGN KEY {$prefix}activitypub_notes_created_by_foreign(created_by) REFERENCES {$prefix}users(id) ON DELETE CASCADE;
|
||||
SQL;
|
||||
$this->db->query($createQuery);
|
||||
}
|
||||
|
||||
public function down()
|
||||
public function down(): void
|
||||
{
|
||||
$this->forge->dropForeignKey(
|
||||
'activitypub_notes',
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -15,6 +15,9 @@ use CodeIgniter\Database\Seeder;
|
|||
|
||||
class AuthSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* @var array<string, string>[]
|
||||
*/
|
||||
protected $groups = [
|
||||
[
|
||||
'name' => 'superadmin',
|
||||
|
@ -37,6 +40,8 @@ class AuthSeeder extends Seeder
|
|||
* ...
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* @var array<string, array<string, string|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<string, string|int>[] $dataGroups
|
||||
*/
|
||||
static function getGroupIdByName(string $name, array $dataGroups): int
|
||||
{
|
||||
foreach ($dataGroups as $group) {
|
||||
if ($group['name'] === $name) {
|
||||
return $group['id'];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ use CodeIgniter\Database\Seeder;
|
|||
|
||||
class CategorySeeder extends Seeder
|
||||
{
|
||||
public function run()
|
||||
public function run(): void
|
||||
{
|
||||
$data = [
|
||||
[
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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'],
|
||||
[
|
||||
|
|
|
@ -15,7 +15,7 @@ use CodeIgniter\Database\Seeder;
|
|||
|
||||
class PlatformSeeder extends Seeder
|
||||
{
|
||||
public function run()
|
||||
public function run(): void
|
||||
{
|
||||
$data = [
|
||||
[
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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.',
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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<string, string>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<string, string>
|
||||
*/
|
||||
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",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
'<?xml version="1.0" encoding="utf-8"?><rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:podcast="https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"/>',
|
||||
))
|
||||
->addChild('channel')
|
||||
->addChild('item');
|
||||
array_to_rss(
|
||||
[
|
||||
'elements' => $this->custom_rss,
|
||||
],
|
||||
$xmlNode,
|
||||
);
|
||||
return str_replace(['<item>', '</item>'], '', $xmlNode->asXML());
|
||||
}
|
||||
|
||||
helper('rss');
|
||||
|
||||
$xmlNode = (new SimpleRSSElement(
|
||||
'<?xml version="1.0" encoding="utf-8"?><rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:podcast="https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"/>',
|
||||
))
|
||||
->addChild('channel')
|
||||
->addChild('item');
|
||||
array_to_rss(
|
||||
[
|
||||
'elements' => $this->custom_rss,
|
||||
],
|
||||
$xmlNode,
|
||||
);
|
||||
return str_replace(['<item>', '</item>'], '', $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
|
|||
'</item></channel></rss>',
|
||||
),
|
||||
)['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)
|
||||
|
|
|
@ -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<string, string>
|
||||
*/
|
||||
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'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,10 +8,13 @@
|
|||
|
||||
namespace App\Entities;
|
||||
|
||||
use CodeIgniter\Entity;
|
||||
use CodeIgniter\Entity\Entity;
|
||||
|
||||
class Language extends Entity
|
||||
{
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'code' => 'string',
|
||||
'native_name' => 'string',
|
||||
|
|
|
@ -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.',
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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<string, string>
|
||||
*/
|
||||
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',
|
||||
|
|
|
@ -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<string, string>
|
||||
*/
|
||||
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'],
|
||||
);
|
||||
|
|
|
@ -8,10 +8,13 @@
|
|||
|
||||
namespace App\Entities;
|
||||
|
||||
use CodeIgniter\Entity;
|
||||
use CodeIgniter\Entity\Entity;
|
||||
|
||||
class Platform extends Entity
|
||||
{
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'slug' => 'string',
|
||||
'type' => 'string',
|
||||
|
|
|
@ -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<int>
|
||||
*/
|
||||
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(
|
||||
'<?xml version="1.0" encoding="utf-8"?><rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:podcast="https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"/>',
|
||||
))->addChild('channel');
|
||||
array_to_rss(
|
||||
[
|
||||
'elements' => $this->custom_rss,
|
||||
],
|
||||
$xmlNode,
|
||||
);
|
||||
return str_replace(
|
||||
['<channel>', '</channel>'],
|
||||
'',
|
||||
$xmlNode->asXML(),
|
||||
);
|
||||
}
|
||||
|
||||
helper('rss');
|
||||
|
||||
$xmlNode = (new SimpleRSSElement(
|
||||
'<?xml version="1.0" encoding="utf-8"?><rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:podcast="https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"/>',
|
||||
))->addChild('channel');
|
||||
array_to_rss(
|
||||
[
|
||||
'elements' => $this->custom_rss,
|
||||
],
|
||||
$xmlNode,
|
||||
);
|
||||
|
||||
return str_replace(['<channel>', '</channel>'], '', $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
|
|||
'</channel></rss>',
|
||||
),
|
||||
)['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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<string, string>
|
||||
*/
|
||||
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'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,10 +8,13 @@
|
|||
|
||||
namespace App\Entities;
|
||||
|
||||
use CodeIgniter\Entity;
|
||||
use CodeIgniter\Entity\Entity;
|
||||
|
||||
class Soundbite extends Entity
|
||||
{
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
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;
|
||||
|
||||
|
|
|
@ -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<string, string>
|
||||
*/
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 <<<HTML
|
||||
<button class="$buttonClass" $attributes>
|
||||
$label
|
||||
<button class="{$buttonClass}" {$attributes}>
|
||||
{$label}
|
||||
</button>
|
||||
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' => '<table class="w-full whitespace-no-wrap">',
|
||||
|
@ -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 = [
|
||||
'<time pubdate datetime="' .
|
||||
$publicationDate->format(DateTime::ATOM) .
|
||||
'" title="' .
|
||||
$publicationDate .
|
||||
'">' .
|
||||
lang('Common.mediumDate', [$publicationDate]) .
|
||||
'</time>',
|
||||
];
|
||||
}
|
||||
$langOptions = [
|
||||
'<time pubdate datetime="' .
|
||||
$publicationDate->format(DateTime::ATOM) .
|
||||
'" title="' .
|
||||
$publicationDate .
|
||||
'">' .
|
||||
lang('Common.mediumDate', [$publicationDate]) .
|
||||
'</time>',
|
||||
];
|
||||
|
||||
$label = lang(
|
||||
'Episode.publication_status.' . $publicationStatus,
|
||||
$transParam,
|
||||
$langOptions,
|
||||
);
|
||||
|
||||
return '<span class="px-1 font-semibold border ' .
|
||||
|
@ -303,15 +305,13 @@ if (!function_exists('publication_button')) {
|
|||
*
|
||||
* Displays the appropriate publication button depending on the publication status.
|
||||
*
|
||||
* @param integer $podcastId
|
||||
* @param integer $episodeId
|
||||
* @param boolean $publicationStatus the episode's publication status *
|
||||
* @return string
|
||||
*/
|
||||
function publication_button(
|
||||
$podcastId,
|
||||
$episodeId,
|
||||
$publicationStatus
|
||||
int $podcastId,
|
||||
int $episodeId,
|
||||
bool $publicationStatus
|
||||
): string {
|
||||
switch ($publicationStatus) {
|
||||
case 'not_published':
|
||||
|
@ -351,17 +351,15 @@ if (!function_exists('episode_numbering')) {
|
|||
/**
|
||||
* Returns relevant translated episode numbering.
|
||||
*
|
||||
* @param int|null $episodeNumber
|
||||
* @param int|null $seasonNumber
|
||||
* @param string $class styling classes
|
||||
* @param string $is_abbr component will show abbreviated numbering if true
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
function episode_numbering(
|
||||
$episodeNumber = null,
|
||||
$seasonNumber = null,
|
||||
$class = '',
|
||||
?int $episodeNumber = null,
|
||||
?int $seasonNumber = null,
|
||||
string $class = '',
|
||||
$isAbbr = false
|
||||
): string {
|
||||
if (!$episodeNumber && !$seasonNumber) {
|
||||
|
@ -409,36 +407,28 @@ if (!function_exists('episode_numbering')) {
|
|||
if (!function_exists('location_link')) {
|
||||
/**
|
||||
* Returns link to display from location info
|
||||
*
|
||||
* @param string $locationName
|
||||
* @param string $locationGeo
|
||||
* @param string $locationOsmid
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function location_link(
|
||||
$locationName,
|
||||
$locationGeo,
|
||||
$locationOsmid,
|
||||
?string $locationName,
|
||||
?string $locationGeo,
|
||||
?string $locationOsmid,
|
||||
$class = ''
|
||||
) {
|
||||
$link = '';
|
||||
|
||||
if (!empty($locationName)) {
|
||||
$link = 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',
|
||||
],
|
||||
);
|
||||
): 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',
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @copyright 2020 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
|
@ -71,20 +72,15 @@ if (!function_exists('form_switch')) {
|
|||
*
|
||||
* Abstracts form_label to stylize it as a switch toggle
|
||||
*
|
||||
* @param array $data
|
||||
* @param string $value
|
||||
* @param boolean $checked
|
||||
* @param mixed $extra
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function form_switch(
|
||||
$label = '',
|
||||
$data = '',
|
||||
array $data = [],
|
||||
string $value = '',
|
||||
bool $checked = false,
|
||||
$class = '',
|
||||
$extra = ''
|
||||
string $class = '',
|
||||
array $extra = []
|
||||
): string {
|
||||
$data['class'] = 'form-switch';
|
||||
|
||||
|
@ -155,31 +151,26 @@ if (!function_exists('form_multiselect')) {
|
|||
/**
|
||||
* Multi-select menu
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $options
|
||||
* @param array $selected
|
||||
* @param mixed $extra
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function form_multiselect(
|
||||
string $name = '',
|
||||
array $options = [],
|
||||
array $selected = [],
|
||||
$customExtra = ''
|
||||
array $customExtra = []
|
||||
): string {
|
||||
$defaultExtra = [
|
||||
'data-class' => $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));
|
||||
|
|
|
@ -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<br>';
|
||||
if (!empty($tagwriter->warnings)) {
|
||||
echo 'There were some warnings:<br>' .
|
||||
implode('<br><br>', $tagwriter->warnings);
|
||||
}
|
||||
} else {
|
||||
echo 'Failed to write tags!<br>' .
|
||||
implode('<br><br>', $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<br>';
|
||||
if (!empty($tagwriter->warnings)) {
|
||||
echo 'There were some warnings:<br>' .
|
||||
implode('<br><br>', $tagwriter->warnings);
|
||||
}
|
||||
} else {
|
||||
echo 'Failed to write tags!<br>' .
|
||||
implode('<br><br>', $tagwriter->errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 '<nav class="' . $class . '">' . $links . '</nav>';
|
||||
return '<nav class="' . $class . '">' . $links . '</nav>';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
"<?xml version='1.0' encoding='utf-8'?><rss version='2.0' xmlns:itunes='$itunes_namespace' xmlns:podcast='$podcast_namespace' xmlns:content='http://purl.org/rss/1.0/modules/content/'></rss>",
|
||||
);
|
||||
|
||||
$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(
|
||||
"<?xml version='1.0' encoding='utf-8'?><rss version='2.0' xmlns:itunes='{$itunes_namespace}' xmlns:podcast='{$podcast_namespace}' xmlns:content='http://purl.org/rss/1.0/modules/content/'></rss>",
|
||||
);
|
||||
}
|
||||
|
||||
// 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 <itunes:category> and <category> 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 <itunes:category> and <category> 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;
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
'<svg',
|
||||
'<svg class="' . $class . '"',
|
||||
$svg_contents,
|
||||
);
|
||||
}
|
||||
if (!function_exists('icon')) {
|
||||
/**
|
||||
* 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 = ''): string
|
||||
{
|
||||
$svg_contents = file_get_contents('assets/icons/' . $name . '.svg');
|
||||
if ($class !== '') {
|
||||
$svg_contents = str_replace(
|
||||
'<svg',
|
||||
'<svg class="' . $class . '"',
|
||||
$svg_contents,
|
||||
);
|
||||
}
|
||||
|
||||
return $svg_contents;
|
||||
return $svg_contents;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the inline svg image
|
||||
*
|
||||
* @param string $name name of the image file without the .svg extension
|
||||
* @param string $class to be added to the svg string
|
||||
* @return string svg contents
|
||||
*/
|
||||
function svg($name, $class = null)
|
||||
{
|
||||
$svg_contents = file_get_contents('assets/images/' . $name . '.svg');
|
||||
if ($class) {
|
||||
$svg_contents = str_replace(
|
||||
'<svg',
|
||||
'<svg class="' . $class . '"',
|
||||
$svg_contents,
|
||||
);
|
||||
if (!function_exists('svg')) {
|
||||
/**
|
||||
* Returns the inline svg image
|
||||
*
|
||||
* @param string $name name of the image file without the .svg extension
|
||||
* @param string $class to be added to the svg string
|
||||
* @return string svg contents
|
||||
*/
|
||||
function svg(string $name, ?string $class = null): string
|
||||
{
|
||||
$svg_contents = file_get_contents('assets/images/' . $name . '.svg');
|
||||
if ($class) {
|
||||
$svg_contents = str_replace(
|
||||
'<svg',
|
||||
'<svg class="' . $class . '"',
|
||||
$svg_contents,
|
||||
);
|
||||
}
|
||||
return $svg_contents;
|
||||
}
|
||||
return $svg_contents;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @copyright 2020 Podlibre
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
if (!function_exists('host_url')) {
|
||||
/**
|
||||
* Return the host URL to use in views
|
||||
|
@ -24,10 +30,8 @@ if (!function_exists('host_url')) {
|
|||
if (!function_exists('current_season_url')) {
|
||||
/**
|
||||
* Return the podcast URL with season number to use in views
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function current_season_url()
|
||||
function current_season_url(): string
|
||||
{
|
||||
$season_query_string = '';
|
||||
if (isset($_GET['season'])) {
|
||||
|
@ -42,36 +46,27 @@ if (!function_exists('current_season_url')) {
|
|||
if (!function_exists('location_url')) {
|
||||
/**
|
||||
* Returns URL to display from location info
|
||||
*
|
||||
* @param string $locationName
|
||||
* @param string $locationGeo
|
||||
* @param string $locationOsmid
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function location_url($locationName, $locationGeo, $locationOsmid)
|
||||
{
|
||||
$uri = '';
|
||||
|
||||
function location_url(
|
||||
string $locationName,
|
||||
?string $locationGeo = null,
|
||||
?string $locationOsmid = null
|
||||
): string {
|
||||
if (!empty($locationOsmid)) {
|
||||
$uri =
|
||||
'https://www.openstreetmap.org/' .
|
||||
return 'https://www.openstreetmap.org/' .
|
||||
['N' => '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<podcastName>[a-zA-Z0-9\_]{1,32})\/episodes\/(?P<episodeSlug>[a-zA-Z0-9\-]{1,191})/',
|
||||
'~@(?P<podcastName>[a-zA-Z0-9\_]{1,32})\/episodes\/(?P<episodeSlug>[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'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
],
|
||||
|
|
|
@ -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!",
|
||||
],
|
||||
];
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
],
|
||||
|
|
|
@ -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.",
|
||||
],
|
||||
];
|
||||
|
|
|
@ -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!',
|
||||
],
|
||||
];
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue