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
Select Git revision
  • alpha
  • beta
  • develop
  • docs/fix-readme
  • docs/update-vitepress
  • draft/rss-feed
  • feat/dashboard
  • feat/episodes-page-ux
  • feat/generator-user-agent
  • feat/headliner
  • feat/new-languages
  • feat/plugins
  • fix/federation
  • fix/forms-ux
  • i18n
  • main
  • next
  • refactor/transcripts
  • v1.0.0
  • v1.0.0-alpha.1
  • v1.0.0-alpha.10
  • v1.0.0-alpha.11
  • v1.0.0-alpha.12
  • v1.0.0-alpha.13
  • v1.0.0-alpha.14
  • v1.0.0-alpha.15
  • v1.0.0-alpha.16
  • v1.0.0-alpha.17
  • v1.0.0-alpha.18
  • v1.0.0-alpha.19
  • v1.0.0-alpha.2
  • v1.0.0-alpha.20
  • v1.0.0-alpha.21
  • v1.0.0-alpha.22
  • v1.0.0-alpha.23
  • v1.0.0-alpha.24
  • v1.0.0-alpha.25
  • v1.0.0-alpha.26
  • v1.0.0-alpha.27
  • v1.0.0-alpha.28
  • v1.0.0-alpha.29
  • v1.0.0-alpha.3
  • v1.0.0-alpha.30
  • v1.0.0-alpha.31
  • v1.0.0-alpha.32
  • v1.0.0-alpha.33
  • v1.0.0-alpha.34
  • v1.0.0-alpha.35
  • v1.0.0-alpha.36
  • v1.0.0-alpha.37
  • v1.0.0-alpha.38
  • v1.0.0-alpha.39
  • v1.0.0-alpha.4
  • v1.0.0-alpha.40
  • v1.0.0-alpha.41
  • v1.0.0-alpha.42
  • v1.0.0-alpha.43
  • v1.0.0-alpha.44
  • v1.0.0-alpha.45
  • v1.0.0-alpha.46
  • v1.0.0-alpha.47
  • v1.0.0-alpha.48
  • v1.0.0-alpha.49
  • v1.0.0-alpha.5
  • v1.0.0-alpha.50
  • v1.0.0-alpha.51
  • v1.0.0-alpha.52
  • v1.0.0-alpha.53
  • v1.0.0-alpha.54
  • v1.0.0-alpha.55
  • v1.0.0-alpha.56
  • v1.0.0-alpha.57
  • v1.0.0-alpha.58
  • v1.0.0-alpha.59
  • v1.0.0-alpha.6
  • v1.0.0-alpha.60
  • v1.0.0-alpha.61
  • v1.0.0-alpha.62
  • v1.0.0-alpha.63
  • v1.0.0-alpha.64
  • v1.0.0-alpha.65
  • v1.0.0-alpha.66
  • v1.0.0-alpha.67
  • v1.0.0-alpha.68
  • v1.0.0-alpha.69
  • v1.0.0-alpha.7
  • v1.0.0-alpha.70
  • v1.0.0-alpha.71
  • v1.0.0-alpha.72
  • v1.0.0-alpha.73
  • v1.0.0-alpha.74
  • v1.0.0-alpha.75
  • v1.0.0-alpha.76
  • v1.0.0-alpha.77
  • v1.0.0-alpha.78
  • v1.0.0-alpha.79
  • v1.0.0-alpha.8
  • v1.0.0-alpha.80
  • v1.0.0-alpha.9
  • v1.0.0-beta.1
  • v1.0.0-beta.10
  • v1.0.0-beta.11
  • v1.0.0-beta.12
  • v1.0.0-beta.13
  • v1.0.0-beta.14
  • v1.0.0-beta.15
  • v1.0.0-beta.16
  • v1.0.0-beta.17
  • v1.0.0-beta.18
  • v1.0.0-beta.19
  • v1.0.0-beta.2
  • v1.0.0-beta.20
  • v1.0.0-beta.21
  • v1.0.0-beta.22
  • v1.0.0-beta.23
  • v1.0.0-beta.24
  • v1.0.0-beta.3
  • v1.0.0-beta.4
118 results

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
Select Git revision
  • alpha
  • de
  • feat/external-chapters
  • feat/import-externals
  • main
  • master
  • refactor/base
  • v1.0.0-alpha.1
  • v1.0.0-alpha.10
  • v1.0.0-alpha.11
  • v1.0.0-alpha.12
  • v1.0.0-alpha.13
  • v1.0.0-alpha.14
  • v1.0.0-alpha.15
  • v1.0.0-alpha.16
  • v1.0.0-alpha.17
  • v1.0.0-alpha.18
  • v1.0.0-alpha.19
  • v1.0.0-alpha.2
  • v1.0.0-alpha.20
  • v1.0.0-alpha.21
  • v1.0.0-alpha.22
  • v1.0.0-alpha.23
  • v1.0.0-alpha.24
  • v1.0.0-alpha.25
  • v1.0.0-alpha.26
  • v1.0.0-alpha.27
  • v1.0.0-alpha.28
  • v1.0.0-alpha.29
  • v1.0.0-alpha.3
  • v1.0.0-alpha.30
  • v1.0.0-alpha.31
  • v1.0.0-alpha.32
  • v1.0.0-alpha.33
  • v1.0.0-alpha.34
  • v1.0.0-alpha.35
  • v1.0.0-alpha.36
  • v1.0.0-alpha.37
  • v1.0.0-alpha.38
  • v1.0.0-alpha.39
  • v1.0.0-alpha.4
  • v1.0.0-alpha.40
  • v1.0.0-alpha.41
  • v1.0.0-alpha.42
  • v1.0.0-alpha.43
  • v1.0.0-alpha.44
  • v1.0.0-alpha.45
  • v1.0.0-alpha.46
  • v1.0.0-alpha.47
  • v1.0.0-alpha.48
  • v1.0.0-alpha.49
  • v1.0.0-alpha.5
  • v1.0.0-alpha.50
  • v1.0.0-alpha.51
  • v1.0.0-alpha.52
  • v1.0.0-alpha.53
  • v1.0.0-alpha.54
  • v1.0.0-alpha.55
  • v1.0.0-alpha.56
  • v1.0.0-alpha.57
  • v1.0.0-alpha.6
  • v1.0.0-alpha.7
  • v1.0.0-alpha.8
  • v1.0.0-alpha.9
