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 1292 additions and 1227 deletions
<?php <?php
declare(strict_types=1);
namespace Config; namespace Config;
use App\Entities\Actor;
use App\Entities\Post;
use App\Models\EpisodeModel;
use CodeIgniter\Debug\Toolbar\Collectors\Database;
use CodeIgniter\Events\Events; use CodeIgniter\Events\Events;
use CodeIgniter\Exceptions\FrameworkException; use CodeIgniter\Exceptions\FrameworkException;
use CodeIgniter\HotReloader\HotReloader;
/* /*
* -------------------------------------------------------------------- * --------------------------------------------------------------------
...@@ -22,7 +29,7 @@ use CodeIgniter\Exceptions\FrameworkException; ...@@ -22,7 +29,7 @@ use CodeIgniter\Exceptions\FrameworkException;
* Events::on('create', [$myInstance, 'myMethod']); * Events::on('create', [$myInstance, 'myMethod']);
*/ */
Events::on('pre_system', function () { Events::on('pre_system', static function (): void {
if (ENVIRONMENT !== 'testing') { if (ENVIRONMENT !== 'testing') {
if (ini_get('zlib.output_compression')) { if (ini_get('zlib.output_compression')) {
throw FrameworkException::forEnabledZlibOutputCompression(); throw FrameworkException::forEnabledZlibOutputCompression();
...@@ -32,9 +39,7 @@ Events::on('pre_system', function () { ...@@ -32,9 +39,7 @@ Events::on('pre_system', function () {
ob_end_flush(); ob_end_flush();
} }
ob_start(function ($buffer) { ob_start(static fn ($buffer) => $buffer);
return $buffer;
});
} }
/* /*
...@@ -43,98 +48,284 @@ Events::on('pre_system', function () { ...@@ -43,98 +48,284 @@ Events::on('pre_system', function () {
* -------------------------------------------------------------------- * --------------------------------------------------------------------
* If you delete, they will no longer be collected. * If you delete, they will no longer be collected.
*/ */
if (CI_DEBUG) { if (CI_DEBUG && ! is_cli()) {
Events::on( Events::on('DBQuery', Database::class . '::collect');
'DBQuery', service('toolbar')
'CodeIgniter\Debug\Toolbar\Collectors\Database::collect', ->respond();
);
Services::toolbar()->respond(); // Hot Reload route - for framework use on the hot reloader.
if (ENVIRONMENT === 'development') {
service('routes')->get('__hot-reload', static function (): void {
(new HotReloader())->run();
});
}
} }
}); });
Events::on('login', function ($user) { /*
helper('auth'); * --------------------------------------------------------------------
* Fediverse events
* --------------------------------------------------------------------
*/
/**
* @param Actor $actor
* @param Actor $targetActor
*/
Events::on('on_follow', static function ($actor, $targetActor): void {
if ($actor->is_podcast) {
cache()
->deleteMatching("podcast#{$actor->podcast->id}*");
cache()
->deleteMatching("page_podcast#{$actor->podcast->id}*");
}
// set interact_as_actor_id value if ($targetActor->is_podcast) {
$userPodcasts = $user->podcasts; cache()
if ($userPodcasts = $user->podcasts) { ->deleteMatching("podcast#{$targetActor->podcast->id}*");
set_interact_as_actor($userPodcasts[0]->id); cache()
->deleteMatching("page_podcast#{$targetActor->podcast->id}*");
} }
}); });
Events::on('logout', function ($user) { /**
helper('auth'); * @param Actor $actor
* @param Actor $targetActor
*/
Events::on('on_undo_follow', static function ($actor, $targetActor): void {
if ($actor->is_podcast) {
cache()
->deleteMatching("podcast#{$actor->podcast->id}*");
cache()
->deleteMatching("page_podcast#{$actor->podcast->id}*");
}
// remove user's interact_as_actor session if ($targetActor->is_podcast) {
remove_interact_as_actor($user->id); cache()
->deleteMatching("podcast#{$targetActor->podcast->id}*");
cache()
->deleteMatching("page_podcast#{$targetActor->podcast->id}*");
}
}); });
/* /**
* -------------------------------------------------------------------- * @param Post $post
* ActivityPub events
* --------------------------------------------------------------------
* Update episode metadata counts
*/ */
Events::on('on_note_add', function ($note) { Events::on('on_post_add', static function ($post): void {
if ($note->episode_id) { model(EpisodeModel::class, false)->builder()
model('EpisodeModel') ->where('id', $post->episode_id)
->where('id', $note->episode_id) ->increment('posts_count');
->increment('notes_total'); if ($post->actor->is_podcast) {
// Removing all of the podcast pages is a bit overkill, but works to avoid caching bugs
// same for other events below
cache()
->deleteMatching("podcast#{$post->actor->podcast->id}*");
cache()
->deleteMatching("page_podcast#{$post->actor->podcast->id}*");
} }
}); });
Events::on('on_note_remove', function ($note) { /**
if ($note->episode_id) { * @param Post $post
model('EpisodeModel') */
->where('id', $note->episode_id) Events::on('on_post_remove', static function ($post): void {
->decrement('notes_total', 1 + $note->reblogs_count); if ($episodeId = $post->episode_id) {
model(EpisodeModel::class, false)->builder()
->where('id', $episodeId)
->decrement('posts_count');
}
if ($post->actor->is_podcast) {
cache()
->deleteMatching("podcast#{$post->actor->podcast->id}*");
cache()
->deleteMatching("page_podcast#{$post->actor->podcast->id}*");
}
model('EpisodeModel') cache()
->where('id', $note->episode_id) ->deleteMatching("page_post#{$post->id}*");
->decrement('reblogs_total', $note->reblogs_count); });
/**
* @param Actor $actor
* @param Post $post
*/
Events::on('on_post_reblog', static function ($actor, $post): void {
if ($post->actor->is_podcast) {
cache()
->deleteMatching("podcast#{$post->actor->podcast->id}*");
cache()
->deleteMatching("page_podcast#{$post->actor->podcast->id}*");
}
model('EpisodeModel') if ($actor->is_podcast) {
->where('id', $note->episode_id) cache()->deleteMatching("podcast#{$actor->podcast->id}*");
->decrement('favourites_total', $note->favourites_count); cache()
->deleteMatching("page_podcast#{$actor->podcast->id}*");
}
cache()
->deleteMatching("page_post#{$post->id}*");
if ($post->in_reply_to_id !== null) {
cache()->deleteMatching("page_post#{$post->in_reply_to_id}");
} }
}); });
Events::on('on_note_reblog', function ($actor, $note) { /**
if ($episodeId = $note->episode_id) { * @param Post $reblogPost
model('EpisodeModel') */
->where('id', $episodeId) Events::on('on_post_undo_reblog', static function ($reblogPost): void {
->increment('reblogs_total'); $post = $reblogPost->reblog_of_post;
if ($post->actor->is_podcast) {
cache()
->deleteMatching("podcast#{$post->actor->podcast->id}*");
cache()
->deleteMatching("page_podcast#{$post->actor->podcast->id}*");
}
model('EpisodeModel') cache()
->where('id', $episodeId) ->deleteMatching("page_post#{$post->id}*");
->increment('notes_total'); cache()
->deleteMatching("page_post#{$reblogPost->id}*");
if ($post->in_reply_to_id !== null) {
cache()->deleteMatching("page_post#{$post->in_reply_to_id}");
}
if ($reblogPost->actor->is_podcast) {
cache()
->deleteMatching("podcast#{$reblogPost->actor->podcast->id}*");
cache()
->deleteMatching("page_podcast#{$reblogPost->actor->podcast->id}*");
} }
}); });
Events::on('on_note_undo_reblog', function ($reblogNote) { /**
if ($episodeId = $reblogNote->reblog_of_note->episode_id) { * @param Post $reply
model('EpisodeModel') */
->where('id', $episodeId) Events::on('on_post_reply', static function ($reply): void {
->decrement('reblogs_total'); $post = $reply->reply_to_post;
if ($post->in_reply_to_id === null) {
model(EpisodeModel::class, false)->builder()
->where('id', $post->episode_id)
->increment('comments_count');
}
model('EpisodeModel') if ($post->actor->is_podcast) {
->where('id', $episodeId) cache()
->decrement('notes_total'); ->deleteMatching("podcast-{$post->actor->podcast->handle}*");
cache()
->deleteMatching("podcast#{$post->actor->podcast->id}*");
cache()
->deleteMatching("page_podcast#{$post->actor->podcast->id}*");
} }
cache()
->deleteMatching("page_post#{$post->id}*");
}); });
Events::on('on_note_favourite', function ($actor, $note) { /**
if ($note->episode_id) { * @param Post $reply
model('EpisodeModel') */
->where('id', $note->episode_id) Events::on('on_reply_remove', static function ($reply): void {
->increment('favourites_total'); $post = $reply->reply_to_post;
if ($post->in_reply_to_id === null) {
model(EpisodeModel::class, false)->builder()
->where('id', $post->episode_id)
->decrement('comments_count');
}
if ($post->actor->is_podcast) {
cache()
->deleteMatching("podcast-{$post->actor->podcast->handle}*");
cache()
->deleteMatching("page_podcast#{$post->actor->podcast->id}*");
cache()
->deleteMatching("podcast#{$post->actor->podcast->id}*");
} }
cache()
->deleteMatching("page_post#{$post->id}*");
cache()
->deleteMatching("page_post#{$reply->id}*");
}); });
Events::on('on_note_undo_favourite', function ($actor, $note) { /**
if ($note->episode_id) { * @param Actor $actor
model('EpisodeModel') * @param Post $post
->where('id', $note->episode_id) */
->decrement('favourites_total'); Events::on('on_post_favourite', static function ($actor, $post): void {
if ($post->actor->is_podcast) {
cache()
->deleteMatching("podcast#{$post->actor->podcast->id}*");
cache()
->deleteMatching("page_podcast#{$post->actor->podcast->id}*");
}
cache()
->deleteMatching("page_post#{$post->id}*");
if ($post->in_reply_to_id !== null) {
cache()->deleteMatching("page_post#{$post->in_reply_to_id}*");
}
if ($actor->is_podcast) {
cache()->deleteMatching("podcast#{$actor->podcast->id}*");
cache()
->deleteMatching("page_podcast#{$actor->podcast->id}*");
} }
}); });
/**
* @param Actor $actor
* @param Post $post
*/
Events::on('on_post_undo_favourite', static function ($actor, $post): void {
if ($post->actor->is_podcast) {
cache()
->deleteMatching("podcast#{$post->actor->podcast->id}*");
cache()
->deleteMatching("page_podcast#{$post->actor->podcast->id}*");
}
cache()
->deleteMatching("page_post#{$post->id}*");
if ($post->in_reply_to_id !== null) {
cache()->deleteMatching("page_post#{$post->in_reply_to_id}*");
}
if ($actor->is_podcast) {
cache()->deleteMatching("podcast#{$actor->podcast->id}*");
cache()
->deleteMatching("page_podcast#{$actor->podcast->id}*");
}
});
Events::on('on_block_actor', static function (int $actorId): void {
cache()->deleteMatching('page_podcast*');
cache()
->deleteMatching('podcast*');
cache()
->deleteMatching('page_post*');
});
Events::on('on_unblock_actor', static function (int $actorId): void {
cache()->deleteMatching('page_podcast*');
cache()
->deleteMatching('podcast*');
cache()
->deleteMatching('page_post*');
});
Events::on('on_block_domain', static function (string $domainName): void {
cache()->deleteMatching('page_podcast*');
cache()
->deleteMatching('podcast*');
cache()
->deleteMatching('page_post*');
});
Events::on('on_unblock_domain', static function (string $domainName): void {
cache()->deleteMatching('page_podcast*');
cache()
->deleteMatching('podcast*');
cache()
->deleteMatching('page_post*');
});
<?php <?php
declare(strict_types=1);
namespace Config; namespace Config;
use CodeIgniter\Config\BaseConfig; use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Debug\ExceptionHandler;
use CodeIgniter\Debug\ExceptionHandlerInterface;
use Psr\Log\LogLevel;
use Throwable;
/** /**
* Setup how the exception handler works. * Setup how the exception handler works.
...@@ -17,10 +23,8 @@ class Exceptions extends BaseConfig ...@@ -17,10 +23,8 @@ class Exceptions extends BaseConfig
* through Services::Log. * through Services::Log.
* *
* Default: true * Default: true
*
* @var boolean
*/ */
public $log = true; public bool $log = true;
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
...@@ -29,9 +33,9 @@ class Exceptions extends BaseConfig ...@@ -29,9 +33,9 @@ class Exceptions extends BaseConfig
* Any status codes here will NOT be logged if logging is turned on. * Any status codes here will NOT be logged if logging is turned on.
* By default, only 404 (Page Not Found) exceptions are ignored. * By default, only 404 (Page Not Found) exceptions are ignored.
* *
* @var array * @var list<int>
*/ */
public $ignoreCodes = [404]; public array $ignoreCodes = [404];
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
...@@ -41,8 +45,64 @@ class Exceptions extends BaseConfig ...@@ -41,8 +45,64 @@ class Exceptions extends BaseConfig
* directories that hold the views used to generate errors. * directories that hold the views used to generate errors.
* *
* Default: APPPATH.'Views/errors' * Default: APPPATH.'Views/errors'
*/
public string $errorViewPath = APPPATH . 'Views/errors';
/**
* --------------------------------------------------------------------------
* HIDE FROM DEBUG TRACE
* --------------------------------------------------------------------------
* Any data that you would like to hide from the debug trace.
* In order to specify 2 levels, use "/" to separate.
* ex. ['server', 'setup/password', 'secret_token']
*
* @var list<string>
*/
public array $sensitiveDataInTrace = [];
/**
* --------------------------------------------------------------------------
* WHETHER TO THROW AN EXCEPTION ON DEPRECATED ERRORS
* --------------------------------------------------------------------------
* If set to `true`, DEPRECATED errors are only logged and no exceptions are
* thrown. This option also works for user deprecations.
*/
public bool $logDeprecations = true;
/**
* --------------------------------------------------------------------------
* LOG LEVEL THRESHOLD FOR DEPRECATIONS
* --------------------------------------------------------------------------
* If `$logDeprecations` is set to `true`, this sets the log level
* to which the deprecation will be logged. This should be one of the log
* levels recognized by PSR-3.
*
* The related `Config\Logger::$threshold` should be adjusted, if needed,
* to capture logging the deprecations.
*/
public string $deprecationLogLevel = LogLevel::WARNING;
/*
* DEFINE THE HANDLERS USED
* --------------------------------------------------------------------------
* Given the HTTP status code, returns exception handler that
* should be used to deal with this error. By default, it will run CodeIgniter's
* default handler and display the error information in the expected format
* for CLI, HTTP, or AJAX requests, as determined by is_cli() and the expected
* response format.
*
* Custom handlers can be returned if you want to handle one or more specific
* error codes yourself like:
* *
* @var string * if (in_array($statusCode, [400, 404, 500])) {
* return new \App\Libraries\MyExceptionHandler();
* }
* if ($exception instanceOf PageNotFoundException) {
* return new \App\Libraries\MyExceptionHandler();
* }
*/ */
public $errorViewPath = APPPATH . 'Views/errors'; public function handler(int $statusCode, Throwable $exception): ExceptionHandlerInterface
{
return new ExceptionHandler($this);
}
} }
<?php
declare(strict_types=1);
namespace Config;
use CodeIgniter\Config\BaseConfig;
/**
* Enable/disable backward compatibility breaking features.
*/
class Feature extends BaseConfig
{
/**
* Use improved new auto routing instead of the legacy version.
*/
public bool $autoRoutesImproved = true;
/**
* Use filter execution order in 4.4 or before.
*/
public bool $oldFilterOrder = false;
/**
* The behavior of `limit(0)` in Query Builder.
*
* If true, `limit(0)` returns all records. (the behavior of 4.4.x or before in version 4.x.)
* If false, `limit(0)` returns no records. (the behavior of 3.1.9 or later in version 3.x.)
*/
public bool $limitZeroAsAll = true;
/**
* Use strict location negotiation.
*
* By default, the locale is selected based on a loose comparison of the language code (ISO 639-1)
* Enabling strict comparison will also consider the region code (ISO 3166-1 alpha-2).
*/
public bool $strictLocaleNegotiation = false;
}
<?php
declare(strict_types=1);
/**
* @copyright 2022 Ad Aures
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace Config;
use App\Libraries\NoteObject;
use Exception;
use Modules\Fediverse\Config\Fediverse as FediverseBaseConfig;
class Fediverse extends FediverseBaseConfig
{
/**
* --------------------------------------------------------------------
* ActivityPub Objects
* --------------------------------------------------------------------
*/
public string $noteObject = NoteObject::class;
public string $defaultAvatarImagePath = 'castopod-avatar_thumbnail.webp';
public string $defaultAvatarImageMimetype = 'image/webp';
public function __construct()
{
parent::__construct();
try {
$appTheme = service('settings')
->get('App.theme');
$defaultBanner = config('Images')
->podcastBannerDefaultPaths[$appTheme] ?? config('Images')->podcastBannerDefaultPaths['default'];
} catch (Exception) {
$defaultBanner = config('Images')
->podcastBannerDefaultPaths['default'];
}
['dirname' => $dirname, 'extension' => $extension, 'filename' => $filename] = pathinfo(
$defaultBanner['path'],
);
$defaultBannerPath = $filename;
if ($dirname !== '.') {
$defaultBannerPathList = [$dirname, $filename];
$defaultBannerPath = implode('/', $defaultBannerPathList);
}
helper('media');
$this->defaultCoverImagePath = $defaultBannerPath . '_federation.' . $extension;
$this->defaultCoverImageMimetype = $defaultBanner['mimetype'];
}
}
<?php <?php
declare(strict_types=1);
namespace Config; namespace Config;
use App\Filters\AllowCorsFilter;
use CodeIgniter\Config\BaseConfig; use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Filters\CSRF; use CodeIgniter\Filters\CSRF;
use CodeIgniter\Filters\DebugToolbar; use CodeIgniter\Filters\DebugToolbar;
use CodeIgniter\Filters\ForceHTTPS;
use CodeIgniter\Filters\Honeypot; use CodeIgniter\Filters\Honeypot;
use CodeIgniter\Filters\InvalidChars;
use CodeIgniter\Filters\PageCache;
use CodeIgniter\Filters\PerformanceMetrics;
use CodeIgniter\Filters\SecureHeaders;
use Modules\Auth\Filters\PermissionFilter;
class Filters extends BaseConfig class Filters extends BaseConfig
{ {
/** /**
* Configures aliases for Filter classes to * Configures aliases for Filter classes to make reading things nicer and simpler.
* make reading things nicer and simpler. *
* @var array<string, class-string|list<class-string>>
*
* [filter_name => classname]
* or [filter_name => [classname1, classname2, ...]]
*/
public array $aliases = [
'csrf' => CSRF::class,
'toolbar' => DebugToolbar::class,
'honeypot' => Honeypot::class,
'invalidchars' => InvalidChars::class,
'secureheaders' => SecureHeaders::class,
'allow-cors' => AllowCorsFilter::class,
'cors' => Cors::class,
'forcehttps' => ForceHTTPS::class,
'pagecache' => PageCache::class,
'performance' => PerformanceMetrics::class,
];
/**
* List of special required filters.
*
* The filters listed here are special. They are applied before and after
* other kinds of filters, and always applied even if a route does not exist.
* *
* @var array * Filters set by default provide framework functionality. If removed,
* those functions will no longer work.
*
* @see https://codeigniter.com/user_guide/incoming/filters.html#provided-filters
*
* @var array{before: list<string>, after: list<string>}
*/ */
public $aliases = [ public array $required = [
'csrf' => CSRF::class, 'before' => [
'toolbar' => DebugToolbar::class, 'forcehttps', // Force Global Secure Requests
'honeypot' => Honeypot::class, 'pagecache', // Web Page Caching
'login' => \Myth\Auth\Filters\LoginFilter::class, ],
'role' => \Myth\Auth\Filters\RoleFilter::class, 'after' => [
'permission' => \App\Filters\PermissionFilter::class, 'pagecache', // Web Page Caching
'activity-pub' => \ActivityPub\Filters\ActivityPubFilter::class, 'performance', // Performance Metrics
'toolbar', // Debug Toolbar
],
]; ];
/** /**
* List of filter aliases that are always * List of filter aliases that are always applied before and after every request.
* applied before and after every request.
* *
* @var array * @var array<string, array<string, array<string, string|array<string>>>>|array<string, list<string>>
*/ */
public $globals = [ public array $globals = [
'before' => [ 'before' => [
// 'honeypot', // 'honeypot',
// 'csrf', 'csrf' => [
'except' => [
'@[a-zA-Z0-9\_]{1,32}/inbox',
'api/rest/v1/episodes',
'api/rest/v1/episodes/[0-9]+/publish',
],
],
// 'invalidchars',
], ],
'after' => [ 'after' => [
'toolbar',
// 'honeypot', // 'honeypot',
// 'secureheaders',
], ],
]; ];
/** /**
* List of filter aliases that works on a * List of filter aliases that works on a particular HTTP method (GET, POST, etc.).
* particular HTTP method (GET, POST, etc.).
* *
* Example: * Example: 'POST' => ['foo', 'bar']
* 'post' => ['csrf', 'throttle']
* *
* @var array * If you use this, you should disable auto-routing because auto-routing permits any HTTP method to access a
* controller. Accessing the controller with a method you don’t expect could bypass the filter.
*
* @var array<string, list<string>>
*/ */
public $methods = []; public array $methods = [];
/** /**
* List of filter aliases that should run on any * List of filter aliases that should run on any before or after URI patterns.
* before or after URI patterns.
* *
* Example: * Example: 'isLoggedIn' => ['before' => ['account/*', 'profiles/*']]
* 'isLoggedIn' => ['before' => ['account/*', 'profiles/*']]
* *
* @var array * @var array<string, array<string, list<string>>>
*/ */
public $filters = []; public array $filters = [];
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
$this->filters = [ $this->filters = [
'login' => ['before' => [config('App')->adminGateway . '*']], 'session' => [
'before' => [config('Admin')->gateway . '*', config('Analytics')->gateway . '*'],
],
'podcast-unlock' => [
'before' => ['*@*/episodes/*'],
],
]; ];
$this->aliases['permission'] = PermissionFilter::class;
} }
} }
<?php <?php
declare(strict_types=1);
namespace Config; namespace Config;
use CodeIgniter\Config\ForeignCharacters as BaseForeignCharacters; use CodeIgniter\Config\ForeignCharacters as BaseForeignCharacters;
/**
* @immutable
*/
class ForeignCharacters extends BaseForeignCharacters class ForeignCharacters extends BaseForeignCharacters
{ {
} }
<?php <?php
declare(strict_types=1);
namespace Config; namespace Config;
use CodeIgniter\Config\BaseConfig; use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Format\FormatterInterface; use CodeIgniter\Format\JSONFormatter;
use CodeIgniter\Format\XMLFormatter;
class Format extends BaseConfig class Format extends BaseConfig
{ {
...@@ -20,9 +23,9 @@ class Format extends BaseConfig ...@@ -20,9 +23,9 @@ class Format extends BaseConfig
* These formats are only checked when the data passed to the respond() * These formats are only checked when the data passed to the respond()
* method is an array. * method is an array.
* *
* @var string[] * @var list<string>
*/ */
public $supportedResponseFormats = [ public array $supportedResponseFormats = [
'application/json', 'application/json',
'application/xml', // machine-readable XML 'application/xml', // machine-readable XML
'text/xml', // human-readable XML 'text/xml', // human-readable XML
...@@ -39,10 +42,10 @@ class Format extends BaseConfig ...@@ -39,10 +42,10 @@ class Format extends BaseConfig
* *
* @var array<string, string> * @var array<string, string>
*/ */
public $formatters = [ public array $formatters = [
'application/json' => 'CodeIgniter\Format\JSONFormatter', 'application/json' => JSONFormatter::class,
'application/xml' => 'CodeIgniter\Format\XMLFormatter', 'application/xml' => XMLFormatter::class,
'text/xml' => 'CodeIgniter\Format\XMLFormatter', 'text/xml' => XMLFormatter::class,
]; ];
/** /**
...@@ -55,25 +58,9 @@ class Format extends BaseConfig ...@@ -55,25 +58,9 @@ class Format extends BaseConfig
* *
* @var array<string, int> * @var array<string, int>
*/ */
public $formatterOptions = [ public array $formatterOptions = [
'application/json' => JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES, 'application/json' => JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES,
'application/xml' => 0, 'application/xml' => 0,
'text/xml' => 0, 'text/xml' => 0,
]; ];
//--------------------------------------------------------------------
/**
* 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)
{
return Services::format()->getFormatter($mime);
}
} }
<?php <?php
declare(strict_types=1);
namespace Config; namespace Config;
use CodeIgniter\Config\BaseConfig; use CodeIgniter\Config\BaseConfig;
...@@ -23,22 +25,22 @@ class Generators extends BaseConfig ...@@ -23,22 +25,22 @@ class Generators extends BaseConfig
* *
* YOU HAVE BEEN WARNED! * YOU HAVE BEEN WARNED!
* *
* @var array<string, string> * @var array<string, string|array<string,string>>
*/ */
public $views = [ public array $views = [
'make:command' => 'make:cell' => [
'CodeIgniter\Commands\Generators\Views\command.tpl.php', 'class' => 'CodeIgniter\Commands\Generators\Views\cell.tpl.php',
'make:controller' => 'view' => 'CodeIgniter\Commands\Generators\Views\cell_view.tpl.php',
'CodeIgniter\Commands\Generators\Views\controller.tpl.php', ],
'make:entity' => 'CodeIgniter\Commands\Generators\Views\entity.tpl.php', 'make:command' => 'CodeIgniter\Commands\Generators\Views\command.tpl.php',
'make:filter' => 'CodeIgniter\Commands\Generators\Views\filter.tpl.php', 'make:config' => 'CodeIgniter\Commands\Generators\Views\config.tpl.php',
'make:migration' => 'make:controller' => 'CodeIgniter\Commands\Generators\Views\controller.tpl.php',
'CodeIgniter\Commands\Generators\Views\migration.tpl.php', 'make:entity' => 'CodeIgniter\Commands\Generators\Views\entity.tpl.php',
'make:model' => 'CodeIgniter\Commands\Generators\Views\model.tpl.php', 'make:filter' => 'CodeIgniter\Commands\Generators\Views\filter.tpl.php',
'make:seeder' => 'CodeIgniter\Commands\Generators\Views\seeder.tpl.php', 'make:migration' => 'CodeIgniter\Commands\Generators\Views\migration.tpl.php',
'make:validation' => 'make:model' => 'CodeIgniter\Commands\Generators\Views\model.tpl.php',
'CodeIgniter\Commands\Generators\Views\validation.tpl.php', 'make:seeder' => 'CodeIgniter\Commands\Generators\Views\seeder.tpl.php',
'session:migration' => 'make:validation' => 'CodeIgniter\Commands\Generators\Views\validation.tpl.php',
'CodeIgniter\Commands\Generators\Views\migration.tpl.php', 'session:migration' => 'CodeIgniter\Commands\Generators\Views\migration.tpl.php',
]; ];
} }
<?php <?php
declare(strict_types=1);
namespace Config; namespace Config;
use CodeIgniter\Config\BaseConfig; use CodeIgniter\Config\BaseConfig;
...@@ -8,36 +10,35 @@ class Honeypot extends BaseConfig ...@@ -8,36 +10,35 @@ class Honeypot extends BaseConfig
{ {
/** /**
* Makes Honeypot visible or not to human * Makes Honeypot visible or not to human
*
* @var boolean
*/ */
public $hidden = true; public bool $hidden = true;
/** /**
* Honeypot Label Content * Honeypot Label Content
*
* @var string
*/ */
public $label = 'Fill This Field'; public string $label = 'Fill This Field';
/** /**
* Honeypot Field Name * Honeypot Field Name
*
* @var string
*/ */
public $name = 'honeypot'; public string $name = 'honeypot';
/** /**
* Honeypot HTML Template * Honeypot HTML Template
*
* @var string
*/ */
public $template = '<label>{label}</label><input type="text" name="{name}" value=""/>'; public string $template = '<label>{label}</label><input type="text" name="{name}" value=""/>';
/** /**
* Honeypot container * Honeypot container
* *
* @var string * If you enabled CSP, you can remove `style="display:none"`.
*/
public string $container = '<div style="display:none">{template}</div>';
/**
* The id attribute for Honeypot container tag
*
* Used when CSP is enabled.
*/ */
public $container = '<div style="display:none">{template}</div>'; public string $containerId = 'hpc';
} }
<?php <?php
declare(strict_types=1);
namespace Config; namespace Config;
use CodeIgniter\Config\BaseConfig; use CodeIgniter\Config\BaseConfig;
...@@ -10,95 +12,195 @@ class Images extends BaseConfig ...@@ -10,95 +12,195 @@ class Images extends BaseConfig
{ {
/** /**
* Default handler used if no other handler is specified. * Default handler used if no other handler is specified.
*
* @var string
*/ */
public $defaultHandler = 'gd'; public string $defaultHandler = 'gd';
/** /**
* The path to the image library. * The path to the image library. Required for ImageMagick, GraphicsMagick, or NetPBM.
* Required for ImageMagick, GraphicsMagick, or NetPBM.
*
* @var string
*/ */
public $libraryPath = '/usr/local/bin/convert'; public string $libraryPath = '/usr/local/bin/convert';
/** /**
* The available handler classes. * The available handler classes.
* *
* @var array<string, string> * @var array<string, string>
*/ */
public $handlers = [ public array $handlers = [
'gd' => GDHandler::class, 'gd' => GDHandler::class,
'imagick' => ImageMagickHandler::class, 'imagick' => ImageMagickHandler::class,
]; ];
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Uploaded images resizing sizes (in px) | Uploaded images sizes (in px)
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| The sizes listed below determine the resizing of images when uploaded. | The sizes listed below determine the resizing of images when uploaded.
| All uploaded images are of 1:1 ratio (width and height are the same). */
*/
/** /**
* @var integer * Podcast cover image sizes
*/
public $thumbnailSize = 150;
/**
* @var integer
*/
public $mediumSize = 320;
/**
* @var integer
*/
public $largeSize = 1024;
/**
* Size of images linked in the rss feed (should be between 1400 and 3000)
* *
* @var integer * Uploaded podcast covers are of 1:1 ratio (width and height are the same).
*
* Size of images linked in the rss feed (should be between 1400 and 3000). Size for ID3 tag cover art (should be
* between 300 and 800)
*
* Array values are as follows: 'name' => [width, height]
*
* @var array<string, array<string, int|string>>
*/ */
public $feedSize = 1400; public array $podcastCoverSizes = [
'tiny' => [
'width' => 40,
'height' => 40,
'mimetype' => 'image/webp',
'extension' => 'webp',
],
'thumbnail' => [
'width' => 150,
'height' => 150,
'mimetype' => 'image/webp',
'extension' => 'webp',
],
'medium' => [
'width' => 320,
'height' => 320,
'mimetype' => 'image/webp',
'extension' => 'webp',
],
'large' => [
'width' => 1024,
'height' => 1024,
'mimetype' => 'image/webp',
'extension' => 'webp',
],
'feed' => [
'width' => 1400,
'height' => 1400,
],
'id3' => [
'width' => 500,
'height' => 500,
],
'og' => [
'width' => 1200,
'height' => 1200,
],
'federation' => [
'width' => 400,
'height' => 400,
],
'webmanifest192' => [
'width' => 192,
'height' => 192,
'mimetype' => 'image/png',
'extension' => 'png',
],
'webmanifest512' => [
'width' => 512,
'height' => 512,
'mimetype' => 'image/png',
'extension' => 'png',
],
];
/** /**
* Size for ID3 tag cover art (should be between 300 and 800) * Podcast header cover image
* *
* @var integer * Uploaded podcast header covers are of 3:1 ratio
*
* @var array<string, array<string, int|string>>
*/ */
public $id3Size = 500; public array $podcastBannerSizes = [
'small' => [
/* 'width' => 320,
|-------------------------------------------------------------------------- 'height' => 128,
| Uploaded images naming extensions 'mimetype' => 'image/webp',
|-------------------------------------------------------------------------- 'extension' => 'webp',
| The properties listed below set the name extensions for the resized images ],
*/ 'medium' => [
'width' => 960,
'height' => 320,
'mimetype' => 'image/webp',
'extension' => 'webp',
],
'federation' => [
'width' => 1500,
'height' => 500,
],
];
/** public string $avatarDefaultPath = 'assets/images/castopod-avatar.jpg';
* @var string
*/
public $thumbnailExtension = '_thumbnail';
/** public string $avatarDefaultMimeType = 'image/jpg';
* @var string
*/
public $mediumExtension = '_medium';
/** /**
* @var string * @var array<string, array<string, string>>
*/ */
public $largeExtension = '_large'; public array $podcastBannerDefaultPaths = [
'default' => [
'path' => 'assets/images/castopod-banner-pine.jpg',
'mimetype' => 'image/jpeg',
],
'pine' => [
'path' => 'assets/images/castopod-banner-pine.jpg',
'mimetype' => 'image/jpeg',
],
'crimson' => [
'path' => 'assets/images/castopod-banner-crimson.jpg',
'mimetype' => 'image/jpeg',
],
'amber' => [
'path' => 'assets/images/castopod-banner-amber.jpg',
'mimetype' => 'image/jpeg',
],
'lake' => [
'path' => 'assets/images/castopod-banner-lake.jpg',
'mimetype' => 'image/jpeg',
],
'jacaranda' => [
'path' => 'assets/images/castopod-banner-jacaranda.jpg',
'mimetype' => 'image/jpeg',
],
'onyx' => [
'path' => 'assets/images/castopod-banner-onyx.jpg',
'mimetype' => 'image/jpeg',
],
];
/** public string $podcastBannerDefaultMimeType = 'image/jpeg';
* @var string
*/
public $feedExtension = '_feed';
/** /**
* @var string * Person image
*
* Uploaded person images are of 1:1 ratio (width and height are the same).
*
* Array values are as follows: 'name' => [width, height]
*
* @var array<string, array<string, int|string>>
*/ */
public $id3Extension = '_id3'; public array $personAvatarSizes = [
'federation' => [
'width' => 400,
'height' => 400,
],
'tiny' => [
'width' => 40,
'height' => 40,
'mimetype' => 'image/webp',
'extension' => 'webp',
],
'thumbnail' => [
'width' => 150,
'height' => 150,
'mimetype' => 'image/webp',
'extension' => 'webp',
],
'medium' => [
'width' => 320,
'height' => 320,
'mimetype' => 'image/webp',
'extension' => 'webp',
],
];
} }
<?php <?php
declare(strict_types=1);
namespace Config; namespace Config;
use CodeIgniter\Config\BaseConfig; use Kint\Parser\ConstructablePluginInterface;
use Kint\Renderer\Renderer; use Kint\Renderer\Rich\TabPluginInterface;
use Kint\Renderer\Rich\ValuePluginInterface;
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* Kint
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* *
* We use Kint's `RichRenderer` and `CLIRenderer`. This area contains options * We use Kint's `RichRenderer` and `CLIRenderer`. This area contains options
...@@ -15,47 +17,56 @@ use Kint\Renderer\Renderer; ...@@ -15,47 +17,56 @@ use Kint\Renderer\Renderer;
* *
* @see https://kint-php.github.io/kint/ for details on these settings. * @see https://kint-php.github.io/kint/ for details on these settings.
*/ */
class Kint extends BaseConfig class Kint
{ {
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Global Settings | Global Settings
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
*/ */
public $plugins = null; /**
* @var list<class-string<ConstructablePluginInterface>|ConstructablePluginInterface>|null
*/
public ?array $plugins = [];
public $maxDepth = 6; public int $maxDepth = 6;
public $displayCalledFrom = true; public bool $displayCalledFrom = true;
public $expanded = false; public bool $expanded = false;
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| RichRenderer Settings | RichRenderer Settings
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
*/ */
public $richTheme = 'aante-light.css';
public $richFolder = false; public string $richTheme = 'aante-light.css';
public $richSort = Renderer::SORT_FULL; public bool $richFolder = false;
public $richObjectPlugins = null; /**
* @var array<string, class-string<ValuePluginInterface>>|null
*/
public ?array $richObjectPlugins = [];
public $richTabPlugins = null; /**
* @var array<string, class-string<TabPluginInterface>>|null
*/
public ?array $richTabPlugins = [];
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| CLI Settings | CLI Settings
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
*/ */
public $cliColors = true;
public bool $cliColors = true;
public $cliForceUTF8 = false; public bool $cliForceUTF8 = false;
public $cliDetectWidth = true; public bool $cliDetectWidth = true;
public $cliMinWidth = 40; public int $cliMinWidth = 40;
} }
<?php <?php
declare(strict_types=1);
namespace Config; namespace Config;
use CodeIgniter\Config\BaseConfig; use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Log\Handlers\FileHandler;
class Logger extends BaseConfig class Logger extends BaseConfig
{ {
...@@ -35,9 +38,9 @@ class Logger extends BaseConfig ...@@ -35,9 +38,9 @@ class Logger extends BaseConfig
* For a live site you'll usually enable Critical or higher (3) to be logged otherwise * For a live site you'll usually enable Critical or higher (3) to be logged otherwise
* your log files will fill up very fast. * your log files will fill up very fast.
* *
* @var integer|array * @var int|list<int>
*/ */
public $threshold = 4; public int | array $threshold = (ENVIRONMENT === 'production') ? 4 : 9;
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
...@@ -46,10 +49,8 @@ class Logger extends BaseConfig ...@@ -46,10 +49,8 @@ class Logger extends BaseConfig
* *
* Each item that is logged has an associated date. You can use PHP date * Each item that is logged has an associated date. You can use PHP date
* codes to set your own date formatting * codes to set your own date formatting
*
* @var string
*/ */
public $dateFormat = 'Y-m-d H:i:s'; public string $dateFormat = 'Y-m-d H:i:s';
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
...@@ -59,7 +60,7 @@ class Logger extends BaseConfig ...@@ -59,7 +60,7 @@ class Logger extends BaseConfig
* The logging system supports multiple actions to be taken when something * The logging system supports multiple actions to be taken when something
* is logged. This is done by allowing for multiple Handlers, special classes * is logged. This is done by allowing for multiple Handlers, special classes
* designed to write the log to their chosen destinations, whether that is * designed to write the log to their chosen destinations, whether that is
* a file on the getServer, a cloud-based service, or even taking actions such * a file on the server, a cloud-based service, or even taking actions such
* as emailing the dev team. * as emailing the dev team.
* *
* Each handler is defined by the class name used for that handler, and it * Each handler is defined by the class name used for that handler, and it
...@@ -74,28 +75,19 @@ class Logger extends BaseConfig ...@@ -74,28 +75,19 @@ class Logger extends BaseConfig
* Handlers are executed in the order defined in this array, starting with * Handlers are executed in the order defined in this array, starting with
* the handler on top and continuing down. * the handler on top and continuing down.
* *
* @var array * @var array<class-string, array<string, int|list<string>|string>>
*/ */
public $handlers = [ public array $handlers = [
/* /*
* -------------------------------------------------------------------- * --------------------------------------------------------------------
* File Handler * File Handler
* -------------------------------------------------------------------- * --------------------------------------------------------------------
*/ */
'CodeIgniter\Log\Handlers\FileHandler' => [ FileHandler::class => [
/* /*
* The log levels that this handler will handle. * The log levels that this handler will handle.
*/ */
'handles' => [ 'handles' => ['critical', 'alert', 'emergency', 'debug', 'error', 'info', 'notice', 'warning'],
'critical',
'alert',
'emergency',
'debug',
'error',
'info',
'notice',
'warning',
],
/* /*
* The default filename extension for log files. * The default filename extension for log files.
...@@ -123,16 +115,31 @@ class Logger extends BaseConfig ...@@ -123,16 +115,31 @@ class Logger extends BaseConfig
'path' => '', 'path' => '',
], ],
/** /*
* The ChromeLoggerHandler requires the use of the Chrome web browser * The ChromeLoggerHandler requires the use of the Chrome web browser
* and the ChromeLogger extension. Uncomment this block to use it. * and the ChromeLogger extension. Uncomment this block to use it.
*/ */
// 'CodeIgniter\Log\Handlers\ChromeLoggerHandler' => [ // 'CodeIgniter\Log\Handlers\ChromeLoggerHandler' => [
// /* // /*
// * The log levels that this handler will handle. // * The log levels that this handler will handle.
// */ // */
// 'handles' => ['critical', 'alert', 'emergency', 'debug', // 'handles' => ['critical', 'alert', 'emergency', 'debug',
// 'error', 'info', 'notice', 'warning'], // 'error', 'info', 'notice', 'warning'],
// ] // ],
/*
* The ErrorlogHandler writes the logs to PHP's native `error_log()` function.
* Uncomment this block to use it.
*/
// 'CodeIgniter\Log\Handlers\ErrorlogHandler' => [
// /* The log levels this handler can handle. */
// 'handles' => ['critical', 'alert', 'emergency', 'debug', 'error', 'info', 'notice', 'warning'],
//
// /*
// * The message type where the error should go. Can be 0 or 4, or use the
// * class constants: `ErrorlogHandler::TYPE_OS` (0) or `ErrorlogHandler::TYPE_SAPI` (4)
// */
// 'messageType' => 0,
// ],
]; ];
} }
<?php <?php
declare(strict_types=1);
namespace Config; namespace Config;
use CodeIgniter\Config\BaseConfig; use CodeIgniter\Config\BaseConfig;
...@@ -15,10 +17,8 @@ class Migrations extends BaseConfig ...@@ -15,10 +17,8 @@ class Migrations extends BaseConfig
* *
* You should enable migrations whenever you intend to do a schema migration * You should enable migrations whenever you intend to do a schema migration
* and disable it back when you're done. * and disable it back when you're done.
*
* @var boolean
*/ */
public $enabled = true; public bool $enabled = true;
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
...@@ -27,13 +27,9 @@ class Migrations extends BaseConfig ...@@ -27,13 +27,9 @@ class Migrations extends BaseConfig
* *
* This is the name of the table that will store the current migrations state. * This is the name of the table that will store the current migrations state.
* When migrations runs it will store in a database table which migration * When migrations runs it will store in a database table which migration
* level the system is at. It then compares the migration level in this * files have already been run.
* table to the $config['migration_version'] if they are not the same it
* will migrate up. This must be set.
*
* @var string
*/ */
public $table = 'migrations'; public string $table = 'migrations';
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
...@@ -48,8 +44,6 @@ class Migrations extends BaseConfig ...@@ -48,8 +44,6 @@ class Migrations extends BaseConfig
* - YmdHis_ * - YmdHis_
* - Y-m-d-His_ * - Y-m-d-His_
* - Y_m_d_His_ * - Y_m_d_His_
*
* @var string
*/ */
public $timestampFormat = 'Y-m-d-His_'; public string $timestampFormat = 'Y-m-d-His_';
} }
<?php <?php
declare(strict_types=1);
namespace Config; namespace Config;
/** /**
* Mimes * This file contains an array of mime types. It is used by the Upload class to help identify allowed file types.
* *
* This file contains an array of mime types. It is used by the * When more than one variation for an extension exist (like jpg, jpeg, etc) the most common one should be first in the
* Upload class to help identify allowed file types. * array to aid the guess* methods. The same applies when more than one mime-type exists for a single extension.
* *
* When more than one variation for an extension exist (like jpg, jpeg, etc) * When working with mime types, please make sure you have the ´fileinfo´ extension enabled to reliably detect the
* the most common one should be first in the array to aid the guess* * media types.
* methods. The same applies when more than one mime-type exists for a
* single extension.
* *
* When working with mime types, please make sure you have the ´fileinfo´ * @immutable
* extension enabled to reliably detect the media types.
*/ */
class Mimes class Mimes
{ {
/** /**
* Map of extensions to mime types. * Map of extensions to mime types.
* *
* @var array * @var array<string, list<string>|string>
*/ */
public static $mimes = [ public static $mimes = [
'hqx' => [ 'hqx' => [
...@@ -54,25 +52,24 @@ class Mimes ...@@ -54,25 +52,24 @@ class Mimes
'dms' => 'application/octet-stream', 'dms' => 'application/octet-stream',
'lha' => 'application/octet-stream', 'lha' => 'application/octet-stream',
'lzh' => 'application/octet-stream', 'lzh' => 'application/octet-stream',
'exe' => ['application/octet-stream', 'application/x-msdownload'], 'exe' => ['application/octet-stream',
'application/vnd.microsoft.portable-executable',
'application/x-dosexec',
'application/x-msdownload'],
'class' => 'application/octet-stream', 'class' => 'application/octet-stream',
'psd' => ['application/x-photoshop', 'image/vnd.adobe.photoshop'], 'psd' => ['application/x-photoshop', 'image/vnd.adobe.photoshop'],
'so' => 'application/octet-stream', 'so' => 'application/octet-stream',
'sea' => 'application/octet-stream', 'sea' => 'application/octet-stream',
'dll' => 'application/octet-stream', 'dll' => 'application/octet-stream',
'oda' => 'application/oda', 'oda' => 'application/oda',
'pdf' => [ 'pdf' => ['application/pdf', 'application/force-download', 'application/x-download'],
'application/pdf', 'ai' => ['application/pdf', 'application/postscript'],
'application/force-download', 'eps' => 'application/postscript',
'application/x-download', 'ps' => 'application/postscript',
], 'smi' => 'application/smil',
'ai' => ['application/pdf', 'application/postscript'], 'smil' => 'application/smil',
'eps' => 'application/postscript', 'mif' => 'application/vnd.mif',
'ps' => 'application/postscript', 'xls' => [
'smi' => 'application/smil',
'smil' => 'application/smil',
'mif' => 'application/vnd.mif',
'xls' => [
'application/vnd.ms-excel', 'application/vnd.ms-excel',
'application/msexcel', 'application/msexcel',
'application/x-msexcel', 'application/x-msexcel',
...@@ -92,21 +89,17 @@ class Mimes ...@@ -92,21 +89,17 @@ class Mimes
'application/vnd.ms-office', 'application/vnd.ms-office',
'application/msword', 'application/msword',
], ],
'pptx' => [ 'pptx' => ['application/vnd.openxmlformats-officedocument.presentationml.presentation'],
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'application/x-zip',
'application/zip',
],
'wbxml' => 'application/wbxml', 'wbxml' => 'application/wbxml',
'wmlc' => 'application/wmlc', 'wmlc' => 'application/wmlc',
'dcr' => 'application/x-director', 'dcr' => 'application/x-director',
'dir' => 'application/x-director', 'dir' => 'application/x-director',
'dxr' => 'application/x-director', 'dxr' => 'application/x-director',
'dvi' => 'application/x-dvi', 'dvi' => 'application/x-dvi',
'gtar' => 'application/x-gtar', 'gtar' => 'application/x-gtar',
'gz' => 'application/x-gzip', 'gz' => 'application/x-gzip',
'gzip' => 'application/x-gzip', 'gzip' => 'application/x-gzip',
'php' => [ 'php' => [
'application/x-php', 'application/x-php',
'application/x-httpd-php', 'application/x-httpd-php',
'application/php', 'application/php',
...@@ -114,46 +107,41 @@ class Mimes ...@@ -114,46 +107,41 @@ class Mimes
'text/x-php', 'text/x-php',
'application/x-httpd-php-source', 'application/x-httpd-php-source',
], ],
'php4' => 'application/x-httpd-php', 'php4' => 'application/x-httpd-php',
'php3' => 'application/x-httpd-php', 'php3' => 'application/x-httpd-php',
'phtml' => 'application/x-httpd-php', 'phtml' => 'application/x-httpd-php',
'phps' => 'application/x-httpd-php-source', 'phps' => 'application/x-httpd-php-source',
'js' => ['application/x-javascript', 'text/plain'], 'js' => ['application/x-javascript', 'text/plain'],
'swf' => 'application/x-shockwave-flash', 'swf' => 'application/x-shockwave-flash',
'sit' => 'application/x-stuffit', 'sit' => 'application/x-stuffit',
'tar' => 'application/x-tar', 'tar' => 'application/x-tar',
'tgz' => ['application/x-tar', 'application/x-gzip-compressed'], 'tgz' => ['application/x-tar', 'application/x-gzip-compressed'],
'z' => 'application/x-compress', 'z' => 'application/x-compress',
'xhtml' => 'application/xhtml+xml', 'xhtml' => 'application/xhtml+xml',
'xht' => 'application/xhtml+xml', 'xht' => 'application/xhtml+xml',
'zip' => [ 'zip' => [
'application/x-zip', 'application/x-zip',
'application/zip', 'application/zip',
'application/x-zip-compressed', 'application/x-zip-compressed',
'application/s-compressed', 'application/s-compressed',
'multipart/x-zip', 'multipart/x-zip',
], ],
'rar' => [ 'rar' => ['application/vnd.rar', 'application/x-rar', 'application/rar', 'application/x-rar-compressed'],
'application/vnd.rar', 'mid' => 'audio/midi',
'application/x-rar',
'application/rar',
'application/x-rar-compressed',
],
'mid' => 'audio/midi',
'midi' => 'audio/midi', 'midi' => 'audio/midi',
'mp3' => ['audio/mpeg', 'audio/mpg', 'audio/mpeg3', 'audio/mp3', 'application/octet-stream'],
'mpga' => 'audio/mpeg', 'mpga' => 'audio/mpeg',
'mp2' => 'audio/mpeg', 'mp2' => 'audio/mpeg',
'mp3' => ['audio/mpeg', 'audio/mpg', 'audio/mpeg3', 'audio/mp3'], 'aif' => ['audio/x-aiff', 'audio/aiff'],
'aif' => ['audio/x-aiff', 'audio/aiff'],
'aiff' => ['audio/x-aiff', 'audio/aiff'], 'aiff' => ['audio/x-aiff', 'audio/aiff'],
'aifc' => 'audio/x-aiff', 'aifc' => 'audio/x-aiff',
'ram' => 'audio/x-pn-realaudio', 'ram' => 'audio/x-pn-realaudio',
'rm' => 'audio/x-pn-realaudio', 'rm' => 'audio/x-pn-realaudio',
'rpm' => 'audio/x-pn-realaudio-plugin', 'rpm' => 'audio/x-pn-realaudio-plugin',
'ra' => 'audio/x-realaudio', 'ra' => 'audio/x-realaudio',
'rv' => 'video/vnd.rn-realvideo', 'rv' => 'video/vnd.rn-realvideo',
'wav' => ['audio/x-wav', 'audio/wave', 'audio/wav'], 'wav' => ['audio/x-wav', 'audio/wave', 'audio/wav'],
'bmp' => [ 'bmp' => [
'image/bmp', 'image/bmp',
'image/x-bmp', 'image/x-bmp',
'image/x-bitmap', 'image/x-bitmap',
...@@ -166,52 +154,48 @@ class Mimes ...@@ -166,52 +154,48 @@ class Mimes
'application/x-bmp', 'application/x-bmp',
'application/x-win-bitmap', 'application/x-win-bitmap',
], ],
'gif' => 'image/gif', 'gif' => 'image/gif',
'jpg' => ['image/jpeg', 'image/pjpeg'], 'jpg' => ['image/jpeg', 'image/pjpeg'],
'jpeg' => ['image/jpeg', 'image/pjpeg'], 'jpeg' => ['image/jpeg', 'image/pjpeg'],
'jpe' => ['image/jpeg', 'image/pjpeg'], 'jpe' => ['image/jpeg', 'image/pjpeg'],
'jp2' => ['image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'], 'jp2' => ['image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'],
'j2k' => ['image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'], 'j2k' => ['image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'],
'jpf' => ['image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'], 'jpf' => ['image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'],
'jpg2' => ['image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'], 'jpg2' => ['image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'],
'jpx' => ['image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'], 'jpx' => ['image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'],
'jpm' => ['image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'], 'jpm' => ['image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'],
'mj2' => ['image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'], 'mj2' => ['image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'],
'mjp2' => ['image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'], 'mjp2' => ['image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'],
'png' => ['image/png', 'image/x-png'], 'png' => ['image/png', 'image/x-png'],
'tif' => 'image/tiff', 'webp' => 'image/webp',
'tiff' => 'image/tiff', 'tif' => 'image/tiff',
'css' => ['text/css', 'text/plain'], 'tiff' => 'image/tiff',
'html' => ['text/html', 'text/plain'], 'css' => ['text/css', 'text/plain'],
'htm' => ['text/html', 'text/plain'], 'html' => ['text/html', 'text/plain'],
'htm' => ['text/html', 'text/plain'],
'shtml' => ['text/html', 'text/plain'], 'shtml' => ['text/html', 'text/plain'],
'txt' => 'text/plain', 'txt' => 'text/plain',
'text' => 'text/plain', 'text' => 'text/plain',
'log' => ['text/plain', 'text/x-log'], 'log' => ['text/plain', 'text/x-log'],
'rtx' => 'text/richtext', 'rtx' => 'text/richtext',
'rtf' => 'text/rtf', 'rtf' => 'text/rtf',
'xml' => ['application/xml', 'text/xml', 'text/plain'], 'xml' => ['application/xml', 'text/xml', 'text/plain'],
'xsl' => ['application/xml', 'text/xsl', 'text/xml'], 'xsl' => ['application/xml', 'text/xsl', 'text/xml'],
'mpeg' => 'video/mpeg', 'mpeg' => 'video/mpeg',
'mpg' => 'video/mpeg', 'mpg' => 'video/mpeg',
'mpe' => 'video/mpeg', 'mpe' => 'video/mpeg',
'qt' => 'video/quicktime', 'qt' => 'video/quicktime',
'mov' => 'video/quicktime', 'mov' => 'video/quicktime',
'avi' => [ 'avi' => ['video/x-msvideo', 'video/msvideo', 'video/avi', 'application/x-troff-msvideo'],
'video/x-msvideo',
'video/msvideo',
'video/avi',
'application/x-troff-msvideo',
],
'movie' => 'video/x-sgi-movie', 'movie' => 'video/x-sgi-movie',
'doc' => ['application/msword', 'application/vnd.ms-office'], 'doc' => ['application/msword', 'application/vnd.ms-office'],
'docx' => [ 'docx' => [
'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/zip', 'application/zip',
'application/msword', 'application/msword',
'application/x-zip', 'application/x-zip',
], ],
'dot' => ['application/msword', 'application/vnd.ms-office'], 'dot' => ['application/msword', 'application/vnd.ms-office'],
'dotx' => [ 'dotx' => [
'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/zip', 'application/zip',
...@@ -224,66 +208,52 @@ class Mimes ...@@ -224,66 +208,52 @@ class Mimes
'application/msword', 'application/msword',
'application/x-zip', 'application/x-zip',
], ],
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
'word' => ['application/msword', 'application/octet-stream'], 'word' => ['application/msword', 'application/octet-stream'],
'xl' => 'application/excel', 'xl' => 'application/excel',
'eml' => 'message/rfc822', 'eml' => 'message/rfc822',
'json' => ['application/json', 'text/json'], 'json' => ['application/json', 'text/json', 'text/plain'],
'pem' => [ 'pem' => ['application/x-x509-user-cert', 'application/x-pem-file', 'application/octet-stream'],
'application/x-x509-user-cert', 'p10' => ['application/x-pkcs10', 'application/pkcs10'],
'application/x-pem-file', 'p12' => 'application/x-pkcs12',
'application/octet-stream', 'p7a' => 'application/x-pkcs7-signature',
], 'p7c' => ['application/pkcs7-mime', 'application/x-pkcs7-mime'],
'p10' => ['application/x-pkcs10', 'application/pkcs10'], 'p7m' => ['application/pkcs7-mime', 'application/x-pkcs7-mime'],
'p12' => 'application/x-pkcs12', 'p7r' => 'application/x-pkcs7-certreqresp',
'p7a' => 'application/x-pkcs7-signature', 'p7s' => 'application/pkcs7-signature',
'p7c' => ['application/pkcs7-mime', 'application/x-pkcs7-mime'], 'crt' => ['application/x-x509-ca-cert', 'application/x-x509-user-cert', 'application/pkix-cert'],
'p7m' => ['application/pkcs7-mime', 'application/x-pkcs7-mime'], 'crl' => ['application/pkix-crl', 'application/pkcs-crl'],
'p7r' => 'application/x-pkcs7-certreqresp', 'der' => 'application/x-x509-ca-cert',
'p7s' => 'application/pkcs7-signature', 'kdb' => 'application/octet-stream',
'crt' => [ 'pgp' => 'application/pgp',
'application/x-x509-ca-cert', 'gpg' => 'application/gpg-keys',
'application/x-x509-user-cert', 'sst' => 'application/octet-stream',
'application/pkix-cert', 'csr' => 'application/octet-stream',
], 'rsa' => 'application/x-pkcs7',
'crl' => ['application/pkix-crl', 'application/pkcs-crl'], 'cer' => ['application/pkix-cert', 'application/x-x509-ca-cert'],
'der' => 'application/x-x509-ca-cert', '3g2' => 'video/3gpp2',
'kdb' => 'application/octet-stream', '3gp' => ['video/3gp', 'video/3gpp'],
'pgp' => 'application/pgp', 'mp4' => 'video/mp4',
'gpg' => 'application/gpg-keys', 'm4a' => ['audio/m4a', 'audio/x-m4a', 'application/octet-stream'],
'sst' => 'application/octet-stream', 'f4v' => ['video/mp4', 'video/x-f4v'],
'csr' => 'application/octet-stream', 'flv' => 'video/x-flv',
'rsa' => 'application/x-pkcs7',
'cer' => ['application/pkix-cert', 'application/x-x509-ca-cert'],
'3g2' => 'video/3gpp2',
'3gp' => ['video/3gp', 'video/3gpp'],
'mp4' => 'video/mp4',
'm4a' => 'audio/x-m4a',
'f4v' => ['video/mp4', 'video/x-f4v'],
'flv' => 'video/x-flv',
'webm' => 'video/webm', 'webm' => 'video/webm',
'aac' => 'audio/x-acc', 'aac' => 'audio/x-acc',
'm4u' => 'application/vnd.mpegurl', 'm4u' => 'application/vnd.mpegurl',
'm3u' => 'text/plain', 'm3u' => 'text/plain',
'xspf' => 'application/xspf+xml', 'xspf' => 'application/xspf+xml',
'vlc' => 'application/videolan', 'vlc' => 'application/videolan',
'wmv' => ['video/x-ms-wmv', 'video/x-ms-asf'], 'wmv' => ['video/x-ms-wmv', 'video/x-ms-asf'],
'au' => 'audio/x-au', 'au' => 'audio/x-au',
'ac3' => 'audio/ac3', 'ac3' => 'audio/ac3',
'flac' => 'audio/x-flac', 'flac' => 'audio/x-flac',
'ogg' => ['audio/ogg', 'video/ogg', 'application/ogg'], 'ogg' => ['audio/ogg', 'video/ogg', 'application/ogg'],
'kmz' => [ 'kmz' => ['application/vnd.google-earth.kmz', 'application/zip', 'application/x-zip'],
'application/vnd.google-earth.kmz', 'kml' => ['application/vnd.google-earth.kml+xml', 'application/xml', 'text/xml'],
'application/zip', 'ics' => 'text/calendar',
'application/x-zip',
],
'kml' => [
'application/vnd.google-earth.kml+xml',
'application/xml',
'text/xml',
],
'ics' => 'text/calendar',
'ical' => 'text/calendar', 'ical' => 'text/calendar',
'zsh' => 'text/x-scriptzsh', 'zsh' => 'text/x-scriptzsh',
'7zip' => [ '7zip' => [
'application/x-compressed', 'application/x-compressed',
'application/x-zip-compressed', 'application/x-zip-compressed',
...@@ -308,28 +278,22 @@ class Mimes ...@@ -308,28 +278,22 @@ class Mimes
], ],
'svg' => ['image/svg+xml', 'image/svg', 'application/xml', 'text/xml'], 'svg' => ['image/svg+xml', 'image/svg', 'application/xml', 'text/xml'],
'vcf' => 'text/x-vcard', 'vcf' => 'text/x-vcard',
'srt' => ['text/srt', 'text/plain', 'application/octet-stream'], 'srt' => ['application/x-subrip', 'text/srt', 'text/plain', 'application/octet-stream'],
'vtt' => ['text/vtt', 'text/plain'], 'vtt' => ['text/vtt', 'text/plain'],
'ico' => ['image/x-icon', 'image/x-ico', 'image/vnd.microsoft.icon'], 'ico' => ['image/x-icon', 'image/x-ico', 'image/vnd.microsoft.icon'],
'stl' => [ 'stl' => ['application/sla', 'application/vnd.ms-pki.stl', 'application/x-navistyle'],
'application/sla',
'application/vnd.ms-pki.stl',
'application/x-navistyle',
],
]; ];
/** /**
* Attempts to determine the best mime type for the given file extension. * 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. * @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), '. '); $extension = trim(strtolower($extension), '. ');
if (!array_key_exists($extension, static::$mimes)) { if (! array_key_exists($extension, static::$mimes)) {
return null; return null;
} }
...@@ -341,45 +305,28 @@ class Mimes ...@@ -341,45 +305,28 @@ class Mimes
/** /**
* Attempts to determine the best file extension for a given mime type. * 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) * @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. * @return string|null The extension determined, or null if unable to match.
*/ */
public static function guessExtensionFromType( public static function guessExtensionFromType(string $type, ?string $proposedExtension = null): ?string
string $type, {
string $proposedExtension = null
) {
$type = trim(strtolower($type), '. '); $type = trim(strtolower($type), '. ');
$proposedExtension = trim(strtolower($proposedExtension)); $proposedExtension = trim(strtolower($proposedExtension ?? ''));
if ($proposedExtension !== '') { if (
if ( $proposedExtension !== ''
array_key_exists($proposedExtension, static::$mimes) && && array_key_exists($proposedExtension, static::$mimes)
in_array( && in_array($type, (array) static::$mimes[$proposedExtension], true)
$type, ) {
is_string(static::$mimes[$proposedExtension]) // The detected mime type matches with the proposed extension.
? [static::$mimes[$proposedExtension]] return $proposedExtension;
: static::$mimes[$proposedExtension],
true,
)
) {
// The detected mime type matches with the proposed extension.
return $proposedExtension;
}
// An extension was proposed, but the media type does not match the mime type list.
return null;
} }
// Reverse check the mime type list if no extension was proposed. // Reverse check the mime type list if no extension was proposed.
// This search is order sensitive! // This search is order sensitive!
foreach (static::$mimes as $ext => $types) { foreach (static::$mimes as $ext => $types) {
if ( if (in_array($type, (array) $types, true)) {
(is_string($types) && $types === $type) ||
(is_array($types) && in_array($type, $types, true))
) {
return $ext; return $ext;
} }
} }
......
<?php <?php
declare(strict_types=1);
namespace Config; namespace Config;
use CodeIgniter\Modules\Modules as BaseModules; use CodeIgniter\Modules\Modules as BaseModules;
/**
* Modules Configuration.
*
* NOTE: This class is required prior to Autoloader instantiation,
* and does not extend BaseConfig.
*
* @immutable
*/
class Modules extends BaseModules class Modules extends BaseModules
{ {
/** /**
...@@ -12,7 +22,7 @@ class Modules extends BaseModules ...@@ -12,7 +22,7 @@ class Modules extends BaseModules
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* *
* If true, then auto-discovery will happen across all elements listed in * If true, then auto-discovery will happen across all elements listed in
* $activeExplorers below. If false, no auto-discovery will happen at all, * $aliases below. If false, no auto-discovery will happen at all,
* giving a slight performance boost. * giving a slight performance boost.
* *
* @var boolean * @var boolean
...@@ -31,6 +41,29 @@ class Modules extends BaseModules ...@@ -31,6 +41,29 @@ class Modules extends BaseModules
*/ */
public $discoverInComposer = true; public $discoverInComposer = true;
/**
* The Composer package list for Auto-Discovery
* This setting is optional.
*
* E.g.:
* [
* 'only' => [
* // List up all packages to auto-discover
* 'codeigniter4/shield',
* ],
* ]
* or
* [
* 'exclude' => [
* // List up packages to exclude.
* 'pestphp/pest',
* ],
* ]
*
* @var array{only?: list<string>, exclude?: list<string>}
*/
public $composerPackages = [];
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* Auto-Discovery Rules * Auto-Discovery Rules
...@@ -41,7 +74,7 @@ class Modules extends BaseModules ...@@ -41,7 +74,7 @@ class Modules extends BaseModules
* *
* If it is not listed, only the base application elements will be used. * If it is not listed, only the base application elements will be used.
* *
* @var string[] * @var list<string>
*/ */
public $aliases = ['events', 'filters', 'registrars', 'routes', 'services']; public $aliases = ['events', 'filters', 'registrars', 'routes', 'services'];
} }
<?php
declare(strict_types=1);
namespace Config;
/**
* Optimization Configuration.
*
* NOTE: This class does not extend BaseConfig for performance reasons.
* So you cannot replace the property values with Environment Variables.
*
* @immutable
*/
class Optimize
{
/**
* --------------------------------------------------------------------------
* Config Caching
* --------------------------------------------------------------------------
*
* @see https://codeigniter.com/user_guide/concepts/factories.html#config-caching
*/
public bool $configCacheEnabled = false;
/**
* --------------------------------------------------------------------------
* Config Caching
* --------------------------------------------------------------------------
*
* @see https://codeigniter.com/user_guide/concepts/autoloader.html#file-locator-caching
*/
public bool $locatorCacheEnabled = false;
}
<?php <?php
declare(strict_types=1);
namespace Config; namespace Config;
use CodeIgniter\Config\BaseConfig; use CodeIgniter\Config\BaseConfig;
...@@ -20,10 +22,10 @@ class Pager extends BaseConfig ...@@ -20,10 +22,10 @@ class Pager extends BaseConfig
* *
* @var array<string, string> * @var array<string, string>
*/ */
public $templates = [ public array $templates = [
'default_full' => 'App\Views\pager\default_full', 'default_full' => 'App\Views\pager\default_full',
'default_simple' => 'CodeIgniter\Pager\Views\default_simple', 'default_simple' => 'CodeIgniter\Pager\Views\default_simple',
'default_head' => 'CodeIgniter\Pager\Views\default_head', 'default_head' => 'CodeIgniter\Pager\Views\default_head',
]; ];
/** /**
...@@ -32,8 +34,6 @@ class Pager extends BaseConfig ...@@ -32,8 +34,6 @@ class Pager extends BaseConfig
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* *
* The default number of results shown in a single page. * The default number of results shown in a single page.
*
* @var integer
*/ */
public $perPage = 20; public int $perPage = 20;
} }
<?php <?php
declare(strict_types=1);
namespace Config; namespace Config;
/** /**
* Paths * Holds the paths that are used by the system to locate the main directories, app, system, etc.
*
* Holds the paths that are used by the system to
* locate the main directories, app, system, etc.
* *
* Modifying these allows you to restructure your application, * Modifying these allows you to restructure your application, share a system folder between multiple applications, and
* share a system folder between multiple applications, and more. * more.
* *
* All paths are relative to the project's root folder. * All paths are relative to the project's root folder.
*/ */
...@@ -23,11 +22,10 @@ class Paths ...@@ -23,11 +22,10 @@ class Paths
* *
* This must contain the name of your "system" folder. Include * This must contain the name of your "system" folder. Include
* the path if the folder is not in the same directory as this file. * the path if the folder is not in the same directory as this file.
*
* @var string
*/ */
public $systemDirectory = public string $systemDirectory =
__DIR__ . '/../../vendor/codeigniter4/codeigniter4/system'; __DIR__ . '/../../vendor/codeigniter4/framework/system';
/** /**
* --------------------------------------------------------------- * ---------------------------------------------------------------
* APPLICATION FOLDER NAME * APPLICATION FOLDER NAME
...@@ -35,14 +33,12 @@ class Paths ...@@ -35,14 +33,12 @@ class Paths
* *
* If you want this front controller to use a different "app" * If you want this front controller to use a different "app"
* folder than the default one you can set its name here. The folder * folder than the default one you can set its name here. The folder
* can also be renamed or relocated anywhere on your getServer. If * can also be renamed or relocated anywhere on your server. If
* you do, use a full getServer path. * you do, use a full server path.
* *
* @see http://codeigniter.com/user_guide/general/managing_apps.html * @see http://codeigniter.com/user_guide/general/managing_apps.html
*
* @var string
*/ */
public $appDirectory = __DIR__ . '/..'; public string $appDirectory = __DIR__ . '/..';
/** /**
* --------------------------------------------------------------- * ---------------------------------------------------------------
...@@ -54,10 +50,8 @@ class Paths ...@@ -54,10 +50,8 @@ class Paths
* need write permission to a single place that can be tucked away * need write permission to a single place that can be tucked away
* for maximum security, keeping it out of the app and/or * for maximum security, keeping it out of the app and/or
* system directories. * system directories.
*
* @var string
*/ */
public $writableDirectory = __DIR__ . '/../../writable'; public string $writableDirectory = __DIR__ . '/../../writable';
/** /**
* --------------------------------------------------------------- * ---------------------------------------------------------------
...@@ -65,10 +59,8 @@ class Paths ...@@ -65,10 +59,8 @@ class Paths
* --------------------------------------------------------------- * ---------------------------------------------------------------
* *
* This variable must contain the name of your "tests" directory. * This variable must contain the name of your "tests" directory.
*
* @var string
*/ */
public $testsDirectory = __DIR__ . '/../../tests'; public string $testsDirectory = __DIR__ . '/../../tests';
/** /**
* --------------------------------------------------------------- * ---------------------------------------------------------------
...@@ -78,9 +70,7 @@ class Paths ...@@ -78,9 +70,7 @@ class Paths
* This variable must contain the name of the directory that * This variable must contain the name of the directory that
* contains the view files used by your application. By * contains the view files used by your application. By
* default this is in `app/Views`. This value * default this is in `app/Views`. This value
* is used when no value is provided to `Services::renderer()`. * is used when no value is provided to `service('renderer')`.
*
* @var string
*/ */
public $viewDirectory = __DIR__ . '/../Views'; public string $viewDirectory = __DIR__ . '/../Views';
} }
<?php
declare(strict_types=1);
namespace Config;
use CodeIgniter\Config\Publisher as BasePublisher;
/**
* Publisher Configuration
*
* Defines basic security restrictions for the Publisher class to prevent abuse by injecting malicious files into a
* project.
*/
class Publisher extends BasePublisher
{
/**
* A list of allowed destinations with a (pseudo-)regex of allowed files for each destination. Attempts to publish
* to directories not in this list will result in a PublisherException. Files that do no fit the pattern will cause
* copy/merge to fail.
*
* @var array<string, string>
*/
public $restrictions = [
ROOTPATH => '*',
FCPATH => '#\.(s?css|js|map|html?|xml|json|webmanifest|ttf|eot|woff2?|gif|jpe?g|tiff?|png|webp|bmp|ico|svg)$#i',
];
}
<?php <?php
namespace Config; declare(strict_types=1);
// Create a new instance of our RouteCollection class. use CodeIgniter\Router\RouteCollection;
$routes = Services::routes();
// Load the system's routing file first, so that the app and ENVIRONMENT
// can override as needed.
if (file_exists(SYSTEMPATH . 'Config/Routes.php')) {
require SYSTEMPATH . 'Config/Routes.php';
}
/**
* --------------------------------------------------------------------
* Router Setup
* --------------------------------------------------------------------
*/
$routes->setDefaultNamespace('App\Controllers');
$routes->setDefaultController('Home');
$routes->setDefaultMethod('index');
$routes->setTranslateURIDashes(false);
$routes->set404Override();
$routes->setAutoRoute(false);
/** /**
* @var RouteCollection $routes
*
* -------------------------------------------------------------------- * --------------------------------------------------------------------
* Placeholder definitions * Placeholder definitions
* -------------------------------------------------------------------- * --------------------------------------------------------------------
*/ */
$routes->addPlaceholder('podcastName', '[a-zA-Z0-9\_]{1,32}'); $routes->addPlaceholder('podcastHandle', '[a-zA-Z0-9\_]{1,32}');
$routes->addPlaceholder('slug', '[a-zA-Z0-9\-]{1,191}'); $routes->addPlaceholder('slug', '[a-zA-Z0-9\-]{1,128}');
$routes->addPlaceholder('base64', '[A-Za-z0-9\.\_]+\-{0,2}'); $routes->addPlaceholder('base64', '[A-Za-z0-9\.\_]+\-{0,2}');
$routes->addPlaceholder('platformType', '\bpodcasting|\bsocial|\bfunding'); $routes->addPlaceholder('postAction', '\bfavourite|\breblog|\breply');
$routes->addPlaceholder('noteAction', '\bfavourite|\breblog|\breply'); $routes->addPlaceholder('embedTheme', '\blight|\bdark|\blight-transparent|\bdark-transparent');
$routes->addPlaceholder(
'embeddablePlayerTheme',
'\blight|\bdark|\blight-transparent|\bdark-transparent',
);
$routes->addPlaceholder( $routes->addPlaceholder(
'uuid', 'uuid',
'[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}', '[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}',
...@@ -49,735 +28,281 @@ $routes->addPlaceholder( ...@@ -49,735 +28,281 @@ $routes->addPlaceholder(
* -------------------------------------------------------------------- * --------------------------------------------------------------------
*/ */
// We get a performance increase by specifying the default $routes->get('manifest.webmanifest', 'WebmanifestController', [
// route since we don't have to scan directories. 'as' => 'webmanifest',
$routes->get('/', 'Home::index', ['as' => 'home']);
// Install Wizard route
$routes->group(config('App')->installGateway, function ($routes) {
$routes->get('/', 'Install', ['as' => 'install']);
$routes->post('instance-config', 'Install::attemptInstanceConfig', [
'as' => 'instance-config',
]);
$routes->post('database-config', 'Install::attemptDatabaseConfig', [
'as' => 'database-config',
]);
$routes->post('cache-config', 'Install::attemptCacheConfig', [
'as' => 'cache-config',
]);
$routes->post('create-superadmin', 'Install::attemptCreateSuperAdmin', [
'as' => 'create-superadmin',
]);
});
// Route for podcast audio file analytics (/audio/pack(podcast_id,episode_id,bytes_threshold,filesize,duration,date)/podcast_folder/filename.mp3)
$routes->head('audio/(:base64)/(:any)', 'Analytics::hit/$1/$2', [
'as' => 'analytics_hit',
]); ]);
$routes->get('audio/(:base64)/(:any)', 'Analytics::hit/$1/$2', [ $routes->get('themes/colors', 'ColorsController', [
'as' => 'analytics_hit', 'as' => 'themes-colors-css',
]); ]);
// Show the Unknown UserAgents // health check
$routes->get('.well-known/unknown-useragents', 'UnknownUserAgents'); $routes->get('/health', 'HomeController::health', [
$routes->get('.well-known/unknown-useragents/(:num)', 'UnknownUserAgents/$1'); 'as' => 'health',
]);
$routes->get('.well-known/platforms', 'Platform');
// Admin area
$routes->group(
config('App')->adminGateway,
['namespace' => 'App\Controllers\Admin'],
function ($routes) {
$routes->get('/', 'Home', [
'as' => 'admin',
]);
$routes->group('persons', function ($routes) {
$routes->get('/', 'Person', [
'as' => 'person-list',
'filter' => 'permission:person-list',
]);
$routes->get('new', 'Person::create', [
'as' => 'person-create',
'filter' => 'permission:person-create',
]);
$routes->post('new', 'Person::attemptCreate', [
'filter' => 'permission:person-create',
]);
$routes->group('(:num)', function ($routes) {
$routes->get('/', 'Person::view/$1', [
'as' => 'person-view',
'filter' => 'permission:person-view',
]);
$routes->get('edit', 'Person::edit/$1', [
'as' => 'person-edit',
'filter' => 'permission:person-edit',
]);
$routes->post('edit', 'Person::attemptEdit/$1', [
'filter' => 'permission:person-edit',
]);
$routes->add('delete', 'Person::delete/$1', [
'as' => 'person-delete',
'filter' => 'permission:person-delete',
]);
});
});
// Podcasts
$routes->group('podcasts', function ($routes) {
$routes->get('/', 'Podcast::list', [
'as' => 'podcast-list',
]);
$routes->get('new', 'Podcast::create', [
'as' => 'podcast-create',
'filter' => 'permission:podcasts-create',
]);
$routes->post('new', 'Podcast::attemptCreate', [
'filter' => 'permission:podcasts-create',
]);
$routes->get('import', 'PodcastImport', [
'as' => 'podcast-import',
'filter' => 'permission:podcasts-import',
]);
$routes->post('import', 'PodcastImport::attemptImport', [
'filter' => 'permission:podcasts-import',
]);
// Podcast
// Use ids in admin area to help permission and group lookups
$routes->group('(:num)', function ($routes) {
$routes->get('/', 'Podcast::view/$1', [
'as' => 'podcast-view',
'filter' => 'permission:podcasts-view,podcast-view',
]);
$routes->get('edit', 'Podcast::edit/$1', [
'as' => 'podcast-edit',
'filter' => 'permission:podcast-edit',
]);
$routes->post('edit', 'Podcast::attemptEdit/$1', [
'filter' => 'permission:podcast-edit',
]);
$routes->get('delete', 'Podcast::delete/$1', [
'as' => 'podcast-delete',
'filter' => 'permission:podcasts-delete',
]);
$routes->group('persons', function ($routes) {
$routes->get('/', 'PodcastPerson/$1', [
'as' => 'podcast-person-manage',
'filter' => 'permission:podcast-edit',
]);
$routes->post('/', 'PodcastPerson::attemptAdd/$1', [
'filter' => 'permission:podcast-edit',
]);
$routes->get(
'(:num)/remove',
'PodcastPerson::remove/$1/$2',
[
'as' => 'podcast-person-remove',
'filter' => 'permission:podcast-edit',
],
);
});
$routes->group('analytics', function ($routes) {
$routes->get('/', 'Podcast::viewAnalytics/$1', [
'as' => 'podcast-analytics',
'filter' => 'permission:podcasts-view,podcast-view',
]);
$routes->get(
'webpages',
'Podcast::viewAnalyticsWebpages/$1',
[
'as' => 'podcast-analytics-webpages',
'filter' => 'permission:podcasts-view,podcast-view',
],
);
$routes->get(
'locations',
'Podcast::viewAnalyticsLocations/$1',
[
'as' => 'podcast-analytics-locations',
'filter' => 'permission:podcasts-view,podcast-view',
],
);
$routes->get(
'unique-listeners',
'Podcast::viewAnalyticsUniqueListeners/$1',
[
'as' => 'podcast-analytics-unique-listeners',
'filter' => 'permission:podcasts-view,podcast-view',
],
);
$routes->get(
'listening-time',
'Podcast::viewAnalyticsListeningTime/$1',
[
'as' => 'podcast-analytics-listening-time',
'filter' => 'permission:podcasts-view,podcast-view',
],
);
$routes->get(
'time-periods',
'Podcast::viewAnalyticsTimePeriods/$1',
[
'as' => 'podcast-analytics-time-periods',
'filter' => 'permission:podcasts-view,podcast-view',
],
);
$routes->get(
'players',
'Podcast::viewAnalyticsPlayers/$1',
[
'as' => 'podcast-analytics-players',
'filter' => 'permission:podcasts-view,podcast-view',
],
);
});
$routes->get(
'analytics-data/(:segment)',
'AnalyticsData::getData/$1/$2',
[
'as' => 'analytics-full-data',
'filter' => 'permission:podcasts-view,podcast-view',
],
);
$routes->get(
'analytics-data/(:segment)/(:segment)',
'AnalyticsData::getData/$1/$2/$3',
[
'as' => 'analytics-data',
'filter' => 'permission:podcasts-view,podcast-view',
],
);
$routes->get(
'analytics-data/(:segment)/(:segment)/(:num)',
'AnalyticsData::getData/$1/$2/$3/$4',
[
'as' => 'analytics-filtered-data',
'filter' => 'permission:podcasts-view,podcast-view',
],
);
// Podcast episodes
$routes->group('episodes', function ($routes) {
$routes->get('/', 'Episode::list/$1', [
'as' => 'episode-list',
'filter' =>
'permission:episodes-list,podcast_episodes-list',
]);
$routes->get('new', 'Episode::create/$1', [
'as' => 'episode-create',
'filter' => 'permission:podcast_episodes-create',
]);
$routes->post('new', 'Episode::attemptCreate/$1', [
'filter' => 'permission:podcast_episodes-create',
]);
// Episode
$routes->group('(:num)', function ($routes) {
$routes->get('/', 'Episode::view/$1/$2', [
'as' => 'episode-view',
'filter' =>
'permission:episodes-view,podcast_episodes-view',
]);
$routes->get('edit', 'Episode::edit/$1/$2', [
'as' => 'episode-edit',
'filter' => 'permission:podcast_episodes-edit',
]);
$routes->post('edit', 'Episode::attemptEdit/$1/$2', [
'filter' => 'permission:podcast_episodes-edit',
]);
$routes->get('publish', 'Episode::publish/$1/$2', [
'as' => 'episode-publish',
'filter' =>
'permission:podcast-manage_publications',
]);
$routes->post(
'publish',
'Episode::attemptPublish/$1/$2',
[
'filter' =>
'permission:podcast-manage_publications',
],
);
$routes->get(
'publish-edit',
'Episode::publishEdit/$1/$2',
[
'as' => 'episode-publish_edit',
'filter' =>
'permission:podcast-manage_publications',
],
);
$routes->post(
'publish-edit',
'Episode::attemptPublishEdit/$1/$2',
[
'filter' =>
'permission:podcast-manage_publications',
],
);
$routes->get('unpublish', 'Episode::unpublish/$1/$2', [
'as' => 'episode-unpublish',
'filter' =>
'permission:podcast-manage_publications',
]);
$routes->post(
'unpublish',
'Episode::attemptUnpublish/$1/$2',
[
'filter' =>
'permission:podcast-manage_publications',
],
);
$routes->get('delete', 'Episode::delete/$1/$2', [
'as' => 'episode-delete',
'filter' => 'permission:podcast_episodes-delete',
]);
$routes->get(
'transcript-delete',
'Episode::transcriptDelete/$1/$2',
[
'as' => 'transcript-delete',
'filter' => 'permission:podcast_episodes-edit',
],
);
$routes->get(
'chapters-delete',
'Episode::chaptersDelete/$1/$2',
[
'as' => 'chapters-delete',
'filter' => 'permission:podcast_episodes-edit',
],
);
$routes->get(
'soundbites',
'Episode::soundbitesEdit/$1/$2',
[
'as' => 'soundbites-edit',
'filter' => 'permission:podcast_episodes-edit',
],
);
$routes->post(
'soundbites',
'Episode::soundbitesAttemptEdit/$1/$2',
[
'filter' => 'permission:podcast_episodes-edit',
],
);
$routes->get(
'soundbites/(:num)/delete',
'Episode::soundbiteDelete/$1/$2/$3',
[
'as' => 'soundbite-delete',
'filter' => 'permission:podcast_episodes-edit',
],
);
$routes->get(
'embeddable-player',
'Episode::embeddablePlayer/$1/$2',
[
'as' => 'embeddable-player-add',
'filter' => 'permission:podcast_episodes-edit',
],
);
$routes->group('persons', function ($routes) {
$routes->get('/', 'EpisodePerson/$1/$2', [
'as' => 'episode-person-manage',
'filter' => 'permission:podcast_episodes-edit',
]);
$routes->post(
'/',
'EpisodePerson::attemptAdd/$1/$2',
[
'filter' =>
'permission:podcast_episodes-edit',
],
);
$routes->get(
'(:num)/remove',
'EpisodePerson::remove/$1/$2/$3',
[
'as' => 'episode-person-remove',
'filter' =>
'permission:podcast_episodes-edit',
],
);
});
});
});
// Podcast contributors
$routes->group('contributors', function ($routes) {
$routes->get('/', 'Contributor::list/$1', [
'as' => 'contributor-list',
'filter' =>
'permission:podcasts-view,podcast-manage_contributors',
]);
$routes->get('add', 'Contributor::add/$1', [
'as' => 'contributor-add',
'filter' => 'permission:podcast-manage_contributors',
]);
$routes->post('add', 'Contributor::attemptAdd/$1', [
'filter' => 'permission:podcast-manage_contributors',
]);
// Contributor
$routes->group('(:num)', function ($routes) {
$routes->get('/', 'Contributor::view/$1/$2', [
'as' => 'contributor-view',
'filter' =>
'permission:podcast-manage_contributors',
]);
$routes->get('edit', 'Contributor::edit/$1/$2', [
'as' => 'contributor-edit',
'filter' =>
'permission:podcast-manage_contributors',
]);
$routes->post(
'edit',
'Contributor::attemptEdit/$1/$2',
[
'filter' =>
'permission:podcast-manage_contributors',
],
);
$routes->get('remove', 'Contributor::remove/$1/$2', [
'as' => 'contributor-remove',
'filter' =>
'permission:podcast-manage_contributors',
]);
});
});
$routes->group('platforms', function ($routes) {
$routes->get(
'/',
'PodcastPlatform::platforms/$1/podcasting',
[
'as' => 'platforms-podcasting',
'filter' => 'permission:podcast-manage_platforms',
],
);
$routes->get(
'social',
'PodcastPlatform::platforms/$1/social',
[
'as' => 'platforms-social',
'filter' => 'permission:podcast-manage_platforms',
],
);
$routes->get(
'funding',
'PodcastPlatform::platforms/$1/funding',
[
'as' => 'platforms-funding',
'filter' => 'permission:podcast-manage_platforms',
],
);
$routes->post(
'save/(:platformType)',
'PodcastPlatform::attemptPlatformsUpdate/$1/$2',
[
'as' => 'platforms-save',
'filter' => 'permission:podcast-manage_platforms',
],
);
$routes->get(
'(:slug)/podcast-platform-remove',
'PodcastPlatform::removePodcastPlatform/$1/$2',
[
'as' => 'podcast-platform-remove',
'filter' => 'permission:podcast-manage_platforms',
],
);
});
});
});
// Instance wide Fediverse config
$routes->group('fediverse', function ($routes) {
$routes->get('/', 'Fediverse::dashboard', [
'as' => 'fediverse-dashboard',
]);
$routes->get('blocked-actors', 'Fediverse::blockedActors', [
'as' => 'fediverse-blocked-actors',
'filter' => 'permission:fediverse-block_actors',
]);
$routes->get('blocked-domains', 'Fediverse::blockedDomains', [
'as' => 'fediverse-blocked-domains',
'filter' => 'permission:fediverse-block_domains',
]);
});
// Pages
$routes->group('pages', function ($routes) {
$routes->get('/', 'Page::list', ['as' => 'page-list']);
$routes->get('new', 'Page::create', [
'as' => 'page-create',
'filter' => 'permission:pages-manage',
]);
$routes->post('new', 'Page::attemptCreate', [
'filter' => 'permission:pages-manage',
]);
$routes->group('(:num)', function ($routes) {
$routes->get('/', 'Page::view/$1', ['as' => 'page-view']);
$routes->get('edit', 'Page::edit/$1', [
'as' => 'page-edit',
'filter' => 'permission:pages-manage',
]);
$routes->post('edit', 'Page::attemptEdit/$1', [
'filter' => 'permission:pages-manage',
]);
$routes->get('delete', 'Page::delete/$1', [
'as' => 'page-delete',
'filter' => 'permission:pages-manage',
]);
});
});
// Users
$routes->group('users', function ($routes) {
$routes->get('/', 'User::list', [
'as' => 'user-list',
'filter' => 'permission:users-list',
]);
$routes->get('new', 'User::create', [
'as' => 'user-create',
'filter' => 'permission:users-create',
]);
$routes->post('new', 'User::attemptCreate', [
'filter' => 'permission:users-create',
]);
// User
$routes->group('(:num)', function ($routes) {
$routes->get('/', 'User::view/$1', [
'as' => 'user-view',
'filter' => 'permission:users-view',
]);
$routes->get('edit', 'User::edit/$1', [
'as' => 'user-edit',
'filter' => 'permission:users-manage_authorizations',
]);
$routes->post('edit', 'User::attemptEdit/$1', [
'filter' => 'permission:users-manage_authorizations',
]);
$routes->get('ban', 'User::ban/$1', [
'as' => 'user-ban',
'filter' => 'permission:users-manage_bans',
]);
$routes->get('unban', 'User::unBan/$1', [
'as' => 'user-unban',
'filter' => 'permission:users-manage_bans',
]);
$routes->get('force-pass-reset', 'User::forcePassReset/$1', [
'as' => 'user-force_pass_reset',
'filter' => 'permission:users-force_pass_reset',
]);
$routes->get('delete', 'User::delete/$1', [
'as' => 'user-delete',
'filter' => 'permission:users-delete',
]);
});
});
// My account // We get a performance increase by specifying the default
$routes->group('my-account', function ($routes) { // route since we don't have to scan directories.
$routes->get('/', 'MyAccount', [ $routes->get('/', 'HomeController', [
'as' => 'my-account', 'as' => 'home',
]); ]);
$routes->get('change-password', 'MyAccount::changePassword/$1', [
'as' => 'change-password',
]);
$routes->post('change-password', 'MyAccount::attemptChange/$1');
});
},
);
/** $routes->get('.well-known/platforms', 'Platform');
* Overwriting Myth:auth routes file
*/
$routes->group(config('App')->authGateway, function ($routes) {
// Login/out
$routes->get('login', 'Auth::login', ['as' => 'login']);
$routes->post('login', 'Auth::attemptLogin');
$routes->get('logout', 'Auth::logout', ['as' => 'logout']);
// Registration service('auth')
$routes->get('register', 'Auth::register', [ ->routes($routes);
'as' => 'register',
]);
$routes->post('register', 'Auth::attemptRegister');
// Activation // Podcast's Public routes
$routes->get('activate-account', 'Auth::activateAccount', [ $routes->group('@(:podcastHandle)', static function ($routes): void {
'as' => 'activate-account', // override default Fediverse Library's actor route
]); $routes->options('/', 'ActivityPubController::preflight');
$routes->get('resend-activate-account', 'Auth::resendActivateAccount', [ $routes->get('/', 'PodcastController::activity/$1', [
'as' => 'resend-activate-account', 'as' => 'podcast-activity',
'alternate-content' => [
'application/activity+json' => [
'namespace' => 'Modules\Fediverse\Controllers',
'controller-method' => 'ActorController::index/$1',
],
'application/podcast-activity+json' => [
'namespace' => 'App\Controllers',
'controller-method' => 'PodcastController::podcastActor/$1',
],
'application/ld+json; profile="https://www.w3.org/ns/activitystreams' => [
'namespace' => 'Modules\Fediverse\Controllers',
'controller-method' => 'ActorController::index/$1',
],
],
'filter' => 'allow-cors',
]); ]);
$routes->get('manifest.webmanifest', 'WebmanifestController::podcastManifest/$1', [
// Forgot/Resets 'as' => 'podcast-webmanifest',
$routes->get('forgot', 'Auth::forgotPassword', [
'as' => 'forgot',
]); ]);
$routes->post('forgot', 'Auth::attemptForgot'); $routes->get('links', 'PodcastController::links/$1', [
$routes->get('reset-password', 'Auth::resetPassword', [ 'as' => 'podcast-links',
'as' => 'reset-password',
]); ]);
$routes->post('reset-password', 'Auth::attemptReset'); $routes->get('about', 'PodcastController::about/$1', [
}); 'as' => 'podcast-about',
// Podcast's Public routes
$routes->group('@(:podcastName)', function ($routes) {
$routes->get('/', 'Podcast::activity/$1', [
'as' => 'podcast-activity',
]); ]);
// override default ActivityPub Library's actor route $routes->options('episodes', 'ActivityPubController::preflight');
$routes->get('/', 'Podcast::activity/$1', [ $routes->get('episodes', 'PodcastController::episodes/$1', [
'as' => 'actor', 'as' => 'podcast-episodes',
'alternate-content' => [ 'alternate-content' => [
'application/activity+json' => [ 'application/activity+json' => [
'namespace' => 'ActivityPub\Controllers', 'namespace' => 'App\Controllers',
'controller-method' => 'ActorController/$1', 'controller-method' => 'PodcastController::episodeCollection/$1',
],
'application/podcast-activity+json' => [
'namespace' => 'App\Controllers',
'controller-method' => 'PodcastController::episodeCollection/$1',
], ],
'application/ld+json; profile="https://www.w3.org/ns/activitystreams' => [ 'application/ld+json; profile="https://www.w3.org/ns/activitystreams' => [
'namespace' => 'ActivityPub\Controllers', 'namespace' => 'App\Controllers',
'controller-method' => 'ActorController/$1', 'controller-method' => 'PodcastController::episodeCollection/$1',
], ],
], ],
'filter' => 'allow-cors',
]); ]);
$routes->get('episodes', 'Podcast::episodes/$1', [ $routes->group('episodes/(:slug)', static function ($routes): void {
'as' => 'podcast-episodes', $routes->options('/', 'ActivityPubController::preflight');
]); $routes->get('/', 'EpisodeController::index/$1/$2', [
$routes->group('episodes/(:slug)', function ($routes) { 'as' => 'episode',
$routes->get('/', 'Episode/$1/$2', [ 'alternate-content' => [
'as' => 'episode', 'application/activity+json' => [
'namespace' => 'App\Controllers',
'controller-method' => 'EpisodeController::episodeObject/$1/$2',
],
'application/podcast-activity+json' => [
'namespace' => 'App\Controllers',
'controller-method' => 'EpisodeController::episodeObject/$1/$2',
],
'application/ld+json; profile="https://www.w3.org/ns/activitystreams' => [
'namespace' => 'App\Controllers',
'controller-method' => 'EpisodeController::episodeObject/$1/$2',
],
],
'filter' => 'allow-cors',
]);
$routes->get('activity', 'EpisodeController::activity/$1/$2', [
'as' => 'episode-activity',
]);
$routes->get('chapters', 'EpisodeController::chapters/$1/$2', [
'as' => 'episode-chapters',
]); ]);
$routes->get('oembed.json', 'Episode::oembedJSON/$1/$2', [ $routes->get('transcript', 'EpisodeController::transcript/$1/$2', [
'as' => 'episode-transcript',
]);
$routes->options('comments', 'ActivityPubController::preflight');
$routes->get('comments', 'EpisodeController::comments/$1/$2', [
'as' => 'episode-comments',
'application/activity+json' => [
'controller-method' => 'EpisodeController::comments/$1/$2',
],
'application/podcast-activity+json' => [
'controller-method' => 'EpisodeController::comments/$1/$2',
],
'application/ld+json; profile="https://www.w3.org/ns/activitystreams' => [
'controller-method' => 'EpisodeController::comments/$1/$2',
],
'filter' => 'allow-cors',
]);
$routes->options('comments/(:uuid)', 'ActivityPubController::preflight');
$routes->get('comments/(:uuid)', 'EpisodeCommentController::view/$1/$2/$3', [
'as' => 'episode-comment',
'application/activity+json' => [
'controller-method' => 'EpisodeController::commentObject/$1/$2',
],
'application/podcast-activity+json' => [
'controller-method' => 'EpisodeController::commentObject/$1/$2',
],
'application/ld+json; profile="https://www.w3.org/ns/activitystreams' => [
'controller-method' => 'EpisodeController::commentObject/$1/$2',
],
'filter' => 'allow-cors',
]);
$routes->get('comments/(:uuid)/replies', 'EpisodeCommentController::replies/$1/$2/$3', [
'as' => 'episode-comment-replies',
]);
$routes->post('comments/(:uuid)/like', 'EpisodeCommentController::likeAction/$1/$2/$3', [
'as' => 'episode-comment-attempt-like',
]);
$routes->get('oembed.json', 'EpisodeController::oembedJSON/$1/$2', [
'as' => 'episode-oembed-json', 'as' => 'episode-oembed-json',
]); ]);
$routes->get('oembed.xml', 'Episode::oembedXML/$1/$2', [ $routes->get('oembed.xml', 'EpisodeController::oembedXML/$1/$2', [
'as' => 'episode-oembed-xml', 'as' => 'episode-oembed-xml',
]); ]);
$routes->group('embeddable-player', function ($routes) { $routes->group('embed', static function ($routes): void {
$routes->get('/', 'Episode::embeddablePlayer/$1/$2', [ $routes->get('/', 'EpisodeController::embed/$1/$2', [
'as' => 'embeddable-player', 'as' => 'embed',
]); ]);
$routes->get( $routes->get('(:embedTheme)', 'EpisodeController::embed/$1/$2/$3', [
'(:embeddablePlayerTheme)', 'as' => 'embed-theme',
'Episode::embeddablePlayer/$1/$2/$3', ],);
[
'as' => 'embeddable-player-theme',
],
);
}); });
}); });
$routes->head('feed.xml', 'FeedController::index/$1', [
$routes->head('feed.xml', 'Feed/$1', ['as' => 'podcast_feed']); 'as' => 'podcast-rss-feed',
$routes->get('feed.xml', 'Feed/$1', ['as' => 'podcast_feed']); ]);
$routes->get('feed.xml', 'FeedController::index/$1', [
'as' => 'podcast-rss-feed',
]);
$routes->head('feed', 'FeedController::index/$1');
$routes->get('feed', 'FeedController::index/$1');
}); });
// Other pages // audio routes
$routes->get('/credits', 'Page::credits', ['as' => 'credits']); $routes->head('/audio/@(:podcastHandle)/(:slug).(:alphanum)', 'EpisodeAudioController::index/$1/$2', [
$routes->get('/(:slug)', 'Page/$1', ['as' => 'page']); 'as' => 'episode-audio',
], );
$routes->get('/audio/@(:podcastHandle)/(:slug).(:alphanum)', 'EpisodeAudioController::index/$1/$2', [
'as' => 'episode-audio',
], );
// episode preview link
$routes->get('/p/(:uuid)', 'EpisodePreviewController::index/$1', [
'as' => 'episode-preview',
]);
$routes->get('/p/(:uuid)/activity', 'EpisodePreviewController::activity/$1', [
'as' => 'episode-preview-activity',
]);
$routes->get('/p/(:uuid)/chapters', 'EpisodePreviewController::chapters/$1', [
'as' => 'episode-preview-chapters',
]);
$routes->get('/p/(:uuid)/transcript', 'EpisodePreviewController::transcript/$1', [
'as' => 'episode-preview-transcript',
]);
// interacting as an actor // Other pages
$routes->post('interact-as-actor', 'Auth::attemptInteractAsActor', [ $routes->get('/credits', 'CreditsController', [
'as' => 'interact-as-actor', 'as' => 'credits',
]);
$routes->get('/map', 'MapController', [
'as' => 'map',
]);
$routes->get('/episodes-markers', 'MapController::getEpisodesMarkers', [
'as' => 'episodes-markers',
]);
$routes->get('/pages/(:slug)', 'PageController::index/$1', [
'as' => 'page',
]); ]);
/** /**
* Overwriting ActivityPub routes file * Overwriting Fediverse routes file
*/ */
$routes->group('@(:podcastName)', function ($routes) { $routes->group('@(:podcastHandle)', static function ($routes): void {
$routes->post('notes/new', 'Note::attemptCreate/$1', [ $routes->post('posts/new', 'PostController::createAction/$1', [
'as' => 'note-attempt-create', 'as' => 'post-attempt-create',
'filter' => 'permission:podcast-manage_publications', 'filter' => 'permission:podcast$1.manage-publications',
]); ]);
// Note // Post
$routes->group('notes/(:uuid)', function ($routes) { $routes->group('posts/(:uuid)', static function ($routes): void {
$routes->get('/', 'Note/$1/$2', [ $routes->options('/', 'ActivityPubController::preflight');
'as' => 'note', $routes->get('/', 'PostController::view/$1/$2', [
'as' => 'post',
'alternate-content' => [ 'alternate-content' => [
'application/activity+json' => [ 'application/activity+json' => [
'namespace' => 'ActivityPub\Controllers', 'namespace' => 'Modules\Fediverse\Controllers',
'controller-method' => 'NoteController/$2', 'controller-method' => 'PostController::index/$2',
], ],
'application/ld+json; profile="https://www.w3.org/ns/activitystreams' => [ 'application/ld+json; profile="https://www.w3.org/ns/activitystreams' => [
'namespace' => 'ActivityPub\Controllers', 'namespace' => 'Modules\Fediverse\Controllers',
'controller-method' => 'NoteController/$2', 'controller-method' => 'PostController::index/$2',
], ],
], ],
'filter' => 'allow-cors',
]); ]);
$routes->get('replies', 'Note/$1/$2', [ $routes->options('replies', 'ActivityPubController::preflight');
'as' => 'note-replies', $routes->get('replies', 'PostController::index/$1/$2', [
'as' => 'post-replies',
'alternate-content' => [ 'alternate-content' => [
'application/activity+json' => [ 'application/activity+json' => [
'namespace' => 'ActivityPub\Controllers', 'namespace' => 'Modules\Fediverse\Controllers',
'controller-method' => 'NoteController::replies/$2', 'controller-method' => 'PostController::replies/$2',
], ],
'application/ld+json; profile="https://www.w3.org/ns/activitystreams' => [ 'application/ld+json; profile="https://www.w3.org/ns/activitystreams' => [
'namespace' => 'ActivityPub\Controllers', 'namespace' => 'Modules\Fediverse\Controllers',
'controller-method' => 'NoteController::replies/$2', 'controller-method' => 'PostController::replies/$2',
], ],
], ],
'filter' => 'allow-cors',
]); ]);
// Actions // Actions
$routes->post('action', 'Note::attemptAction/$1/$2', [ $routes->post('action', 'PostController::action/$1/$2', [
'as' => 'note-attempt-action', 'as' => 'post-attempt-action',
'filter' => 'permission:podcast-interact_as', 'filter' => 'permission:podcast$1.interact-as',
]);
$routes->post('block-actor', 'Note::attemptBlockActor/$1/$2', [
'as' => 'note-attempt-block-actor',
'filter' => 'permission:fediverse-block_actors',
]);
$routes->post('block-domain', 'Note::attemptBlockDomain/$1/$2', [
'as' => 'note-attempt-block-domain',
'filter' => 'permission:fediverse-block_domains',
]); ]);
$routes->post('delete', 'Note::attemptDelete/$1/$2', [ $routes->post(
'as' => 'note-attempt-delete', 'block-actor',
'filter' => 'permission:podcast-manage_publications', 'PostController::blockActorAction/$1/$2',
]); [
'as' => 'post-attempt-block-actor',
$routes->get('remote/(:noteAction)', 'Note::remoteAction/$1/$2/$3', [ 'filter' => 'permission:fediverse.manage-blocks',
'as' => 'note-remote-action', ],
);
$routes->post(
'block-domain',
'PostController::blockDomainAction/$1/$2',
[
'as' => 'post-attempt-block-domain',
'filter' => 'permission:fediverse.manage-blocks',
],
);
$routes->post('delete', 'PostController::deleteAction/$1/$2', [
'as' => 'post-attempt-delete',
'filter' => 'permission:podcast$1.manage-publications',
]); ]);
$routes->get(
'remote/(:postAction)',
'PostController::remoteActionAction/$1/$2/$3',
[
'as' => 'post-remote-action',
],
);
}); });
$routes->get('follow', 'ActorController::followView/$1', [
$routes->get('follow', 'Actor::follow/$1', [
'as' => 'follow', 'as' => 'follow',
]); ]);
$routes->get('outbox', 'Actor::outbox/$1', [ $routes->get('outbox', 'ActorController::outbox/$1', [
'as' => 'outbox', 'as' => 'outbox',
'filter' => 'activity-pub:verify-activitystream', 'filter' => 'fediverse:verify-activitystream',
]); ]);
}); });
/*
* --------------------------------------------------------------------
* Additional Routing
* --------------------------------------------------------------------
*
* There will often be times that you need additional routing and you
* need it to be able to override any defaults in this file. Environment
* based routes is one such time. require() additional route files here
* to make that happen.
*
* You will have access to the $routes object within that file without
* needing to reload it.
*/
if (file_exists(APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php')) {
require APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php';
}