Compare commits

...

36 Commits

Author SHA1 Message Date
crowdin 82959beca3 Merge branch 'i18n' into 'develop'
New Crowdin updates

See merge request adaures/castopod!343
2024-04-26 15:04:49 +00:00
Yassine Doghri fe73e9fae9 fix(platforms): add platforms service + reduce memory consumption when rendering platform cards 2024-04-26 10:45:30 +00:00
Yassine Doghri d4a36f811b chore: update CodeIgniter to 4.5.1 + other dependencies to latest 2024-04-26 09:26:22 +00:00
crowdin 17ca57d567 New translations podcast.php (French traditional)
[ci skip]
2024-04-24 10:08:04 +00:00
crowdin 4052e05df8 New translations podcast.php (Serbian (Latin))
[ci skip]
2024-04-24 10:08:01 +00:00
crowdin 53e6fcf0e8 New translations podcast.php (Occitan)
[ci skip]
2024-04-24 10:07:59 +00:00
crowdin b0c70fa4a0 New translations podcast.php (Scottish Gaelic)
[ci skip]
2024-04-24 10:07:57 +00:00
crowdin 5597b0cb04 New translations podcast.php (Breton)
[ci skip]
2024-04-24 10:07:55 +00:00
crowdin f3e30d6c84 New translations podcast.php (French, Canada)
[ci skip]
2024-04-24 10:07:53 +00:00
crowdin 6b38b711ae New translations podcast.php (Kazakh)
[ci skip]
2024-04-24 10:07:51 +00:00
crowdin b82882edcc New translations podcast.php (Norwegian Nynorsk)
[ci skip]
2024-04-24 10:07:48 +00:00
crowdin 0de6ff7b2f New translations podcast.php (Persian)
[ci skip]
2024-04-24 10:07:46 +00:00
crowdin cc35db17d6 New translations podcast.php (Indonesian)
[ci skip]
2024-04-24 10:07:43 +00:00
crowdin 4ea54a2ee3 New translations podcast.php (Portuguese, Brazilian)
[ci skip]
2024-04-24 10:07:41 +00:00
crowdin aa792f5e8e New translations podcast.php (Galician)
[ci skip]
2024-04-24 10:07:38 +00:00
crowdin ad25a13cf7 New translations podcast.php (Chinese Traditional)
[ci skip]
2024-04-24 10:07:36 +00:00
crowdin f4e7a143fd New translations podcast.php (Chinese Simplified)
[ci skip]
2024-04-24 10:07:34 +00:00
crowdin b78b032c2f New translations podcast.php (Ukrainian)
[ci skip]
2024-04-24 10:07:32 +00:00
crowdin feec53875b New translations podcast.php (Swedish)
[ci skip]
2024-04-24 10:07:30 +00:00
crowdin 7ecc8c184c New translations podcast.php (Slovak)
[ci skip]
2024-04-24 10:07:28 +00:00
crowdin 19eaab6bcd New translations podcast.php (Russian)
[ci skip]
2024-04-24 10:07:25 +00:00
crowdin 76d068946a New translations podcast.php (Portuguese)
[ci skip]
2024-04-24 10:07:23 +00:00
crowdin b3cc9497f2 New translations podcast.php (Polish)
[ci skip]
2024-04-24 10:07:21 +00:00
crowdin de04948b67 New translations podcast.php (Dutch)
[ci skip]
2024-04-24 10:07:19 +00:00
crowdin e330976608 New translations podcast.php (Korean)
[ci skip]
2024-04-24 10:07:16 +00:00
crowdin 552c74db75 New translations podcast.php (Japanese)
[ci skip]
2024-04-24 10:07:14 +00:00
crowdin 1ba2b45dbb New translations podcast.php (Italian)
[ci skip]
2024-04-24 10:07:09 +00:00
crowdin b3e4f43165 New translations podcast.php (Basque)
[ci skip]
2024-04-24 10:07:06 +00:00
crowdin 3d8fca9e2f New translations podcast.php (Greek)
[ci skip]
2024-04-24 10:07:04 +00:00
crowdin 46f891ca23 New translations podcast.php (German)
[ci skip]
2024-04-24 10:07:02 +00:00
crowdin d04b203e37 New translations podcast.php (Danish)
[ci skip]
2024-04-24 10:07:00 +00:00
crowdin 3caa5b3f23 New translations podcast.php (Catalan)
[ci skip]
2024-04-24 10:06:58 +00:00
crowdin e704f048ea New translations podcast.php (Arabic)
[ci skip]
2024-04-24 10:06:55 +00:00
crowdin faf4a99f8e New translations podcast.php (Spanish)
[ci skip]
2024-04-24 10:06:52 +00:00
crowdin eee678a42c New translations podcast.php (French)
[ci skip]
2024-04-24 10:06:49 +00:00
crowdin b228c12584 New translations podcast.php (Romanian)
[ci skip]
2024-04-24 10:06:46 +00:00
141 changed files with 3331 additions and 2875 deletions

View File

@ -4,7 +4,7 @@
# ⚠️ NOT optimized for production
# should be used only for development purposes
#---------------------------------------------------
FROM php:8.1-fpm
FROM php:8.2-fpm
LABEL maintainer="Yassine Doghri <yassine@doghri.fr>"

View File

@ -12,7 +12,7 @@ services:
environment:
CI_ENVIRONMENT: development
vite_environment: development
app_forceGlobalSecureRequests: false
app_forceGlobalSecureRequests: 0 #false
app_baseURL: http://localhost:8080/
media_baseURL: http://localhost:8080/
admin_gateway: cp-admin
@ -23,7 +23,7 @@ services:
database_default_username: castopod
database_default_password: castopod
database_default_DBPrefix: cp_
restapi_enabled: true
restapi_enabled: 1 #true
email_fromEmail: hello@castopod.local
email_SMTPCrypto: ""
email_SMTPHost: mailpit

1
.gitignore vendored
View File

@ -132,7 +132,6 @@ tmp/
/results/
/phpunit*.xml
/.phpunit.*.cache
# js package manager
yarn.lock

View File

@ -1,6 +1,2 @@
<IfModule authz_core_module>
Require all denied
</IfModule>
<IfModule !authz_core_module>
Deny from all
</IfModule>
<IfModule authz_core_module> Require all denied </IfModule>
<IfModule !authz_core_module> Deny from all </IfModule>

View File

@ -61,6 +61,30 @@ class App extends BaseConfig
*/
public string $uriProtocol = 'REQUEST_URI';
/*
*--------------------------------------------------------------------------
* Allowed URL Characters
*--------------------------------------------------------------------------
*
* This lets you specify which characters are permitted within your URLs.
* When someone tries to submit a URL with disallowed characters they will
* get a warning message.
*
* As a security measure you are STRONGLY encouraged to restrict URLs to
* as few characters as possible.
*
* By default, only these are allowed: `a-z 0-9~%.:_-`
*
* Set an empty string to allow all characters -- but only if you are insane.
*
* The configured value is actually a regular expression character group
* and it will be used as: '/\A[<permittedURIChars>]+\z/iu'
*
* DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!!
*
*/
public string $permittedURIChars = 'a-z 0-9~%.:_\-@';
/**
* --------------------------------------------------------------------------
* Default Locale

View File

@ -29,23 +29,17 @@ class Autoload extends AutoloadConfig
* their location on the file system. These are used by the autoloader
* to locate files the first time they have been instantiated.
*
* The '/app' and '/system' directories are already mapped for you.
* you may change the name of the 'App' namespace if you wish,
* The 'Config' (APPPATH . 'Config') and 'CodeIgniter' (SYSTEMPATH) are
* already mapped for you.
*
* You may change the name of the 'App' namespace if you wish,
* but this should be done prior to creating any namespaced classes,
* else you will need to modify all of those classes for this to work.
*
* Prototype:
*
* $psr4 = [
* 'CodeIgniter' => SYSTEMPATH,
* 'App' => APPPATH
* ];
*
* @var array<string, list<string>|string>
*/
public $psr4 = [
APP_NAMESPACE => APPPATH,
'Config' => APPPATH . 'Config/',
'Modules' => ROOTPATH . 'modules/',
'Modules\Admin' => ROOTPATH . 'modules/Admin/',
'Modules\Analytics' => ROOTPATH . 'modules/Analytics/',
@ -68,7 +62,6 @@ class Autoload extends AutoloadConfig
/**
* -------------------------------------------------------------------
* Class Map
* -------------------------------------------------------------------
* The class map provides a map of class names and their exact
* location on the drive. Classes loaded in this manner will have

View File

@ -11,8 +11,10 @@ declare(strict_types=1);
*
* If you set 'display_errors' to '1', CI4's detailed error report will show.
*/
error_reporting(E_ALL & ~E_DEPRECATED);
// If you want to suppress more types of errors.
// error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT & ~E_USER_NOTICE & ~E_USER_DEPRECATED);
ini_set('display_errors', '0');
error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT & ~E_USER_NOTICE & ~E_USER_DEPRECATED);
/**
* --------------------------------------------------------------------------

View File

@ -48,25 +48,6 @@ class Cache extends BaseConfig
*/
public string $storePath = WRITEPATH . 'cache/';
/**
* --------------------------------------------------------------------------
* Cache Include Query String
* --------------------------------------------------------------------------
*
* Whether to take the URL query string into consideration when generating
* output cache files. Valid options are:
*
* false = Disabled
* true = Enabled, take all query parameters into account.
* Please be aware that this may result in numerous cache
* files generated for the same page over and over again.
* ['q'] = Enabled, but only take into account the specified list
* of query parameters.
*
* @var boolean|string[]
*/
public bool | array $cacheQueryString = false;
/**
* --------------------------------------------------------------------------
* Key Prefix
@ -170,4 +151,23 @@ class Cache extends BaseConfig
'redis' => RedisHandler::class,
'wincache' => WincacheHandler::class,
];
/**
* --------------------------------------------------------------------------
* Web Page Caching: Cache Include Query String
* --------------------------------------------------------------------------
*
* Whether to take the URL query string into consideration when generating
* output cache files. Valid options are:
*
* false = Disabled
* true = Enabled, take all query parameters into account.
* Please be aware that this may result in numerous cache
* files generated for the same page over and over again.
* ['q'] = Enabled, but only take into account the specified list
* of query parameters.
*
* @var bool|list<string>
*/
public $cacheQueryString = false;
}

View File