64 results
Show changes
Showing
with 1280 additions and 1358 deletions
<?php <?php
declare(strict_types=1);
namespace Config; namespace Config;
/** /**
* Holds the paths that are used by the system to * Holds the paths that are used by the system to locate the main directories, app, system, etc.
* locate the main directories, app, system, etc. *
* Modifying these allows you to re-structure 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.
*/ */
class Paths class Paths
{ {
/* /**
*--------------------------------------------------------------- * ---------------------------------------------------------------
* SYSTEM FOLDER NAME * SYSTEM FOLDER NAME
*--------------------------------------------------------------- * ---------------------------------------------------------------
* *
* This variable must contain the name of your "system" folder. * This must contain the name of your "system" folder. Include
* Include the path if the folder is not in the same directory * the path if the folder is not in the same directory as this file.
* as this file.
*/ */
public $systemDirectory = public string $systemDirectory =
__DIR__ . '/../../vendor/codeigniter4/codeigniter4/system'; __DIR__ . '/../../vendor/codeigniter4/framework/system';
/* /**
*--------------------------------------------------------------- * ---------------------------------------------------------------
* APPLICATION FOLDER NAME * APPLICATION FOLDER NAME
*--------------------------------------------------------------- * ---------------------------------------------------------------
* *
* 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. For more info please see the user guide: * you do, use a full server path.
* http://codeigniter.com/user_guide/general/managing_apps.html
* *
* NO TRAILING SLASH! * @see http://codeigniter.com/user_guide/general/managing_apps.html
*/ */
public $appDirectory = __DIR__ . '/..'; public string $appDirectory = __DIR__ . '/..';
/* /**
* --------------------------------------------------------------- * ---------------------------------------------------------------
* WRITABLE DIRECTORY NAME * WRITABLE DIRECTORY NAME
* --------------------------------------------------------------- * ---------------------------------------------------------------
...@@ -51,22 +51,18 @@ class Paths ...@@ -51,22 +51,18 @@ class Paths
* 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.
*/ */
public $writableDirectory = __DIR__ . '/../../writable'; public string $writableDirectory = __DIR__ . '/../../writable';
/* /**
* --------------------------------------------------------------- * ---------------------------------------------------------------
* TESTS DIRECTORY NAME * TESTS DIRECTORY NAME
* --------------------------------------------------------------- * ---------------------------------------------------------------
* *
* This variable must contain the name of your "tests" directory. * This variable must contain the name of your "tests" directory.
* The writable directory allows you to group all directories that
* need write permission to a single place that can be tucked away
* for maximum security, keeping it out of the app and/or
* system directories.
*/ */
public $testsDirectory = __DIR__ . '/../../tests'; public string $testsDirectory = __DIR__ . '/../../tests';
/* /**
* --------------------------------------------------------------- * ---------------------------------------------------------------
* VIEW DIRECTORY NAME * VIEW DIRECTORY NAME
* --------------------------------------------------------------- * ---------------------------------------------------------------
...@@ -74,7 +70,7 @@ class Paths ...@@ -74,7 +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')`.
*/ */
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,191}'); $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('postAction', '\bfavourite|\breblog|\breply');
$routes->addPlaceholder('embedTheme', '\blight|\bdark|\blight-transparent|\bdark-transparent');
$routes->addPlaceholder(
'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}',
);
/** /**
* -------------------------------------------------------------------- * --------------------------------------------------------------------
...@@ -38,405 +28,281 @@ $routes->addPlaceholder('slug', '[a-zA-Z0-9\-]{1,191}'); ...@@ -38,405 +28,281 @@ $routes->addPlaceholder('slug', '[a-zA-Z0-9\-]{1,191}');
* -------------------------------------------------------------------- * --------------------------------------------------------------------
*/ */
$routes->get('manifest.webmanifest', 'WebmanifestController', [
'as' => 'webmanifest',
]);
$routes->get('themes/colors', 'ColorsController', [
'as' => 'themes-colors-css',
]);
// health check
$routes->get('/health', 'HomeController::health', [
'as' => 'health',
]);
// We get a performance increase by specifying the default // We get a performance increase by specifying the default
// route since we don't have to scan directories. // route since we don't have to scan directories.
$routes->get('/', 'Home::index', ['as' => 'home']); $routes->get('/', 'HomeController', [
'as' => 'home',
// Install Wizard route ]);
$routes->group(config('App')->installGateway, function ($routes) {
$routes->get('/', 'Install', ['as' => 'install']); $routes->get('.well-known/platforms', 'Platform');
$routes->post('instance-config', 'Install::attemptInstanceConfig', [
'as' => 'instance-config', service('auth')
->routes($routes);
// Podcast's Public routes
$routes->group('@(:podcastHandle)', static function ($routes): void {
// override default Fediverse Library's actor route
$routes->options('/', 'ActivityPubController::preflight');
$routes->get('/', 'PodcastController::activity/$1', [
'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->post('database-config', 'Install::attemptDatabaseConfig', [ $routes->get('manifest.webmanifest', 'WebmanifestController::podcastManifest/$1', [
'as' => 'database-config', 'as' => 'podcast-webmanifest',
]); ]);
$routes->post('cache-config', 'Install::attemptCacheConfig', [ $routes->get('links', 'PodcastController::links/$1', [
'as' => 'cache-config', 'as' => 'podcast-links',
]); ]);
$routes->post('create-superadmin', 'Install::attemptCreateSuperAdmin', [ $routes->get('about', 'PodcastController::about/$1', [
'as' => 'create-superadmin', 'as' => 'podcast-about',
]); ]);
}); $routes->options('episodes', 'ActivityPubController::preflight');
$routes->get('episodes', 'PodcastController::episodes/$1', [
// Route for podcast audio file analytics (/audio/podcast_id/episode_id/bytes_threshold/filesize/podcast_folder/filename.mp3) 'as' => 'podcast-episodes',
$routes->add( 'alternate-content' => [
'audio/(:num)/(:num)/(:num)/(:num)/(:any)', 'application/activity+json' => [
'Analytics::hit/$1/$2/$3/$4/$5', 'namespace' => 'App\Controllers',
[ 'controller-method' => 'PodcastController::episodeCollection/$1',
'as' => 'analytics_hit', ],
] 'application/podcast-activity+json' => [
); 'namespace' => 'App\Controllers',
'controller-method' => 'PodcastController::episodeCollection/$1',
// Show the Unknown UserAgents ],
$routes->get('.well-known/unknown-useragents', 'UnknownUserAgents'); 'application/ld+json; profile="https://www.w3.org/ns/activitystreams' => [
$routes->get('.well-known/unknown-useragents/(:num)', 'UnknownUserAgents/$1'); 'namespace' => 'App\Controllers',
'controller-method' => 'PodcastController::episodeCollection/$1',
// Admin area ],
$routes->group( ],
config('App')->adminGateway, 'filter' => 'allow-cors',
['namespace' => 'App\Controllers\Admin'], ]);
function ($routes) { $routes->group('episodes/(:slug)', static function ($routes): void {
$routes->get('/', 'Home', [ $routes->options('/', 'ActivityPubController::preflight');
'as' => 'admin', $routes->get('/', 'EpisodeController::index/$1/$2', [
'as' => 'episode',
'alternate-content' => [
'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', [
$routes->get('my-podcasts', 'Podcast::myPodcasts', [ 'as' => 'episode-activity',
'as' => 'my-podcasts',
]); ]);
$routes->get('chapters', 'EpisodeController::chapters/$1/$2', [
// Podcasts 'as' => 'episode-chapters',
$routes->group('podcasts', function ($routes) { ]);
$routes->get('/', 'Podcast::list', [ $routes->get('transcript', 'EpisodeController::transcript/$1/$2', [
'as' => 'podcast-list', 'as' => 'episode-transcript',
]); ]);
$routes->get('new', 'Podcast::create', [ $routes->options('comments', 'ActivityPubController::preflight');
'as' => 'podcast-create', $routes->get('comments', 'EpisodeController::comments/$1/$2', [
'filter' => 'permission:podcasts-create', 'as' => 'episode-comments',
]); 'application/activity+json' => [
$routes->post('new', 'Podcast::attemptCreate', [ 'controller-method' => 'EpisodeController::comments/$1/$2',
'filter' => 'permission:podcasts-create', ],
]); 'application/podcast-activity+json' => [
$routes->get('import', 'Podcast::import', [ 'controller-method' => 'EpisodeController::comments/$1/$2',
'as' => 'podcast-import', ],
'filter' => 'permission:podcasts-import', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams' => [
]); 'controller-method' => 'EpisodeController::comments/$1/$2',
$routes->post('import', 'Podcast::attemptImport', [ ],
'filter' => 'permission:podcasts-import', 'filter' => 'allow-cors',
]); ]);
$routes->options('comments/(:uuid)', 'ActivityPubController::preflight');
// Podcast $routes->get('comments/(:uuid)', 'EpisodeCommentController::view/$1/$2/$3', [
// Use ids in admin area to help permission and group lookups 'as' => 'episode-comment',
$routes->group('(:num)', function ($routes) { 'application/activity+json' => [
$routes->get('/', 'Podcast::view/$1', [ 'controller-method' => 'EpisodeController::commentObject/$1/$2',
'as' => 'podcast-view', ],
'filter' => 'permission:podcasts-view,podcast-view', 'application/podcast-activity+json' => [
]); 'controller-method' => 'EpisodeController::commentObject/$1/$2',
$routes->get('edit', 'Podcast::edit/$1', [ ],
'as' => 'podcast-edit', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams' => [
'filter' => 'permission:podcast-edit', 'controller-method' => 'EpisodeController::commentObject/$1/$2',
]); ],
$routes->post('edit', 'Podcast::attemptEdit/$1', [ 'filter' => 'allow-cors',
'filter' => 'permission:podcast-edit', ]);
]); $routes->get('comments/(:uuid)/replies', 'EpisodeCommentController::replies/$1/$2/$3', [
$routes->add('delete', 'Podcast::delete/$1', [ 'as' => 'episode-comment-replies',
'as' => 'podcast-delete', ]);
'filter' => 'permission:podcasts-delete', $routes->post('comments/(:uuid)/like', 'EpisodeCommentController::likeAction/$1/$2/$3', [
]); 'as' => 'episode-comment-attempt-like',
]);
$routes->group('analytics', function ($routes) { $routes->get('oembed.json', 'EpisodeController::oembedJSON/$1/$2', [
$routes->get('/', 'Podcast::viewAnalytics/$1', [ 'as' => 'episode-oembed-json',
'as' => 'podcast-analytics', ]);
'filter' => 'permission:podcasts-view,podcast-view', $routes->get('oembed.xml', 'EpisodeController::oembedXML/$1/$2', [
]); 'as' => 'episode-oembed-xml',
$routes->get( ]);
'webpages', $routes->group('embed', static function ($routes): void {
'Podcast::viewAnalyticsWebpages/$1', $routes->get('/', 'EpisodeController::embed/$1/$2', [
[ 'as' => 'embed',
'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(
'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->add('delete', 'Episode::delete/$1/$2', [
'as' => 'episode-delete',
'filter' => 'permission:podcast_episodes-delete',
]);
});
});
// 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->add('remove', 'Contributor::remove/$1/$2', [
'as' => 'contributor-remove',
'filter' =>
'permission:podcast-manage_contributors',
]);
});
});
$routes->group('settings', function ($routes) {
$routes->get('/', 'PodcastSettings/$1', [
'as' => 'podcast-settings',
]);
$routes->get('platforms', 'PodcastSettings::platforms/$1', [
'as' => 'platforms',
'filter' => 'permission:podcast-manage_platforms',
]);
$routes->post(
'platforms',
'PodcastSettings::attemptPlatformsUpdate/$1',
['filter' => 'permission:podcast-manage_platforms']
);
$routes->add(
'platforms/(:num)/remove-link',
'PodcastSettings::removePlatformLink/$1/$2',
[
'as' => 'platforms-remove',
'filter' => 'permission:podcast-manage_platforms',
]
);
});
});
});
// 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->add('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',
]); ]);
$routes->get('(:embedTheme)', 'EpisodeController::embed/$1/$2/$3', [
// User 'as' => 'embed-theme',
$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->add('ban', 'User::ban/$1', [
'as' => 'user-ban',
'filter' => 'permission:users-manage_bans',
]);
$routes->add('unban', 'User::unBan/$1', [
'as' => 'user-unban',
'filter' => 'permission:users-manage_bans',
]);
$routes->add('force-pass-reset', 'User::forcePassReset/$1', [
'as' => 'user-force_pass_reset',
'filter' => 'permission:users-force_pass_reset',
]);
$routes->add('delete', 'User::delete/$1', [
'as' => 'user-delete',
'filter' => 'permission:users-delete',
]);
});
}); });
});
$routes->head('feed.xml', 'FeedController::index/$1', [
'as' => 'podcast-rss-feed',
]);
$routes->get('feed.xml', 'FeedController::index/$1', [
'as' => 'podcast-rss-feed',
]);
$routes->head('feed', 'FeedController::index/$1');
$routes->get('feed', 'FeedController::index/$1');
});
// My account // audio routes
$routes->group('my-account', function ($routes) { $routes->head('/audio/@(:podcastHandle)/(:slug).(:alphanum)', 'EpisodeAudioController::index/$1/$2', [
$routes->get('/', 'MyAccount', [ 'as' => 'episode-audio',
'as' => 'my-account', ], );
]); $routes->get('/audio/@(:podcastHandle)/(:slug).(:alphanum)', 'EpisodeAudioController::index/$1/$2', [
$routes->get('change-password', 'MyAccount::changePassword/$1', [ 'as' => 'episode-audio',
'as' => 'change-password', ], );
]);
$routes->post('change-password', 'MyAccount::attemptChange/$1'); // 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',
]);
// Other pages
$routes->get('/credits', 'CreditsController', [
'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 Myth:auth routes file * Overwriting Fediverse routes file
*/ */
$routes->group(config('App')->authGateway, function ($routes) { $routes->group('@(:podcastHandle)', static function ($routes): void {
// Login/out $routes->post('posts/new', 'PostController::createAction/$1', [
$routes->get('login', 'Auth::login', ['as' => 'login']); 'as' => 'post-attempt-create',
$routes->post('login', 'Auth::attemptLogin'); 'filter' => 'permission:podcast$1.manage-publications',
$routes->get('logout', 'Auth::logout', ['as' => 'logout']);
// Registration
$routes->get('register', 'Auth::register', [
'as' => 'register',
]);
$routes->post('register', 'Auth::attemptRegister');
// Activation
$routes->get('activate-account', 'Auth::activateAccount', [
'as' => 'activate-account',
]); ]);
$routes->get('resend-activate-account', 'Auth::resendActivateAccount', [ // Post
'as' => 'resend-activate-account', $routes->group('posts/(:uuid)', static function ($routes): void {
]); $routes->options('/', 'ActivityPubController::preflight');
$routes->get('/', 'PostController::view/$1/$2', [
// Forgot/Resets 'as' => 'post',
$routes->get('forgot', 'Auth::forgotPassword', [ 'alternate-content' => [
'as' => 'forgot', 'application/activity+json' => [
'namespace' => 'Modules\Fediverse\Controllers',
'controller-method' => 'PostController::index/$2',
],
'application/ld+json; profile="https://www.w3.org/ns/activitystreams' => [
'namespace' => 'Modules\Fediverse\Controllers',
'controller-method' => 'PostController::index/$2',
],
],
'filter' => 'allow-cors',
]);
$routes->options('replies', 'ActivityPubController::preflight');
$routes->get('replies', 'PostController::index/$1/$2', [
'as' => 'post-replies',
'alternate-content' => [
'application/activity+json' => [
'namespace' => 'Modules\Fediverse\Controllers',
'controller-method' => 'PostController::replies/$2',
],
'application/ld+json; profile="https://www.w3.org/ns/activitystreams' => [
'namespace' => 'Modules\Fediverse\Controllers',
'controller-method' => 'PostController::replies/$2',
],
],
'filter' => 'allow-cors',
]);
// Actions
$routes->post('action', 'PostController::action/$1/$2', [
'as' => 'post-attempt-action',
'filter' => 'permission:podcast$1.interact-as',
]);
$routes->post(
'block-actor',
'PostController::blockActorAction/$1/$2',
[
'as' => 'post-attempt-block-actor',
'filter' => 'permission:fediverse.manage-blocks',
],
);
$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', [
'as' => 'follow',
]); ]);
$routes->post('forgot', 'Auth::attemptForgot'); $routes->get('outbox', 'ActorController::outbox/$1', [
$routes->get('reset-password', 'Auth::resetPassword', [ 'as' => 'outbox',
'as' => 'reset-password', 'filter' => 'fediverse:verify-activitystream',
]); ]);
$routes->post('reset-password', 'Auth::attemptReset');
}); });
// Public routes
$routes->group('@(:podcastName)', function ($routes) {
$routes->get('/', 'Podcast/$1', ['as' => 'podcast']);
$routes->get('(:slug)', 'Episode/$1/$2', [
'as' => 'episode',
]);
$routes->get('feed.xml', 'Feed/$1', ['as' => 'podcast_feed']);
});
$routes->get('/(:slug)', 'Page/$1', ['as' => 'page']);
/**
* --------------------------------------------------------------------
* Additional Routing
* --------------------------------------------------------------------
*
* There will often be times that you need additional routing and you
* need to it 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';
}
<?php
declare(strict_types=1);
namespace Config;
use CodeIgniter\Config\Routing as BaseRouting;
/**
* Routing configuration
*/
class Routing extends BaseRouting
{
/**
* For Defined Routes.
* An array of files that contain route definitions.
* Route files are read in order, with the first match
* found taking precedence.
*
* Default: APPPATH . 'Config/Routes.php'
*
* @var list<string>
*/
public array $routeFiles = [
APPPATH . 'Config/Routes.php',
ROOTPATH . 'modules/Admin/Config/Routes.php',
ROOTPATH . 'modules/Analytics/Config/Routes.php',
ROOTPATH . 'modules/Api/Rest/V1/Config/Routes.php',
ROOTPATH . 'modules/Auth/Config/Routes.php',
ROOTPATH . 'modules/Fediverse/Config/Routes.php',
ROOTPATH . 'modules/Install/Config/Routes.php',
ROOTPATH . 'modules/Platforms/Config/Routes.php',
ROOTPATH . 'modules/PodcastImport/Config/Routes.php',
ROOTPATH . 'modules/PremiumPodcasts/Config/Routes.php',
];
/**
* For Defined Routes and Auto Routing.
* The default namespace to use for Controllers when no other
* namespace has been specified.
*
* Default: 'App\Controllers'
*/
public string $defaultNamespace = 'App\Controllers';
/**
* For Auto Routing.
* The default controller to use when no other controller has been
* specified.
*
* Default: 'Home'
*/
public string $defaultController = 'HomeController';
/**
* For Defined Routes and Auto Routing.
* The default method to call on the controller when no other
* method has been set in the route.
*
* Default: 'index'
*/
public string $defaultMethod = 'index';
/**
* For Auto Routing.
* Whether to translate dashes in URIs for controller/method to underscores.
* Primarily useful when using the auto-routing.
*
* Default: false
*/
public bool $translateURIDashes = false;
/**
* Sets the class/method that should be called if routing doesn't
* find a match. It can be the controller/method name like: Users::index
*
* This setting is passed to the Router class and handled there.
*
* If you want to use a closure, you will have to set it in the
* routes file by calling:
*
* $routes->set404Override(function() {
* // Do something here
* });
*
* Example:
* public $override404 = 'App\Errors::show404';
*/
public ?string $override404 = null;
/**
* If TRUE, the system will attempt to match the URI against
* Controllers by matching each segment against folders/files
* in APPPATH/Controllers, when a match wasn't found against
* defined routes.
*
* If FALSE, will stop searching and do NO automatic routing.
*/
public bool $autoRoute = false;
/**
* For Defined Routes.
* If TRUE, will enable the use of the 'prioritize' option
* when defining routes.
*
* Default: false
*/
public bool $prioritize = false;
/**
* For Defined Routes.
* If TRUE, matched multiple URI segments will be passed as one parameter.
*
* Default: false
*/
public bool $multipleSegmentsOneParam = false;
/**
* For Auto Routing (Improved).
* Map of URI segments and namespaces.
*
* The key is the first URI segment. The value is the controller namespace.
* E.g.,
* [
* 'blog' => 'Acme\Blog\Controllers',
* ]
*
* @var array<string, string> [ uri_segment => namespace ]
*/
public array $moduleRoutes = [];
/**
* For Auto Routing (Improved).
* Whether to translate dashes in URIs for controller/method to CamelCase.
* E.g., blog-controller -> BlogController
*
* If you enable this, $translateURIDashes is ignored.
*
* Default: false
*/
public bool $translateUriToCamelCase = true;
}
<?php
declare(strict_types=1);
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Security extends BaseConfig
{
/**
* --------------------------------------------------------------------------
* CSRF Protection Method
* --------------------------------------------------------------------------
*
* Protection Method for Cross Site Request Forgery protection.
*
* @var 'cookie'|'session'
*/
public string $csrfProtection = 'session';
/**
* --------------------------------------------------------------------------
* CSRF Token Randomization
* --------------------------------------------------------------------------
*
* Randomize the CSRF Token for added security.
*/
public bool $tokenRandomize = true;
/**
* --------------------------------------------------------------------------
* CSRF Token Name
* --------------------------------------------------------------------------
*
* Token name for Cross Site Request Forgery protection.
*/
public string $tokenName = 'csrf_test_name';
/**
* --------------------------------------------------------------------------
* CSRF Header Name
* --------------------------------------------------------------------------
*
* Header name for Cross Site Request Forgery protection.
*/
public string $headerName = 'X-CSRF-TOKEN';
/**
* --------------------------------------------------------------------------
* CSRF Cookie Name
* --------------------------------------------------------------------------
*
* Cookie name for Cross Site Request Forgery protection.
*/
public string $cookieName = 'csrf_cookie_name';
/**
* --------------------------------------------------------------------------
* CSRF Expires
* --------------------------------------------------------------------------
*
* Expiration time for Cross Site Request Forgery protection cookie.
*
* Defaults to two hours (in seconds).
*/
public int $expires = 7200;
/**
* --------------------------------------------------------------------------
* CSRF Regenerate
* --------------------------------------------------------------------------
*
* Regenerate CSRF Token on every submission.
*/
public bool $regenerate = true;
/**
* --------------------------------------------------------------------------
* CSRF Redirect
* --------------------------------------------------------------------------
*
* @see https://codeigniter4.github.io/userguide/libraries/security.html#redirection-on-failure
*/
public bool $redirect = (ENVIRONMENT === 'production');
}
<?php <?php
declare(strict_types=1);
namespace Config; namespace Config;
use CodeIgniter\Config\Services as CoreServices;
use CodeIgniter\Model;
use App\Authorization\FlatAuthorization;
use App\Authorization\PermissionModel;
use App\Authorization\GroupModel;
use App\Libraries\Breadcrumb; use App\Libraries\Breadcrumb;
use App\Models\UserModel; use App\Libraries\HtmlHead;
use Myth\Auth\Models\LoginModel; use App\Libraries\Negotiate;
use App\Libraries\Router;
require_once SYSTEMPATH . 'Config/Services.php'; use CodeIgniter\Config\BaseService;
use CodeIgniter\HTTP\Request;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\Router\RouteCollectionInterface;
/** /**
* Services Configuration file. * Services Configuration file.
* *
* Services are simply other classes/libraries that the system uses * Services are simply other classes/libraries that the system uses to do its job. This is used by CodeIgniter to allow
* to do its job. This is used by CodeIgniter to allow the core of the * the core of the framework to be swapped out easily without affecting the usage within the rest of your application.
* framework to be swapped out easily without affecting the usage within
* the rest of your application.
* *
* This file holds any application-specific services, or service overrides * This file holds any application-specific services, or service overrides that you might need. An example has been
* that you might need. An example has been included with the general * included with the general method format you should use for your service methods. For more examples, see the core
* method format you should use for your service methods. For more examples, * Services file at system/Config/Services.php.
* see the core Services file at system/Config/Services.php.
*/ */
class Services extends CoreServices class Services extends BaseService
{ {
public static function authentication( /**
string $lib = 'local', * The Router class uses a RouteCollection's array of routes, and determines the correct Controller and Method to
Model $userModel = null, * execute.
Model $loginModel = null, */
bool $getShared = true public static function router(
) { ?RouteCollectionInterface $routes = null,
?Request $request = null,
bool $getShared = true,
): Router {
if ($getShared) { if ($getShared) {
return self::getSharedInstance( return static::getSharedInstance('router', $routes, $request);
'authentication',
$lib,
$userModel,
$loginModel
);
}
// config() checks first in app/Config
$config = config('Auth');
$class = $config->authenticationLibs[$lib];
$instance = new $class($config);
if (empty($userModel)) {
$userModel = new UserModel();
} }
if (empty($loginModel)) { $routes ??= static::routes();
$loginModel = new LoginModel(); $request ??= static::request();
}
return $instance->setUserModel($userModel)->setLoginModel($loginModel); return new Router($routes, $request);
} }
public static function authorization( /**
Model $groupModel = null, * The Negotiate class provides the content negotiation features for working the request to determine correct
Model $permissionModel = null, * language, encoding, charset, and more.
Model $userModel = null, */
bool $getShared = true public static function negotiator(?RequestInterface $request = null, bool $getShared = true): Negotiate
) { {
if ($getShared) { if ($getShared) {
return self::getSharedInstance( return static::getSharedInstance('negotiator', $request);
'authorization',
$groupModel,
$permissionModel,
$userModel
);
} }
if (is_null($groupModel)) { $request ??= static::request();
$groupModel = new GroupModel();
}
if (is_null($permissionModel)) { return new Negotiate($request);
$permissionModel = new PermissionModel(); }
}
$instance = new FlatAuthorization($groupModel, $permissionModel);
if (is_null($userModel)) { public static function breadcrumb(bool $getShared = true): Breadcrumb
$userModel = new UserModel(); {
if ($getShared) {
return self::getSharedInstance('breadcrumb');
} }
return $instance->setUserModel($userModel); return new Breadcrumb();
} }
public static function breadcrumb(bool $getShared = true) public static function html_head(bool $getShared = true): HtmlHead
{ {
if ($getShared) { if ($getShared) {
return self::getSharedInstance('breadcrumb'); return self::getSharedInstance('html_head');
} }
return new Breadcrumb(); return new HtmlHead();
} }
} }
<?php
declare(strict_types=1);
namespace Config;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Session\Handlers\BaseHandler;
use CodeIgniter\Session\Handlers\FileHandler;
class Session extends BaseConfig
{
/**
* --------------------------------------------------------------------------
* Session Driver
* --------------------------------------------------------------------------
*
* The session storage driver to use:
* - `CodeIgniter\Session\Handlers\FileHandler`
* - `CodeIgniter\Session\Handlers\DatabaseHandler`
* - `CodeIgniter\Session\Handlers\MemcachedHandler`
* - `CodeIgniter\Session\Handlers\RedisHandler`
*
* @var class-string<BaseHandler>
*/
public string $driver = FileHandler::class;
/**
* --------------------------------------------------------------------------
* Session Cookie Name
* --------------------------------------------------------------------------
*
* The session cookie name, must contain only [0-9a-z_-] characters
*/
public string $cookieName = 'ci_session';
/**
* --------------------------------------------------------------------------
* Session Expiration
* --------------------------------------------------------------------------
*
* The number of SECONDS you want the session to last.
* Setting to 0 (zero) means expire when the browser is closed.
*/
public int $expiration = 7200;
/**
* --------------------------------------------------------------------------
* Session Save Path
* --------------------------------------------------------------------------
*
* The location to save sessions to and is driver dependent.
*
* For the 'files' driver, it's a path to a writable directory.
* WARNING: Only absolute paths are supported!
*
* For the 'database' driver, it's a table name.
* Please read up the manual for the format with other session drivers.
*
* IMPORTANT: You are REQUIRED to set a valid save path!
*/
public string $savePath = WRITEPATH . 'session';
/**
* --------------------------------------------------------------------------
* Session Match IP
* --------------------------------------------------------------------------
*
* Whether to match the user's IP address when reading the session data.
*
* WARNING: If you're using the database driver, don't forget to update
* your session table's PRIMARY KEY when changing this setting.
*/
public bool $matchIP = false;
/**
* --------------------------------------------------------------------------
* Session Time to Update
* --------------------------------------------------------------------------
*
* How many seconds between CI regenerating the session ID.
*/
public int $timeToUpdate = 300;
/**
* --------------------------------------------------------------------------
* Session Regenerate Destroy
* --------------------------------------------------------------------------
*
* Whether to destroy session data associated with the old session ID
* when auto-regenerating the session ID. When set to FALSE, the data
* will be later deleted by the garbage collector.
*/
public bool $regenerateDestroy = false;
/**
* --------------------------------------------------------------------------
* Session Database Group
* --------------------------------------------------------------------------
*
* DB Group for the database session.
*/
public ?string $DBGroup = null;
/**
* --------------------------------------------------------------------------
* Lock Retry Interval (microseconds)
* --------------------------------------------------------------------------
*
* This is used for RedisHandler.
*
* Time (microseconds) to wait if lock cannot be acquired.
* The default is 100,000 microseconds (= 0.1 seconds).
*/
public int $lockRetryInterval = 100_000;
/**
* --------------------------------------------------------------------------
* Lock Max Retries
* --------------------------------------------------------------------------
*
* This is used for RedisHandler.
*
* Maximum number of lock acquisition attempts.
* The default is 300 times. That is lock timeout is about 30 (0.1 * 300)
* seconds.
*/
public int $lockMaxRetries = 300;
}
<?php
declare(strict_types=1);
namespace Config;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Tasks\Scheduler;
class Tasks extends BaseConfig
{
/**
* --------------------------------------------------------------------------
* Should performance metrics be logged
* --------------------------------------------------------------------------
*
* If true, will log the time it takes for each task to run.
* Requires the settings table to have been created previously.
*/
public bool $logPerformance = false;
/**
* --------------------------------------------------------------------------
* Maximum performance logs
* --------------------------------------------------------------------------
*
* The maximum number of logs that should be saved per Task.
* Lower numbers reduced the amount of database required to
* store the logs.
*/
public int $maxLogsPerTask = 10;
/**
* Register any tasks within this method for the application.
* Called by the TaskRunner.
*/
public function init(Scheduler $schedule): void
{
$schedule->command('fediverse:broadcast')
->everyMinute()
->named('fediverse-broadcast');
$schedule->command('websub:publish')
->everyMinute()
->named('websub-publish');
$schedule->command('video-clips:generate')
->everyMinute()
->named('video-clips-generate');
$schedule->command('podcast:import')
->everyMinute()
->named('podcast-import');
$schedule->command('episodes:compute-downloads')
->everyHour()
->named('episodes:compute-downloads');
}
}
<?php <?php
declare(strict_types=1);
namespace Config; namespace Config;
use CodeIgniter\Config\BaseConfig; use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Debug\Toolbar\Collectors\Database;
use CodeIgniter\Debug\Toolbar\Collectors\Events;
use CodeIgniter\Debug\Toolbar\Collectors\Files;
use CodeIgniter\Debug\Toolbar\Collectors\Logs;
use CodeIgniter\Debug\Toolbar\Collectors\Routes;
use CodeIgniter\Debug\Toolbar\Collectors\Timers;
use CodeIgniter\Debug\Toolbar\Collectors\Views;
/**
* --------------------------------------------------------------------------
* Debug Toolbar
* --------------------------------------------------------------------------
*
* The Debug Toolbar provides a way to see information about the performance
* and state of your application during that page display. By default it will
* NOT be displayed under production environments, and will only display if
* `CI_DEBUG` is true, since if it's not, there's not much to display anyway.
*/
class Toolbar extends BaseConfig class Toolbar extends BaseConfig
{ {
/* /**
|-------------------------------------------------------------------------- * --------------------------------------------------------------------------
| Debug Toolbar * Toolbar Collectors
|-------------------------------------------------------------------------- * --------------------------------------------------------------------------
| The Debug Toolbar provides a way to see information about the performance *
| and state of your application during that page display. By default it will * List of toolbar collectors that will be called when Debug Toolbar
| NOT be displayed under production environments, and will only display if * fires up and collects data from.
| CI_DEBUG is true, since if it's not, there's not much to display anyway. *
| * @var list<class-string>
| toolbarMaxHistory = Number of history files, 0 for none or -1 for unlimited */
| public array $collectors = [
*/ Timers::class,
public $collectors = [ Database::class,
\CodeIgniter\Debug\Toolbar\Collectors\Timers::class, Logs::class,
\CodeIgniter\Debug\Toolbar\Collectors\Database::class, Views::class,
\CodeIgniter\Debug\Toolbar\Collectors\Logs::class, // Cache::class,
\CodeIgniter\Debug\Toolbar\Collectors\Views::class, Files::class,
\CodeIgniter\Debug\Toolbar\Collectors\Cache::class, Routes::class,
\CodeIgniter\Debug\Toolbar\Collectors\Files::class, Events::class,
\CodeIgniter\Debug\Toolbar\Collectors\Routes::class,
\CodeIgniter\Debug\Toolbar\Collectors\Events::class,
\Myth\Auth\Collectors\Auth::class,
]; ];
/* /**
|-------------------------------------------------------------------------- * --------------------------------------------------------------------------
| Max History * Collect Var Data
|-------------------------------------------------------------------------- * --------------------------------------------------------------------------
| The Toolbar allows you to view recent requests that have been made to *
| the application while the toolbar is active. This allows you to quickly * If set to false var data from the views will not be collected. Useful to
| view and compare multiple requests. * avoid high memory usage when there are lots of data passed to the view.
| */
| $maxHistory sets a limit on the number of past requests that are stored, public bool $collectVarData = true;
| helping to conserve file space used to store them. You can set it to
| 0 (zero) to not have any history stored, or -1 for unlimited history. /**
| * --------------------------------------------------------------------------
*/ * Max History
public $maxHistory = 20; * --------------------------------------------------------------------------
*
* `$maxHistory` sets a limit on the number of past requests that are stored,
* helping to conserve file space used to store them. You can set it to
* 0 (zero) to not have any history stored, or -1 for unlimited history.
*/
public int $maxHistory = 20;
/**
* --------------------------------------------------------------------------
* Toolbar Views Path
* --------------------------------------------------------------------------
*
* The full path to the the views that are used by the toolbar.
* This MUST have a trailing slash.
*/
public string $viewsPath = SYSTEMPATH . 'Debug/Toolbar/Views/';
/**
* --------------------------------------------------------------------------
* Max Queries
* --------------------------------------------------------------------------
*
* If the Database Collector is enabled, it will log every query that the
* the system generates so they can be displayed on the toolbar's timeline
* and in the query log. This can lead to memory issues in some instances
* with hundreds of queries.
*
* `$maxQueries` defines the maximum amount of queries that will be stored.
*/
public int $maxQueries = 100;
/* /**
|-------------------------------------------------------------------------- * --------------------------------------------------------------------------
| Toolbar Views Path * Watched Directories
|-------------------------------------------------------------------------- * --------------------------------------------------------------------------
| The full path to the the views that are used by the toolbar. *
| MUST have a trailing slash. * Contains an array of directories that will be watched for changes and
| * used to determine if the hot-reload feature should reload the page or not.
*/ * We restrict the values to keep performance as high as possible.
public $viewsPath = SYSTEMPATH . 'Debug/Toolbar/Views/'; *
* NOTE: The ROOTPATH will be prepended to all values.
*
* @var list<string>
*/
public array $watchedDirectories = ['app', 'modules', 'themes'];
/* /**
|-------------------------------------------------------------------------- * --------------------------------------------------------------------------
| Max Queries * Watched File Extensions
|-------------------------------------------------------------------------- * --------------------------------------------------------------------------
| If the Database Collector is enabled, it will log every query that the *
| the system generates so they can be displayed on the toolbar's timeline * Contains an array of file extensions that will be watched for changes and
| and in the query log. This can lead to memory issues in some instances * used to determine if the hot-reload feature should reload the page or not.
| with hundreds of queries. *
| * @var list<string>
| $maxQueries defines the maximum amount of queries that will be stored. */
| public array $watchedExtensions = ['php', 'css', 'js', 'html', 'svg', 'json', 'env'];
*/
public $maxQueries = 100;
} }
<?php <?php
declare(strict_types=1);
namespace Config; namespace Config;
use CodeIgniter\Config\BaseConfig; use CodeIgniter\Config\BaseConfig;
/**
* -------------------------------------------------------------------
* User Agents
* -------------------------------------------------------------------
*
* This file contains four arrays of user agent data. It is used by the
* User Agent Class to help identify browser, platform, robot, and
* mobile device data. The array keys are used to identify the device
* and the array values are used to set the actual name of the item.
*/
class UserAgents extends BaseConfig class UserAgents extends BaseConfig
{ {
/* /**
| ------------------------------------------------------------------- * -------------------------------------------------------------------
| USER AGENT TYPES * OS Platforms
| ------------------------------------------------------------------- * -------------------------------------------------------------------
| This file contains four arrays of user agent data. It is used by the *
| User Agent Class to help identify browser, platform, robot, and * @var array<string, string>
| mobile device data. The array keys are used to identify the device */
| and the array values are used to set the actual name of the item. public array $platforms = [
*/
public $platforms = [
'windows nt 10.0' => 'Windows 10', 'windows nt 10.0' => 'Windows 10',
'windows nt 6.3' => 'Windows 8.1', 'windows nt 6.3' => 'Windows 8.1',
'windows nt 6.2' => 'Windows 8', 'windows nt 6.2' => 'Windows 8',
'windows nt 6.1' => 'Windows 7', 'windows nt 6.1' => 'Windows 7',
'windows nt 6.0' => 'Windows Vista', 'windows nt 6.0' => 'Windows Vista',
'windows nt 5.2' => 'Windows 2003', 'windows nt 5.2' => 'Windows 2003',
'windows nt 5.1' => 'Windows XP', 'windows nt 5.1' => 'Windows XP',
'windows nt 5.0' => 'Windows 2000', 'windows nt 5.0' => 'Windows 2000',
'windows nt 4.0' => 'Windows NT 4.0', 'windows nt 4.0' => 'Windows NT 4.0',
'winnt4.0' => 'Windows NT 4.0', 'winnt4.0' => 'Windows NT 4.0',
'winnt 4.0' => 'Windows NT', 'winnt 4.0' => 'Windows NT',
'winnt' => 'Windows NT', 'winnt' => 'Windows NT',
'windows 98' => 'Windows 98', 'windows 98' => 'Windows 98',
'win98' => 'Windows 98', 'win98' => 'Windows 98',
'windows 95' => 'Windows 95', 'windows 95' => 'Windows 95',
'win95' => 'Windows 95', 'win95' => 'Windows 95',
'windows phone' => 'Windows Phone', 'windows phone' => 'Windows Phone',
'windows' => 'Unknown Windows OS', 'windows' => 'Unknown Windows OS',
'android' => 'Android', 'android' => 'Android',
'blackberry' => 'BlackBerry', 'blackberry' => 'BlackBerry',
'iphone' => 'iOS', 'iphone' => 'iOS',
'ipad' => 'iOS', 'ipad' => 'iOS',
'ipod' => 'iOS', 'ipod' => 'iOS',
'os x' => 'Mac OS X', 'os x' => 'Mac OS X',
'ppc mac' => 'Power PC Mac', 'ppc mac' => 'Power PC Mac',
'freebsd' => 'FreeBSD', 'freebsd' => 'FreeBSD',
'ppc' => 'Macintosh', 'ppc' => 'Macintosh',
'linux' => 'Linux', 'linux' => 'Linux',
'debian' => 'Debian', 'debian' => 'Debian',
'sunos' => 'Sun Solaris', 'sunos' => 'Sun Solaris',
'beos' => 'BeOS', 'beos' => 'BeOS',
'apachebench' => 'ApacheBench', 'apachebench' => 'ApacheBench',
'aix' => 'AIX', 'aix' => 'AIX',
'irix' => 'Irix', 'irix' => 'Irix',
'osf' => 'DEC OSF', 'osf' => 'DEC OSF',
'hp-ux' => 'HP-UX', 'hp-ux' => 'HP-UX',
'netbsd' => 'NetBSD', 'netbsd' => 'NetBSD',
'bsdi' => 'BSDi', 'bsdi' => 'BSDi',
'openbsd' => 'OpenBSD', 'openbsd' => 'OpenBSD',
'gnu' => 'GNU/Linux', 'gnu' => 'GNU/Linux',
'unix' => 'Unknown Unix OS', 'unix' => 'Unknown Unix OS',
'symbian' => 'Symbian OS', 'symbian' => 'Symbian OS',
]; ];
// The order of this array should NOT be changed. Many browsers return /**
// multiple browser types so we want to identify the sub-type first. * -------------------------------------------------------------------
public $browsers = [ * Browsers
'OPR' => 'Opera', * -------------------------------------------------------------------
'Flock' => 'Flock', *
'Edge' => 'Spartan', * The order of this array should NOT be changed. Many browsers return
* multiple browser types so we want to identify the subtype first.
*
* @var array<string, string>
*/
public array $browsers = [
'OPR' => 'Opera',
'Flock' => 'Flock',
'Edge' => 'Spartan',
'Edg' => 'Edge',
'Chrome' => 'Chrome', 'Chrome' => 'Chrome',
// Opera 10+ always reports Opera/9.80 and appends Version/<real version> to the user agent string // Opera 10+ always reports Opera/9.80 and appends Version/<real version> to the user agent string
'Opera.*?Version' => 'Opera', 'Opera.*?Version' => 'Opera',
'Opera' => 'Opera', 'Opera' => 'Opera',
'MSIE' => 'Internet Explorer', 'MSIE' => 'Internet Explorer',
'Internet Explorer' => 'Internet Explorer', 'Internet Explorer' => 'Internet Explorer',
'Trident.* rv' => 'Internet Explorer', 'Trident.* rv' => 'Internet Explorer',
'Shiira' => 'Shiira', 'Shiira' => 'Shiira',
'Firefox' => 'Firefox', 'Firefox' => 'Firefox',
'Chimera' => 'Chimera', 'Chimera' => 'Chimera',
'Phoenix' => 'Phoenix', 'Phoenix' => 'Phoenix',
'Firebird' => 'Firebird', 'Firebird' => 'Firebird',
'Camino' => 'Camino', 'Camino' => 'Camino',
'Netscape' => 'Netscape', 'Netscape' => 'Netscape',
'OmniWeb' => 'OmniWeb', 'OmniWeb' => 'OmniWeb',
'Safari' => 'Safari', 'Safari' => 'Safari',
'Mozilla' => 'Mozilla', 'Mozilla' => 'Mozilla',
'Konqueror' => 'Konqueror', 'Konqueror' => 'Konqueror',
'icab' => 'iCab', 'icab' => 'iCab',
'Lynx' => 'Lynx', 'Lynx' => 'Lynx',
'Links' => 'Links', 'Links' => 'Links',
'hotjava' => 'HotJava', 'hotjava' => 'HotJava',
'amaya' => 'Amaya', 'amaya' => 'Amaya',
'IBrowse' => 'IBrowse', 'IBrowse' => 'IBrowse',
'Maxthon' => 'Maxthon', 'Maxthon' => 'Maxthon',
'Ubuntu' => 'Ubuntu Web Browser', 'Ubuntu' => 'Ubuntu Web Browser',
'Vivaldi' => 'Vivaldi', 'Vivaldi' => 'Vivaldi',
]; ];
public $mobiles = [ /**
* -------------------------------------------------------------------
* Mobiles
* -------------------------------------------------------------------
*
* @var array<string, string>
*/
public array $mobiles = [
// legacy array, old values commented out // legacy array, old values commented out
'mobileexplorer' => 'Mobile Explorer', 'mobileexplorer' => 'Mobile Explorer',
// 'openwave' => 'Open Wave', // 'openwave' => 'Open Wave',
...@@ -113,108 +139,116 @@ class UserAgents extends BaseConfig ...@@ -113,108 +139,116 @@ class UserAgents extends BaseConfig
// 'motorola' => 'Motorola' // 'motorola' => 'Motorola'
// Phones and Manufacturers // Phones and Manufacturers
'motorola' => 'Motorola', 'motorola' => 'Motorola',
'nokia' => 'Nokia', 'nokia' => 'Nokia',
'palm' => 'Palm', 'palm' => 'Palm',
'iphone' => 'Apple iPhone', 'iphone' => 'Apple iPhone',
'ipad' => 'iPad', 'ipad' => 'iPad',
'ipod' => 'Apple iPod Touch', 'ipod' => 'Apple iPod Touch',
'sony' => 'Sony Ericsson', 'sony' => 'Sony Ericsson',
'ericsson' => 'Sony Ericsson', 'ericsson' => 'Sony Ericsson',
'blackberry' => 'BlackBerry', 'blackberry' => 'BlackBerry',
'cocoon' => 'O2 Cocoon', 'cocoon' => 'O2 Cocoon',
'blazer' => 'Treo', 'blazer' => 'Treo',
'lg' => 'LG', 'lg' => 'LG',
'amoi' => 'Amoi', 'amoi' => 'Amoi',
'xda' => 'XDA', 'xda' => 'XDA',
'mda' => 'MDA', 'mda' => 'MDA',
'vario' => 'Vario', 'vario' => 'Vario',
'htc' => 'HTC', 'htc' => 'HTC',
'samsung' => 'Samsung', 'samsung' => 'Samsung',
'sharp' => 'Sharp', 'sharp' => 'Sharp',
'sie-' => 'Siemens', 'sie-' => 'Siemens',
'alcatel' => 'Alcatel', 'alcatel' => 'Alcatel',
'benq' => 'BenQ', 'benq' => 'BenQ',
'ipaq' => 'HP iPaq', 'ipaq' => 'HP iPaq',
'mot-' => 'Motorola', 'mot-' => 'Motorola',
'playstation portable' => 'PlayStation Portable', 'playstation portable' => 'PlayStation Portable',
'playstation 3' => 'PlayStation 3', 'playstation 3' => 'PlayStation 3',
'playstation vita' => 'PlayStation Vita', 'playstation vita' => 'PlayStation Vita',
'hiptop' => 'Danger Hiptop', 'hiptop' => 'Danger Hiptop',
'nec-' => 'NEC', 'nec-' => 'NEC',
'panasonic' => 'Panasonic', 'panasonic' => 'Panasonic',
'philips' => 'Philips', 'philips' => 'Philips',
'sagem' => 'Sagem', 'sagem' => 'Sagem',
'sanyo' => 'Sanyo', 'sanyo' => 'Sanyo',
'spv' => 'SPV', 'spv' => 'SPV',
'zte' => 'ZTE', 'zte' => 'ZTE',
'sendo' => 'Sendo', 'sendo' => 'Sendo',
'nintendo dsi' => 'Nintendo DSi', 'nintendo dsi' => 'Nintendo DSi',
'nintendo ds' => 'Nintendo DS', 'nintendo ds' => 'Nintendo DS',
'nintendo 3ds' => 'Nintendo 3DS', 'nintendo 3ds' => 'Nintendo 3DS',
'wii' => 'Nintendo Wii', 'wii' => 'Nintendo Wii',
'open web' => 'Open Web', 'open web' => 'Open Web',
'openweb' => 'OpenWeb', 'openweb' => 'OpenWeb',
// Operating Systems // Operating Systems
'android' => 'Android', 'android' => 'Android',
'symbian' => 'Symbian', 'symbian' => 'Symbian',
'SymbianOS' => 'SymbianOS', 'SymbianOS' => 'SymbianOS',
'elaine' => 'Palm', 'elaine' => 'Palm',
'series60' => 'Symbian S60', 'series60' => 'Symbian S60',
'windows ce' => 'Windows CE', 'windows ce' => 'Windows CE',
// Browsers // Browsers
'obigo' => 'Obigo', 'obigo' => 'Obigo',
'netfront' => 'Netfront Browser', 'netfront' => 'Netfront Browser',
'openwave' => 'Openwave Browser', 'openwave' => 'Openwave Browser',
'mobilexplorer' => 'Mobile Explorer', 'mobilexplorer' => 'Mobile Explorer',
'operamini' => 'Opera Mini', 'operamini' => 'Opera Mini',
'opera mini' => 'Opera Mini', 'opera mini' => 'Opera Mini',
'opera mobi' => 'Opera Mobile', 'opera mobi' => 'Opera Mobile',
'fennec' => 'Firefox Mobile', 'fennec' => 'Firefox Mobile',
// Other // Other
'digital paths' => 'Digital Paths', 'digital paths' => 'Digital Paths',
'avantgo' => 'AvantGo', 'avantgo' => 'AvantGo',
'xiino' => 'Xiino', 'xiino' => 'Xiino',
'novarra' => 'Novarra Transcoder', 'novarra' => 'Novarra Transcoder',
'vodafone' => 'Vodafone', 'vodafone' => 'Vodafone',
'docomo' => 'NTT DoCoMo', 'docomo' => 'NTT DoCoMo',
'o2' => 'O2', 'o2' => 'O2',
// Fallback // Fallback
'mobile' => 'Generic Mobile', 'mobile' => 'Generic Mobile',
'wireless' => 'Generic Mobile', 'wireless' => 'Generic Mobile',
'j2me' => 'Generic Mobile', 'j2me' => 'Generic Mobile',
'midp' => 'Generic Mobile', 'midp' => 'Generic Mobile',
'cldc' => 'Generic Mobile', 'cldc' => 'Generic Mobile',
'up.link' => 'Generic Mobile', 'up.link' => 'Generic Mobile',
'up.browser' => 'Generic Mobile', 'up.browser' => 'Generic Mobile',
'smartphone' => 'Generic Mobile', 'smartphone' => 'Generic Mobile',
'cellphone' => 'Generic Mobile', 'cellphone' => 'Generic Mobile',
]; ];
// There are hundreds of bots but these are the most common. /**
public $robots = [ * -------------------------------------------------------------------
'googlebot' => 'Googlebot', * Robots
'msnbot' => 'MSNBot', * -------------------------------------------------------------------
'baiduspider' => 'Baiduspider', *
'bingbot' => 'Bing', * There are hundred of bots but these are the most common.
'slurp' => 'Inktomi Slurp', *
'yahoo' => 'Yahoo', * @var array<string, string>
'ask jeeves' => 'Ask Jeeves', */
'fastcrawler' => 'FastCrawler', public array $robots = [
'infoseek' => 'InfoSeek Robot 1.0', 'googlebot' => 'Googlebot',
'lycos' => 'Lycos', 'msnbot' => 'MSNBot',
'yandex' => 'YandexBot', 'baiduspider' => 'Baiduspider',
'bingbot' => 'Bing',
'slurp' => 'Inktomi Slurp',
'yahoo' => 'Yahoo',
'ask jeeves' => 'Ask Jeeves',
'fastcrawler' => 'FastCrawler',
'infoseek' => 'InfoSeek Robot 1.0',
'lycos' => 'Lycos',
'yandex' => 'YandexBot',
'mediapartners-google' => 'MediaPartners Google', 'mediapartners-google' => 'MediaPartners Google',
'CRAZYWEBCRAWLER' => 'Crazy Webcrawler', 'CRAZYWEBCRAWLER' => 'Crazy Webcrawler',
'adsbot-google' => 'AdsBot Google', 'adsbot-google' => 'AdsBot Google',
'feedfetcher-google' => 'Feedfetcher Google', 'feedfetcher-google' => 'Feedfetcher Google',
'curious george' => 'Curious George', 'curious george' => 'Curious George',
'ia_archiver' => 'Alexa Crawler', 'ia_archiver' => 'Alexa Crawler',
'MJ12bot' => 'Majestic-12', 'MJ12bot' => 'Majestic-12',
'Uptimebot' => 'Uptimebot', 'Uptimebot' => 'Uptimebot',
]; ];
} }
<?php <?php
declare(strict_types=1);
namespace Config; namespace Config;
class Validation use App\Validation\FileRules as AppFileRules;
{ use App\Validation\OtherRules;
//-------------------------------------------------------------------- use CodeIgniter\Config\BaseConfig;
// Setup use CodeIgniter\Validation\StrictRules\CreditCardRules;
//-------------------------------------------------------------------- use CodeIgniter\Validation\StrictRules\FileRules;
use CodeIgniter\Validation\StrictRules\FormatRules;
use CodeIgniter\Validation\StrictRules\Rules;
class Validation extends BaseConfig
{
/** /**
* Stores the classes that contain the * Stores the classes that contain the rules that are available.
* rules that are available.
* *
* @var array * @var list<string>
*/ */
public $ruleSets = [ public array $ruleSets = [
\CodeIgniter\Validation\Rules::class, Rules::class,
\CodeIgniter\Validation\FormatRules::class, FormatRules::class,
\CodeIgniter\Validation\CreditCardRules::class, FileRules::class,
\App\Validation\Rules::class, CreditCardRules::class,
\App\Validation\FileRules::class, AppFileRules::class,
\Myth\Auth\Authentication\Passwords\ValidationRules::class, OtherRules::class,
]; ];
/** /**
* Specifies the views that are used to display the * Specifies the views that are used to display the errors.
* errors.
* *
* @var array * @var array<string, string>
*/ */
public $templates = [ public array $templates = [
'list' => 'CodeIgniter\Validation\Views\list', 'list' => 'CodeIgniter\Validation\Views\list',
'single' => 'CodeIgniter\Validation\Views\single', 'single' => 'CodeIgniter\Validation\Views\single',
]; ];
//--------------------------------------------------------------------
// Rules
//--------------------------------------------------------------------
} }
<?php <?php
declare(strict_types=1);
namespace Config; namespace Config;
class View extends \CodeIgniter\Config\View use CodeIgniter\Config\View as BaseView;
use CodeIgniter\View\ViewDecoratorInterface;
use ViewComponents\Decorator;
/**
* @phpstan-type parser_callable (callable(mixed): mixed)
* @phpstan-type parser_callable_string (callable(mixed): mixed)&string
*/
class View extends BaseView
{ {
/** /**
* When false, the view method will clear the data between each * When false, the view method will clear the data between each call. This keeps your data safe and ensures there is
* call. This keeps your data safe and ensures there is no accidental * no accidental leaking between calls, so you would need to explicitly pass the data to each view. You might prefer
* leaking between calls, so you would need to explicitly pass the data * to have the data stick around between calls so that it is available to all views. If that is the case, set
* to each view. You might prefer to have the data stick around between * $saveData to true.
* calls so that it is available to all views. If that is the case, *
* set $saveData to true. * @var boolean
*/ */
public $saveData = true; public $saveData = true;
/** /**
* Parser Filters map a filter name with any PHP callable. When the * Parser Filters map a filter name with any PHP callable. When the Parser prepares a variable for display, it will
* Parser prepares a variable for display, it will chain it * chain it through the filters in the order defined, inserting any parameters. To prevent potential abuse, all
* through the filters in the order defined, inserting any parameters. * filters MUST be defined here in order for them to be available for use within the Parser.
* To prevent potential abuse, all filters MUST be defined here
* in order for them to be available for use within the Parser.
* *
* Examples: * Examples: { title|esc(js) } { created_on|date(Y-m-d)|esc(attr) }
* { title|esc(js) } *
* { created_on|date(Y-m-d)|esc(attr) } * @var array<string, string>
* @phpstan-var array<string, parser_callable_string>
*/ */
public $filters = []; public $filters = [];
/** /**
* Parser Plugins provide a way to extend the functionality provided * Parser Plugins provide a way to extend the functionality provided by the core Parser by creating aliases that
* by the core Parser by creating aliases that will be replaced with * will be replaced with any callable. Can be single or tag pair.
* any callable. Can be single or tag pair. *
* @var array<string, callable|list<string>|string>
* @phpstan-var array<string, list<parser_callable_string>|parser_callable_string|parser_callable>
*/ */
public $plugins = []; public $plugins = [];
/**
* View Decorators are class methods that will be run in sequence to have a chance to alter the generated output
* just prior to caching the results.
*
* All classes must implement CodeIgniter\View\ViewDecoratorInterface
*
* @var list<class-string<ViewDecoratorInterface>>
*/
public array $decorators = [Decorator::class];
} }
<?php
declare(strict_types=1);
namespace Config;
use ViewComponents\Config\ViewComponents as ViewComponentsConfig;
class ViewComponents extends ViewComponentsConfig
{
/**
* @var string[]
*/
public array $lookupPaths = [
ROOTPATH . 'themes/cp_app/',
ROOTPATH . 'themes/cp_admin/',
ROOTPATH . 'themes/cp_auth/',
ROOTPATH . 'themes/cp_install/',
];
}
<?php
// app/Config/Vite.php
declare(strict_types=1);
namespace Config;
use CodeIgniterVite\Config\Vite as ViteConfig;
class Vite extends ViteConfig
{
public function __construct()
{
parent::__construct();
$adminGateway = config('Admin')
->gateway;
$installGateway = config('Install')
->gateway;
$this->routesAssets = [
[
'routes' => ['*'],
'exclude' => [$adminGateway . '*', $installGateway . '*'],
'assets' => ['styles/site.css', 'js/app.ts', 'js/podcast.ts', 'js/audio-player.ts'],
],
[
'routes' => ['/map'],
'assets' => ['js/map.ts'],
],
[
'routes' => ['/' . $adminGateway . '*'],
'assets' => ['styles/admin.css', 'js/admin.ts', 'js/admin-audio-player.ts'],
],
[
'routes' => [$installGateway . '*'],
'assets' => ['styles/install.css'],
],
];
}
}
<?php
declare(strict_types=1);
/**
* @copyright 2021 Ad Aures
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers;
use Modules\Fediverse\Controllers\ActivityPubController as FediverseActivityPubController;
class ActivityPubController extends FediverseActivityPubController
{
}
<?php
declare(strict_types=1);
/**
* @copyright 2020 Ad Aures
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers;
use Modules\Analytics\AnalyticsTrait;
use Modules\Fediverse\Controllers\ActorController as FediverseActorController;
class ActorController extends FediverseActorController
{
use AnalyticsTrait;
/**
* @var list<string>
*/
protected $helpers = ['svg', 'components', 'misc', 'seo'];
public function followView(): string
{
// @phpstan-ignore-next-line
$this->registerPodcastWebpageHit($this->actor->podcast->id);
helper(['form', 'components', 'svg']);
// @phpstan-ignore-next-line
set_follow_metatags($this->actor);
$data = [
'actor' => $this->actor,
];
return view('podcast/follow', $data);
}
}
<?php
/**
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers\Admin;
use App\Models\PodcastModel;
use App\Models\EpisodeModel;
class AnalyticsData extends BaseController
{
/**
* @var \App\Entities\Podcast|null
*/
protected $podcast;
protected $className;
protected $methodName;
protected $episode;
public function _remap($method, ...$params)
{
if (count($params) > 1) {
if (!($this->podcast = (new PodcastModel())->find($params[0]))) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(
'Podcast not found: ' . $params[0]
);
}
$this->className = '\App\Models\Analytics' . $params[1] . 'Model';
$this->methodName =
'getData' . (empty($params[2]) ? '' : $params[2]);
if (count($params) > 3) {
if (
!($this->episode = (new EpisodeModel())
->where([
'podcast_id' => $this->podcast->id,
'id' => $params[3],
])
->first())
) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(
'Episode not found: ' . $params[3]
);
}
}
}
return $this->$method();
}
public function getData()
{
$analytics_model = new $this->className();
$methodName = $this->methodName;
if ($this->episode) {
return $this->response->setJSON(
$analytics_model->$methodName(
$this->podcast->id,
$this->episode->id
)
);
} else {
return $this->response->setJSON(
$analytics_model->$methodName($this->podcast->id)
);
}
}
}
<?php
namespace App\Controllers\Admin;
/**
* Class BaseController
*
* BaseController provides a convenient place for loading components
* and performing functions that are needed by all your controllers.
* Extend this class in any new controllers:
* class Home extends BaseController
*
* For security be sure to declare any new methods as protected or private.
*
* @package CodeIgniter
*/
use CodeIgniter\Controller;
class BaseController extends Controller
{
/**
* An array of helpers to be loaded automatically upon
* class instantiation. These helpers will be available
* to all other controllers that extend BaseController.
*
* @var array
*/
protected $helpers = ['auth', 'breadcrumb', 'svg', 'components'];
/**
* Constructor.
*/
public function initController(
\CodeIgniter\HTTP\RequestInterface $request,
\CodeIgniter\HTTP\ResponseInterface $response,
\Psr\Log\LoggerInterface $logger
) {
// Do Not Edit This Line
parent::initController($request, $response, $logger);
//--------------------------------------------------------------------
// Preload any models, libraries, etc, here.
//--------------------------------------------------------------------
// E.g.:
// $this->session = \Config\Services::session();
}
}
<?php
/**
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers\Admin;
use App\Authorization\GroupModel;
use App\Models\PodcastModel;
use App\Models\UserModel;
class Contributor extends BaseController
{
/**
* @var \App\Entities\Podcast
*/
protected $podcast;
/**
* @var \App\Entities\User|null
*/
protected $user;
public function _remap($method, ...$params)
{
$this->podcast = (new PodcastModel())->getPodcastById($params[0]);
if (count($params) > 1) {
if (
!($this->user = (new UserModel())->getPodcastContributor(
$params[1],
$params[0]
))
) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
}
return $this->$method();
}
public function list()
{
$data = [
'podcast' => $this->podcast,
];
replace_breadcrumb_params([0 => $this->podcast->title]);
return view('admin/contributor/list', $data);
}
public function view()
{
$data = [
'contributor' => (new UserModel())->getPodcastContributor(
$this->user->id,
$this->podcast->id
),
];
replace_breadcrumb_params([
0 => $this->podcast->title,
1 => $this->user->username,
]);
return view('admin/contributor/view', $data);
}
public function add()
{
helper('form');
$users = (new UserModel())->findAll();
$userOptions = array_reduce(
$users,
function ($result, $user) {
$result[$user->id] = $user->username;
return $result;
},
[]
);
$roles = (new GroupModel())->getContributorRoles();
$roleOptions = array_reduce(
$roles,
function ($result, $role) {
$result[$role->id] = lang('Contributor.roles.' . $role->name);
return $result;
},
[]
);
$data = [
'podcast' => $this->podcast,
'userOptions' => $userOptions,
'roleOptions' => $roleOptions,
];
replace_breadcrumb_params([0 => $this->podcast->title]);
return view('admin/contributor/add', $data);
}
public function attemptAdd()
{
try {
(new PodcastModel())->addPodcastContributor(
$this->request->getPost('user'),
$this->podcast->id,
$this->request->getPost('role')
);
} catch (\Exception $e) {
return redirect()
->back()
->withInput()
->with('errors', [
lang('Contributor.messages.alreadyAddedError'),
]);
}
return redirect()->route('contributor-list', [$this->podcast->id]);
}
public function edit()
{
helper('form');
$roles = (new GroupModel())->getContributorRoles();
$roleOptions = array_reduce(
$roles,
function ($result, $role) {
$result[$role->id] = lang('Contributor.roles.' . $role->name);
return $result;
},
[]
);
$data = [
'podcast' => $this->podcast,
'user' => $this->user,
'contributorGroupId' => (new PodcastModel())->getContributorGroupId(
$this->user->id,
$this->podcast->id
),
'roleOptions' => $roleOptions,
];
replace_breadcrumb_params([
0 => $this->podcast->title,
1 => $this->user->username,
]);
return view('admin/contributor/edit', $data);
}
public function attemptEdit()
{
(new PodcastModel())->updatePodcastContributor(
$this->user->id,
$this->podcast->id,
$this->request->getPost('role')
);
return redirect()->route('contributor-list', [$this->podcast->id]);
}
public function remove()
{
if ($this->podcast->created_by == $this->user->id) {
return redirect()
->back()
->with('errors', [
lang('Contributor.messages.removeOwnerContributorError'),
]);
}
$podcastModel = new PodcastModel();
if (
!$podcastModel->removePodcastContributor(
$this->user->id,
$this->podcast->id
)
) {
return redirect()
->back()
->with('errors', $podcastModel->errors());
}
return redirect()
->back()
->with(
'message',
lang('Contributor.messages.removeContributorSuccess', [
'username' => $this->user->username,
'podcastTitle' => $this->podcast->title,
])
);
}
}
<?php
/**
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers\Admin;
use App\Models\EpisodeModel;
use App\Models\PodcastModel;
class Episode extends BaseController
{
/**
* @var \App\Entities\Podcast
*/
protected $podcast;
/**
* @var \App\Entities\Episode|null
*/
protected $episode;
public function _remap($method, ...$params)
{
$this->podcast = (new PodcastModel())->getPodcastById($params[0]);
if (count($params) > 1) {
if (
!($this->episode = (new EpisodeModel())
->where([
'id' => $params[1],
'podcast_id' => $params[0],
])
->first())
) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
}
return $this->$method();
}
public function list()
{
$episodes = (new EpisodeModel())
->where('podcast_id', $this->podcast->id)
->orderBy('created_at', 'desc');
$data = [
'podcast' => $this->podcast,
'episodes' => $episodes->paginate(10),
'pager' => $episodes->pager,
];
replace_breadcrumb_params([
0 => $this->podcast->title,
]);
return view('admin/episode/list', $data);
}
public function view()
{
$data = [
'podcast' => $this->podcast,
'episode' => $this->episode,
];
replace_breadcrumb_params([
0 => $this->podcast->title,
1 => $this->episode->title,
]);
return view('admin/episode/view', $data);
}
public function create()
{
helper(['form']);
$data = [
'podcast' => $this->podcast,
];
replace_breadcrumb_params([
0 => $this->podcast->title,
]);
return view('admin/episode/create', $data);
}
public function attemptCreate()
{
$rules = [
'enclosure' => 'uploaded[enclosure]|ext_in[enclosure,mp3,m4a]',
'image' =>
'is_image[image]|ext_in[image,jpg,png]|min_dims[image,1400,1400]|is_image_squared[image]',
'publication_date' => 'valid_date[Y-m-d]|permit_empty',
'publication_time' =>
'regex_match[/^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/]|permit_empty',
];
if (!$this->validate($rules)) {
return redirect()
->back()
->withInput()
->with('errors', $this->validator->getErrors());
}
$newEpisode = new \App\Entities\Episode([
'podcast_id' => $this->podcast->id,
'title' => $this->request->getPost('title'),
'slug' => $this->request->getPost('slug'),
'guid' => '',
'enclosure' => $this->request->getFile('enclosure'),
'description' => $this->request->getPost('description'),
'image' => $this->request->getFile('image'),
'parental_advisory' =>
$this->request->getPost('parental_advisory') !== 'undefined'
? $this->request->getPost('parental_advisory')
: null,
'number' => $this->request->getPost('episode_number'),
'season_number' => $this->request->getPost('season_number'),
'type' => $this->request->getPost('type'),
'block' => $this->request->getPost('block') == 'yes',
'created_by' => user(),
'updated_by' => user(),
]);
$newEpisode->setPublishedAt(
$this->request->getPost('publication_date'),
$this->request->getPost('publication_time')
);
$episodeModel = new EpisodeModel();
if (!($newEpisodeId = $episodeModel->insert($newEpisode, true))) {
return redirect()
->back()
->withInput()
->with('errors', $episodeModel->errors());
}
// update podcast's episode_description_footer if changed
$podcastModel = new PodcastModel();
if ($this->podcast->hasChanged('episode_description_footer')) {
$this->podcast->episode_description_footer = $this->request->getPost(
'description_footer'
);
if (!$podcastModel->update($this->podcast->id, $this->podcast)) {
return redirect()
->back()
->withInput()
->with('errors', $podcastModel->errors());
}
}
return redirect()->route('episode-view', [
$this->podcast->id,
$newEpisodeId,
]);
}
public function edit()
{
helper(['form']);
$data = [
'podcast' => $this->podcast,
'episode' => $this->episode,
];
replace_breadcrumb_params([
0 => $this->podcast->title,
1 => $this->episode->title,
]);
return view('admin/episode/edit', $data);
}
public function attemptEdit()
{
$rules = [
'enclosure' =>
'uploaded[enclosure]|ext_in[enclosure,mp3,m4a]|permit_empty',
'image' =>
'is_image[image]|ext_in[image,jpg,png]|min_dims[image,1400,1400]|is_image_squared[image]',
'publication_date' => 'valid_date[Y-m-d]|permit_empty',
'publication_time' =>
'regex_match[/^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/]|permit_empty',
];
if (!$this->validate($rules)) {
return redirect()
->back()
->withInput()
->with('errors', $this->validator->getErrors());
}
$this->episode->title = $this->request->getPost('title');
$this->episode->slug = $this->request->getPost('slug');
$this->episode->description = $this->request->getPost('description');
$this->episode->parental_advisory =
$this->request->getPost('parental_advisory') !== 'undefined'
? $this->request->getPost('parental_advisory')
: null;
$this->episode->number = $this->request->getPost('episode_number');
$this->episode->season_number = $this->request->getPost('season_number')
? $this->request->getPost('season_number')
: null;
$this->episode->type = $this->request->getPost('type');
$this->episode->block = $this->request->getPost('block') == 'yes';
$this->episode->setPublishedAt(
$this->request->getPost('publication_date'),
$this->request->getPost('publication_time')
);
$this->episode->updated_by = user();
$enclosure = $this->request->getFile('enclosure');
if ($enclosure->isValid()) {
$this->episode->enclosure = $enclosure;
}
$image = $this->request->getFile('image');
if ($image) {
$this->episode->image = $image;
}
$episodeModel = new EpisodeModel();
if (!$episodeModel->update($this->episode->id, $this->episode)) {
return redirect()
->back()
->withInput()
->with('errors', $episodeModel->errors());
}
// update podcast's episode_description_footer if changed
$this->podcast->episode_description_footer = $this->request->getPost(
'description_footer'
);
if ($this->podcast->hasChanged('episode_description_footer')) {
$podcastModel = new PodcastModel();
if (!$podcastModel->update($this->podcast->id, $this->podcast)) {
return redirect()
->back()
->withInput()
->with('errors', $podcastModel->errors());
}
}
return redirect()->route('episode-view', [
$this->podcast->id,
$this->episode->id,
]);
}
public function delete()
{
(new EpisodeModel())->delete($this->episode->id);
return redirect()->route('episode-list', [$this->podcast->id]);
}
}