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 761 additions and 1547 deletions
<?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 <?php
declare(strict_types=1);
namespace Config; namespace Config;
use CodeIgniter\Config\BaseConfig; use CodeIgniter\Config\BaseConfig;
...@@ -8,85 +10,77 @@ class Security extends BaseConfig ...@@ -8,85 +10,77 @@ class Security extends BaseConfig
{ {
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* CSRF Token Name * CSRF Protection Method
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* *
* Token name for Cross Site Request Forgery protection cookie. * Protection Method for Cross Site Request Forgery protection.
* *
* @var string * @var 'cookie'|'session'
*/ */
public $tokenName = 'csrf_test_name'; public string $csrfProtection = 'session';
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* CSRF Header Name * CSRF Token Randomization
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* *
* Token name for Cross Site Request Forgery protection cookie. * Randomize the CSRF Token for added security.
*
* @var string
*/ */
public $headerName = 'X-CSRF-TOKEN'; public bool $tokenRandomize = true;
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* CSRF Cookie Name * CSRF Token Name
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* *
* Cookie name for Cross Site Request Forgery protection cookie. * Token name for Cross Site Request Forgery protection.
*
* @var string
*/ */
public $cookieName = 'csrf_cookie_name'; public string $tokenName = 'csrf_test_name';
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* CSRF Expires * CSRF Header Name
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* *
* Expiration time for Cross Site Request Forgery protection cookie. * Header name for Cross Site Request Forgery protection.
*
* Defaults to two hours (in seconds).
*
* @var integer
*/ */
public $expires = 7200; public string $headerName = 'X-CSRF-TOKEN';
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* CSRF Regenerate * CSRF Cookie Name
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* *
* Regenerate CSRF Token on every request. * Cookie name for Cross Site Request Forgery protection.
*
* @var boolean
*/ */
public $regenerate = true; public string $cookieName = 'csrf_cookie_name';
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* CSRF Redirect * CSRF Expires
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* *
* Redirect to previous page with error on failure. * Expiration time for Cross Site Request Forgery protection cookie.
* *
* @var boolean * Defaults to two hours (in seconds).
*/ */
public $redirect = true; public int $expires = 7200;
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* CSRF SameSite * CSRF Regenerate
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* *
* Setting for CSRF SameSite cookie token. * Regenerate CSRF Token on every submission.
* */
* Allowed values are: None - Lax - Strict - ''. public bool $regenerate = true;
*
* Defaults to `Lax` as recommended in this link: /**
* @see https://portswigger.net/web-security/csrf/samesite-cookies * --------------------------------------------------------------------------
* CSRF Redirect
* --------------------------------------------------------------------------
* *
* @var string 'Lax'|'None'|'Strict' * @see https://codeigniter4.github.io/userguide/libraries/security.html#redirection-on-failure
*/ */
public $samesite = 'Lax'; public bool $redirect = (ENVIRONMENT === 'production');
} }
<?php <?php
declare(strict_types=1);
namespace Config; namespace Config;
use CodeIgniter\Config\BaseService;
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\Libraries\HtmlHead;
use App\Libraries\Negotiate; use App\Libraries\Negotiate;
use App\Libraries\Router; use App\Libraries\Router;
use App\Models\UserModel; use CodeIgniter\Config\BaseService;
use CodeIgniter\HTTP\Request; use CodeIgniter\HTTP\Request;
use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\Router\RouteCollectionInterface; use CodeIgniter\Router\RouteCollectionInterface;
use Myth\Auth\Models\LoginModel;
/** /**
* 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 BaseService class Services extends BaseService
{ {
/** /**
* The Router class uses a RouteCollection's array of routes, and determines * The Router class uses a RouteCollection's array of routes, and determines the correct Controller and Method to
* the correct Controller and Method to execute. * execute.
*
* @param RouteCollectionInterface|null $routes
* @param Request|null $request
* @param boolean $getShared
*
* @return Router
*/ */
public static function router( public static function router(
RouteCollectionInterface $routes = null, ?RouteCollectionInterface $routes = null,
Request $request = null, ?Request $request = null,
bool $getShared = true bool $getShared = true,
) { ): Router {
if ($getShared) { if ($getShared) {
return static::getSharedInstance('router', $routes, $request); return static::getSharedInstance('router', $routes, $request);
} }
$routes = $routes ?? static::routes(); $routes ??= static::routes();
$request = $request ?? static::request(); $request ??= static::request();
return new Router($routes, $request); return new Router($routes, $request);
} }
/** /**
* The Negotiate class provides the content negotiation features for * The Negotiate class provides the content negotiation features for working the request to determine correct
* working the request to determine correct language, encoding, charset, * language, encoding, charset, and more.
* and more.
*
* @param RequestInterface|null $request
* @param boolean $getShared
*
* @return Negotiate
*/ */
public static function negotiator( public static function negotiator(?RequestInterface $request = null, bool $getShared = true): Negotiate
RequestInterface $request = null, {
bool $getShared = true
) {
if ($getShared) { if ($getShared) {
return static::getSharedInstance('negotiator', $request); return static::getSharedInstance('negotiator', $request);
} }
$request = $request ?? static::request(); $request ??= static::request();
return new Negotiate($request); return new Negotiate($request);
} }
public static function authentication( public static function breadcrumb(bool $getShared = true): Breadcrumb
string $lib = 'local', {
Model $userModel = null,
Model $loginModel = null,
bool $getShared = true
) {
if ($getShared) {
return self::getSharedInstance(
'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)) {
$loginModel = new LoginModel();
}
return $instance->setUserModel($userModel)->setLoginModel($loginModel);
}
public static function authorization(
Model $groupModel = null,
Model $permissionModel = null,
Model $userModel = null,
bool $getShared = true
) {
if ($getShared) { if ($getShared) {
return self::getSharedInstance( return self::getSharedInstance('breadcrumb');
'authorization',
$groupModel,
$permissionModel,
$userModel,
);
}
if (is_null($groupModel)) {
$groupModel = new GroupModel();
}
if (is_null($permissionModel)) {
$permissionModel = new PermissionModel();
}
$instance = new FlatAuthorization($groupModel, $permissionModel);
if (is_null($userModel)) {
$userModel = new UserModel();
} }
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;
...@@ -31,9 +33,9 @@ class Toolbar extends BaseConfig ...@@ -31,9 +33,9 @@ class Toolbar extends BaseConfig
* List of toolbar collectors that will be called when Debug Toolbar * List of toolbar collectors that will be called when Debug Toolbar
* fires up and collects data from. * fires up and collects data from.
* *
* @var string[] * @var list<class-string>
*/ */
public $collectors = [ public array $collectors = [
Timers::class, Timers::class,
Database::class, Database::class,
Logs::class, Logs::class,
...@@ -44,6 +46,16 @@ class Toolbar extends BaseConfig ...@@ -44,6 +46,16 @@ class Toolbar extends BaseConfig
Events::class, Events::class,
]; ];
/**
* --------------------------------------------------------------------------
* Collect Var Data
* --------------------------------------------------------------------------
*
* If set to false var data from the views will not be collected. Useful to
* avoid high memory usage when there are lots of data passed to the view.
*/
public bool $collectVarData = true;
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* Max History * Max History
...@@ -52,10 +64,8 @@ class Toolbar extends BaseConfig ...@@ -52,10 +64,8 @@ class Toolbar extends BaseConfig
* `$maxHistory` sets a limit on the number of past requests that are stored, * `$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 * 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. * 0 (zero) to not have any history stored, or -1 for unlimited history.
*
* @var integer
*/ */
public $maxHistory = 20; public int $maxHistory = 20;
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
...@@ -64,10 +74,8 @@ class Toolbar extends BaseConfig ...@@ -64,10 +74,8 @@ class Toolbar extends BaseConfig
* *
* The full path to the the views that are used by the toolbar. * The full path to the the views that are used by the toolbar.
* This MUST have a trailing slash. * This MUST have a trailing slash.
*
* @var string
*/ */
public $viewsPath = SYSTEMPATH . 'Debug/Toolbar/Views/'; public string $viewsPath = SYSTEMPATH . 'Debug/Toolbar/Views/';
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
...@@ -80,8 +88,33 @@ class Toolbar extends BaseConfig ...@@ -80,8 +88,33 @@ class Toolbar extends BaseConfig
* with hundreds of queries. * with hundreds of queries.
* *
* `$maxQueries` defines the maximum amount of queries that will be stored. * `$maxQueries` defines the maximum amount of queries that will be stored.
*/
public int $maxQueries = 100;
/**
* --------------------------------------------------------------------------
* Watched Directories
* --------------------------------------------------------------------------
*
* Contains an array of directories that will be watched for changes and
* used to determine if the hot-reload feature should reload the page or not.
* We restrict the values to keep performance as high as possible.
*
* NOTE: The ROOTPATH will be prepended to all values.
*
* @var list<string>
*/
public array $watchedDirectories = ['app', 'modules', 'themes'];
/**
* --------------------------------------------------------------------------
* Watched File Extensions
* --------------------------------------------------------------------------
*
* Contains an array of file extensions that will be watched for changes and
* used to determine if the hot-reload feature should reload the page or not.
* *
* @var integer * @var list<string>
*/ */
public $maxQueries = 100; public array $watchedExtensions = ['php', 'css', 'js', 'html', 'svg', 'json', 'env'];
} }
<?php <?php
declare(strict_types=1);
namespace Config; namespace Config;
use CodeIgniter\Config\BaseConfig; use CodeIgniter\Config\BaseConfig;
...@@ -22,49 +25,49 @@ class UserAgents extends BaseConfig ...@@ -22,49 +25,49 @@ class UserAgents extends BaseConfig
* *
* @var array<string, string> * @var array<string, string>
*/ */
public $platforms = [ public array $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',
]; ];
/** /**
...@@ -77,38 +80,38 @@ class UserAgents extends BaseConfig ...@@ -77,38 +80,38 @@ class UserAgents extends BaseConfig
* *
* @var array<string, string> * @var array<string, string>
*/ */
public $browsers = [ public array $browsers = [
'OPR' => 'Opera', 'OPR' => 'Opera',
'Flock' => 'Flock', 'Flock' => 'Flock',
'Edge' => 'Spartan', 'Edge' => 'Spartan',
'Edg' => 'Edge', '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',
]; ];
/** /**
...@@ -118,7 +121,7 @@ class UserAgents extends BaseConfig ...@@ -118,7 +121,7 @@ class UserAgents extends BaseConfig
* *
* @var array<string, string> * @var array<string, string>
*/ */
public $mobiles = [ 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',
...@@ -136,86 +139,86 @@ class UserAgents extends BaseConfig ...@@ -136,86 +139,86 @@ 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',
]; ];
/** /**
...@@ -227,25 +230,25 @@ class UserAgents extends BaseConfig ...@@ -227,25 +230,25 @@ class UserAgents extends BaseConfig
* *
* @var array<string, string> * @var array<string, string>
*/ */
public $robots = [ public array $robots = [
'googlebot' => 'Googlebot', 'googlebot' => 'Googlebot',
'msnbot' => 'MSNBot', 'msnbot' => 'MSNBot',
'baiduspider' => 'Baiduspider', 'baiduspider' => 'Baiduspider',
'bingbot' => 'Bing', 'bingbot' => 'Bing',
'slurp' => 'Inktomi Slurp', 'slurp' => 'Inktomi Slurp',
'yahoo' => 'Yahoo', 'yahoo' => 'Yahoo',
'ask jeeves' => 'Ask Jeeves', 'ask jeeves' => 'Ask Jeeves',
'fastcrawler' => 'FastCrawler', 'fastcrawler' => 'FastCrawler',
'infoseek' => 'InfoSeek Robot 1.0', 'infoseek' => 'InfoSeek Robot 1.0',
'lycos' => 'Lycos', 'lycos' => 'Lycos',
'yandex' => 'YandexBot', '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;
use App\Validation\FileRules as AppFileRules; use App\Validation\FileRules as AppFileRules;
use App\Validation\Rules as AppRules; use App\Validation\OtherRules;
use CodeIgniter\Validation\CreditCardRules; use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Validation\FileRules; use CodeIgniter\Validation\StrictRules\CreditCardRules;
use CodeIgniter\Validation\FormatRules; use CodeIgniter\Validation\StrictRules\FileRules;
use CodeIgniter\Validation\Rules; use CodeIgniter\Validation\StrictRules\FormatRules;
use Myth\Auth\Authentication\Passwords\ValidationRules as PasswordRules; use CodeIgniter\Validation\StrictRules\Rules;
class Validation class Validation extends BaseConfig
{ {
//--------------------------------------------------------------------
// Setup
//--------------------------------------------------------------------
/** /**
* Stores the classes that contain the * Stores the classes that contain the rules that are available.
* rules that are available.
* *
* @var string[] * @var list<string>
*/ */
public $ruleSets = [ public array $ruleSets = [
Rules::class, Rules::class,
FormatRules::class, FormatRules::class,
FileRules::class, FileRules::class,
CreditCardRules::class, CreditCardRules::class,
AppRules::class,
AppFileRules::class, AppFileRules::class,
PasswordRules::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<string, string> * @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;
use CodeIgniter\Config\View as BaseView; 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 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 * @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 * @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 * @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
/**
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers;
class Actor extends \ActivityPub\Controllers\ActorController
{
public function follow()
{
helper(['form', 'components', 'svg']);
$data = [
'actor' => $this->actor,
];
return view('podcast/follow', $data);
}
}
<?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;
use CodeIgniter\Controller;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Psr\Log\LoggerInterface;
/**
* 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.
*/
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', 'misc'];
/**
* Constructor.
*
* @param RequestInterface $request
* @param ResponseInterface $response
* @param LoggerInterface $logger
*/
public function initController(
RequestInterface $request,
ResponseInterface $response,
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\Entities\Note;
use App\Models\EpisodeModel;
use App\Models\NoteModel;
use App\Models\PodcastModel;
use App\Models\SoundbiteModel;
use CodeIgniter\I18n\Time;
class Episode extends BaseController
{
/**
* @var \App\Entities\Podcast
*/
protected $podcast;
/**
* @var \App\Entities\Episode|null
*/
protected $episode;
/**
* @var \App\Entities\Soundbite|null
*/
protected $soundbites;
public function _remap($method, ...$params)
{
if (
!($this->podcast = (new PodcastModel())->getPodcastById($params[0]))
) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
if (count($params) > 1) {
if (
!($this->episode = (new EpisodeModel())
->where([
'id' => $params[1],
'podcast_id' => $params[0],
])
->first())
) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
unset($params[1]);
unset($params[0]);
}
return $this->$method(...$params);
}
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]',
'transcript' => 'ext_in[transcript,txt,html,srt,json]|permit_empty',
'chapters' => 'ext_in[chapters,json]|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_markdown' => $this->request->getPost('description'),
'image' => $this->request->getFile('image'),
'location' => $this->request->getPost('location_name'),
'transcript' => $this->request->getFile('transcript'),
'chapters' => $this->request->getFile('chapters'),
'parental_advisory' =>
$this->request->getPost('parental_advisory') !== 'undefined'
? $this->request->getPost('parental_advisory')
: null,
'number' => $this->request->getPost('episode_number')
? $this->request->getPost('episode_number')
: null,
'season_number' => $this->request->getPost('season_number')
? $this->request->getPost('season_number')
: null,
'type' => $this->request->getPost('type'),
'is_blocked' => $this->request->getPost('block') == 'yes',
'custom_rss_string' => $this->request->getPost('custom_rss'),
'created_by' => user()->id,
'updated_by' => user()->id,
'published_at' => null,
]);
$episodeModel = new EpisodeModel();
if (!($newEpisodeId = $episodeModel->insert($newEpisode, true))) {
return redirect()
->back()
->withInput()
->with('errors', $episodeModel->errors());
}
// update podcast's episode_description_footer_markdown if changed
$podcastModel = new PodcastModel();
if ($this->podcast->hasChanged('episode_description_footer_markdown')) {
$this->podcast->episode_description_footer_markdown = $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]',
'transcript' => 'ext_in[transcript,txt,html,srt,json]|permit_empty',
'chapters' => 'ext_in[chapters,json]|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_markdown = $this->request->getPost(
'description',
);
$this->episode->location = $this->request->getPost('location_name');
$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->request->getPost('episode_number')
: null;
$this->episode->season_number = $this->request->getPost('season_number')
? $this->request->getPost('season_number')
: null;
$this->episode->type = $this->request->getPost('type');
$this->episode->is_blocked = $this->request->getPost('block') == 'yes';
$this->episode->custom_rss_string = $this->request->getPost(
'custom_rss',
);
$this->episode->updated_by = user()->id;
$enclosure = $this->request->getFile('enclosure');
if ($enclosure->isValid()) {
$this->episode->enclosure = $enclosure;
}
$image = $this->request->getFile('image');
if ($image) {
$this->episode->image = $image;
}
$transcript = $this->request->getFile('transcript');
if ($transcript->isValid()) {
$this->episode->transcript = $transcript;
}
$chapters = $this->request->getFile('chapters');
if ($chapters->isValid()) {
$this->episode->chapters = $chapters;
}
$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_markdown if changed
$this->podcast->episode_description_footer_markdown = $this->request->getPost(
'description_footer',
);
if ($this->podcast->hasChanged('episode_description_footer_markdown')) {
$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 transcriptDelete()
{
unlink($this->episode->transcript);
$this->episode->transcript_uri = null;
$episodeModel = new EpisodeModel();
if (!$episodeModel->update($this->episode->id, $this->episode)) {
return redirect()
->back()
->withInput()
->with('errors', $episodeModel->errors());
}
return redirect()->back();
}
public function chaptersDelete()
{
unlink($this->episode->chapters);
$this->episode->chapters_uri = null;
$episodeModel = new EpisodeModel();
if (!$episodeModel->update($this->episode->id, $this->episode)) {
return redirect()
->back()
->withInput()
->with('errors', $episodeModel->errors());
}
return redirect()->back();
}
public function publish()
{
if ($this->episode->publication_status === 'not_published') {
helper(['form']);
$data = [
'podcast' => $this->podcast,
'episode' => $this->episode,
];
replace_breadcrumb_params([
0 => $this->podcast->title,
1 => $this->episode->title,
]);
return view('admin/episode/publish', $data);
} else {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
}
public function attemptPublish()
{
$rules = [
'publication_method' => 'required',
'scheduled_publication_date' =>
'valid_date[Y-m-d H:i]|permit_empty',
];
if (!$this->validate($rules)) {
return redirect()
->back()
->withInput()
->with('errors', $this->validator->getErrors());
}
$db = \Config\Database::connect();
$db->transStart();
$newNote = new Note([
'actor_id' => $this->podcast->actor_id,
'episode_id' => $this->episode->id,
'message' => $this->request->getPost('message'),
'created_by' => user_id(),
]);
$publishMethod = $this->request->getPost('publication_method');
if ($publishMethod === 'schedule') {
$scheduledPublicationDate = $this->request->getPost(
'scheduled_publication_date',
);
if ($scheduledPublicationDate) {
$scheduledDateUTC = Time::createFromFormat(
'Y-m-d H:i',
$scheduledPublicationDate,
$this->request->getPost('client_timezone'),
)->setTimezone('UTC');
$this->episode->published_at = $scheduledDateUTC;
$newNote->published_at = $scheduledDateUTC;
} else {
$db->transRollback();
return redirect()
->back()
->withInput()
->with('error', 'Schedule date must be set!');
}
} else {
$dateNow = Time::now();
$this->episode->published_at = $dateNow;
$newNote->published_at = $dateNow;
}
$noteModel = new NoteModel();
if (!$noteModel->addNote($newNote)) {
$db->transRollback();
return redirect()
->back()
->withInput()
->with('errors', $noteModel->errors());
}
$episodeModel = new EpisodeModel();
if (!$episodeModel->update($this->episode->id, $this->episode)) {
$db->transRollback();
return redirect()
->back()
->withInput()
->with('errors', $episodeModel->errors());
}
$db->transComplete();
return redirect()->route('episode-view', [
$this->podcast->id,
$this->episode->id,
]);
}
public function publishEdit()
{
if ($this->episode->publication_status === 'scheduled') {
helper(['form']);
$data = [
'podcast' => $this->podcast,
'episode' => $this->episode,
'note' => (new NoteModel())
->where([
'actor_id' => $this->podcast->actor_id,
'episode_id' => $this->episode->id,
])
->first(),
];
replace_breadcrumb_params([
0 => $this->podcast->title,
1 => $this->episode->title,
]);
return view('admin/episode/publish_edit', $data);
} else {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
}
public function attemptPublishEdit()
{
$rules = [
'note_id' => 'required',
'publication_method' => 'required',
'scheduled_publication_date' =>
'valid_date[Y-m-d H:i]|permit_empty',
];
if (!$this->validate($rules)) {
return redirect()
->back()
->withInput()
->with('errors', $this->validator->getErrors());
}
$db = \Config\Database::connect();
$db->transStart();
$note = (new NoteModel())->getNoteById(
$this->request->getPost('note_id'),
);
$note->message = $this->request->getPost('message');
$publishMethod = $this->request->getPost('publication_method');
if ($publishMethod === 'schedule') {
$scheduledPublicationDate = $this->request->getPost(
'scheduled_publication_date',
);
if ($scheduledPublicationDate) {
$scheduledDateUTC = Time::createFromFormat(
'Y-m-d H:i',
$scheduledPublicationDate,
$this->request->getPost('client_timezone'),
)->setTimezone('UTC');
$this->episode->published_at = $scheduledDateUTC;
$note->published_at = $scheduledDateUTC;
} else {
$db->transRollback();
return redirect()
->back()
->withInput()
->with('error', 'Schedule date must be set!');
}
} else {
$dateNow = Time::now();
$this->episode->published_at = $dateNow;
$note->published_at = $dateNow;
}
$noteModel = new NoteModel();
if (!$noteModel->editNote($note)) {
$db->transRollback();
return redirect()
->back()
->withInput()
->with('errors', $noteModel->errors());
}
$episodeModel = new EpisodeModel();
if (!$episodeModel->update($this->episode->id, $this->episode)) {
$db->transRollback();
return redirect()
->back()
->withInput()
->with('errors', $episodeModel->errors());
}
$db->transComplete();
return redirect()->route('episode-view', [
$this->podcast->id,
$this->episode->id,
]);
}
public function unpublish()
{
if ($this->episode->publication_status === 'published') {
helper(['form']);
$data = [
'podcast' => $this->podcast,
'episode' => $this->episode,
];
replace_breadcrumb_params([
0 => $this->podcast->title,
1 => $this->episode->title,
]);
return view('admin/episode/unpublish', $data);
} else {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
}
public function attemptUnpublish()
{
$rules = [
'understand' => 'required',
];
if (!$this->validate($rules)) {
return redirect()
->back()
->withInput()
->with('errors', $this->validator->getErrors());
}
$db = \Config\Database::connect();
$db->transStart();
$allNotesLinkedToEpisode = (new NoteModel())
->where([
'episode_id' => $this->episode->id,
])
->findAll();
foreach ($allNotesLinkedToEpisode as $note) {
(new NoteModel())->removeNote($note);
}
// set episode published_at to null to unpublish
$this->episode->published_at = null;
$episodeModel = new EpisodeModel();
if (!$episodeModel->update($this->episode->id, $this->episode)) {
$db->transRollback();
return redirect()
->back()
->withInput()
->with('errors', $episodeModel->errors());
}
$db->transComplete();
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]);
}
public function soundbitesEdit()
{
helper(['form']);
$data = [
'podcast' => $this->podcast,
'episode' => $this->episode,
];
replace_breadcrumb_params([
0 => $this->podcast->title,
1 => $this->episode->title,
]);
return view('admin/episode/soundbites', $data);
}
public function soundbitesAttemptEdit()
{
$soundbites_array = $this->request->getPost('soundbites_array');
$rules = [
'soundbites_array.0.start_time' =>
'permit_empty|required_with[soundbites_array.0.duration]|decimal|greater_than_equal_to[0]',
'soundbites_array.0.duration' =>
'permit_empty|required_with[soundbites_array.0.start_time]|decimal|greater_than_equal_to[0]',
];
foreach ($soundbites_array as $soundbite_id => $soundbite) {
$rules += [
"soundbites_array.{$soundbite_id}.start_time" => 'required|decimal|greater_than_equal_to[0]',
"soundbites_array.{$soundbite_id}.duration" => 'required|decimal|greater_than_equal_to[0]',
];
}
if (!$this->validate($rules)) {
return redirect()
->back()
->withInput()
->with('errors', $this->validator->getErrors());
}
foreach ($soundbites_array as $soundbite_id => $soundbite) {
if (
!empty($soundbite['start_time']) &&
!empty($soundbite['duration'])
) {
$data = [
'podcast_id' => $this->podcast->id,
'episode_id' => $this->episode->id,
'start_time' => $soundbite['start_time'],
'duration' => $soundbite['duration'],
'label' => $soundbite['label'],
'updated_by' => user()->id,
];
if ($soundbite_id == 0) {
$data += ['created_by' => user()->id];
} else {
$data += ['id' => $soundbite_id];
}
$soundbiteModel = new SoundbiteModel();
if (!$soundbiteModel->save($data)) {
return redirect()
->back()
->withInput()
->with('errors', $soundbiteModel->errors());
}
}
}
return redirect()->route('soundbites-edit', [
$this->podcast->id,
$this->episode->id,
]);
}
public function soundbiteDelete($soundbiteId)
{
(new SoundbiteModel())->deleteSoundbite(
$this->podcast->id,
$this->episode->id,
$soundbiteId,
);
return redirect()->route('soundbites-edit', [
$this->podcast->id,
$this->episode->id,
]);
}
public function embeddablePlayer()
{
helper(['form']);
$data = [
'podcast' => $this->podcast,
'episode' => $this->episode,
'themes' => EpisodeModel::$themes,
];
replace_breadcrumb_params([
0 => $this->podcast->title,
1 => $this->episode->title,
]);
return view('admin/episode/embeddable_player', $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\EpisodePersonModel;
use App\Models\PodcastModel;
use App\Models\EpisodeModel;
use App\Models\PersonModel;
class EpisodePerson extends BaseController
{
/**
* @var \App\Entities\Podcast
*/
protected $podcast;
/**
* @var \App\Entities\Episode
*/
protected $episode;
public function _remap($method, ...$params)
{
if (count($params) > 1) {
if (
!($this->podcast = (new PodcastModel())->getPodcastById(
$params[0]
))
) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
if (
!($this->episode = (new EpisodeModel())
->where([
'id' => $params[1],
'podcast_id' => $params[0],
])
->first())
) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
} else {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
unset($params[1]);
unset($params[0]);
return $this->$method(...$params);
}
public function index()
{
helper('form');
$data = [
'episode' => $this->episode,
'podcast' => $this->podcast,
'episodePersons' => (new EpisodePersonModel())->getPersonsByEpisodeId(
$this->podcast->id,
$this->episode->id
),
'personOptions' => (new PersonModel())->getPersonOptions(),
'taxonomyOptions' => (new PersonModel())->getTaxonomyOptions(),
];
replace_breadcrumb_params([
0 => $this->podcast->title,
1 => $this->episode->title,
]);
return view('admin/episode/person', $data);
}
public function attemptAdd()
{
$rules = [
'person' => 'required',
];
if (!$this->validate($rules)) {
return redirect()
->back()
->withInput()
->with('errors', $this->validator->getErrors());
}
(new EpisodePersonModel())->addEpisodePersons(
$this->podcast->id,
$this->episode->id,
$this->request->getPost('person'),
$this->request->getPost('person_group_role')
);
return redirect()->back();
}
public function remove($episodePersonId)
{
(new EpisodePersonModel())->removeEpisodePersons(
$this->podcast->id,
$this->episode->id,
$episodePersonId
);
return redirect()->back();
}
}
<?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 ActivityPub\Models\BlockedDomainModel;
class Fediverse extends BaseController
{
public function dashboard()
{
return view('admin/fediverse/dashboard');
}
public function blockedActors()
{
helper(['form']);
$blockedActors = model('ActorModel')->getBlockedActors();
return view('admin/fediverse/blocked_actors', [
'blockedActors' => $blockedActors,
]);
}
public function blockedDomains()
{
helper(['form']);
$blockedDomains = model('BlockedDomainModel')->getBlockedDomains();
return view('admin/fediverse/blocked_domains', [
'blockedDomains' => $blockedDomains,
]);
}
}