@ -35,28 +35,28 @@ class ContentSecurityPolicy extends BaseConfig
/**
* Will default to self if not overridden
*
* @var string|string[]|null
* @var list<string>|string|null
*/
public string | array | null $defaultSrc = null;
/**
* Lists allowed scripts' URLs.
*
* @var string|string[]
* @var list<string>|string
*/
public string | array $scriptSrc = 'self';
/**
* Lists allowed stylesheets' URLs.
*
* @var string|string[]
* @var list<string>|string
*/
public string | array $styleSrc = 'self';
/**
* Defines the origins from which images can be loaded.
*
* @var string|string[]
* @var list<string>|string
*/
public string | array $imageSrc = 'self';
@ -65,35 +65,35 @@ class ContentSecurityPolicy extends BaseConfig
*
* Will default to self if not overridden
*
* @var string|string[]|null
* @var list<string>|string|null
*/
public string | array | null $baseURI = null;
/**
* Lists the URLs for workers and embedded frame contents
*
* @var string|string[]
* @var list<string>|string
*/
public string | array $childSrc = 'self';
/**
* Limits the origins that you can connect to (via XHR, WebSockets, and EventSource).
*
* @var string|string[]
* @var list<string>|string
*/
public string | array $connectSrc = 'self';
/**
* Specifies the origins that can serve web fonts.
*
* @var string|string[]
* @var list<string>|string
*/
public string | array $fontSrc;
/**
* Lists valid endpoints for submission from `<form>` tags.
*
* @var string|string[]
* @var list<string>|string
*/
public string | array $formAction = 'self';
@ -102,47 +102,47 @@ class ContentSecurityPolicy extends BaseConfig
* `<embed>`, and `<applet>` tags. This directive can't be used in `<meta>` tags and applies only to non-HTML
* resources.
*
* @var string|string[]|null
* @var list<string>|string|null
*/
public string | array | null $frameAncestors = null;
/**
* The frame-src directive restricts the URLs which may be loaded into nested browsing contexts.
*
* @var string[]|string|null
* @var list<string>|string|null
*/
public string | array | null $frameSrc = null;
/**
* Restricts the origins allowed to deliver video and audio.
*
* @var string|string[]|null
* @var list<string>|string|null
*/
public string | array | null $mediaSrc = null;
/**
* Allows control over Flash and other plugins.
*
* @var string|string[]
* @var list<string>|string
*/
public string | array $objectSrc = 'self';
/**
* @var string|string[]|null
* @var list<string>|string|null
*/
public string | array | null $manifestSrc = null;
/**
* Limits the kinds of plugins a page may invoke.
*
* @var string|string[]|null
* @var list<string>|string|null
*/
public string | array | null $pluginTypes = null;
/**
* List of actions allowed.
*
* @var string|string[]|null
* @var list<string>|string|null
*/
public string | array | null $sandbox = null;

107
app/Config/Cors.php Normal file
View File

@ -0,0 +1,107 @@
<?php
declare(strict_types=1);
namespace Config;
use CodeIgniter\Config\BaseConfig;
/**
* Cross-Origin Resource Sharing (CORS) Configuration
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
*/
class Cors extends BaseConfig
{
/**
* The default CORS configuration.
*
* @var array{
* allowedOrigins: list<string>,
* allowedOriginsPatterns: list<string>,
* supportsCredentials: bool,
* allowedHeaders: list<string>,
* exposedHeaders: list<string>,
* allowedMethods: list<string>,
* maxAge: int,
* }
*/
public array $default = [
/**
* Origins for the `Access-Control-Allow-Origin` header.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
*
* E.g.:
* - ['http://localhost:8080']
* - ['https://www.example.com']
*/
'allowedOrigins' => [],
/**
* Origin regex patterns for the `Access-Control-Allow-Origin` header.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
*
* NOTE: A pattern specified here is part of a regular expression. It will
* be actually `#\A<pattern>\z#`.
*
* E.g.:
* - ['https://\w+\.example\.com']
*/
'allowedOriginsPatterns' => [],
/**
* Weather to send the `Access-Control-Allow-Credentials` header.
*
* The Access-Control-Allow-Credentials response header tells browsers whether
* the server allows cross-origin HTTP requests to include credentials.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
*/
'supportsCredentials' => false,
/**
* Set headers to allow.
*
* The Access-Control-Allow-Headers response header is used in response to
* a preflight request which includes the Access-Control-Request-Headers to
* indicate which HTTP headers can be used during the actual request.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers
*/
'allowedHeaders' => [],
/**
* Set headers to expose.
*
* The Access-Control-Expose-Headers response header allows a server to
* indicate which response headers should be made available to scripts running
* in the browser, in response to a cross-origin request.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers
*/
'exposedHeaders' => [],
/**
* Set methods to allow.
*
* The Access-Control-Allow-Methods response header specifies one or more
* methods allowed when accessing a resource in response to a preflight
* request.
*
* E.g.:
* - ['GET', 'POST', 'PUT', 'DELETE']
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
*/
'allowedMethods' => [],
/**
* Set how many seconds the results of a preflight request can be cached.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age
*/
'maxAge' => 7200,
];
}

View File

@ -45,6 +45,11 @@ class Database extends Config
'failover' => [],
'port' => 3306,
'numberNative' => false,
'dateFormat' => [
'date' => 'Y-m-d',
'datetime' => 'Y-m-d H:i:s',
'time' => 'H:i:s',
],
];
/**
@ -64,7 +69,7 @@ class Database extends Config
'pConnect' => false,
'DBDebug' => true,
'charset' => 'utf8',
'DBCollat' => 'utf8_general_ci',
'DBCollat' => '',
'swapPre' => '',
'encrypt' => false,
'compress' => false,
@ -73,6 +78,11 @@ class Database extends Config
'port' => 3306,
'foreignKeys' => true,
'busyTimeout' => 1000,
'dateFormat' => [
'date' => 'Y-m-d',
'datetime' => 'Y-m-d H:i:s',
'time' => 'H:i:s',
],
];
//--------------------------------------------------------------------

View File

@ -33,7 +33,7 @@ class Exceptions extends BaseConfig
* Any status codes here will NOT be logged if logging is turned on.
* By default, only 404 (Page Not Found) exceptions are ignored.
*
* @var int[]
* @var list<int>
*/
public array $ignoreCodes = [404];
@ -56,7 +56,7 @@ class Exceptions extends BaseConfig
* In order to specify 2 levels, use "/" to separate.
* ex. ['server', 'setup/password', 'secret_token']
*
* @var string[]
* @var list<string>
*/
public array $sensitiveDataInTrace = [];

View File

@ -11,22 +11,21 @@ use CodeIgniter\Config\BaseConfig;
*/
class Feature extends BaseConfig
{
/**
* Enable multiple filters for a route or not.
*
* If you enable this:
* - CodeIgniter\CodeIgniter::handleRequest() uses:
* - CodeIgniter\Filters\Filters::enableFilters(), instead of enableFilter()
* - CodeIgniter\CodeIgniter::tryToRouteIt() uses:
* - CodeIgniter\Router\Router::getFilters(), instead of getFilter()
* - CodeIgniter\Router\Router::handle() uses:
* - property $filtersInfo, instead of $filterInfo
* - CodeIgniter\Router\RouteCollection::getFiltersForRoute(), instead of getFilterForRoute()
*/
public bool $multipleFilters = false;
/**
* Use improved new auto routing instead of the default legacy version.
*/
public bool $autoRoutesImproved = false;
/**
* Use filter execution order in 4.4 or before.
*/
public bool $oldFilterOrder = false;
/**
* The behavior of `limit(0)` in Query Builder.
*
* If true, `limit(0)` returns all records. (the behavior of 4.4.x or before in version 4.x.)
* If false, `limit(0)` returns no records. (the behavior of 3.1.9 or later in version 3.x.)
*/
public bool $limitZeroAsAll = true;
}

View File

