Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • adaures/castopod
  • mkljczk/castopod-host
  • spaetz/castopod-host
  • PatrykMis/castopod
  • jonas/castopod
  • ajeremias/castopod
  • misuzu/castopod
  • KrzysztofDomanczyk/castopod
  • Behel/castopod
  • nebulon/castopod
  • ewen/castopod
  • NeoluxConsulting/castopod
  • nateritter/castopod-og
  • prcutler/castopod
14 results
Show changes
Showing
with 1840 additions and 559 deletions
<?php
declare(strict_types=1);
namespace Config;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Session\Handlers\BaseHandler;
use CodeIgniter\Session\Handlers\FileHandler;
class Session extends BaseConfig
{
/**
* --------------------------------------------------------------------------
* Session Driver
* --------------------------------------------------------------------------
*
* The session storage driver to use:
* - `CodeIgniter\Session\Handlers\FileHandler`
* - `CodeIgniter\Session\Handlers\DatabaseHandler`
* - `CodeIgniter\Session\Handlers\MemcachedHandler`
* - `CodeIgniter\Session\Handlers\RedisHandler`
*
* @var class-string<BaseHandler>
*/
public string $driver = FileHandler::class;
/**
* --------------------------------------------------------------------------
* Session Cookie Name
* --------------------------------------------------------------------------
*
* The session cookie name, must contain only [0-9a-z_-] characters
*/
public string $cookieName = 'ci_session';
/**
* --------------------------------------------------------------------------
* Session Expiration
* --------------------------------------------------------------------------
*
* The number of SECONDS you want the session to last.
* Setting to 0 (zero) means expire when the browser is closed.
*/
public int $expiration = 7200;
/**
* --------------------------------------------------------------------------
* Session Save Path
* --------------------------------------------------------------------------
*
* The location to save sessions to and is driver dependent.
*
* For the 'files' driver, it's a path to a writable directory.
* WARNING: Only absolute paths are supported!
*
* For the 'database' driver, it's a table name.
* Please read up the manual for the format with other session drivers.
*
* IMPORTANT: You are REQUIRED to set a valid save path!
*/
public string $savePath = WRITEPATH . 'session';
/**
* --------------------------------------------------------------------------
* Session Match IP
* --------------------------------------------------------------------------
*
* Whether to match the user's IP address when reading the session data.
*
* WARNING: If you're using the database driver, don't forget to update
* your session table's PRIMARY KEY when changing this setting.
*/
public bool $matchIP = false;
/**
* --------------------------------------------------------------------------
* Session Time to Update
* --------------------------------------------------------------------------
*
* How many seconds between CI regenerating the session ID.
*/
public int $timeToUpdate = 300;
/**
* --------------------------------------------------------------------------
* Session Regenerate Destroy
* --------------------------------------------------------------------------
*
* Whether to destroy session data associated with the old session ID
* when auto-regenerating the session ID. When set to FALSE, the data
* will be later deleted by the garbage collector.
*/
public bool $regenerateDestroy = false;
/**
* --------------------------------------------------------------------------
* Session Database Group
* --------------------------------------------------------------------------
*
* DB Group for the database session.
*/
public ?string $DBGroup = null;
/**
* --------------------------------------------------------------------------
* Lock Retry Interval (microseconds)
* --------------------------------------------------------------------------
*
* This is used for RedisHandler.
*
* Time (microseconds) to wait if lock cannot be acquired.
* The default is 100,000 microseconds (= 0.1 seconds).
*/
public int $lockRetryInterval = 100_000;
/**
* --------------------------------------------------------------------------
* Lock Max Retries
* --------------------------------------------------------------------------
*
* This is used for RedisHandler.
*
* Maximum number of lock acquisition attempts.
* The default is 300 times. That is lock timeout is about 30 (0.1 * 300)
* seconds.
*/
public int $lockMaxRetries = 300;
}
<?php
declare(strict_types=1);
namespace Config;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Tasks\Scheduler;
class Tasks extends BaseConfig
{
/**
* --------------------------------------------------------------------------
* Should performance metrics be logged
* --------------------------------------------------------------------------
*
* If true, will log the time it takes for each task to run.
* Requires the settings table to have been created previously.
*/
public bool $logPerformance = false;
/**
* --------------------------------------------------------------------------
* Maximum performance logs
* --------------------------------------------------------------------------
*
* The maximum number of logs that should be saved per Task.
* Lower numbers reduced the amount of database required to
* store the logs.
*/
public int $maxLogsPerTask = 10;
/**
* Register any tasks within this method for the application.
* Called by the TaskRunner.
*/
public function init(Scheduler $schedule): void
{
$schedule->command('fediverse:broadcast')
->everyMinute()
->named('fediverse-broadcast');
$schedule->command('websub:publish')
->everyMinute()
->named('websub-publish');
$schedule->command('video-clips:generate')
->everyMinute()
->named('video-clips-generate');
$schedule->command('podcast:import')
->everyMinute()
->named('podcast-import');
$schedule->command('episodes:compute-downloads')
->everyHour()
->named('episodes:compute-downloads');
}
}
<?php namespace Config;
<?php
declare(strict_types=1);
namespace Config;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Debug\Toolbar\Collectors\Database;
use CodeIgniter\Debug\Toolbar\Collectors\Events;
use CodeIgniter\Debug\Toolbar\Collectors\Files;
use CodeIgniter\Debug\Toolbar\Collectors\Logs;
use CodeIgniter\Debug\Toolbar\Collectors\Routes;
use CodeIgniter\Debug\Toolbar\Collectors\Timers;
use CodeIgniter\Debug\Toolbar\Collectors\Views;
/**
* --------------------------------------------------------------------------
* Debug Toolbar
* --------------------------------------------------------------------------
*
* The Debug Toolbar provides a way to see information about the performance
* and state of your application during that page display. By default it will
* NOT be displayed under production environments, and will only display if
* `CI_DEBUG` is true, since if it's not, there's not much to display anyway.
*/
class Toolbar extends BaseConfig
{
/*
|--------------------------------------------------------------------------
| Debug Toolbar
|--------------------------------------------------------------------------
| The Debug Toolbar provides a way to see information about the performance
| and state of your application during that page display. By default it will
| NOT be displayed under production environments, and will only display if
| CI_DEBUG is true, since if it's not, there's not much to display anyway.
|
| toolbarMaxHistory = Number of history files, 0 for none or -1 for unlimited
|
*/
public $collectors = [
\CodeIgniter\Debug\Toolbar\Collectors\Timers::class,
\CodeIgniter\Debug\Toolbar\Collectors\Database::class,
\CodeIgniter\Debug\Toolbar\Collectors\Logs::class,
\CodeIgniter\Debug\Toolbar\Collectors\Views::class,
\CodeIgniter\Debug\Toolbar\Collectors\Cache::class,
\CodeIgniter\Debug\Toolbar\Collectors\Files::class,
\CodeIgniter\Debug\Toolbar\Collectors\Routes::class,
\CodeIgniter\Debug\Toolbar\Collectors\Events::class,
/**
* --------------------------------------------------------------------------
* Toolbar Collectors
* --------------------------------------------------------------------------
*
* List of toolbar collectors that will be called when Debug Toolbar
* fires up and collects data from.
*
* @var list<class-string>
*/
public array $collectors = [
Timers::class,
Database::class,
Logs::class,
Views::class,
// Cache::class,
Files::class,
Routes::class,
Events::class,
];
/*
|--------------------------------------------------------------------------
| Max History
|--------------------------------------------------------------------------
| The Toolbar allows you to view recent requests that have been made to
| the application while the toolbar is active. This allows you to quickly
| view and compare multiple requests.
|
| $maxHistory sets a limit on the number of past requests that are stored,
| helping to conserve file space used to store them. You can set it to
| 0 (zero) to not have any history stored, or -1 for unlimited history.
|
*/
public $maxHistory = 20;
/**
* --------------------------------------------------------------------------
* Collect Var Data
* --------------------------------------------------------------------------
*
* If set to false var data from the views will not be collected. Useful to
* avoid high memory usage when there are lots of data passed to the view.
*/
public bool $collectVarData = true;
/**
* --------------------------------------------------------------------------
* Max History
* --------------------------------------------------------------------------
*
* `$maxHistory` sets a limit on the number of past requests that are stored,
* helping to conserve file space used to store them. You can set it to
* 0 (zero) to not have any history stored, or -1 for unlimited history.
*/
public int $maxHistory = 20;
/**
* --------------------------------------------------------------------------
* Toolbar Views Path
* --------------------------------------------------------------------------
*
* The full path to the the views that are used by the toolbar.
* This MUST have a trailing slash.
*/
public string $viewsPath = SYSTEMPATH . 'Debug/Toolbar/Views/';
/**
* --------------------------------------------------------------------------
* Max Queries
* --------------------------------------------------------------------------
*
* If the Database Collector is enabled, it will log every query that the
* the system generates so they can be displayed on the toolbar's timeline
* and in the query log. This can lead to memory issues in some instances
* with hundreds of queries.
*
* `$maxQueries` defines the maximum amount of queries that will be stored.
*/
public int $maxQueries = 100;
/*
|--------------------------------------------------------------------------
| Toolbar Views Path
|--------------------------------------------------------------------------
| The full path to the the views that are used by the toolbar.
| MUST have a trailing slash.
|
*/
public $viewsPath = SYSTEMPATH . 'Debug/Toolbar/Views/';
/**
* --------------------------------------------------------------------------
* Watched Directories
* --------------------------------------------------------------------------
*
* Contains an array of directories that will be watched for changes and
* used to determine if the hot-reload feature should reload the page or not.
* We restrict the values to keep performance as high as possible.
*
* NOTE: The ROOTPATH will be prepended to all values.
*
* @var list<string>
*/
public array $watchedDirectories = ['app', 'modules', 'themes'];
/*
|--------------------------------------------------------------------------
| Max Queries
|--------------------------------------------------------------------------
| If the Database Collector is enabled, it will log every query that the
| the system generates so they can be displayed on the toolbar's timeline
| and in the query log. This can lead to memory issues in some instances
| with hundreds of queries.
|
| $maxQueries defines the maximum amount of queries that will be stored.
|
*/
public $maxQueries = 100;
/**
* --------------------------------------------------------------------------
* Watched File Extensions
* --------------------------------------------------------------------------
*
* Contains an array of file extensions that will be watched for changes and
* used to determine if the hot-reload feature should reload the page or not.
*
* @var list<string>
*/
public array $watchedExtensions = ['php', 'css', 'js', 'html', 'svg', 'json', 'env'];
}
<?php namespace Config;
<?php
declare(strict_types=1);
namespace Config;
use CodeIgniter\Config\BaseConfig;
/**
* -------------------------------------------------------------------
* User Agents
* -------------------------------------------------------------------
*
* This file contains four arrays of user agent data. It is used by the
* User Agent Class to help identify browser, platform, robot, and
* mobile device data. The array keys are used to identify the device
* and the array values are used to set the actual name of the item.
*/
class UserAgents extends BaseConfig
{
/*
| -------------------------------------------------------------------
| USER AGENT TYPES
| -------------------------------------------------------------------
| This file contains four arrays of user agent data. It is used by the
| User Agent Class to help identify browser, platform, robot, and
| mobile device data. The array keys are used to identify the device
| and the array values are used to set the actual name of the item.
*/
public $platforms = [
/**
* -------------------------------------------------------------------
* OS Platforms
* -------------------------------------------------------------------
*
* @var array<string, string>
*/
public array $platforms = [
'windows nt 10.0' => 'Windows 10',
'windows nt 6.3' => 'Windows 8.1',
'windows nt 6.2' => 'Windows 8',
'windows nt 6.1' => 'Windows 7',
'windows nt 6.0' => 'Windows Vista',
'windows nt 5.2' => 'Windows 2003',
'windows nt 5.1' => 'Windows XP',
'windows nt 5.0' => 'Windows 2000',
'windows nt 4.0' => 'Windows NT 4.0',
'winnt4.0' => 'Windows NT 4.0',
'winnt 4.0' => 'Windows NT',
'winnt' => 'Windows NT',
'windows 98' => 'Windows 98',
'win98' => 'Windows 98',
'windows 95' => 'Windows 95',
'win95' => 'Windows 95',
'windows phone' => 'Windows Phone',
'windows' => 'Unknown Windows OS',
'android' => 'Android',
'blackberry' => 'BlackBerry',
'iphone' => 'iOS',
'ipad' => 'iOS',
'ipod' => 'iOS',
'os x' => 'Mac OS X',
'ppc mac' => 'Power PC Mac',
'freebsd' => 'FreeBSD',
'ppc' => 'Macintosh',
'linux' => 'Linux',
'debian' => 'Debian',
'sunos' => 'Sun Solaris',
'beos' => 'BeOS',
'apachebench' => 'ApacheBench',
'aix' => 'AIX',
'irix' => 'Irix',
'osf' => 'DEC OSF',
'hp-ux' => 'HP-UX',
'netbsd' => 'NetBSD',
'bsdi' => 'BSDi',
'openbsd' => 'OpenBSD',
'gnu' => 'GNU/Linux',
'unix' => 'Unknown Unix OS',
'symbian' => 'Symbian OS',
'windows nt 6.3' => 'Windows 8.1',
'windows nt 6.2' => 'Windows 8',
'windows nt 6.1' => 'Windows 7',
'windows nt 6.0' => 'Windows Vista',
'windows nt 5.2' => 'Windows 2003',
'windows nt 5.1' => 'Windows XP',
'windows nt 5.0' => 'Windows 2000',
'windows nt 4.0' => 'Windows NT 4.0',
'winnt4.0' => 'Windows NT 4.0',
'winnt 4.0' => 'Windows NT',
'winnt' => 'Windows NT',
'windows 98' => 'Windows 98',
'win98' => 'Windows 98',
'windows 95' => 'Windows 95',
'win95' => 'Windows 95',
'windows phone' => 'Windows Phone',
'windows' => 'Unknown Windows OS',
'android' => 'Android',
'blackberry' => 'BlackBerry',
'iphone' => 'iOS',
'ipad' => 'iOS',
'ipod' => 'iOS',
'os x' => 'Mac OS X',
'ppc mac' => 'Power PC Mac',
'freebsd' => 'FreeBSD',
'ppc' => 'Macintosh',
'linux' => 'Linux',
'debian' => 'Debian',
'sunos' => 'Sun Solaris',
'beos' => 'BeOS',
'apachebench' => 'ApacheBench',
'aix' => 'AIX',
'irix' => 'Irix',
'osf' => 'DEC OSF',
'hp-ux' => 'HP-UX',
'netbsd' => 'NetBSD',
'bsdi' => 'BSDi',
'openbsd' => 'OpenBSD',
'gnu' => 'GNU/Linux',
'unix' => 'Unknown Unix OS',
'symbian' => 'Symbian OS',
];
// The order of this array should NOT be changed. Many browsers return
// multiple browser types so we want to identify the sub-type first.
public $browsers = [
'OPR' => 'Opera',
'Flock' => 'Flock',
'Edge' => 'Spartan',
/**
* -------------------------------------------------------------------
* Browsers
* -------------------------------------------------------------------
*
* The order of this array should NOT be changed. Many browsers return
* multiple browser types so we want to identify the subtype first.
*
* @var array<string, string>
*/
public array $browsers = [
'OPR' => 'Opera',
'Flock' => 'Flock',
'Edge' => 'Spartan',
'Edg' => 'Edge',
'Chrome' => 'Chrome',
// Opera 10+ always reports Opera/9.80 and appends Version/<real version> to the user agent string
'Opera.*?Version' => 'Opera',
'Opera' => 'Opera',
'MSIE' => 'Internet Explorer',
'Opera.*?Version' => 'Opera',
'Opera' => 'Opera',
'MSIE' => 'Internet Explorer',
'Internet Explorer' => 'Internet Explorer',
'Trident.* rv' => 'Internet Explorer',
'Shiira' => 'Shiira',
'Firefox' => 'Firefox',
'Chimera' => 'Chimera',
'Phoenix' => 'Phoenix',
'Firebird' => 'Firebird',
'Camino' => 'Camino',
'Netscape' => 'Netscape',
'OmniWeb' => 'OmniWeb',
'Safari' => 'Safari',
'Mozilla' => 'Mozilla',
'Konqueror' => 'Konqueror',
'icab' => 'iCab',
'Lynx' => 'Lynx',
'Links' => 'Links',
'hotjava' => 'HotJava',
'amaya' => 'Amaya',
'IBrowse' => 'IBrowse',
'Maxthon' => 'Maxthon',
'Ubuntu' => 'Ubuntu Web Browser',
'Vivaldi' => 'Vivaldi',
'Trident.* rv' => 'Internet Explorer',
'Shiira' => 'Shiira',
'Firefox' => 'Firefox',
'Chimera' => 'Chimera',
'Phoenix' => 'Phoenix',
'Firebird' => 'Firebird',
'Camino' => 'Camino',
'Netscape' => 'Netscape',
'OmniWeb' => 'OmniWeb',
'Safari' => 'Safari',
'Mozilla' => 'Mozilla',
'Konqueror' => 'Konqueror',
'icab' => 'iCab',
'Lynx' => 'Lynx',
'Links' => 'Links',
'hotjava' => 'HotJava',
'amaya' => 'Amaya',
'IBrowse' => 'IBrowse',
'Maxthon' => 'Maxthon',
'Ubuntu' => 'Ubuntu Web Browser',
'Vivaldi' => 'Vivaldi',
];
public $mobiles = [
/**
* -------------------------------------------------------------------
* Mobiles
* -------------------------------------------------------------------
*
* @var array<string, string>
*/
public array $mobiles = [
// legacy array, old values commented out
'mobileexplorer' => 'Mobile Explorer',
// 'openwave' => 'Open Wave',
......@@ -111,108 +139,116 @@ class UserAgents extends BaseConfig
// 'motorola' => 'Motorola'
// Phones and Manufacturers
'motorola' => 'Motorola',
'nokia' => 'Nokia',
'palm' => 'Palm',
'iphone' => 'Apple iPhone',
'ipad' => 'iPad',
'ipod' => 'Apple iPod Touch',
'sony' => 'Sony Ericsson',
'ericsson' => 'Sony Ericsson',
'blackberry' => 'BlackBerry',
'cocoon' => 'O2 Cocoon',
'blazer' => 'Treo',
'lg' => 'LG',
'amoi' => 'Amoi',
'xda' => 'XDA',
'mda' => 'MDA',
'vario' => 'Vario',
'htc' => 'HTC',
'samsung' => 'Samsung',
'sharp' => 'Sharp',
'sie-' => 'Siemens',
'alcatel' => 'Alcatel',
'benq' => 'BenQ',
'ipaq' => 'HP iPaq',
'mot-' => 'Motorola',
'motorola' => 'Motorola',
'nokia' => 'Nokia',
'palm' => 'Palm',
'iphone' => 'Apple iPhone',
'ipad' => 'iPad',
'ipod' => 'Apple iPod Touch',
'sony' => 'Sony Ericsson',
'ericsson' => 'Sony Ericsson',
'blackberry' => 'BlackBerry',
'cocoon' => 'O2 Cocoon',
'blazer' => 'Treo',
'lg' => 'LG',
'amoi' => 'Amoi',
'xda' => 'XDA',
'mda' => 'MDA',
'vario' => 'Vario',
'htc' => 'HTC',
'samsung' => 'Samsung',
'sharp' => 'Sharp',
'sie-' => 'Siemens',
'alcatel' => 'Alcatel',
'benq' => 'BenQ',
'ipaq' => 'HP iPaq',
'mot-' => 'Motorola',
'playstation portable' => 'PlayStation Portable',
'playstation 3' => 'PlayStation 3',
'playstation vita' => 'PlayStation Vita',
'hiptop' => 'Danger Hiptop',
'nec-' => 'NEC',
'panasonic' => 'Panasonic',
'philips' => 'Philips',
'sagem' => 'Sagem',
'sanyo' => 'Sanyo',
'spv' => 'SPV',
'zte' => 'ZTE',
'sendo' => 'Sendo',
'nintendo dsi' => 'Nintendo DSi',
'nintendo ds' => 'Nintendo DS',
'nintendo 3ds' => 'Nintendo 3DS',
'wii' => 'Nintendo Wii',
'open web' => 'Open Web',
'openweb' => 'OpenWeb',
'playstation 3' => 'PlayStation 3',
'playstation vita' => 'PlayStation Vita',
'hiptop' => 'Danger Hiptop',
'nec-' => 'NEC',
'panasonic' => 'Panasonic',
'philips' => 'Philips',
'sagem' => 'Sagem',
'sanyo' => 'Sanyo',
'spv' => 'SPV',
'zte' => 'ZTE',
'sendo' => 'Sendo',
'nintendo dsi' => 'Nintendo DSi',
'nintendo ds' => 'Nintendo DS',
'nintendo 3ds' => 'Nintendo 3DS',
'wii' => 'Nintendo Wii',
'open web' => 'Open Web',
'openweb' => 'OpenWeb',
// Operating Systems
'android' => 'Android',
'symbian' => 'Symbian',
'SymbianOS' => 'SymbianOS',
'elaine' => 'Palm',
'series60' => 'Symbian S60',
'android' => 'Android',
'symbian' => 'Symbian',
'SymbianOS' => 'SymbianOS',
'elaine' => 'Palm',
'series60' => 'Symbian S60',
'windows ce' => 'Windows CE',
// Browsers
'obigo' => 'Obigo',
'netfront' => 'Netfront Browser',
'openwave' => 'Openwave Browser',
'obigo' => 'Obigo',
'netfront' => 'Netfront Browser',
'openwave' => 'Openwave Browser',
'mobilexplorer' => 'Mobile Explorer',
'operamini' => 'Opera Mini',
'opera mini' => 'Opera Mini',
'opera mobi' => 'Opera Mobile',
'fennec' => 'Firefox Mobile',
'operamini' => 'Opera Mini',
'opera mini' => 'Opera Mini',
'opera mobi' => 'Opera Mobile',
'fennec' => 'Firefox Mobile',
// Other
'digital paths' => 'Digital Paths',
'avantgo' => 'AvantGo',
'xiino' => 'Xiino',
'novarra' => 'Novarra Transcoder',
'vodafone' => 'Vodafone',
'docomo' => 'NTT DoCoMo',
'o2' => 'O2',
'avantgo' => 'AvantGo',
'xiino' => 'Xiino',
'novarra' => 'Novarra Transcoder',
'vodafone' => 'Vodafone',
'docomo' => 'NTT DoCoMo',
'o2' => 'O2',
// Fallback
'mobile' => 'Generic Mobile',
'wireless' => 'Generic Mobile',
'j2me' => 'Generic Mobile',
'midp' => 'Generic Mobile',
'cldc' => 'Generic Mobile',
'up.link' => 'Generic Mobile',
'mobile' => 'Generic Mobile',
'wireless' => 'Generic Mobile',
'j2me' => 'Generic Mobile',
'midp' => 'Generic Mobile',
'cldc' => 'Generic Mobile',
'up.link' => 'Generic Mobile',
'up.browser' => 'Generic Mobile',
'smartphone' => 'Generic Mobile',
'cellphone' => 'Generic Mobile',
'cellphone' => 'Generic Mobile',
];
// There are hundreds of bots but these are the most common.
public $robots = [
'googlebot' => 'Googlebot',
'msnbot' => 'MSNBot',
'baiduspider' => 'Baiduspider',
'bingbot' => 'Bing',
'slurp' => 'Inktomi Slurp',
'yahoo' => 'Yahoo',
'ask jeeves' => 'Ask Jeeves',
'fastcrawler' => 'FastCrawler',
'infoseek' => 'InfoSeek Robot 1.0',
'lycos' => 'Lycos',
'yandex' => 'YandexBot',
/**
* -------------------------------------------------------------------
* Robots
* -------------------------------------------------------------------
*
* There are hundred of bots but these are the most common.
*
* @var array<string, string>
*/
public array $robots = [
'googlebot' => 'Googlebot',
'msnbot' => 'MSNBot',
'baiduspider' => 'Baiduspider',
'bingbot' => 'Bing',
'slurp' => 'Inktomi Slurp',
'yahoo' => 'Yahoo',
'ask jeeves' => 'Ask Jeeves',
'fastcrawler' => 'FastCrawler',
'infoseek' => 'InfoSeek Robot 1.0',
'lycos' => 'Lycos',
'yandex' => 'YandexBot',
'mediapartners-google' => 'MediaPartners Google',
'CRAZYWEBCRAWLER' => 'Crazy Webcrawler',
'adsbot-google' => 'AdsBot Google',
'feedfetcher-google' => 'Feedfetcher Google',
'curious george' => 'Curious George',
'ia_archiver' => 'Alexa Crawler',
'MJ12bot' => 'Majestic-12',
'Uptimebot' => 'Uptimebot',
'CRAZYWEBCRAWLER' => 'Crazy Webcrawler',
'adsbot-google' => 'AdsBot Google',
'feedfetcher-google' => 'Feedfetcher Google',
'curious george' => 'Curious George',
'ia_archiver' => 'Alexa Crawler',
'MJ12bot' => 'Majestic-12',
'Uptimebot' => 'Uptimebot',
];
}
<?php namespace Config;
<?php
class Validation
{
//--------------------------------------------------------------------
// Setup
//--------------------------------------------------------------------
declare(strict_types=1);
namespace Config;
use App\Validation\FileRules as AppFileRules;
use App\Validation\OtherRules;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Validation\StrictRules\CreditCardRules;
use CodeIgniter\Validation\StrictRules\FileRules;
use CodeIgniter\Validation\StrictRules\FormatRules;
use CodeIgniter\Validation\StrictRules\Rules;
class Validation extends BaseConfig
{
/**
* Stores the classes that contain the
* rules that are available.
* Stores the classes that contain the rules that are available.
*
* @var array
* @var list<string>
*/
public $ruleSets = [
\CodeIgniter\Validation\Rules::class,
\CodeIgniter\Validation\FormatRules::class,
\CodeIgniter\Validation\FileRules::class,
\CodeIgniter\Validation\CreditCardRules::class,
public array $ruleSets = [
Rules::class,
FormatRules::class,
FileRules::class,
CreditCardRules::class,
AppFileRules::class,
OtherRules::class,
];
/**
* Specifies the views that are used to display the
* errors.
* Specifies the views that are used to display the errors.
*
* @var array
* @var array<string, string>
*/
public $templates = [
'list' => 'CodeIgniter\Validation\Views\list',
public array $templates = [
'list' => 'CodeIgniter\Validation\Views\list',
'single' => 'CodeIgniter\Validation\Views\single',
];
//--------------------------------------------------------------------
// Rules
//--------------------------------------------------------------------
}
<?php namespace Config;
<?php
class View extends \CodeIgniter\Config\View
declare(strict_types=1);
namespace Config;
use CodeIgniter\Config\View as BaseView;
use CodeIgniter\View\ViewDecoratorInterface;
use ViewComponents\Decorator;
/**
* @phpstan-type parser_callable (callable(mixed): mixed)
* @phpstan-type parser_callable_string (callable(mixed): mixed)&string
*/
class View extends BaseView
{
/**
* When false, the view method will clear the data between each
* call. This keeps your data safe and ensures there is no accidental
* leaking between calls, so you would need to explicitly pass the data
* to each view. You might prefer to have the data stick around between
* calls so that it is available to all views. If that is the case,
* set $saveData to true.
* When false, the view method will clear the data between each call. This keeps your data safe and ensures there is
* no accidental leaking between calls, so you would need to explicitly pass the data to each view. You might prefer
* to have the data stick around between calls so that it is available to all views. If that is the case, set
* $saveData to true.
*
* @var boolean
*/
public $saveData = true;
/**
* Parser Filters map a filter name with any PHP callable. When the
* Parser prepares a variable for display, it will chain it
* through the filters in the order defined, inserting any parameters.
* To prevent potential abuse, all filters MUST be defined here
* in order for them to be available for use within the Parser.
* Parser Filters map a filter name with any PHP callable. When the Parser prepares a variable for display, it will
* chain it through the filters in the order defined, inserting any parameters. To prevent potential abuse, all
* filters MUST be defined here in order for them to be available for use within the Parser.
*
* Examples: { title|esc(js) } { created_on|date(Y-m-d)|esc(attr) }
*
* Examples:
* { title|esc(js) }
* { created_on|date(Y-m-d)|esc(attr) }
* @var array<string, string>
* @phpstan-var array<string, parser_callable_string>
*/
public $filters = [];
/**
* Parser Plugins provide a way to extend the functionality provided
* by the core Parser by creating aliases that will be replaced with
* any callable. Can be single or tag pair.
* Parser Plugins provide a way to extend the functionality provided by the core Parser by creating aliases that
* will be replaced with any callable. Can be single or tag pair.
*
* @var array<string, callable|list<string>|string>
* @phpstan-var array<string, list<parser_callable_string>|parser_callable_string|parser_callable>
*/
public $plugins = [];
/**
* View Decorators are class methods that will be run in sequence to have a chance to alter the generated output
* just prior to caching the results.
*
* All classes must implement CodeIgniter\View\ViewDecoratorInterface
*
* @var list<class-string<ViewDecoratorInterface>>
*/
public array $decorators = [Decorator::class];
}
<?php
declare(strict_types=1);
namespace Config;
use ViewComponents\Config\ViewComponents as ViewComponentsConfig;
class ViewComponents extends ViewComponentsConfig
{
/**
* @var string[]
*/
public array $lookupPaths = [
ROOTPATH . 'themes/cp_app/',
ROOTPATH . 'themes/cp_admin/',
ROOTPATH . 'themes/cp_auth/',
ROOTPATH . 'themes/cp_install/',
];
}
<?php
// app/Config/Vite.php
declare(strict_types=1);
namespace Config;
use CodeIgniterVite\Config\Vite as ViteConfig;
class Vite extends ViteConfig
{
public function __construct()
{
parent::__construct();
$adminGateway = config('Admin')
->gateway;
$installGateway = config('Install')
->gateway;
$this->routesAssets = [
[
'routes' => ['*'],
'exclude' => [$adminGateway . '*', $installGateway . '*'],
'assets' => ['styles/site.css', 'js/app.ts', 'js/podcast.ts', 'js/audio-player.ts'],
],
[
'routes' => ['/map'],
'assets' => ['js/map.ts'],
],
[
'routes' => ['/' . $adminGateway . '*'],
'assets' => ['styles/admin.css', 'js/admin.ts', 'js/admin-audio-player.ts'],
],
[
'routes' => [$installGateway . '*'],
'assets' => ['styles/install.css'],
],
];
}
}
<?php
declare(strict_types=1);
/**
* @copyright 2020 Podlibre
* @copyright 2021 Ad Aures
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers;
use CodeIgniter\Controller;
use Modules\Fediverse\Controllers\ActivityPubController as FediverseActivityPubController;
class Migrate extends Controller
class ActivityPubController extends FediverseActivityPubController
{
public function index()
{
$migrate = \Config\Services::migrations();
try {
$migrate->latest();
} catch (\Exception $e) {
// Do something with the error here...
}
}
}
<?php
declare(strict_types=1);
/**
* @copyright 2020 Ad Aures
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers;
use Modules\Analytics\AnalyticsTrait;
use Modules\Fediverse\Controllers\ActorController as FediverseActorController;
class ActorController extends FediverseActorController
{
use AnalyticsTrait;
/**
* @var list<string>
*/
protected $helpers = ['svg', 'components', 'misc', 'seo'];
public function followView(): string
{
// @phpstan-ignore-next-line
$this->registerPodcastWebpageHit($this->actor->podcast->id);
helper(['form', 'components', 'svg']);
// @phpstan-ignore-next-line
set_follow_metatags($this->actor);
$data = [
'actor' => $this->actor,
];
return view('podcast/follow', $data);
}
}
<?php namespace App\Controllers;
/**
* Class Analytics
* Creates Analytics controller
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
use CodeIgniter\Controller;
class Analytics extends Controller
{
/**
* An array of helpers to be loaded automatically upon
* class instantiation. These helpers will be available
* to all other controllers that extend Analytics.
*
* @var array
*/
protected $helpers = ['analytics'];
/**
* Constructor.
*/
public function initController(
\CodeIgniter\HTTP\RequestInterface $request,
\CodeIgniter\HTTP\ResponseInterface $response,
\Psr\Log\LoggerInterface $logger
) {
// Do Not Edit This Line
parent::initController($request, $response, $logger);
//--------------------------------------------------------------------
// Preload any models, libraries, etc, here.
//--------------------------------------------------------------------
// E.g.:
// $this->session = \Config\Services::session();
set_user_session_country();
set_user_session_player();
}
// Add one hit to this episode:
public function hit($p_podcast_id, $p_episode_id, ...$filename)
{
podcast_hit($p_podcast_id, $p_episode_id);
return redirect()->to(media_url(implode('/', $filename)));
}
}
<?php
declare(strict_types=1);
namespace App\Controllers;
use CodeIgniter\Controller;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Override;
use Psr\Log\LoggerInterface;
use ViewThemes\Theme;
/**
* Class BaseController
*
* BaseController provides a convenient place for loading components
* and performing functions that are needed by all your controllers.
* Extend this class in any new controllers:
* class Home extends BaseController
* BaseController provides a convenient place for loading components and performing functions that are needed by all
* your controllers. Extend this class in any new controllers: class Home extends BaseController
*
* For security be sure to declare any new methods as protected or private.
*
* @package CodeIgniter
*/
use CodeIgniter\Controller;
class BaseController extends Controller
abstract class BaseController extends Controller
{
/**
* An array of helpers to be loaded automatically upon
* class instantiation. These helpers will be available
* to all other controllers that extend BaseController.
*
* @var array
* @var list<string>
*/
protected $helpers = ['analytics'];
protected $helpers = [];
/**
* Constructor.
* Be sure to declare properties for any property fetch you initialized.
* The creation of dynamic property is deprecated in PHP 8.2.
*/
// protected $session;
#[Override]
public function initController(
\CodeIgniter\HTTP\RequestInterface $request,
\CodeIgniter\HTTP\ResponseInterface $response,
\Psr\Log\LoggerInterface $logger
) {
RequestInterface $request,
ResponseInterface $response,
LoggerInterface $logger,
): void {
$this->helpers = [...$this->helpers, 'svg', 'components', 'misc', 'seo', 'premium_podcasts'];
// Do Not Edit This Line
parent::initController($request, $response, $logger);
//--------------------------------------------------------------------
// Preload any models, libraries, etc, here.
//--------------------------------------------------------------------
// E.g.:
// $this->session = \Config\Services::session();
set_user_session_country();
set_user_session_browser();
set_user_session_referer();
}
protected static function triggerWebpageHit($postcast_id)
{
webpage_hit($postcast_id);
Theme::setTheme('app');
}
}
<?php
declare(strict_types=1);
/**
* @copyright 2020 Ad Aures
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers;
use CodeIgniter\Controller;
use CodeIgniter\HTTP\ResponseInterface;
class ColorsController extends Controller
{
public function index(): ResponseInterface
{
$cacheName = 'colors.css';
if (
! ($colorsCssBody = cache($cacheName))
) {
$colorThemes = config('Colors')
->themes;
$colorsCssBody = '';
foreach ($colorThemes as $name => $color) {
$colorsCssBody .= ".theme-{$name} {";
foreach ($color as $variable => $value) {
$colorsCssBody .= "--color-{$variable}: {$value[0]} {$value[1]}% {$value[2]}%;";
}
$colorsCssBody .= '}';
}
cache()
->save($cacheName, $colorsCssBody, DECADE);
}
return $this->response->setHeader('Content-Type', 'text/css')
->setHeader('charset', 'UTF-8')
->setBody($colorsCssBody);
}
}
<?php
declare(strict_types=1);
/**
* @copyright 2020 Ad Aures
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers;
use App\Entities\Page;
use App\Models\CreditModel;
use App\Models\PodcastModel;
class CreditsController extends BaseController
{
public function index(): string
{
$locale = service('request')
->getLocale();
$cacheName = implode(
'_',
array_filter(['page', 'credits', $locale, auth()->loggedIn() ? 'authenticated' : null]),
);
if (! ($found = cache($cacheName))) {
$page = new Page([
'title' => lang('Person.credits', [], $locale),
'slug' => 'credits',
'content_markdown' => '',
]);
$allPodcasts = (new PodcastModel())->findAll();
$allCredits = (new CreditModel())->findAll();
// Unlike the carpenter, we make a tree from a table:
$personGroup = null;
$personId = null;
$personRole = null;
$credits = [];
foreach ($allCredits as $credit) {
if ($personGroup !== $credit->person_group) {
$personGroup = $credit->person_group;
$personId = $credit->person_id;
$personRole = $credit->person_role;
$credits[$personGroup] = [
'group_label' => $credit->group_label,
'persons' => [
$personId => [
'full_name' => $credit->person->full_name,
'thumbnail_url' => get_avatar_url($credit->person, 'thumbnail'),
'information_url' => $credit->person->information_url,
'roles' => [
$personRole => [
'role_label' => $credit->role_label,
'is_in' => [
[
'link' => $credit->episode_id
? $credit->episode->link
: $credit->podcast->link,
'title' => $credit->episode_id
? (count($allPodcasts) > 1
? esc($credit->podcast->title) . ' › '
: '') .
esc($credit->episode->title) .
episode_numbering(
$credit->episode
->number,
$credit->episode
->season_number,
'text-xs ml-2',
true,
)
: esc($credit->podcast->title),
],
],
],
],
],
],
];
} elseif ($personId !== $credit->person_id) {
$personId = $credit->person_id;
$personRole = $credit->person_role;
$credits[$personGroup]['persons'][$personId] = [
'full_name' => $credit->person->full_name,
'thumbnail_url' => get_avatar_url($credit->person, 'thumbnail'),
'information_url' => $credit->person->information_url,
'roles' => [
$personRole => [
'role_label' => $credit->role_label,
'is_in' => [
[
'link' => $credit->episode_id
? $credit->episode->link
: $credit->podcast->link,
'title' => $credit->episode_id
? (count($allPodcasts) > 1
? esc($credit->podcast->title) . ' › '
: '') .
esc($credit->episode->title) .
episode_numbering(
$credit->episode->number,
$credit->episode
->season_number,
'text-xs ml-2',
true,
)
: esc($credit->podcast->title),
],
],
],
],
];
} elseif ($personRole !== $credit->person_role) {
$personRole = $credit->person_role;
$credits[$personGroup]['persons'][$personId]['roles'][
$personRole
] = [
'role_label' => $credit->role_label,
'is_in' => [
[
'link' => $credit->episode_id
? $credit->episode->link
: $credit->podcast->link,
'title' => $credit->episode_id
? (count($allPodcasts) > 1
? esc($credit->podcast->title) . ' › '
: '') .
esc($credit->episode->title) .
episode_numbering(
$credit->episode->number,
$credit->episode->season_number,
'text-xs ml-2',
true,
)
: esc($credit->podcast->title),
],
],
];
} else {
$credits[$personGroup]['persons'][$personId]['roles'][
$personRole
]['is_in'][] = [
'link' => $credit->episode_id
? $credit->episode->link
: $credit->podcast->link,
'title' => $credit->episode_id
? (count($allPodcasts) > 1
? esc($credit->podcast->title) . ' › '
: '') .
esc($credit->episode->title) .
episode_numbering(
$credit->episode->number,
$credit->episode->season_number,
'text-xs ml-2',
true,
)
: esc($credit->podcast->title),
];
}
}
set_page_metatags($page);
$data = [
'page' => $page,
'credits' => $credits,
];
$found = view('pages/credits', $data);
cache()
->save($cacheName, $found, DECADE);
}
return $found;
}
}
<?php
/**
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers;
use App\Models\EpisodeModel;
use App\Models\PodcastModel;
class Episode extends BaseController
{
protected \App\Entities\Podcast $podcast;
protected ?\App\Entities\Episode $episode;
public function _remap($method, ...$params)
{
$podcast_model = new PodcastModel();
$this->podcast = $podcast_model->where('name', $params[0])->first();
if (count($params) > 1) {
$episode_model = new EpisodeModel();
if (
!($episode = $episode_model
->where([
'podcast_id' => $this->podcast->id,
'slug' => $params[1],
])
->first())
) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
$this->episode = $episode;
}
return $this->$method();
}
public function create()
{
helper(['form']);
if (
!$this->validate([
'enclosure' => 'uploaded[enclosure]|ext_in[enclosure,mp3,m4a]',
'image' =>
'uploaded[image]|is_image[image]|ext_in[image,jpg,png]|permit_empty',
'title' => 'required',
'slug' => 'required',
'description' => 'required',
'type' => 'required',
])
) {
$data = [
'podcast' => $this->podcast,
];
echo view('episode/create', $data);
} else {
$new_episode = new \App\Entities\Episode([
'podcast_id' => $this->podcast->id,
'title' => $this->request->getVar('title'),
'slug' => $this->request->getVar('slug'),
'enclosure' => $this->request->getFile('enclosure'),
'pub_date' => $this->request->getVar('pub_date'),
'description' => $this->request->getVar('description'),
'image' => $this->request->getFile('image'),
'explicit' => $this->request->getVar('explicit') or false,
'number' => $this->request->getVar('episode_number'),
'season_number' => $this->request->getVar('season_number')
? $this->request->getVar('season_number')
: null,
'type' => $this->request->getVar('type'),
'author_name' => $this->request->getVar('author_name'),
'author_email' => $this->request->getVar('author_email'),
'block' => $this->request->getVar('block') or false,
]);
$episode_model = new EpisodeModel();
$episode_model->save($new_episode);
return redirect()->to(
base_url(
route_to(
'episode_view',
$this->podcast->name,
$new_episode->slug
)
)
);
}
}
public function edit()
{
helper(['form']);
if (
!$this->validate([
'enclosure' =>
'uploaded[enclosure]|ext_in[enclosure,mp3,m4a]|permit_empty',
'image' =>
'uploaded[image]|is_image[image]|ext_in[image,jpg,png]|permit_empty',
'title' => 'required',
'slug' => 'required',
'description' => 'required',
'type' => 'required',
])
) {
$data = [
'podcast' => $this->podcast,
'episode' => $this->episode,
];
echo view('episode/edit', $data);
} else {
$this->episode->title = $this->request->getVar('title');
$this->episode->slug = $this->request->getVar('slug');
$this->episode->pub_date = $this->request->getVar('pub_date');
$this->episode->description = $this->request->getVar('description');
$this->episode->explicit =
($this->request->getVar('explicit') or false);
$this->episode->number = $this->request->getVar('episode_number');
$this->episode->season_number = $this->request->getVar(
'season_number'
)
? $this->request->getVar('season_number')
: null;
$this->episode->type = $this->request->getVar('type');
$this->episode->author_name = $this->request->getVar('author_name');
$this->episode->author_email = $this->request->getVar(
'author_email'
);
$this->episode->block = ($this->request->getVar('block') or false);
$enclosure = $this->request->getFile('enclosure');
if ($enclosure->isValid()) {
$this->episode->enclosure = $this->request->getFile(
'enclosure'
);
}
$image = $this->request->getFile('image');
if ($image) {
$this->episode->image = $this->request->getFile('image');
}
$episode_model = new EpisodeModel();
$episode_model->save($this->episode);
return redirect()->to(
base_url(
route_to(
'episode_view',
$this->podcast->name,
$this->episode->slug
)
)
);
}
}
public function view()
{
self::triggerWebpageHit($this->podcast->id);
$data = [
'podcast' => $this->podcast,
'episode' => $this->episode,
];
return view('episode/view', $data);
}
public function delete()
{
$episode_model = new EpisodeModel();
$episode_model->delete($this->episode->id);
return redirect()->to(
base_url(route_to('podcast_view', $this->podcast->name))
);
}
}
<?php
declare(strict_types=1);
/**
* @copyright 2020 Ad Aures
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers;
use App\Entities\Episode;
use App\Entities\Podcast;
use App\Models\EpisodeModel;
use App\Models\PodcastModel;
use CodeIgniter\Controller;
use CodeIgniter\Exceptions\PageNotFoundException;
use CodeIgniter\HTTP\RedirectResponse;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\HTTP\URI;
use Modules\Analytics\Config\Analytics;
use Modules\PremiumPodcasts\Entities\Subscription;
use Modules\PremiumPodcasts\Models\SubscriptionModel;
use Override;
use Psr\Log\LoggerInterface;
class EpisodeAudioController extends Controller
{
/**
* An array of helpers to be loaded automatically upon class instantiation. These helpers will be available to all
* other controllers that extend Analytics.
*
* @var list<string>
*/
protected $helpers = ['analytics'];
protected Podcast $podcast;
protected Episode $episode;
protected Analytics $analyticsConfig;
#[Override]
public function initController(
RequestInterface $request,
ResponseInterface $response,
LoggerInterface $logger,
): void {
// Do Not Edit This Line
parent::initController($request, $response, $logger);
set_user_session_deny_list_ip();
set_user_session_location();
set_user_session_player();
$this->analyticsConfig = config('Analytics');
}
public function _remap(string $method, string ...$params): mixed
{
if (count($params) < 2) {
throw PageNotFoundException::forPageNotFound();
}
if (
! ($podcast = (new PodcastModel())->getPodcastByHandle($params[0])) instanceof Podcast
) {
throw PageNotFoundException::forPageNotFound();
}
$this->podcast = $podcast;
if (
! ($episode = (new EpisodeModel())->getEpisodeBySlug($params[0], $params[1])) instanceof Episode
) {
throw PageNotFoundException::forPageNotFound();
}
$this->episode = $episode;
unset($params[1]);
unset($params[0]);
return $this->{$method}(...$params);
}
public function index(): RedirectResponse | ResponseInterface
{
// check if episode is premium?
$subscription = null;
// check if podcast is already unlocked before any token validation
if ($this->episode->is_premium && ! ($subscription = service('premium_podcasts')->subscription(
$this->episode->podcast->handle,
)) instanceof Subscription) {
// look for token as GET parameter
if (($token = $this->request->getGet('token')) === null) {
return $this->response->setStatusCode(401)
->setJSON([
'errors' => [
'status' => 401,
'title' => 'Unauthorized',
'detail' => 'Episode is premium, you must provide a token to unlock it.',
],
]);
}
// check if there's a valid subscription for the provided token
if (! ($subscription = (new SubscriptionModel())->validateSubscription(
$this->episode->podcast->handle,
$token,
)) instanceof Subscription) {
return $this->response->setStatusCode(401, 'Invalid token!')
->setJSON([
'errors' => [
'status' => 401,
'title' => 'Unauthorized',
'detail' => 'Invalid token!',
],
]);
}
}
$session = service('session');
$serviceName = '';
if ($this->request->getGet('_from')) {
$serviceName = $this->request->getGet('_from');
} elseif ($session->get('embed_domain') !== null) {
$serviceName = $session->get('embed_domain');
} elseif ($session->get('referer') !== null && $session->get('referer') !== '- Direct -') {
$serviceName = parse_url((string) $session->get('referer'), PHP_URL_HOST);
}
$audioFileSize = $this->episode->audio->file_size;
$audioFileHeaderSize = $this->episode->audio->header_size;
$audioDuration = $this->episode->audio->duration;
// bytes_threshold: number of bytes that must be downloaded for an episode to be counted in download analytics
// - if audio is less than or equal to 60s, then take the audio file_size
// - if audio is more than 60s, then take the audio file_header_size + 60s
$bytesThreshold = $audioDuration <= 60
? $audioFileSize
: $audioFileHeaderSize +
(int) floor((($audioFileSize - $audioFileHeaderSize) / $audioDuration) * 60);
podcast_hit(
$this->episode->podcast_id,
$this->episode->id,
$bytesThreshold,
$audioFileSize,
$audioDuration,
$this->episode->published_at->getTimestamp(),
$serviceName,
$subscription instanceof Subscription ? $subscription->id : null,
);
$audioFileURI = new URI(service('file_manager')->getUrl($this->episode->audio->file_key));
$queryParams = [];
foreach ($this->request->getGet() as $key => $value) {
// do not include token in query params
if ($key !== 'token') {
$queryParams[$key] = $value;
}
}
$audioFileURI->setQueryArray($queryParams);
return redirect()->to((string) $audioFileURI);
}
}
<?php
declare(strict_types=1);
/**
* @copyright 2020 Ad Aures
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers;
use App\Entities\Episode;
use App\Entities\EpisodeComment;
use App\Entities\Podcast;
use App\Libraries\CommentObject;
use App\Models\EpisodeCommentModel;
use App\Models\EpisodeModel;
use App\Models\PodcastModel;
use CodeIgniter\Exceptions\PageNotFoundException;
use CodeIgniter\HTTP\RedirectResponse;
use CodeIgniter\HTTP\ResponseInterface;
use Modules\Analytics\AnalyticsTrait;
use Modules\Fediverse\Entities\Actor;
use Modules\Fediverse\Objects\OrderedCollectionObject;
use Modules\Fediverse\Objects\OrderedCollectionPage;
class EpisodeCommentController extends BaseController
{
use AnalyticsTrait;
protected Podcast $podcast;
protected Actor $actor;
protected Episode $episode;
protected EpisodeComment $comment;
public function _remap(string $method, string ...$params): mixed
{
if (count($params) < 3) {
throw PageNotFoundException::forPageNotFound();
}
if (
! ($podcast = (new PodcastModel())->getPodcastByHandle($params[0])) instanceof Podcast
) {
throw PageNotFoundException::forPageNotFound();
}
$this->podcast = $podcast;
$this->actor = $podcast->actor;
if (
! ($episode = (new EpisodeModel())->getEpisodeBySlug($params[0], $params[1])) instanceof Episode
) {
throw PageNotFoundException::forPageNotFound();
}
$this->episode = $episode;
if (
! ($comment = (new EpisodeCommentModel())->getCommentById($params[2])) instanceof EpisodeComment
) {
throw PageNotFoundException::forPageNotFound();
}
$this->comment = $comment;
unset($params[2]);
unset($params[1]);
unset($params[0]);
return $this->{$method}(...$params);
}
public function view(): string
{
$this->registerPodcastWebpageHit($this->podcast->id);
$cacheName = implode(
'_',
array_filter([
'page',
"episode#{$this->episode->id}",
"comment#{$this->comment->id}",
service('request')
->getLocale(),
auth()
->loggedIn() ? 'authenticated' : null,
]),
);
if (! ($cachedView = cache($cacheName))) {
set_episode_comment_metatags($this->comment);
$data = [
'podcast' => $this->podcast,
'actor' => $this->actor,
'episode' => $this->episode,
'comment' => $this->comment,
];
// if user is logged in then send to the authenticated activity view
if (auth()->loggedIn()) {
helper('form');
return view('episode/comment', $data);
}
return view('episode/comment', $data, [
'cache' => DECADE,
'cache_name' => $cacheName,
]);
}
return $cachedView;
}
public function commentObject(): ResponseInterface
{
$commentObject = new CommentObject($this->comment);
return $this->response
->setContentType('application/json')
->setBody($commentObject->toJSON());
}
public function replies(): ResponseInterface
{
/**
* get comment replies
*/
$commentReplies = model(EpisodeCommentModel::class, false)
->where('in_reply_to_id', service('uuid')->fromString($this->comment->id)->getBytes())
->orderBy('created_at', 'ASC');
$pageNumber = (int) $this->request->getGet('page');
if ($pageNumber < 1) {
$commentReplies->paginate(12);
$pager = $commentReplies->pager;
$collection = new OrderedCollectionObject(null, $pager);
} else {
$paginatedReplies = $commentReplies->paginate(12, 'default', $pageNumber);
$pager = $commentReplies->pager;
$orderedItems = [];
if ($paginatedReplies !== null) {
foreach ($paginatedReplies as $reply) {
$replyObject = new CommentObject($reply);
$orderedItems[] = $replyObject;
}
}
$collection = new OrderedCollectionPage($pager, $orderedItems);
}
return $this->response
->setContentType('application/activity+json')
->setBody($collection->toJSON());
}
public function likeAction(): RedirectResponse
{
if (! ($interactAsActor = interact_as_actor()) instanceof Actor) {
return redirect()->back();
}
model('LikeModel')
->toggleLike($interactAsActor, $this->comment);
return redirect()->back();
}
public function replyAction(): RedirectResponse
{
if (! ($interactAsActor = interact_as_actor()) instanceof Actor) {
return redirect()->back();
}
model('LikeModel')
->toggleLike($interactAsActor, $this->comment);
return redirect()->back();
}
}
<?php
declare(strict_types=1);
/**
* @copyright 2020 Ad Aures
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers;
use App\Entities\Episode;
use App\Entities\Podcast;
use App\Libraries\NoteObject;
use App\Libraries\PodcastEpisode;
use App\Models\EpisodeModel;
use App\Models\PodcastModel;
use CodeIgniter\Database\BaseBuilder;
use CodeIgniter\Exceptions\PageNotFoundException;
use CodeIgniter\HTTP\ResponseInterface;
use Config\Embed;
use Modules\Analytics\AnalyticsTrait;
use Modules\Fediverse\Objects\OrderedCollectionObject;
use Modules\Fediverse\Objects\OrderedCollectionPage;
use Modules\Media\FileManagers\FileManagerInterface;
use SimpleXMLElement;
class EpisodeController extends BaseController
{
use AnalyticsTrait;
protected Podcast $podcast;
protected Episode $episode;
public function _remap(string $method, string ...$params): mixed
{
if (count($params) < 2) {
throw PageNotFoundException::forPageNotFound();
}
if (
! ($podcast = (new PodcastModel())->getPodcastByHandle($params[0])) instanceof Podcast
) {
throw PageNotFoundException::forPageNotFound();
}
$this->podcast = $podcast;
if (
! ($episode = (new EpisodeModel())->getEpisodeBySlug($params[0], $params[1])) instanceof Episode
) {
throw PageNotFoundException::forPageNotFound();
}
$this->episode = $episode;
unset($params[1]);
unset($params[0]);
return $this->{$method}(...$params);
}
public function index(): string
{
$this->registerPodcastWebpageHit($this->episode->podcast_id);
$cacheName = implode(
'_',
array_filter([
'page',
"podcast#{$this->podcast->id}",
"episode#{$this->episode->id}",
service('request')
->getLocale(),
is_unlocked($this->podcast->handle) ? 'unlocked' : null,
auth()
->loggedIn() ? 'authenticated' : null,
]),
);
if (! ($cachedView = cache($cacheName))) {
set_episode_metatags($this->episode);
$data = [
'podcast' => $this->podcast,
'episode' => $this->episode,
];
$secondsToNextUnpublishedEpisode = (new EpisodeModel())->getSecondsToNextUnpublishedEpisode(
$this->podcast->id,
);
if (auth()->loggedIn()) {
helper('form');
return view('episode/comments', $data);
}
// The page cache is set to a decade so it is deleted manually upon podcast update
return view('episode/comments', $data, [
'cache' => $secondsToNextUnpublishedEpisode ?: DECADE,
'cache_name' => $cacheName,
]);
}
return $cachedView;
}
public function activity(): string
{
$this->registerPodcastWebpageHit($this->episode->podcast_id);
$cacheName = implode(
'_',
array_filter([
'page',
"podcast#{$this->podcast->id}",
"episode#{$this->episode->id}",
'activity',
service('request')
->getLocale(),
is_unlocked($this->podcast->handle) ? 'unlocked' : null,
auth()
->loggedIn() ? 'authenticated' : null,
]),
);
if (! ($cachedView = cache($cacheName))) {
set_episode_metatags($this->episode);
$data = [
'podcast' => $this->podcast,
'episode' => $this->episode,
];
$secondsToNextUnpublishedEpisode = (new EpisodeModel())->getSecondsToNextUnpublishedEpisode(
$this->podcast->id,
);
if (auth()->loggedIn()) {
helper('form');
return view('episode/activity', $data);
}
// The page cache is set to a decade so it is deleted manually upon podcast update
return view('episode/activity', $data, [
'cache' => $secondsToNextUnpublishedEpisode ?: DECADE,
'cache_name' => $cacheName,
]);
}
return $cachedView;
}
public function chapters(): string
{
$this->registerPodcastWebpageHit($this->episode->podcast_id);
$cacheName = implode(
'_',
array_filter([
'page',
"podcast#{$this->podcast->id}",
"episode#{$this->episode->id}",
'chapters',
service('request')
->getLocale(),
is_unlocked($this->podcast->handle) ? 'unlocked' : null,
auth()
->loggedIn() ? 'authenticated' : null,
]),
);
if (! ($cachedView = cache($cacheName))) {
set_episode_metatags($this->episode);
$data = [
'podcast' => $this->podcast,
'episode' => $this->episode,
];
// get chapters from json file
if (isset($this->episode->chapters->file_key)) {
/** @var FileManagerInterface $fileManager */
$fileManager = service('file_manager');
$episodeChaptersJsonString = (string) $fileManager->getFileContents($this->episode->chapters->file_key);
$chapters = json_decode($episodeChaptersJsonString, true);
$data['chapters'] = $chapters;
}
$secondsToNextUnpublishedEpisode = (new EpisodeModel())->getSecondsToNextUnpublishedEpisode(
$this->podcast->id,
);
if (auth()->loggedIn()) {
helper('form');
return view('episode/chapters', $data);
}
// The page cache is set to a decade so it is deleted manually upon podcast update
return view('episode/chapters', $data, [
'cache' => $secondsToNextUnpublishedEpisode ?: DECADE,
'cache_name' => $cacheName,
]);
}
return $cachedView;
}
public function transcript(): string
{
$this->registerPodcastWebpageHit($this->episode->podcast_id);
$cacheName = implode(
'_',
array_filter([
'page',
"podcast#{$this->podcast->id}",
"episode#{$this->episode->id}",
'transcript',
service('request')
->getLocale(),
is_unlocked($this->podcast->handle) ? 'unlocked' : null,
auth()
->loggedIn() ? 'authenticated' : null,
]),
);
if (! ($cachedView = cache($cacheName))) {
set_episode_metatags($this->episode);
$data = [
'podcast' => $this->podcast,
'episode' => $this->episode,
];
// get transcript from json file
if ($this->episode->transcript !== null) {
$data['transcript'] = $this->episode->transcript;
if ($this->episode->transcript->json_key !== null) {
/** @var FileManagerInterface $fileManager */
$fileManager = service('file_manager');
$transcriptJsonString = (string) $fileManager->getFileContents(
$this->episode->transcript->json_key,
);
$data['captions'] = json_decode($transcriptJsonString, true);
}
}
$secondsToNextUnpublishedEpisode = (new EpisodeModel())->getSecondsToNextUnpublishedEpisode(
$this->podcast->id,
);
if (auth()->loggedIn()) {
helper('form');
return view('episode/transcript', $data);
}
// The page cache is set to a decade so it is deleted manually upon podcast update
return view('episode/transcript', $data, [
'cache' => $secondsToNextUnpublishedEpisode ?: DECADE,
'cache_name' => $cacheName,
]);
}
return $cachedView;
}
public function embed(string $theme = 'light-transparent'): string
{
header('Content-Security-Policy: frame-ancestors http://*:* https://*:*');
$this->registerPodcastWebpageHit($this->episode->podcast_id);
$session = service('session');
if (service('superglobals')->server('HTTP_REFERER') !== null) {
$session->set('embed_domain', parse_url(service('superglobals')->server('HTTP_REFERER'), PHP_URL_HOST));
}
$cacheName = implode(
'_',
array_filter([
'page',
"podcast#{$this->podcast->id}",
"episode#{$this->episode->id}",
'embed',
$theme,
service('request')
->getLocale(),
is_unlocked($this->podcast->handle) ? 'unlocked' : null,
]),
);
if (! ($cachedView = cache($cacheName))) {
$themeData = EpisodeModel::$themes[$theme];
$data = [
'podcast' => $this->podcast,
'episode' => $this->episode,
'theme' => $theme,
'themeData' => $themeData,
];
$secondsToNextUnpublishedEpisode = (new EpisodeModel())->getSecondsToNextUnpublishedEpisode(
$this->podcast->id,
);
// The page cache is set to a decade so it is deleted manually upon podcast update
return view('embed', $data, [
'cache' => $secondsToNextUnpublishedEpisode ?: DECADE,
'cache_name' => $cacheName,
]);
}
return $cachedView;
}
public function oembedJSON(): ResponseInterface
{
return $this->response->setJSON([
'type' => 'rich',
'version' => '1.0',
'title' => $this->episode->title,
'provider_name' => $this->podcast->title,
'provider_url' => $this->podcast->link,
'author_name' => $this->podcast->title,
'author_url' => $this->podcast->link,
'html' => '<iframe src="' .
$this->episode->embed_url .
'" width="100%" height="' . config('Embed')->height . '" frameborder="0" scrolling="no"></iframe>',
'width' => config('Embed')
->width,
'height' => config('Embed')
->height,
'thumbnail_url' => $this->episode->cover->og_url,
'thumbnail_width' => config('Images')
->podcastCoverSizes['og']['width'],
'thumbnail_height' => config('Images')
->podcastCoverSizes['og']['height'],
]);
}
public function oembedXML(): ResponseInterface
{
$oembed = new SimpleXMLElement("<?xml version='1.0' encoding='utf-8' standalone='yes'?><oembed></oembed>");
$oembed->addChild('type', 'rich');
$oembed->addChild('version', '1.0');
$oembed->addChild('title', $this->episode->title);
$oembed->addChild('provider_name', $this->podcast->title);
$oembed->addChild('provider_url', $this->podcast->link);
$oembed->addChild('author_name', $this->podcast->title);
$oembed->addChild('author_url', $this->podcast->link);
$oembed->addChild('thumbnail', $this->episode->cover->og_url);
$oembed->addChild('thumbnail_width', (string) config('Images')->podcastCoverSizes['og']['width']);
$oembed->addChild('thumbnail_height', (string) config('Images')->podcastCoverSizes['og']['height']);
$oembed->addChild(
'html',
htmlspecialchars(
'<iframe src="' .
$this->episode->embed_url .
'" width="100%" height="' . config(
Embed::class,
)->height . '" frameborder="0" scrolling="no"></iframe>',
),
);
$oembed->addChild('width', (string) config('Embed')->width);
$oembed->addChild('height', (string) config('Embed')->height);
// @phpstan-ignore-next-line
return $this->response->setXML($oembed);
}
public function episodeObject(): ResponseInterface
{
$podcastObject = new PodcastEpisode($this->episode);
return $this->response
->setContentType('application/json')
->setBody($podcastObject->toJSON());
}
public function comments(): ResponseInterface
{
/**
* get comments: aggregated replies from posts referring to the episode
*/
$episodeComments = model('PostModel')
->whereIn('in_reply_to_id', fn (BaseBuilder $builder): BaseBuilder => $builder->select('id')
->from('fediverse_posts')
->where('episode_id', $this->episode->id))
->where('`published_at` <= UTC_TIMESTAMP()', null, false)
->orderBy('published_at', 'ASC');
$pageNumber = (int) $this->request->getGet('page');
if ($pageNumber < 1) {
$episodeComments->paginate(12);
$pager = $episodeComments->pager;
$collection = new OrderedCollectionObject(null, $pager);
} else {
$paginatedComments = $episodeComments->paginate(12, 'default', $pageNumber);
$pager = $episodeComments->pager;
$orderedItems = [];
if ($paginatedComments !== null) {
foreach ($paginatedComments as $comment) {
$orderedItems[] = (new NoteObject($comment))->toArray();
}
}
// @phpstan-ignore-next-line
$collection = new OrderedCollectionPage($pager, $orderedItems);
}
return $this->response
->setContentType('application/activity+json')
->setHeader('Access-Control-Allow-Origin', '*')
->setBody($collection->toJSON());
}
}
<?php
declare(strict_types=1);
/**
* @copyright 2023 Ad Aures
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers;
use App\Entities\Episode;
use App\Models\EpisodeModel;
use CodeIgniter\Exceptions\PageNotFoundException;
use Modules\Media\FileManagers\FileManagerInterface;
class EpisodePreviewController extends BaseController
{
protected Episode $episode;
public function _remap(string $method, string ...$params): mixed
{
if (count($params) < 1) {
throw PageNotFoundException::forPageNotFound();
}
// find episode by previewUUID
$episode = (new EpisodeModel())->getEpisodeByPreviewId($params[0]);
if (! $episode instanceof Episode) {
throw PageNotFoundException::forPageNotFound();
}
$this->episode = $episode;
if ($episode->publication_status === 'published') {
// redirect to episode page
return redirect()->route('episode', [$episode->podcast->handle, $episode->slug]);
}
unset($params[0]);
return $this->{$method}(...$params);
}
public function index(): string
{
helper('form');
return view('episode/preview-comments', [
'podcast' => $this->episode->podcast,
'episode' => $this->episode,
]);
}
public function activity(): string
{
helper('form');
return view('episode/preview-activity', [
'podcast' => $this->episode->podcast,
'episode' => $this->episode,
]);
}
public function chapters(): string
{
$data = [
'podcast' => $this->episode->podcast,
'episode' => $this->episode,
];
if (isset($this->episode->chapters->file_key)) {
/** @var FileManagerInterface $fileManager */
$fileManager = service('file_manager');
$episodeChaptersJsonString = (string) $fileManager->getFileContents($this->episode->chapters->file_key);
$chapters = json_decode($episodeChaptersJsonString, true);
$data['chapters'] = $chapters;
}
helper('form');
return view('episode/preview-chapters', $data);
}
public function transcript(): string
{
// get transcript from json file
$data = [
'podcast' => $this->episode->podcast,
'episode' => $this->episode,
];
if ($this->episode->transcript !== null) {
$data['transcript'] = $this->episode->transcript;
if ($this->episode->transcript->json_key !== null) {
/** @var FileManagerInterface $fileManager */
$fileManager = service('file_manager');
$transcriptJsonString = (string) $fileManager->getFileContents(
$this->episode->transcript->json_key,
);
$data['captions'] = json_decode($transcriptJsonString, true);
}
}
helper('form');
return view('episode/preview-transcript', $data);
}
}
<?php
namespace App\Controllers;
use App\Models\PodcastModel;
use CodeIgniter\Controller;
class Feed extends Controller
{
public function index($podcast_name)
{
helper('rss');
$podcast_model = new PodcastModel();
$podcast = $podcast_model->where('name', $podcast_name)->first();
// The page cache is set to a decade so it is deleted manually upon podcast update
$this->cachePage(DECADE);
return $this->response->setXML(get_rss_feed($podcast));
}
}