@ -8,8 +8,11 @@ use App\Filters\AllowCorsFilter;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Filters\CSRF;
use CodeIgniter\Filters\DebugToolbar;
use CodeIgniter\Filters\ForceHTTPS;
use CodeIgniter\Filters\Honeypot;
use CodeIgniter\Filters\InvalidChars;
use CodeIgniter\Filters\PageCache;
use CodeIgniter\Filters\PerformanceMetrics;
use CodeIgniter\Filters\SecureHeaders;
use Modules\Auth\Filters\PermissionFilter;
@ -18,8 +21,10 @@ class Filters extends BaseConfig
/**
* Configures aliases for Filter classes to make reading things nicer and simpler.
*
* @var array<string, class-string|list<class-string>> [filter_name => classname]
* or [filter_name => [classname1, classname2, ...]]
* @var array<string, class-string|list<class-string>>
*
* [filter_name => classname]
* or [filter_name => [classname1, classname2, ...]]
*/
public array $aliases = [
'csrf' => CSRF::class,
@ -28,12 +33,41 @@ class Filters extends BaseConfig
'invalidchars' => InvalidChars::class,
'secureheaders' => SecureHeaders::class,
'allow-cors' => AllowCorsFilter::class,
'cors' => Cors::class,
'forcehttps' => ForceHTTPS::class,
'pagecache' => PageCache::class,
'performance' => PerformanceMetrics::class,
];
/**
* List of special required filters.
*
* The filters listed here are special. They are applied before and after
* other kinds of filters, and always applied even if a route does not exist.
*
* Filters set by default provide framework functionality. If removed,
* those functions will no longer work.
*
* @see https://codeigniter.com/user_guide/incoming/filters.html#provided-filters
*
* @var array{before: list<string>, after: list<string>}
*/
public array $required = [
'before' => [
'forcehttps', // Force Global Secure Requests
'pagecache', // Web Page Caching
],
'after' => [
'pagecache', // Web Page Caching
'performance', // Performance Metrics
'toolbar', // Debug Toolbar
],
];
/**
* List of filter aliases that are always applied before and after every request.
*
* @var array<string, array<string, array<string, string>>>|array<string, list<string>>
* @var array<string, array<string, array<string, string|array<string>>>>>|array<string, list<string>>
*/
public array $globals = [
'before' => [
@ -44,7 +78,6 @@ class Filters extends BaseConfig
// 'invalidchars',
],
'after' => [
'toolbar',
// 'honeypot',
// 'secureheaders',
],
@ -53,12 +86,12 @@ class Filters extends BaseConfig
/**
* List of filter aliases that works on a particular HTTP method (GET, POST, etc.).
*
* Example: 'post' => ['foo', 'bar']
* Example: 'POST' => ['foo', 'bar']
*
* If you use this, you should disable auto-routing because auto-routing permits any HTTP method to access a
* controller. Accessing the controller with a method you dont expect could bypass the filter.
*
* @var array<string, string[]>
* @var array<string, list<string>>
*/
public array $methods = [];
@ -67,7 +100,7 @@ class Filters extends BaseConfig
*
* Example: 'isLoggedIn' => ['before' => ['account/*', 'profiles/*']]
*
* @var array<string, array<string, string[]>>
* @var array<string, array<string, list<string>>>
*/
public array $filters = [];

View File

@ -24,7 +24,7 @@ class Format extends BaseConfig
* These formats are only checked when the data passed to the respond()
* method is an array.
*
* @var string[]
* @var list<string>
*/
public array $supportedResponseFormats = [
'application/json',

View File

@ -28,8 +28,10 @@ class Generators extends BaseConfig
* @var array<string, string>
*/
public array $views = [
'make:cell' => 'CodeIgniter\Commands\Generators\Views\cell.tpl.php',
'make:cell_view' => 'CodeIgniter\Commands\Generators\Views\cell_view.tpl.php',
'make:cell' => [
'class' => 'CodeIgniter\Commands\Generators\Views\cell.tpl.php',
'view' => 'CodeIgniter\Commands\Generators\Views\cell_view.tpl.php',
],
'make:command' => 'CodeIgniter\Commands\Generators\Views\command.tpl.php',
'make:config' => 'CodeIgniter\Commands\Generators\Views\config.tpl.php',
'make:controller' => 'CodeIgniter\Commands\Generators\Views\controller.tpl.php',

View File

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Config;
use CodeIgniter\Config\BaseConfig;
use Kint\Parser\ConstructablePluginInterface;
use Kint\Renderer\AbstractRenderer;
use Kint\Renderer\Rich\TabPluginInterface;
@ -20,7 +19,7 @@ use Kint\Renderer\Rich\ValuePluginInterface;
*
* @see https://kint-php.github.io/kint/ for details on these settings.
*/
class Kint extends BaseConfig
class Kint
{
/*
|--------------------------------------------------------------------------

View File

@ -38,7 +38,7 @@ class Logger extends BaseConfig
* For a live site you'll usually enable Critical or higher (3) to be logged otherwise
* your log files will fill up very fast.
*
* @var int|int[]
* @var int|list<int>
*/
public int | array $threshold = (ENVIRONMENT === 'production') ? 4 : 9;
@ -75,7 +75,7 @@ class Logger extends BaseConfig
* Handlers are executed in the order defined in this array, starting with
* the handler on top and continuing down.
*
* @var array<string, mixed>
* @var array<class-string, array<string, int|list<string>|string>>
*/
public array $handlers = [
/*

View File

@ -22,7 +22,7 @@ class Mimes
/**
* Map of extensions to mime types.
*
* @var array<string, string|string[]>
* @var array<string, list<string>|string>
*/
public static $mimes = [
'hqx' => [

34
app/Config/Optimize.php Normal file
View File

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Config;
/**
* Optimization Configuration.
*
* NOTE: This class does not extend BaseConfig for performance reasons.
* So you cannot replace the property values with Environment Variables.
*
* @immutable
*/
class Optimize
{
/**
* --------------------------------------------------------------------------
* Config Caching
* --------------------------------------------------------------------------
*
* @see https://codeigniter.com/user_guide/concepts/factories.html#config-caching
*/
public bool $configCacheEnabled = false;
/**
* --------------------------------------------------------------------------
* Config Caching
* --------------------------------------------------------------------------
*
* @see https://codeigniter.com/user_guide/concepts/autoloader.html#file-locator-caching
*/
public bool $locatorCacheEnabled = false;
}

View File

@ -12,13 +12,14 @@ use CodeIgniter\Config\Routing as BaseRouting;
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 string[]
* @var list<string>
*/
public array $routeFiles = [
APPPATH . 'Config/Routes.php',
@ -34,6 +35,7 @@ class Routing extends BaseRouting
];
/**
* For Defined Routes and Auto Routing.
* The default namespace to use for Controllers when no other
* namespace has been specified.
*
@ -42,6 +44,7 @@ class Routing extends BaseRouting
public string $defaultNamespace = 'App\Controllers';
/**
* For Auto Routing.
* The default controller to use when no other controller has been
* specified.
*
@ -50,6 +53,7 @@ class Routing extends BaseRouting
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.
*
@ -58,7 +62,8 @@ class Routing extends BaseRouting
public string $defaultMethod = 'index';
/**
* Whether to translate dashes in URIs to underscores.
* For Auto Routing.
* Whether to translate dashes in URIs for controller/method to underscores.
* Primarily useful when using the auto-routing.
*
* Default: false
@ -94,6 +99,7 @@ class Routing extends BaseRouting
public bool $autoRoute = false;
/**
* For Defined Routes.
* If TRUE, will enable the use of the 'prioritize' option
* when defining routes.
*
@ -102,7 +108,16 @@ class Routing extends BaseRouting
public bool $prioritize = false;
/**
* Map of URI segments and namespaces. For Auto Routing (Improved).
* 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.,
@ -113,4 +128,15 @@ class Routing extends BaseRouting
* @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 = false;
}

View File

@ -80,9 +80,9 @@ class Security extends BaseConfig
* CSRF Redirect
* --------------------------------------------------------------------------
*
* Redirect to previous page with error on failure.
* @see https://codeigniter4.github.io/userguide/libraries/security.html#redirection-on-failure
*/
public bool $redirect = false;
public bool $redirect = (ENVIRONMENT === 'production');
/**
* --------------------------------------------------------------------------

View File

@ -37,8 +37,8 @@ class Services extends BaseService
return static::getSharedInstance('router', $routes, $request);
}
$routes = $routes ?? static::routes();
$request = $request ?? static::request();
$routes ??= static::routes();
$request ??= static::request();
return new Router($routes, $request);
}
@ -53,7 +53,7 @@ class Services extends BaseService
return static::getSharedInstance('negotiator', $request);
}
$request = $request ?? static::request();
$request ??= static::request();
return new Negotiate($request);
}

View File

@ -101,4 +101,29 @@ class Session extends BaseConfig
* 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;
}

View File

@ -33,7 +33,7 @@ class Toolbar extends BaseConfig
* List of toolbar collectors that will be called when Debug Toolbar
* fires up and collects data from.
*
* @var string[]
* @var list<class-string>
*/
public array $collectors = [
Timers::class,
@ -51,7 +51,7 @@ class Toolbar extends BaseConfig
* Collect Var Data
* --------------------------------------------------------------------------
*
* If set to false var data from the views will not be colleted. Useful to
* 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;
@ -102,7 +102,7 @@ class Toolbar extends BaseConfig
*
* NOTE: The ROOTPATH will be prepended to all values.
*
* @var string[]
* @var list<string>
*/
public array $watchedDirectories = ['app', 'modules', 'themes'];
@ -114,7 +114,7 @@ class Toolbar extends BaseConfig
* 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 string[]
* @var list<string>
*/
public array $watchedExtensions = ['php', 'css', 'js', 'html', 'svg', 'json', 'env'];
}

View File

@ -16,7 +16,7 @@ class Validation extends BaseConfig
/**
* Stores the classes that contain the rules that are available.
*
* @var string[]
* @var list<string>
*/
public array $ruleSets = [
Rules::class,

View File

@ -9,8 +9,8 @@ use CodeIgniter\View\ViewDecoratorInterface;
use ViewComponents\Decorator;
/**
* @phpstan-type ParserCallable (callable(mixed): mixed)
* @phpstan-type ParserCallableString (callable(mixed): mixed)&string
* @phpstan-type parser_callable (callable(mixed): mixed)
* @phpstan-type parser_callable_string (callable(mixed): mixed)&string
*/
class View extends BaseView
{
@ -31,8 +31,8 @@ class View extends BaseView
*
* Examples: { title|esc(js) } { created_on|date(Y-m-d)|esc(attr) }
*
* @var array<string, string>
* @phpstan-var array<string, ParserCallableString>
* @var array<string, string>
* @phpstan-var array<string, parser_callable_string>
*/
public $filters = [];
@ -40,8 +40,8 @@ class View extends BaseView
* Parser Plugins provide a way to extend the functionality provided by the core Parser by creating aliases that
* will be replaced with any callable. Can be single or tag pair.
*
* @var array<string, array<string>|callable|string>
* @phpstan-var array<string, array<ParserCallableString>|ParserCallableString|ParserCallable>
* @var array<string, callable|list<string>|string>
* @phpstan-var array<string, list<parser_callable_string>|parser_callable_string|parser_callable>
*/
public $plugins = [];
@ -51,7 +51,7 @@ class View extends BaseView
*
* All classes must implement CodeIgniter\View\ViewDecoratorInterface
*
* @var class-string<ViewDecoratorInterface>[]
* @var list<class-string<ViewDecoratorInterface>>
*/
public array $decorators = [Decorator::class];
}

View File

@ -13,8 +13,6 @@ use Psr\Log\LoggerInterface;
use ViewThemes\Theme;
/**
* Class BaseController
*
* BaseController provides a convenient place for loading components and performing functions that are needed by all
* your controllers. Extend this class in any new controllers: class Home extends BaseController
*
@ -41,7 +39,7 @@ abstract class BaseController extends Controller
* class instantiation. These helpers will be available
* to all other controllers that extend BaseController.
*
* @var string[]
* @var list<string>
*/
protected $helpers = [];

View File

@ -40,7 +40,7 @@ class EpisodeAudioController extends Controller
* An array of helpers to be loaded automatically upon class instantiation. These helpers will be available to all
* other controllers that extend Analytics.
*
* @var string[]
* @var list<string>
*/
protected $helpers = ['analytics'];

View File

@ -106,9 +106,7 @@ class EpisodeController extends BaseController
// The page cache is set to a decade so it is deleted manually upon podcast update
return view('episode/comments', $data, [
'cache' => $secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode
: DECADE,
'cache' => $secondsToNextUnpublishedEpisode ?: DECADE,
'cache_name' => $cacheName,
]);
}
@ -157,9 +155,7 @@ class EpisodeController extends BaseController
// The page cache is set to a decade so it is deleted manually upon podcast update
return view('episode/activity', $data, [
'cache' => $secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode
: DECADE,
'cache' => $secondsToNextUnpublishedEpisode ?: DECADE,
'cache_name' => $cacheName,
]);
}
@ -218,9 +214,7 @@ class EpisodeController extends BaseController
// The page cache is set to a decade so it is deleted manually upon podcast update
return view('episode/chapters', $data, [
'cache' => $secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode
: DECADE,
'cache' => $secondsToNextUnpublishedEpisode ?: DECADE,
'cache_name' => $cacheName,
]);
}
@ -284,9 +278,7 @@ class EpisodeController extends BaseController
// The page cache is set to a decade so it is deleted manually upon podcast update
return view('episode/transcript', $data, [
'cache' => $secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode
: DECADE,
'cache' => $secondsToNextUnpublishedEpisode ?: DECADE,
'cache_name' => $cacheName,
]);
}
@ -339,9 +331,7 @@ class EpisodeController extends BaseController
// The page cache is set to a decade so it is deleted manually upon podcast update
return view('embed', $data, [
'cache' => $secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode
: DECADE,
'cache' => $secondsToNextUnpublishedEpisode ?: DECADE,
'cache_name' => $cacheName,
]);
}
@ -420,11 +410,9 @@ class EpisodeController extends BaseController
* get comments: aggregated replies from posts referring to the episode
*/
$episodeComments = model(PostModel::class)
->whereIn('in_reply_to_id', function (BaseBuilder $builder): BaseBuilder {
return $builder->select('id')
->from('fediverse_posts')
->where('episode_id', $this->episode->id);
})
->whereIn('in_reply_to_id', fn (BaseBuilder $builder): BaseBuilder => $builder->select('id')
->from('fediverse_posts')
->where('episode_id', $this->episode->id))
->where('`published_at` <= UTC_TIMESTAMP()', null, false)
->orderBy('published_at', 'ASC');

View File

@ -79,13 +79,7 @@ class FeedController extends Controller
);
cache()
->save(
$cacheName,
$found,
$secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode
: DECADE,
);
->save($cacheName, $found, $secondsToNextUnpublishedEpisode ?: DECADE);
}
return $this->response->setXML($found);

View File

@ -96,9 +96,7 @@ class PodcastController extends BaseController
);
return view('podcast/activity', $data, [
'cache' => $secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode
: DECADE,
'cache' => $secondsToNextUnpublishedEpisode ?: DECADE,
'cache_name' => $cacheName,
]);
}
@ -148,9 +146,7 @@ class PodcastController extends BaseController
);
return view('podcast/about', $data, [
'cache' => $secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode
: DECADE,
'cache' => $secondsToNextUnpublishedEpisode ?: DECADE,
'cache_name' => $cacheName,
]);
}
@ -270,9 +266,7 @@ class PodcastController extends BaseController
$this->podcast->id,
);
return view('podcast/episodes', $data, [
'cache' => $secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode
: DECADE,
'cache' => $secondsToNextUnpublishedEpisode ?: DECADE,
'cache_name' => $cacheName,
]);
}

View File

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;

View File

@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
/**
* CodeIgniter 4.5.1 introduces new DataCaster class that breaks deserialization of import queue tasks.
* This just removes them altogether.
*/
class ClearImportQueue extends Migration
{
public function up(): void
{
service('settings')->forget('Import.queue');
}
public function down(): void
{
// nothing
}
}

View File

@ -77,34 +77,32 @@ class FakePodcastsAnalyticsSeeder extends Seeder
for (
$lineNumber = 0;
$lineNumber < rand(1, (int) $probability1);
$lineNumber < random_int(1, (int) $probability1);
++$lineNumber
) {
$probability2 = floor(exp(6 - $age / 20)) + 10;
$player =
$jsonUserAgents[
rand(1, count($jsonUserAgents) - 1)
random_int(1, count($jsonUserAgents) - 1)
];
$service =
$jsonRSSUserAgents[
rand(1, count($jsonRSSUserAgents) - 1)
random_int(1, count($jsonRSSUserAgents) - 1)
]['slug'];
$app = isset($player['app']) ? $player['app'] : '';
$device = isset($player['device'])
? $player['device']
: '';
$os = isset($player['os']) ? $player['os'] : '';
$isBot = isset($player['bot']) ? $player['bot'] : 0;
$app = $player['app'] ?? '';
$device = $player['device'] ?? '';
$os = $player['os'] ?? '';
$isBot = $player['bot'] ?? 0;
$fakeIp =
rand(0, 255) .
random_int(0, 255) .
'.' .
rand(0, 255) .
random_int(0, 255) .
'.' .
rand(0, 255) .
random_int(0, 255) .
'.' .
rand(0, 255);
random_int(0, 255);
$cityReader = new Reader(WRITEPATH . 'uploads/GeoLite2-City/GeoLite2-City.mmdb');
@ -115,9 +113,7 @@ class FakePodcastsAnalyticsSeeder extends Seeder
try {
$city = $cityReader->city($fakeIp);
$countryCode = $city->country->isoCode === null
? 'N/A'
: $city->country->isoCode;
$countryCode = $city->country->isoCode ?? 'N/A';
$regionCode = $city->subdivisions === []
? 'N/A'
@ -128,20 +124,20 @@ class FakePodcastsAnalyticsSeeder extends Seeder
//Bad luck, bad IP, nothing to do.
}
$hits = rand(0, (int) $probability2);
$hits = random_int(0, (int) $probability2);
$analyticsPodcasts[] = [
'podcast_id' => $podcast->id,
'date' => date('Y-m-d', $date),
'duration' => rand(60, 3600),
'bandwidth' => rand(1000000, 10000000),
'duration' => random_int(60, 3600),
'bandwidth' => random_int(1000000, 10000000),
'hits' => $hits,
'unique_listeners' => $hits,
];
$analyticsPodcastsByHour[] = [
'podcast_id' => $podcast->id,
'date' => date('Y-m-d', $date),
'hour' => rand(0, 23),
'hour' => random_int(0, 23),
'hits' => $hits,
];
$analyticsPodcastsByCountry[] = [

View File

@ -216,23 +216,23 @@ class FakeWebsiteAnalyticsSeeder extends Seeder
for (
$lineNumber = 0;
$lineNumber < rand(1, $probability1);
$lineNumber < random_int(1, $probability1);
++$lineNumber
) {
$probability2 = (int) floor(exp(6 - $age / 20)) + 10;
$domain =
$this->domains[rand(0, count($this->domains) - 1)];
$this->domains[random_int(0, count($this->domains) - 1)];
$keyword =
$this->keywords[
rand(0, count($this->keywords) - 1)
random_int(0, count($this->keywords) - 1)
];
$browser =
$this->browsers[
rand(0, count($this->browsers) - 1)
random_int(0, count($this->browsers) - 1)
];
$hits = rand(0, $probability2);
$hits = random_int(0, $probability2);
$websiteByBrowser[] = [
'podcast_id' => $podcast->id,

View File

@ -483,7 +483,7 @@ class Episode extends Entity
public function setGuid(?string $guid = null): static
{
$this->attributes['guid'] = $guid === null ? $this->getLink() : $guid;
$this->attributes['guid'] = $guid ?? $this->getLink();
return $this;
}

View File

@ -42,24 +42,16 @@ if (! function_exists('write_audio_file_tags')) {
// populate data array
$TagData = [
'title' => [esc($episode->title)],
'artist' => [
$episode->podcast->publisher === null
? esc($episode->podcast->owner_name)
: $episode->podcast->publisher,
],
'title' => [esc($episode->title)],
'artist' => [$episode->podcast->publisher ?? esc($episode->podcast->owner_name)],
'album' => [esc($episode->podcast->title)],
'year' => [$episode->published_at instanceof Time ? $episode->published_at->format('Y') : ''],
'genre' => ['Podcast'],
'comment' => [$episode->description],
'track_number' => [(string) $episode->number],
'copyright_message' => [$episode->podcast->copyright],
'publisher' => [
$episode->podcast->publisher === null
? esc($episode->podcast->owner_name)
: $episode->podcast->publisher,
],
'encoded_by' => ['Castopod'],
'publisher' => [$episode->podcast->publisher ?? esc($episode->podcast->owner_name)],
'encoded_by' => ['Castopod'],
// TODO: find a way to add the remaining tags for podcasts as the library doesn't seem to allow it
// 'website' => [$podcast_url],

View File

@ -164,7 +164,7 @@ if (! function_exists('parse_size')) {
$size = (float) preg_replace('~[^0-9\.]~', '', $size); // Remove the non-numeric characters from the size.
if ($unit !== '') {
// Find the position of the unit in the ordered string which is the power of magnitude to multiply a kilobyte by.
return round($size * pow(1024, (float) stripos('bkmgtpezy', $unit[0])));
return round($size * 1024 ** ((float) stripos('bkmgtpezy', $unit[0])));
}
return round($size);
@ -183,7 +183,7 @@ if (! function_exists('format_bytes')) {
$pow = floor(($bytes ? log($bytes) : 0) / log($is_binary ? 1024 : 1000));
$pow = min($pow, count($units) - 1);
$bytes /= pow($is_binary ? 1024 : 1000, $pow);
$bytes /= ($is_binary ? 1024 : 1000) ** $pow;
return round($bytes, $precision) . $units[$pow];
}

View File

@ -260,12 +260,7 @@ if (! function_exists('get_rss_feed')) {
$itunesNamespace,
);
$channel->addChild(
'author',
$podcast->publisher ? $podcast->publisher : $podcast->owner_name,
$itunesNamespace,
false
);
$channel->addChild('author', $podcast->publisher ?: $podcast->owner_name, $itunesNamespace, false);
$channel->addChild('link', $podcast->link);
$owner = $channel->addChild('owner', null, $itunesNamespace);
@ -354,7 +349,7 @@ if (! function_exists('get_rss_feed')) {
$item->addChild('episodeType', $episode->type, $itunesNamespace);
// If episode is of type trailer, add podcast:trailer tag on channel level
if ($episode->type == 'trailer') {
if ($episode->type === 'trailer') {
$trailer = $channel->addChild('trailer', $episode->title, $podcastNamespace);
$trailer->addAttribute('pubdate', $episode->published_at->format(DATE_RFC2822));
$trailer->addAttribute(

View File

@ -48,7 +48,7 @@ class Router extends CodeIgniterRouter
$matchedKey = $routeKey;
// Are we dealing with a locale?
if (strpos($routeKey, '{locale}') !== false) {
if (str_contains($routeKey, '{locale}')) {
$routeKey = str_replace('{locale}', '[^/]+', $routeKey);
}
@ -73,7 +73,7 @@ class Router extends CodeIgniterRouter
// Store our locale so CodeIgniter object can
// assign it to the Request.
if (strpos($matchedKey, '{locale}') !== false) {
if (str_contains($matchedKey, '{locale}')) {
preg_match(
'#^' . str_replace('{locale}', '(?<locale>[^/]+)', $matchedKey) . '$#u',
$uri,
@ -183,13 +183,13 @@ class Router extends CodeIgniterRouter
[$controller] = explode('::', (string) $handler);
// Checks `/` in controller name
if (strpos($controller, '/') !== false) {
if (str_contains($controller, '/')) {
throw RouterException::forInvalidControllerName($handler);
}
if (strpos((string) $handler, '$') !== false && strpos($routeKey, '(') !== false) {
if (str_contains((string) $handler, '$') && str_contains($routeKey, '(')) {
// Checks dynamic controller
if (strpos($controller, '$') !== false) {
if (str_contains($controller, '$')) {
throw RouterException::forDynamicController($handler);
}

View File

@ -7,8 +7,6 @@ namespace ViewComponents;
use CodeIgniter\View\ViewDecoratorInterface;
/**
* Class Decorator
*
* Enables rendering of View Components into the views.
*
* Borrowed and adapted from https://github.com/lonnieezell/Bonfire2/

View File

@ -19,7 +19,7 @@ class Theme
protected static $defaultTheme = 'app';
/**
* @var string
* @var ?string
*/
protected static $currentTheme;
@ -71,9 +71,7 @@ class Theme
*/
public static function current(): string
{
return static::$currentTheme !== null
? static::$currentTheme
: static::$defaultTheme;
return static::$currentTheme ?? static::$defaultTheme;
}
/**

View File

@ -26,7 +26,7 @@ class CategoryModel extends Model
protected $primaryKey = 'id';
/**
* @var string[]
* @var list<string>
*/
protected $allowedFields = ['parent_id', 'code', 'apple_category', 'google_category'];

View File

@ -33,7 +33,7 @@ class ClipModel extends Model
protected $primaryKey = 'id';
/**
* @var string[]
* @var list<string>
*/
protected $allowedFields = [
'id',

View File

@ -40,7 +40,7 @@ class EpisodeCommentModel extends UuidModel
protected $uuidFields = ['id', 'in_reply_to_id'];
/**
* @var string[]
* @var list<string>
*/
protected $allowedFields = [
'id',
@ -57,7 +57,7 @@ class EpisodeCommentModel extends UuidModel
];
/**
* @var string[]
* @var list<string>
*/
protected $beforeInsert = ['setCommentId'];

View File

@ -62,7 +62,7 @@ class EpisodeModel extends UuidModel
protected $table = 'episodes';
/**
* @var string[]
* @var list<string>
*/
protected $allowedFields = [
'id',
@ -127,17 +127,17 @@ class EpisodeModel extends UuidModel
];
/**
* @var string[]
* @var list<string>
*/
protected $afterInsert = ['writeEnclosureMetadata', 'clearCache'];
/**
* @var string[]
* @var list<string>
*/
protected $afterUpdate = ['clearCache', 'writeEnclosureMetadata'];
/**
* @var string[]
* @var list<string>
*/
protected $beforeDelete = ['clearCache'];
@ -272,13 +272,7 @@ class EpisodeModel extends UuidModel
$secondsToNextUnpublishedEpisode = $this->getSecondsToNextUnpublishedEpisode($podcastId);
cache()
->save(
$cacheName,
$found,
$secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode
: DECADE,
);
->save($cacheName, $found, $secondsToNextUnpublishedEpisode ?: DECADE);
}
return $found;

View File

@ -26,7 +26,7 @@ class LanguageModel extends Model
protected $primaryKey = 'id';
/**
* @var string[]
* @var list<string>
*/
protected $allowedFields = ['code', 'native_name'];

View File

@ -31,7 +31,7 @@ class LikeModel extends UuidModel
protected $uuidFields = ['comment_id'];
/**
* @var string[]
* @var list<string>
*/
protected $allowedFields = ['actor_id', 'comment_id'];

View File

@ -26,7 +26,7 @@ class PageModel extends Model
protected $primaryKey = 'id';
/**
* @var string[]
* @var list<string>
*/
protected $allowedFields = ['id', 'title', 'slug', 'content_markdown', 'content_html'];
@ -55,19 +55,19 @@ class PageModel extends Model
];
/**
* @var string[]
* @var list<string>
*/
protected $afterInsert = ['clearCache'];
/**
* Before update because slug or title might change
*
* @var string[]
* @var list<string>
*/
protected $beforeUpdate = ['clearCache'];
/**
* @var string[]
* @var list<string>
*/
protected $beforeDelete = ['clearCache'];

View File

@ -26,7 +26,7 @@ class PersonModel extends Model
protected $primaryKey = 'id';
/**
* @var string[]
* @var list<string>
*/
protected $allowedFields = [
'id',
@ -64,19 +64,19 @@ class PersonModel extends Model
];
/**
* @var string[]
* @var list<string>
*/
protected $afterInsert = ['clearCache'];
/**
* clear cache before update if by any chance, the person name changes, so will the person link
*
* @var string[]
* @var list<string>
*/
protected $beforeUpdate = ['clearCache'];
/**
* @var string[]
* @var list<string>
*/
protected $beforeDelete = ['clearCache'];

View File

@ -30,7 +30,7 @@ class PodcastModel extends Model
protected $primaryKey = 'id';
/**
* @var string[]
* @var list<string>
*/
protected $allowedFields = [
'id',
@ -104,29 +104,29 @@ class PodcastModel extends Model
];
/**
* @var string[]
* @var list<string>
*/
protected $beforeInsert = ['setPodcastGUID', 'createPodcastActor'];
/**
* @var string[]
* @var list<string>
*/
protected $afterInsert = ['setActorAvatar'];
/**
* @var string[]
* @var list<string>
*/
protected $afterUpdate = ['updatePodcastActor'];
/**
* clear cache before update if by any chance, the podcast name changes, so will the podcast link
*
* @var string[]
* @var list<string>
*/
protected $beforeUpdate = ['clearCache'];
/**
* @var string[]
* @var list<string>
*/
protected $beforeDelete = ['clearCache'];
@ -259,13 +259,7 @@ class PodcastModel extends Model
$secondsToNextUnpublishedEpisode = $episodeModel->getSecondsToNextUnpublishedEpisode($podcastId);
cache()
->save(
$cacheName,
$found,
$secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode
: DECADE,
);
->save($cacheName, $found, $secondsToNextUnpublishedEpisode ?: DECADE);
}
return $found;
@ -295,13 +289,7 @@ class PodcastModel extends Model
$secondsToNextUnpublishedEpisode = $episodeModel->getSecondsToNextUnpublishedEpisode($podcastId);
cache()
->save(
$cacheName,
$found,
$secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode
: DECADE,
);
->save($cacheName, $found, $secondsToNextUnpublishedEpisode ?: DECADE);
}
return $found;
@ -335,11 +323,7 @@ class PodcastModel extends Model
$secondsToNextUnpublishedEpisode = (new EpisodeModel())->getSecondsToNextUnpublishedEpisode($podcastId);
cache()
->save(
$cacheName,
$defaultQuery,
$secondsToNextUnpublishedEpisode ? $secondsToNextUnpublishedEpisode : DECADE
);
->save($cacheName, $defaultQuery, $secondsToNextUnpublishedEpisode ?: DECADE);
}
return $defaultQuery;

View File

@ -42,7 +42,7 @@ class Alert extends Component
$this->variant = 'default';
}
$glyph = icon(($this->glyph === null ? $variants[$this->variant]['glyph'] : $this->glyph), 'flex-shrink-0 mr-2 text-lg');
$glyph = icon(($this->glyph ?? $variants[$this->variant]['glyph']), 'flex-shrink-0 mr-2 text-lg');
$title = $this->title === null ? '' : '<div class="font-semibold">' . $this->title . '</div>';
$class = 'inline-flex w-full p-2 text-sm border rounded ' . $variants[$this->variant]['class'] . ' ' . $this->class;

View File

@ -39,7 +39,6 @@ class Button extends Component
'warning' => 'shadow-sm text-black bg-yellow-500 hover:bg-yellow-600',
'info' => 'shadow-sm text-white bg-blue-500 hover:bg-blue-600',
'disabled' => 'shadow-sm text-black bg-gray-300 cursor-not-allowed',
'link' => 'text-accent-base bg-transparent underline hover:no-underline',
];
$sizeClass = [

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
use CodeIgniter\CLI\CLI;
// The main Exception
CLI::write('[' . get_class($exception) . ']', 'light_gray', 'red');
CLI::write('[' . $exception::class . ']', 'light_gray', 'red');
CLI::write($message);
CLI::write('at ' . CLI::color(clean_path($exception->getFile()) . ':' . $exception->getLine(), 'green'));
CLI::newLine();
@ -16,7 +16,7 @@ while ($prevException = $last->getPrevious()) {
$last = $prevException;
CLI::write(' Caused by:');
CLI::write(' [' . get_class($prevException) . ']', 'red');
CLI::write(' [' . $prevException::class . ']', 'red');
CLI::write(' ' . $prevException->getMessage());
CLI::write(' at ' . CLI::color(clean_path($prevException->getFile()) . ':' . $prevException->getLine(), 'green'));
CLI::newLine();
@ -52,20 +52,11 @@ if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) {
$function .= $padClass . $error['function'];
}
$args = implode(', ', array_map(static function ($value) {
switch (true) {
case is_object($value):
return 'Object(' . get_class($value) . ')';
case is_array($value):
return count($value) ? '[...]' : '[]';
case $value === null:
return 'null'; // return the lowercased version
default:
return var_export($value, true);
}
$args = implode(', ', array_map(static fn ($value) => match (true) {
is_object($value) => 'Object(' . $value::class . ')',
is_array($value) => count($value) ? '[...]' : '[]',
$value === null => 'null', // return the lowercased version
default => var_export($value, true),
}, array_values($error['args'] ?? [])));
$function .= '(' . $args . ')';

View File

@ -18,62 +18,69 @@ body {
margin: 0;
padding: 0;
}
h1 {
font-weight: lighter;
letter-spacing: 0.8;
font-size: 3rem;
color: var(--dark-text-color);
margin: 0;
}
h1.headline {
margin-top: 20%;
font-size: 5rem;
}
.text-center {
text-align: center;
}
p.lead {
font-size: 1.6rem;
}
.container {
max-width: 75rem;
margin: 0 auto;
padding: 1rem;
}
.header {
background: var(--light-bg-color);
color: var(--dark-text-color);
}
.header .container {
padding: 1rem 1.75rem 1.75rem 1.75rem;
padding: 1rem;
}
.header h1 {
font-size: 2.5rem;
font-weight: 500;
}
.header p {
font-size: 1.2rem;
margin: 0;
line-height: 2.5;
}
.header a {
color: var(--brand-primary-color);
margin-left: 2rem;
display: none;
text-decoration: none;
}
.header:hover a {
display: inline;
}
.footer {
.environment {
background: var(--dark-bg-color);
color: var(--light-text-color);
}
.footer .container {
border-top: 1px solid #e7e7e7;
margin-top: 1rem;
text-align: center;
padding: 0.2rem;
}
.source {
@ -86,17 +93,21 @@ p.lead {
margin: 0;
overflow-x: scroll;
}
.source span.line {
line-height: 1.4;
}
.source span.line .number {
color: #666;
}
.source .line .highlight {
display: block;
background: var(--dark-text-color);
color: var(--light-text-color);
}
.source span.highlight .number {
color: #fff;
}
@ -108,37 +119,44 @@ p.lead {
padding: 0;
margin-bottom: -1px;
}
.tabs li {
display: inline;
}
.tabs a:link,
.tabs a:visited {
padding: 0rem 1rem;
padding: 0 1rem;
line-height: 2.7;
text-decoration: none;
color: var(--dark-text-color);
background: var(--light-bg-color);
border: 1px solid rgba(0, 0, 0, 0.15);
border: 1px solid rgb(0 0 0 / 15%);
border-bottom: 0;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
display: inline-block;
}
.tabs a:hover {
background: var(--light-bg-color);
border-color: rgba(0, 0, 0, 0.15);
border-color: rgb(0 0 0 / 15%);
}
.tabs a.active {
background: var(--main-bg-color);
color: var(--main-text-color);
}
.tab-content {
background: var(--main-bg-color);
border: 1px solid rgba(0, 0, 0, 0.15);
border: 1px solid rgb(0 0 0 / 15%);
}
.content {
padding: 1rem;
}
.hide {
display: none;
}
@ -153,26 +171,26 @@ p.lead {
border-radius: 5px;
color: #31708f;
}
ul,
ol {
line-height: 1.8;
}
table {
width: 100%;
overflow: hidden;
}
th {
text-align: left;
border-bottom: 1px solid #e7e7e7;
padding-bottom: 0.5rem;
}
td {
padding: 0.2rem 0.5rem 0.2rem 0;
}
tr:hover td {
background: #f1f1f1;
}
td pre {
white-space: pre-wrap;
}
@ -180,20 +198,25 @@ td pre {
.trace a {
color: inherit;
}
.trace table {
width: auto;
}
.trace tr td:first-child {
min-width: 5em;
font-weight: bold;
}
.trace td {
background: var(--light-bg-color);
padding: 0 1rem;
}
.trace td pre {
margin: 0;
}
.args {
display: none;
}

View File

@ -3,6 +3,7 @@
declare(strict_types=1);
use CodeIgniter\CodeIgniter;
use CodeIgniter\HTTP\Header;
use Config\Services;
$errorId = uniqid('error', true);
@ -26,6 +27,12 @@ $errorId = uniqid('error', true);
<!-- Header -->
<div class="header">
<div class="environment">
Displayed at <?= esc(date('H:i:sa')) ?> &mdash;
PHP: <?= esc(PHP_VERSION) ?> &mdash;
CodeIgniter: <?= esc(CodeIgniter::CI_VERSION) ?> --
Environment: <?= ENVIRONMENT ?>
</div>
<div class="container">
<h1><?= esc($title), esc($exception->getCode() ? ' #' . $exception->getCode() : '') ?></h1>
<p>
@ -57,10 +64,10 @@ while ($prevException = $last->getPrevious()) {
<pre>
Caused by:
<?= esc(get_class($prevException)), esc($prevException->getCode() ? ' #' . $prevException->getCode() : '') ?>
<?= esc($prevException::class), esc($prevException->getCode() ? ' #' . $prevException->getCode() : '') ?>
<?= nl2br(esc($prevException->getMessage())) ?>
<a href="https://www.duckduckgo.com/?q=<?= urlencode(get_class($prevException) . ' ' . preg_replace('#\'.*\'|".*"#Us', '', $prevException->getMessage())) ?>"
<a href="https://www.duckduckgo.com/?q=<?= urlencode($prevException::class . ' ' . preg_replace('#\'.*\'|".*"#Us', '', $prevException->getMessage())) ?>"
rel="noreferrer" target="_blank">search &rarr;</a>
<?= esc(clean_path($prevException->getFile()) . ':' . $prevException->getLine()) ?>
</pre>
@ -117,7 +124,7 @@ while ($prevException = $last->getPrevious()) {
<?php
$params = null;
// Reflection by name is not available for closure function
if (substr($row['function'], -1) !== '}') {
if (! str_ends_with($row['function'], '}')) {
$mirror = isset($row['class']) ? new ReflectionMethod($row['class'], $row['function']) : new ReflectionFunction($row['function']);
$params = $mirror->getParameters();
}
@ -231,7 +238,7 @@ while ($prevException = $last->getPrevious()) {
</tr>
<tr>
<td>HTTP Method</td>
<td><?= esc(strtoupper($request->getMethod())) ?></td>
<td><?= esc($request->getMethod()) ?></td>
</tr>
<tr>
<td>IP Address</td>
@ -315,22 +322,22 @@ while ($prevException = $last->getPrevious()) {
</tr>
</thead>
<tbody>
<?php foreach ($headers as $value) : ?>
<?php
if (empty($value)) {
continue;
}
if (! is_array($value)) {
$value = [$value];
} ?>
<?php foreach ($value as $h) : ?>
<tr>
<td><?= esc($h->getName(), 'html') ?></td>
<td><?= esc($h->getValueLine(), 'html') ?></td>
</tr>
<?php endforeach; ?>
<?php endforeach; ?>
<?php foreach ($headers as $name => $value) : ?>
<tr>
<td><?= esc($name, 'html') ?></td>
<td>
<?php
if ($value instanceof Header) {
echo esc($value->getValueLine(), 'html');
} else {
foreach ($value as $i => $header) {
echo ' (' . $i + 1 . ') ' . esc($header->getValueLine(), 'html');
}
}
?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
@ -352,8 +359,6 @@ $response->setStatusCode(http_response_code());
<?php $headers = $response->headers(); ?>
<?php if (! empty($headers)) : ?>
<?php natsort($headers) ?>
<h3>Headers</h3>
<table>
@ -364,12 +369,22 @@ $response->setStatusCode(http_response_code());
</tr>
</thead>
<tbody>
<?php foreach (array_keys($headers) as $name) : ?>
<?php foreach ($headers as $name => $value) : ?>
<tr>
<td><?= esc($name, 'html') ?></td>
<td><?= esc($response->getHeaderLine($name), 'html') ?></td>
<td>
<?php
if ($value instanceof Header) {
echo esc($response->getHeaderLine($name), 'html');
} else {
foreach ($value as $i => $header) {
echo ' (' . $i + 1 . ') ' . esc($header->getValueLine(), 'html');
}
}
?>
</td>
</tr>
<?php endforeach; ?>
<?php endforeach; ?>
</tbody>
</table>
@ -414,18 +429,5 @@ $response->setStatusCode(http_response_code());
</div> <!-- /container -->
<?php endif; ?>
<div class="footer">
<div class="container">
<p>
Displayed at <?= esc(date('H:i:sa')) ?> &mdash;
PHP: <?= esc(PHP_VERSION) ?> &mdash;
CodeIgniter: <?= esc(CodeIgniter::CI_VERSION) ?> --
Environment: <?= ENVIRONMENT ?>
</p>
</div>
</div>
</body>
</html>

2
builds
View File

@ -1,6 +1,8 @@
#!/usr/bin/env php
<?php
declare(strict_types=1);
define('LATEST_RELEASE', '^4.0');
define('GITHUB_URL', 'https://github.com/codeigniter4/codeigniter4');

View File

@ -9,38 +9,41 @@
"php": "^8.1",
"adaures/ipcat-php": "^v1.0.0",
"adaures/podcast-persons-taxonomy": "^v1.0.1",
"aws/aws-sdk-php": "^3.300.8",
"aws/aws-sdk-php": "^3.305.3",
"chrisjean/php-ico": "^1.0.4",
"cocur/slugify": "^v4.5.1",
"codeigniter4/framework": "v4.4.6",
"codeigniter4/framework": "v4.5.1",
"codeigniter4/settings": "v2.2.0",
"codeigniter4/shield": "v1.0.1",
"codeigniter4/shield": "v1.0.3",
"codeigniter4/tasks": "dev-develop",
"geoip2/geoip2": "v3.0.0",
"james-heinrich/getid3": "^2.0.0-beta5",
"league/commonmark": "^2.4.2",
"league/html-to-markdown": "5.1.1",
"melbahja/seo": "^v2.1.1",
"michalsn/codeigniter4-uuid": "v1.0.2",
"mpratt/embera": "^2.0.36",
"michalsn/codeigniter4-uuid": "v1.1.0",
"mpratt/embera": "^2.0.38",
"opawg/user-agents-v2-php": "dev-main",
"phpseclib/phpseclib": "~2.0.47",
"vlucas/phpdotenv": "v5.6.0",
"whichbrowser/parser": "^v2.1.7",
"whichbrowser/parser": "^v2.1.8",
"yassinedoghri/podcast-feed": "dev-main"
},
"require-dev": {
"captainhook/captainhook": "^5.21.2",
"captainhook/captainhook": "^5.23.0",
"codeigniter/phpstan-codeigniter": "v1.4.3",
"mikey179/vfsstream": "^v1.6.11",
"phpstan/extension-installer": "^1.3.1",
"phpstan/phpstan": "^1.10.59",
"phpunit/phpunit": "^10.5.11",
"rector/rector": "^1.0.1",
"symplify/coding-standard": "^12.0.7",
"symplify/easy-coding-standard": "^12.0.13"
"phpstan/phpstan": "^1.10.67",
"phpunit/phpunit": "^10.5.20",
"rector/rector": "^1.0.4",
"symplify/coding-standard": "^12.1.4",
"symplify/easy-coding-standard": "^12.1.14"
},
"autoload": {
"psr-4": {
"App\\": "app/"
},
"exclude-from-classmap": [
"**/Database/Migrations/**"
]

626
composer.lock generated

File diff suppressed because it is too large Load Diff

30
ecs.php
View File

@ -10,26 +10,20 @@ use Symplify\CodingStandard\Fixer\LineLength\LineLengthFixer;
use Symplify\CodingStandard\Fixer\Naming\StandardizeHereNowDocKeywordFixer;
use Symplify\CodingStandard\Fixer\Spacing\MethodChainingNewlineFixer;
use Symplify\EasyCodingStandard\Config\ECSConfig;
use Symplify\EasyCodingStandard\ValueObject\Set\SetList;
return static function (ECSConfig $ecsConfig): void {
// alternative to CLI arguments, easier to maintain and extend
$ecsConfig->paths([
return ECSConfig::configure()
->withPaths([
__DIR__ . '/app',
__DIR__ . '/modules',
__DIR__ . '/themes',
__DIR__ . '/tests',
__DIR__ . '/public',
__DIR__ . '/builds',
__DIR__ . '/ecs.php',
__DIR__ . '/preload.php',
__DIR__ . '/rector.php',
__DIR__ . '/spark',
]);
$ecsConfig->sets([SetList::CLEAN_CODE, SetList::COMMON, SetList::SYMPLIFY, SetList::PSR_12]);
$ecsConfig->skip([
])
->withRootFiles()
->withPreparedSets(cleanCode: true, common: true, symplify: true, strict: true, psr12: true)
->withSkip([
// skip specific generated files
__DIR__ . '/modules/Admin/Language/*/PersonsTaxonomy.php',
@ -40,11 +34,7 @@ return static function (ECSConfig $ecsConfig): void {
__DIR__ . '/app/Helpers/components_helper.php',
],
LineLengthFixer::class => [
__DIR__ . '/app/Views/*',
__DIR__ . '/modules/**/Views/*',
__DIR__ . '/themes/*',
],
LineLengthFixer::class => [__DIR__ . '/app/Views/*', __DIR__ . '/modules/**/Views/*', __DIR__ . '/themes/*'],
IndentationTypeFixer::class => [
__DIR__ . '/app/Views/*',
@ -65,11 +55,9 @@ return static function (ECSConfig $ecsConfig): void {
BinaryOperatorSpacesFixer::class => [__DIR__ . '/app/Language/*', __DIR__ . '/modules/**/Language/*'],
AssignmentInConditionSniff::class,
]);
$ecsConfig->ruleWithConfiguration(BinaryOperatorSpacesFixer::class, [
])
->withConfiguredRule(BinaryOperatorSpacesFixer::class, [
'operators' => [
'=>' => 'align_single_space_minimal',
],
]);
};

80
env
View File

@ -30,12 +30,15 @@
# DATABASE
#--------------------------------------------------------------------
# If you use MySQLi as tests, first update the values of Config\Database::$tests.
# database.default.hostname = localhost
# database.default.database = ci4
# database.default.username = root
# database.default.password = root
# database.default.DBDriver = MySQLi
# database.default.DBPrefix =
# database.tests.charset = utf8mb4
# database.tests.DBCollat = utf8mb4_general_ci
# database.default.port = 3306
# database.tests.hostname = localhost
@ -46,98 +49,21 @@
# database.tests.DBPrefix =
# database.tests.port = 3306
#--------------------------------------------------------------------
# CONTENT SECURITY POLICY
#--------------------------------------------------------------------
# contentsecuritypolicy.reportOnly = false
# contentsecuritypolicy.defaultSrc = 'none'
# contentsecuritypolicy.scriptSrc = 'self'
# contentsecuritypolicy.styleSrc = 'self'
# contentsecuritypolicy.imageSrc = 'self'
# contentsecuritypolicy.baseURI = null
# contentsecuritypolicy.childSrc = null
# contentsecuritypolicy.connectSrc = 'self'
# contentsecuritypolicy.fontSrc = null
# contentsecuritypolicy.formAction = null
# contentsecuritypolicy.frameAncestors = null
# contentsecuritypolicy.frameSrc = null
# contentsecuritypolicy.mediaSrc = null
# contentsecuritypolicy.objectSrc = null
# contentsecuritypolicy.pluginTypes = null
# contentsecuritypolicy.reportURI = null
# contentsecuritypolicy.sandbox = false
# contentsecuritypolicy.upgradeInsecureRequests = false
# contentsecuritypolicy.styleNonceTag = '{csp-style-nonce}'
# contentsecuritypolicy.scriptNonceTag = '{csp-script-nonce}'
# contentsecuritypolicy.autoNonce = true
#--------------------------------------------------------------------
# COOKIE
#--------------------------------------------------------------------
# cookie.prefix = ''
# cookie.expires = 0
# cookie.path = '/'
# cookie.domain = ''
# cookie.secure = false
# cookie.httponly = false
# cookie.samesite = 'Lax'
# cookie.raw = false
#--------------------------------------------------------------------
# ENCRYPTION
#--------------------------------------------------------------------
# encryption.key =
# encryption.driver = OpenSSL
# encryption.blockSize = 16
# encryption.digest = SHA512
#--------------------------------------------------------------------
# HONEYPOT
#--------------------------------------------------------------------
# honeypot.hidden = 'true'
# honeypot.label = 'Fill This Field'
# honeypot.name = 'honeypot'
# honeypot.template = '<label>{label}</label><input type="text" name="{name}" value=""/>'
# honeypot.container = '<div style="display:none">{template}</div>'
#--------------------------------------------------------------------
# SECURITY
#--------------------------------------------------------------------
# security.csrfProtection = 'cookie'
# security.tokenRandomize = false
# security.tokenName = 'csrf_token_name'
# security.headerName = 'X-CSRF-TOKEN'
# security.cookieName = 'csrf_cookie_name'
# security.expires = 7200
# security.regenerate = true
# security.redirect = false
# security.samesite = 'Lax'
#--------------------------------------------------------------------
# SESSION
#--------------------------------------------------------------------
# session.driver = 'CodeIgniter\Session\Handlers\FileHandler'
# session.cookieName = 'ci_session'
# session.expiration = 7200
# session.savePath = null
# session.matchIP = false
# session.timeToUpdate = 300
# session.regenerateDestroy = false
#--------------------------------------------------------------------
# LOGGER
#--------------------------------------------------------------------
# logger.threshold = 4
#--------------------------------------------------------------------
# CURLRequest
#--------------------------------------------------------------------
# curlrequest.shareOptions = false

View File

@ -12,8 +12,6 @@ use Psr\Log\LoggerInterface;
use ViewThemes\Theme;
/**
* Class BaseController
*
* BaseController provides a convenient place for loading components and performing functions that are needed by all
* your controllers. Extend this class in any new controllers: class Home extends BaseController
*

View File

@ -326,12 +326,8 @@ class EpisodeController extends BaseController
$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->number = $this->request->getPost('episode_number') ?: null;
$this->episode->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');

View File

@ -17,7 +17,7 @@ use CodeIgniter\HTTP\RedirectResponse;
class PageController extends BaseController
{
protected ?Page $page;
protected ?Page $page = null;
public function _remap(string $method, string ...$params): mixed
{

View File

@ -18,7 +18,7 @@ use Modules\Media\Models\MediaModel;
class PersonController extends BaseController
{
protected ?Person $person;
protected ?Person $person = null;
public function _remap(string $method, string ...$params): mixed
{

View File

@ -138,6 +138,9 @@ return [
'If you need RSS tags that Castopod does not handle, set them here.',
'custom_rss' => 'Custom RSS tags for the podcast',
'custom_rss_hint' => 'This will be injected within the ❬channel❭ tag.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'New feed URL',
'new_feed_url_hint' => 'Use this field when you move to another domain or podcast hosting platform. By default, the value is set to the current RSS URL if the podcast is imported.',
'old_feed_url' => 'Old feed URL',

View File

@ -141,6 +141,9 @@ return [
'M\'ho peus ezhomm eus balizennoù RSS ha n\'eus ket anezho e Castopod e c\'hellit o lakaat amañ.',
'custom_rss' => 'Balizennoù RSS personelaet evit ar podkast',
'custom_rss_hint' => 'An dra-se a vo ouzhpennet e-barzh ar valizenn ❬channel❭.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'URL nevez ar wazh',
'new_feed_url_hint' => 'Implijit ar vaezienn-mañ pa cheñchit anv domani pe savenn herberc\'hiañ ho podkast. M\'eo enporzhiet ar podkast e vez lakaet enni URL a-vremañ ar wazh dre ziouer.',
'old_feed_url' => 'URL kozh ar wazh',

View File

@ -138,6 +138,9 @@ return [
'Si necessiteu etiquetes RSS que Castopod no manega, configureu-les aquí.',
'custom_rss' => 'Etiquetes RSS personalitzades per al podcast',
'custom_rss_hint' => 'Això s\'injectarà dins de l\'etiqueta ❬channel❭.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'Nova adreça URL del fil RSS',
'new_feed_url_hint' => 'Utilitzeu aquest camp quan us moveu a un altre domini o plataforma d\'allotjament de podcasts. De manera predeterminada, el valor s\'estableix a l\'URL RSS actual si s\'importa el podcast.',
'old_feed_url' => 'Antiga adreça URL del fil RSS',

View File

@ -138,6 +138,9 @@ return [
'If you need RSS tags that Castopod does not handle, set them here.',
'custom_rss' => 'Custom RSS tags for the podcast',
'custom_rss_hint' => 'This will be injected within the ❬channel❭ tag.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'New feed URL',
'new_feed_url_hint' => 'Use this field when you move to another domain or podcast hosting platform. By default, the value is set to the current RSS URL if the podcast is imported.',
'old_feed_url' => 'Old feed URL',

View File

@ -138,6 +138,9 @@ return [
'Wenn RSS-Tags benötigt werden, die Castopod nicht verwendet, können diese hier gesetzt werden.',
'custom_rss' => 'Eigene RSS-Tags für den Podcast',
'custom_rss_hint' => 'Dies wird innerhalb des ❬channel❭ Tags eingefügt.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'Neue Feed-URL',
'new_feed_url_hint' => 'Benutzen Sie dieses Feld, wenn Sie zu einer anderen Domain oder Podcast-Plattform wechseln. Standardmäßig wird der Wert auf die aktuelle RSS URL gesetzt, wenn der Podcast importiert wird.',
'old_feed_url' => 'Alte Feed-URL',

View File

@ -138,6 +138,9 @@ return [
'If you need RSS tags that Castopod does not handle, set them here.',
'custom_rss' => 'Custom RSS tags for the podcast',
'custom_rss_hint' => 'This will be injected within the ❬channel❭ tag.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'New feed URL',
'new_feed_url_hint' => 'Use this field when you move to another domain or podcast hosting platform. By default, the value is set to the current RSS URL if the podcast is imported.',
'old_feed_url' => 'Old feed URL',

View File

@ -138,6 +138,9 @@ return [
'Si necesita etiquetas RSS que Castopod no maneja, póngalas aquí.',
'custom_rss' => 'Etiquetas RSS personalizadas para el podcast',
'custom_rss_hint' => 'Esto se inyectará dentro de la etiqueta de canal.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'Nueva URL de feed',
'new_feed_url_hint' => 'Utilice este campo cuando se mueva a otro dominio o plataforma de alojamiento podcast. De forma predeterminada, el valor se establece en la URL actual de RSS si el podcast es importado.',
'old_feed_url' => 'Antigua URL del feed RSS',

View File

@ -138,6 +138,9 @@ return [
'If you need RSS tags that Castopod does not handle, set them here.',
'custom_rss' => 'Custom RSS tags for the podcast',
'custom_rss_hint' => 'This will be injected within the ❬channel❭ tag.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'New feed URL',
'new_feed_url_hint' => 'Use this field when you move to another domain or podcast hosting platform. By default, the value is set to the current RSS URL if the podcast is imported.',
'old_feed_url' => 'Old feed URL',

View File

@ -138,6 +138,9 @@ return [
'If you need RSS tags that Castopod does not handle, set them here.',
'custom_rss' => 'Custom RSS tags for the podcast',
'custom_rss_hint' => 'This will be injected within the ❬channel❭ tag.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'New feed URL',
'new_feed_url_hint' => 'Use this field when you move to another domain or podcast hosting platform. By default, the value is set to the current RSS URL if the podcast is imported.',
'old_feed_url' => 'Old feed URL',

View File

@ -138,6 +138,9 @@ return [
'Si vous avez besoin dune balise que nous navons pas couverte, définissez-la ici.',
'custom_rss' => 'Balises RSS personnalisées pour le podcast',
'custom_rss_hint' => 'Ceci sera injecté dans la balise ❬channel❭.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'URL du nouveau flux',
'new_feed_url_hint' => 'Utilisez ce champ lorsque vous déplacez ce podcast vers un autre domaine ou que vous changez dhébergeur. Par défaut, ce champ est rempli avec lURL du flux actuel si le podcast est importé.',
'old_feed_url' => 'URL de l\'ancien flux',

View File

@ -138,6 +138,9 @@ return [
'If you need RSS tags that Castopod does not handle, set them here.',
'custom_rss' => 'Custom RSS tags for the podcast',
'custom_rss_hint' => 'This will be injected within the ❬channel❭ tag.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'New feed URL',
'new_feed_url_hint' => 'Use this field when you move to another domain or podcast hosting platform. By default, the value is set to the current RSS URL if the podcast is imported.',
'old_feed_url' => 'Old feed URL',

View File

@ -138,6 +138,9 @@ return [
'If you need RSS tags that Castopod does not handle, set them here.',
'custom_rss' => 'Custom RSS tags for the podcast',
'custom_rss_hint' => 'This will be injected within the ❬channel❭ tag.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'New feed URL',
'new_feed_url_hint' => 'Use this field when you move to another domain or podcast hosting platform. By default, the value is set to the current RSS URL if the podcast is imported.',
'old_feed_url' => 'Old feed URL',

View File

@ -138,6 +138,9 @@ return [
'If you need RSS tags that Castopod does not handle, set them here.',
'custom_rss' => 'Custom RSS tags for the podcast',
'custom_rss_hint' => 'This will be injected within the ❬channel❭ tag.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'New feed URL',
'new_feed_url_hint' => 'Use this field when you move to another domain or podcast hosting platform. By default, the value is set to the current RSS URL if the podcast is imported.',
'old_feed_url' => 'Old feed URL',

View File

@ -138,6 +138,9 @@ return [
'If you need RSS tags that Castopod does not handle, set them here.',
'custom_rss' => 'Custom RSS tags for the podcast',
'custom_rss_hint' => 'This will be injected within the ❬channel❭ tag.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'New feed URL',
'new_feed_url_hint' => 'Use this field when you move to another domain or podcast hosting platform. By default, the value is set to the current RSS URL if the podcast is imported.',
'old_feed_url' => 'Old feed URL',

View File

@ -138,6 +138,9 @@ return [
'If you need RSS tags that Castopod does not handle, set them here.',
'custom_rss' => 'Custom RSS tags for the podcast',
'custom_rss_hint' => 'This will be injected within the ❬channel❭ tag.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'New feed URL',
'new_feed_url_hint' => 'Use this field when you move to another domain or podcast hosting platform. By default, the value is set to the current RSS URL if the podcast is imported.',
'old_feed_url' => 'Old feed URL',

View File

@ -138,6 +138,9 @@ return [
'If you need RSS tags that Castopod does not handle, set them here.',
'custom_rss' => 'Custom RSS tags for the podcast',
'custom_rss_hint' => 'This will be injected within the ❬channel❭ tag.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'New feed URL',
'new_feed_url_hint' => 'Use this field when you move to another domain or podcast hosting platform. By default, the value is set to the current RSS URL if the podcast is imported.',
'old_feed_url' => 'Old feed URL',

View File

@ -138,6 +138,9 @@ return [
'If you need RSS tags that Castopod does not handle, set them here.',
'custom_rss' => 'Custom RSS tags for the podcast',
'custom_rss_hint' => 'This will be injected within the ❬channel❭ tag.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'New feed URL',
'new_feed_url_hint' => 'Use this field when you move to another domain or podcast hosting platform. By default, the value is set to the current RSS URL if the podcast is imported.',
'old_feed_url' => 'Old feed URL',

View File

@ -138,6 +138,9 @@ return [
'If you need RSS tags that Castopod does not handle, set them here.',
'custom_rss' => 'Custom RSS tags for the podcast',
'custom_rss_hint' => 'This will be injected within the ❬channel❭ tag.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'New feed URL',
'new_feed_url_hint' => 'Use this field when you move to another domain or podcast hosting platform. By default, the value is set to the current RSS URL if the podcast is imported.',
'old_feed_url' => 'Old feed URL',

View File

@ -138,6 +138,9 @@ return [
'If you need RSS tags that Castopod does not handle, set them here.',
'custom_rss' => 'Custom RSS tags for the podcast',
'custom_rss_hint' => 'This will be injected within the ❬channel❭ tag.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'New feed URL',
'new_feed_url_hint' => 'Use this field when you move to another domain or podcast hosting platform. By default, the value is set to the current RSS URL if the podcast is imported.',
'old_feed_url' => 'Old feed URL',

View File

@ -138,6 +138,9 @@ return [
'If you need RSS tags that Castopod does not handle, set them here.',
'custom_rss' => 'Custom RSS tags for the podcast',
'custom_rss_hint' => 'This will be injected within the ❬channel❭ tag.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'New feed URL',
'new_feed_url_hint' => 'Use this field when you move to another domain or podcast hosting platform. By default, the value is set to the current RSS URL if the podcast is imported.',
'old_feed_url' => 'Old feed URL',

View File

@ -138,6 +138,9 @@ return [
'Viss du treng RSS-merkelappar som Castopod ikkje handterer, kan du skriva dei inn her.',
'custom_rss' => 'Eigne RSS-merkelappar for podkasten',
'custom_rss_hint' => 'Dette blir sett inn i ❬channel❭-elementet.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'Ny straum-URL',
'new_feed_url_hint' => 'Bruk dette feltet når du flyttar til eit anna domene eller vertsplattform. Standardvalet for verdien er den noverande RSS-adresse viss podkasten er importert.',
'old_feed_url' => 'Gamal straum-URL',

View File

@ -138,6 +138,9 @@ return [
'If you need RSS tags that Castopod does not handle, set them here.',
'custom_rss' => 'Custom RSS tags for the podcast',
'custom_rss_hint' => 'This will be injected within the ❬channel❭ tag.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'New feed URL',
'new_feed_url_hint' => 'Use this field when you move to another domain or podcast hosting platform. By default, the value is set to the current RSS URL if the podcast is imported.',
'old_feed_url' => 'Old feed URL',

View File

@ -138,6 +138,9 @@ return [
'Jeśli potrzebujesz tagów RSS, których Castopod nie obsługuje, ustaw je tutaj.',
'custom_rss' => 'Własne tagi RSS dla podcastu',
'custom_rss_hint' => 'Zostaną wstawione w tagu ❬channel❭.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'Nowy adres URL kanału',
'new_feed_url_hint' => 'Użyj tego pola, gdy przenosisz się do innej domeny lub platformy hostingowej podcastu. Domyślnie wartość jest ustawiona na bieżący adres URL RSS, jeśli podcast jest importowany.',
'old_feed_url' => 'Old feed URL',

View File

@ -138,6 +138,9 @@ return [
'Se você precisa de tags RSS que Castopod não lida, defina-as aqui.',
'custom_rss' => 'Tags RSS personalizadas para o podcast',
'custom_rss_hint' => 'Isto será injetado dentro da tag ❬channel❭.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'Nova URL de feed',
'new_feed_url_hint' => 'Use este campo ao mover este podcast para outro domínio ou alterar hosts. Por padrão, este campo é preenchido com a URL do feed RSS atual se o podcast for importado.',
'old_feed_url' => 'URL de feed antigo',

View File

@ -138,6 +138,9 @@ return [
'If you need RSS tags that Castopod does not handle, set them here.',
'custom_rss' => 'Custom RSS tags for the podcast',
'custom_rss_hint' => 'This will be injected within the ❬channel❭ tag.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'New feed URL',
'new_feed_url_hint' => 'Use this field when you move to another domain or podcast hosting platform. By default, the value is set to the current RSS URL if the podcast is imported.',
'old_feed_url' => 'Old feed URL',

View File

@ -138,6 +138,9 @@ return [
'If you need RSS tags that Castopod does not handle, set them here.',
'custom_rss' => 'Custom RSS tags for the podcast',
'custom_rss_hint' => 'This will be injected within the ❬channel❭ tag.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'New feed URL',
'new_feed_url_hint' => 'Use this field when you move to another domain or podcast hosting platform. By default, the value is set to the current RSS URL if the podcast is imported.',
'old_feed_url' => 'Old feed URL',

View File

@ -138,6 +138,9 @@ return [
'If you need RSS tags that Castopod does not handle, set them here.',
'custom_rss' => 'Custom RSS tags for the podcast',
'custom_rss_hint' => 'This will be injected within the ❬channel❭ tag.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'New feed URL',
'new_feed_url_hint' => 'Use this field when you move to another domain or podcast hosting platform. By default, the value is set to the current RSS URL if the podcast is imported.',
'old_feed_url' => 'Old feed URL',

View File

@ -138,6 +138,9 @@ return [
'If you need RSS tags that Castopod does not handle, set them here.',
'custom_rss' => 'Custom RSS tags for the podcast',
'custom_rss_hint' => 'This will be injected within the ❬channel❭ tag.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'New feed URL',
'new_feed_url_hint' => 'Use this field when you move to another domain or podcast hosting platform. By default, the value is set to the current RSS URL if the podcast is imported.',
'old_feed_url' => 'Old feed URL',

View File

@ -138,6 +138,9 @@ return [
'Ukoliko su vam potrebni RSS tagovi koje Castopod ne obrađuje, postavite ih ovde.',
'custom_rss' => 'Posebni RSS tagovi epizode',
'custom_rss_hint' => 'Ovo će biti ubačeno u ❬channel❭ tag.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'Novi URL fid',
'new_feed_url_hint' => 'Koristite ovo polje kada prelazite na drugi domen ili platformu za hostovanje podkasta. Podrazumevano, vrednost je podešena na trenutni RSS URL ako je podkast uvezen.',
'old_feed_url' => 'Stari URL fid',

View File

@ -138,6 +138,9 @@ return [
'Om du behöver RSS-taggar som Castopod inte hanterar, ställ in dem här.',
'custom_rss' => 'Anpassade RSS-taggar för podcasten',
'custom_rss_hint' => 'Detta kommer att injiceras i <unk> kanal<unk> taggen.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'Ny flödes-URL',
'new_feed_url_hint' => 'Använd detta fält när du flyttar till en annan domän eller podcast webbhotell. Som standard är värdet inställt på nuvarande RSS-URL om podcasten importeras.',
'old_feed_url' => 'Gammal flödes-URL',

View File

@ -138,6 +138,9 @@ return [
'If you need RSS tags that Castopod does not handle, set them here.',
'custom_rss' => 'Custom RSS tags for the podcast',
'custom_rss_hint' => 'This will be injected within the ❬channel❭ tag.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'New feed URL',
'new_feed_url_hint' => 'Use this field when you move to another domain or podcast hosting platform. By default, the value is set to the current RSS URL if the podcast is imported.',
'old_feed_url' => 'Old feed URL',

View File

@ -138,6 +138,9 @@ return [
'如果您需要 Castopod 无法处理的 RSS 标签,请在此处设置它们。',
'custom_rss' => '播客的自定义 RSS 标签',
'custom_rss_hint' => '这将被注入到 ❬channel❭ 标签中。',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => '新摘要网址',
'new_feed_url_hint' => '当你迁移到另一个域或播客托管平台时,请使用此字段。 默认情况下,播客导入时,该值为当前的 RSS 网址。',
'old_feed_url' => '旧摘要网址',

View File

@ -138,6 +138,9 @@ return [
'If you need RSS tags that Castopod does not handle, set them here.',
'custom_rss' => 'Custom RSS tags for the podcast',
'custom_rss_hint' => 'This will be injected within the ❬channel❭ tag.',
'verify_txt' => 'Ownership verification TXT',
'verify_txt_hint' => 'Rather than relying on email, certain third-party services may confirm your podcast ownership by requesting you to embed a verification text within your feed.',
'verify_txt_helper' => 'This text is injected into a <podcast:txt purpose="verify"> tag.',
'new_feed_url' => 'New feed URL',
'new_feed_url_hint' => 'Use this field when you move to another domain or podcast hosting platform. By default, the value is set to the current RSS URL if the podcast is imported.',
'old_feed_url' => 'Old feed URL',

View File

@ -30,9 +30,7 @@ trait AnalyticsTrait
$referer = $session->get('referer');
$domain =
parse_url((string) $referer, PHP_URL_HOST) === null
? '- Direct -'
: parse_url((string) $referer, PHP_URL_HOST);
parse_url((string) $referer, PHP_URL_HOST) ?? '- Direct -';
parse_str((string) parse_url((string) $referer, PHP_URL_QUERY), $queries);
$keywords = $queries['q'] ?? null;

Some files were not shown because too many files have changed in this diff Show More