fix(security): add csrf filter + prevent xss attacks by escaping user input

- update CI4 to v4.1.9's stable production package
- update php and js dependencies to latest
This commit is contained in:
Yassine Doghri 2022-03-04 14:33:48 +00:00
parent a597cf4ecf
commit cd2e1e1dc3
182 changed files with 4295 additions and 4099 deletions

View File

@ -24,7 +24,7 @@ class Database extends Config
/**
* The default database connection.
*
* @var array<string, string|bool|int|array>
* @var array<string, mixed>
*/
public array $default = [
'DSN' => '',
@ -51,7 +51,7 @@ class Database extends Config
*
* @noRector StringClassNameToClassConstantRector
*
* @var array<string, string|bool|int|array>
* @var array<string, mixed>
*/
public array $tests = [
'DSN' => '',

View File

@ -11,6 +11,7 @@ declare(strict_types=1);
namespace Config;
use App\Libraries\NoteObject;
use Exception;
use Modules\Fediverse\Config\Fediverse as FediverseBaseConfig;
class Fediverse extends FediverseBaseConfig
@ -26,10 +27,15 @@ class Fediverse extends FediverseBaseConfig
{
parent::__construct();
$defaultBanner = config('Images')
->podcastBannerDefaultPaths[service('settings')->get('App.theme')] ?? config(
'Images'
)->podcastBannerDefaultPaths['default'];
try {
$appTheme = service('settings')
->get('App.theme');
$defaultBanner = config('Images')
->podcastBannerDefaultPaths[$appTheme] ?? config('Images')->podcastBannerDefaultPaths['default'];
} catch (Exception) {
$defaultBanner = config('Images')
->podcastBannerDefaultPaths['default'];
}
['dirname' => $dirname, 'extension' => $extension, 'filename' => $filename] = pathinfo(
$defaultBanner['path']

View File

@ -39,18 +39,19 @@ class Filters extends BaseConfig
/**
* List of filter aliases that are always applied before and after every request.
*
* @var array<string, string[]>
* @var array<string, mixed>
*/
public array $globals = [
'before' => [
// 'honeypot',
// 'csrf',
'csrf' => [
'except' => ['@[a-zA-Z0-9\_]{1,32}/inbox'],
],
// 'invalidchars',
],
'after' => [
'toolbar',
// 'honeypot',
// 'honeypot',
// 'secureheaders',
],
];
@ -79,7 +80,7 @@ class Filters extends BaseConfig
$this->filters = [
'login' => [
'before' => [config('Admin') ->gateway . '*', config('Analytics') ->gateway . '*'],
'before' => [config('Admin')->gateway . '*', config('Analytics')->gateway . '*'],
],
];
}

View File

@ -26,7 +26,7 @@ class Paths
* the path if the folder is not in the same directory as this file.
*/
public string $systemDirectory =
__DIR__ . '/../../vendor/codeigniter4/codeigniter4/system';
__DIR__ . '/../../vendor/codeigniter4/framework/system';
/**
* ---------------------------------------------------------------

View File

@ -26,7 +26,7 @@ class Security extends BaseConfig
*
* Randomize the CSRF Token for added security.
*/
public bool $tokenRandomize = false;
public bool $tokenRandomize = true;
/**
* --------------------------------------------------------------------------

View File

@ -33,6 +33,7 @@ class ColorsController extends Controller
foreach ($color as $variable => $value) {
$colorsCssBody .= "--color-{$variable}: {$value[0]} {$value[1]}% {$value[2]}%;";
}
$colorsCssBody .= '}';
}

View File

@ -109,6 +109,7 @@ class EpisodeCommentController extends BaseController
helper('form');
return view('episode/comment', $data);
}
return view('episode/comment', $data, [
'cache' => DECADE,
'cache_name' => $cacheName,

View File

@ -134,6 +134,7 @@ class EpisodeController extends BaseController
return view('episode/activity', $data);
}
// The page cache is set to a decade so it is deleted manually upon podcast update
return view('episode/activity', $data, [
'cache' => $secondsToNextUnpublishedEpisode

View File

@ -34,6 +34,7 @@ class MapController extends BaseController
'cache_name' => $cacheName,
]);
}
return $found;
}
@ -50,19 +51,21 @@ class MapController extends BaseController
$found[] = [
'latitude' => $episode->location->latitude,
'longitude' => $episode->location->longitude,
'location_name' => $episode->location->name,
'location_name' => esc($episode->location->name),
'location_url' => $episode->location->url,
'episode_link' => $episode->link,
'podcast_link' => $episode->podcast->link,
'cover_url' => $episode->cover->thumbnail_url,
'podcast_title' => $episode->podcast->title,
'episode_title' => $episode->title,
'podcast_title' => esc($episode->podcast->title),
'episode_title' => esc($episode->title),
];
}
// The page cache is set to a decade so it is deleted manually upon episode update
cache()
->save($cacheName, $found, DECADE);
}
return $this->response->setJSON($found);
}
}

View File

@ -87,7 +87,6 @@ class PostController extends FediversePostController
if (! ($cachedView = cache($cacheName))) {
$data = [
// @phpstan-ignore-next-line
'metatags' => get_post_metatags($this->post),
'post' => $this->post,
'podcast' => $this->podcast,
@ -98,6 +97,7 @@ class PostController extends FediversePostController
helper('form');
return view('post/post', $data);
}
return view('post/post', $data, [
'cache' => DECADE,
'cache_name' => $cacheName,
@ -111,7 +111,7 @@ class PostController extends FediversePostController
{
$rules = [
'message' => 'required|max_length[500]',
'episode_url' => 'valid_url|permit_empty',
'episode_url' => 'valid_url_strict|permit_empty',
];
if (! $this->validate($rules)) {
@ -246,7 +246,6 @@ class PostController extends FediversePostController
if (! ($cachedView = cache($cacheName))) {
$data = [
// @phpstan-ignore-next-line
'metatags' => get_remote_actions_metatags($this->post, $action),
'podcast' => $this->podcast,
'actor' => $this->actor,

View File

@ -18,7 +18,7 @@ use CodeIgniter\HTTP\ResponseInterface;
class WebmanifestController extends Controller
{
/**
* @var array<string, string>
* @var array<string, array<string, string>>
*/
public const THEME_COLORS = [
'pine' => [
@ -50,10 +50,8 @@ class WebmanifestController extends Controller
public function index(): ResponseInterface
{
$webmanifest = [
'name' => service('settings')
->get('App.siteName'),
'description' => service('settings')
->get('App.siteDescription'),
'name' => esc(service('settings') ->get('App.siteName')),
'description' => esc(service('settings') ->get('App.siteDescription')),
'lang' => service('request')
->getLocale(),
'start_url' => base_url(),
@ -89,12 +87,12 @@ class WebmanifestController extends Controller
}
$webmanifest = [
'name' => $podcast->title,
'short_name' => '@' . $podcast->handle,
'name' => esc($podcast->title),
'short_name' => '@' . esc($podcast->handle),
'description' => $podcast->description,
'lang' => $podcast->language_code,
'start_url' => $podcast->link,
'scope' => '/@' . $podcast->handle,
'scope' => '/@' . esc($podcast->handle),
'display' => 'standalone',
'orientation' => 'portrait',
'theme_color' => self::THEME_COLORS[service('settings')->get('App.theme')]['theme'],

View File

@ -37,7 +37,7 @@ class AuthSeeder extends Seeder
*
* ``` context => [ [action, description], [action, description], ... ] ```
*
* @var array<string, array<string, string|array>[]>
* @var array<string, array<string, string|string[]>[]>
*/
protected array $permissions = [
'settings' => [
@ -302,12 +302,14 @@ class AuthSeeder extends Seeder
->ignore(true)
->insertBatch($dataPermissions);
}
if ($this->db->table('auth_groups')->countAll() < count($dataGroups)) {
$this->db
->table('auth_groups')
->ignore(true)
->insertBatch($dataGroups);
}
if ($this->db->table('auth_groups_permissions')->countAll() < count($dataGroupsPermissions)) {
$this->db
->table('auth_groups_permissions')

View File

@ -169,6 +169,7 @@ class FakePodcastsAnalyticsSeeder extends Seeder
];
}
}
$this->db
->table('analytics_podcasts')
->ignore(true)

View File

@ -248,6 +248,7 @@ class FakeWebsiteAnalyticsSeeder extends Seeder
];
}
}
$this->db
->table('analytics_website_by_browser')
->ignore(true)

View File

@ -367,6 +367,7 @@ class Episode extends Entity
if ($this->transcript !== null) {
return $this->transcript->file_url;
}
return $this->transcript_remote_url;
}
@ -452,16 +453,14 @@ class Episode extends Entity
public function getLink(): string
{
return url_to('episode', $this->getPodcast()->handle, $this->attributes['slug']);
return url_to('episode', esc($this->getPodcast()->handle), esc($this->attributes['slug']));
}
public function getEmbedUrl(string $theme = null): string
{
return base_url(
$theme
? route_to('embed-theme', $this->getPodcast() ->handle, $this->attributes['slug'], $theme,)
: route_to('embed', $this->getPodcast()->handle, $this->attributes['slug']),
);
return $theme
? url_to('embed-theme', esc($this->getPodcast()->handle), esc($this->attributes['slug']), $theme,)
: url_to('embed', esc($this->getPodcast()->handle), esc($this->attributes['slug']));
}
public function setGuid(?string $guid = null): static
@ -611,7 +610,7 @@ class Episode extends Entity
'elements' => $this->custom_rss,
], $xmlNode);
return str_replace(['<item>', '</item>'], '', $xmlNode->asXML());
return (string) str_replace(['<item>', '</item>'], '', $xmlNode->asXML());
}
/**

View File

@ -102,7 +102,6 @@ class EpisodeComment extends UuidEntity
->getActorById($this->actor_id);
}
// @phpstan-ignore-next-line
return $this->actor;
}

View File

@ -45,6 +45,7 @@ class Location extends Entity
$latitude = floatval($geoArray[0]);
$longitude = floatval($geoArray[1]);
}
parent::__construct([
'name' => $name,
'geo' => $geo,

View File

@ -43,7 +43,6 @@ class Audio extends BaseMedia
$this->attributes['file_mimetype'] = $audioMetadata['mime_type'];
$this->attributes['file_size'] = $audioMetadata['filesize'];
// @phpstan-ignore-next-line
$this->attributes['description'] = @$audioMetadata['id3v2']['comments']['comment'][0];
$this->attributes['file_metadata'] = json_encode($audioMetadata, JSON_INVALID_UTF8_SUBSTITUTE);

View File

@ -48,7 +48,6 @@ class Image extends BaseMedia
{
parent::setFile($file);
// @phpstan-ignore-next-line
if ($this->file_mimetype === 'image/jpeg' && $metadata = @exif_read_data(
media_path($this->file_path),
null,

View File

@ -202,7 +202,6 @@ class Podcast extends Entity
->getActorById($this->actor_id);
}
// @phpstan-ignore-next-line
return $this->actor;
}
@ -517,11 +516,12 @@ class Podcast extends Entity
}
/**
* @return int[]
* @return int[]|string[]
*/
public function getOtherCategoriesIds(): array
{
if ($this->other_categories_ids === null) {
// @phpstan-ignore-next-line
$this->other_categories_ids = array_column($this->getOtherCategories(), 'id');
}
@ -586,7 +586,7 @@ class Podcast extends Entity
'elements' => $this->custom_rss,
], $xmlNode);
return str_replace(['<channel>', '</channel>'], '', $xmlNode->asXML());
return (string) str_replace(['<channel>', '</channel>'], '', $xmlNode->asXML());
}
/**

View File

@ -9,7 +9,6 @@ declare(strict_types=1);
*/
use App\Models\ActorModel;
use CodeIgniter\Database\Exceptions\DataException;
use Modules\Auth\Entities\User;
use Modules\Fediverse\Entities\Actor;
@ -83,9 +82,6 @@ if (! function_exists('interact_as_actor')) {
}
if (! function_exists('can_user_interact')) {
/**
* @throws DataException
*/
function can_user_interact(): bool
{
return (bool) interact_as_actor();

View File

@ -31,6 +31,6 @@ if (! function_exists('replace_breadcrumb_params')) {
function replace_breadcrumb_params(array $newParams): void
{
$breadcrumb = Services::breadcrumb();
$breadcrumb->replaceParams($newParams);
$breadcrumb->replaceParams(esc($newParams));
}
}

View File

@ -84,6 +84,7 @@ if (! function_exists('data_table')) {
foreach ($columns as $column) {
$rowData[] = $column['cell']($row, ...$rest);
}
$table->addRow($rowData);
}
} else {
@ -141,7 +142,6 @@ if (! function_exists('publication_button')) {
*/
function publication_button(int $podcastId, int $episodeId, string $publicationStatus): string
{
/** @phpstan-ignore-next-line */
switch ($publicationStatus) {
case 'not_published':
$label = lang('Episode.publish');
@ -243,10 +243,10 @@ if (! function_exists('location_link')) {
return anchor(
$location->url,
icon('map-pin', 'mr-2') . $location->name,
icon('map-pin', 'mr-2 flex-shrink-0') . '<span class="truncate">' . esc($location->name) . '</span>',
[
'class' =>
'inline-flex items-baseline hover:underline focus:ring-accent' .
'w-full overflow-hidden inline-flex items-baseline hover:underline focus:ring-accent' .
($class === '' ? '' : " {$class}"),
'target' => '_blank',
'rel' => 'noreferrer noopener',

View File

@ -38,13 +38,13 @@ if (! function_exists('write_audio_file_tags')) {
// populate data array
$TagData = [
'title' => [$episode->title],
'title' => [esc($episode->title)],
'artist' => [
$episode->podcast->publisher === null
? $episode->podcast->owner_name
? esc($episode->podcast->owner_name)
: $episode->podcast->publisher,
],
'album' => [$episode->podcast->title],
'album' => [esc($episode->podcast->title)],
'year' => [$episode->published_at !== null ? $episode->published_at->format('Y') : ''],
'genre' => ['Podcast'],
'comment' => [$episode->description],
@ -52,7 +52,7 @@ if (! function_exists('write_audio_file_tags')) {
'copyright_message' => [$episode->podcast->copyright],
'publisher' => [
$episode->podcast->publisher === null
? $episode->podcast->owner_name
? esc($episode->podcast->owner_name)
: $episode->podcast->publisher,
],
'encoded_by' => ['Castopod'],

View File

@ -92,6 +92,7 @@ if (! function_exists('download_file')) {
return new File($tmpFilePath);
}
}
if (! function_exists('media_path')) {
/**
* Prefixes the root media path to a given uri
@ -104,6 +105,7 @@ if (! function_exists('media_path')) {
if (is_array($uri)) {
$uri = implode('/', $uri);
}
$uri = trim($uri, '/');
return config('App')->mediaRoot . '/' . $uri;
@ -122,6 +124,7 @@ if (! function_exists('media_base_url')) {
if (is_array($uri)) {
$uri = implode('/', $uri);
}
$uri = trim($uri, '/');
$appConfig = config('App');

View File

@ -151,14 +151,17 @@ if (! function_exists('format_duration')) {
if ($seconds < 60) {
return '0:' . sprintf('%02d', $seconds);
}
if ($seconds < 3600) {
// < 1 hour: returns MM:SS
return ltrim(gmdate('i:s', $seconds), '0');
}
if ($seconds < 36000) {
// < 10 hours: returns H:MM:SS
return ltrim(gmdate('H:i:s', $seconds), '0');
}
return gmdate('H:i:s', $seconds);
}
}
@ -177,14 +180,17 @@ if (! function_exists('format_duration_symbol')) {
if ($seconds < 60) {
return $seconds . 's';
}
if ($seconds < 3600) {
// < 1 hour: returns MM:SS
return ltrim(gmdate('i\m\i\n s\s', $seconds), '0');
}
if ($seconds < 36000) {
// < 10 hours: returns H:MM:SS
return ltrim(gmdate('h\h i\m\i\n s\s', $seconds), '0');
}
return gmdate('h\h i\m\i\n s\s', $seconds);
}
}
@ -232,6 +238,7 @@ if (! function_exists('file_upload_max_size')) {
$max_size = $upload_max;
}
}
return $max_size;
}
}

View File

@ -29,7 +29,7 @@ if (! function_exists('render_page_links')) {
'class' => 'px-2 py-1 underline hover:no-underline focus:ring-accent',
]);
foreach ($pages as $page) {
$links .= anchor($page->link, $page->title, [
$links .= anchor($page->link, esc($page->title), [
'class' => 'px-2 py-1 underline hover:no-underline focus:ring-accent',
]);
}

View File

@ -60,18 +60,16 @@ if (! function_exists('get_rss_feed')) {
$channel->addChild('language', $podcast->language_code);
if ($podcast->location !== null) {
$locationElement = $channel->addChild(
'location',
htmlspecialchars($podcast->location->name),
$podcastNamespace,
);
$locationElement = $channel->addChild('location', $podcast->location->name, $podcastNamespace);
if ($podcast->location->geo !== null) {
$locationElement->addAttribute('geo', $podcast->location->geo);
}
if ($podcast->location->osm !== null) {
$locationElement->addAttribute('osm', $podcast->location->osm);
}
}
if ($podcast->payment_pointer !== null) {
$valueElement = $channel->addChild('value', null, $podcastNamespace);
$valueElement->addAttribute('type', 'webmonetization');
@ -83,6 +81,7 @@ if (! function_exists('get_rss_feed')) {
$recipientElement->addAttribute('address', $podcast->payment_pointer);
$recipientElement->addAttribute('split', '100');
}
$channel
->addChild('locked', $podcast->is_locked ? 'yes' : 'no', $podcastNamespace)
->addAttribute('owner', $podcast->owner_email);
@ -96,8 +95,9 @@ if (! function_exists('get_rss_feed')) {
if ($podcastingPlatform->account_id !== null) {
$podcastingPlatformElement->addAttribute('id', $podcastingPlatform->account_id);
}
if ($podcastingPlatform->link_url !== null) {
$podcastingPlatformElement->addAttribute('url', htmlspecialchars($podcastingPlatform->link_url));
$podcastingPlatformElement->addAttribute('url', $podcastingPlatform->link_url);
}
}
@ -127,6 +127,7 @@ if (! function_exists('get_rss_feed')) {
if ($socialPlatform->account_id !== null) {
$socialElement->addAttribute('accountId', esc($socialPlatform->account_id));
}
if ($socialPlatform->link_url !== null) {
$socialElement->addAttribute('accountUrl', esc($socialPlatform->link_url));
}
@ -179,17 +180,13 @@ if (! function_exists('get_rss_feed')) {
);
$fundingPlatformElement->addAttribute('platform', $fundingPlatform->slug);
if ($fundingPlatform->link_url !== null) {
$fundingPlatformElement->addAttribute('url', htmlspecialchars($fundingPlatform->link_url));
$fundingPlatformElement->addAttribute('url', $fundingPlatform->link_url);
}
}
foreach ($podcast->persons as $person) {
foreach ($person->roles as $role) {
$personElement = $channel->addChild(
'person',
htmlspecialchars($person->full_name),
$podcastNamespace,
);
$personElement = $channel->addChild('person', $person->full_name, $podcastNamespace,);
$personElement->addAttribute('img', $person->avatar->medium_url);
@ -199,14 +196,12 @@ if (! function_exists('get_rss_feed')) {
$personElement->addAttribute(
'role',
htmlspecialchars(
lang("PersonsTaxonomy.persons.{$role->group}.roles.{$role->role}.label", [], 'en'),
),
lang("PersonsTaxonomy.persons.{$role->group}.roles.{$role->role}.label", [], 'en'),
);
$personElement->addAttribute(
'group',
htmlspecialchars(lang("PersonsTaxonomy.persons.{$role->group}.label", [], 'en')),
lang("PersonsTaxonomy.persons.{$role->group}.label", [], 'en'),
);
}
}
@ -242,6 +237,7 @@ if (! function_exists('get_rss_feed')) {
if ($podcast->is_blocked) {
$channel->addChild('block', 'Yes', $itunesNamespace);
}
if ($podcast->is_completed) {
$channel->addChild('complete', 'Yes', $itunesNamespace);
}
@ -275,18 +271,16 @@ if (! function_exists('get_rss_feed')) {
$item->addChild('guid', $episode->guid);
$item->addChild('pubDate', $episode->published_at->format(DATE_RFC1123));
if ($episode->location !== null) {
$locationElement = $item->addChild(
'location',
htmlspecialchars($episode->location->name),
$podcastNamespace,
);
$locationElement = $item->addChild('location', $episode->location->name, $podcastNamespace,);
if ($episode->location->geo !== null) {
$locationElement->addAttribute('geo', $episode->location->geo);
}
if ($episode->location->osm !== null) {
$locationElement->addAttribute('osm', $episode->location->osm);
}
}
$item->addChildWithCDATA('description', $episode->getDescriptionHtml($serviceSlug));
$item->addChild('duration', (string) $episode->audio->duration, $itunesNamespace);
$item->addChild('link', $episode->link);
@ -420,6 +414,7 @@ if (! function_exists('add_category_tag')) {
$itunesCategoryChild->addAttribute('text', $category->apple_category);
$node->addChild('category', $category->parent->apple_category);
}
$node->addChild('category', $category->apple_category);
}
}
@ -445,10 +440,12 @@ if (! function_exists('rss_to_array')) {
foreach ($rssNode->attributes() as $key => $value) {
$arrayNode['attributes'][$key] = (string) $value;
}
$textcontent = trim((string) $rssNode);
if (strlen($textcontent) > 0) {
$arrayNode['content'] = $textcontent;
}
foreach ($nameSpaces as $currentNameSpace) {
foreach ($rssNode->children($currentNameSpace) as $childXmlNode) {
$arrayNode['elements'][] = rss_to_array($childXmlNode);
@ -485,6 +482,7 @@ if (! function_exists('array_to_rss')) {
$childXmlNode->addAttribute($attributeKey, $attributeValue);
}
}
array_to_rss($childArrayNode, $childXmlNode);
}
}

View File

@ -25,12 +25,13 @@ if (! function_exists('get_podcast_metatags')) {
if ($podcast->category->parent_id !== null) {
$category .= $podcast->category->parent->apple_category . ' ';
}
$category .= $podcast->category->apple_category;
$schema = new Schema(
new Thing('PodcastSeries', [
'name' => $podcast->title,
'headline' => $podcast->title,
'name' => esc($podcast->title),
'headline' => esc($podcast->title),
'url' => current_url(),
'sameAs' => $podcast->link,
'identifier' => $podcast->guid,
@ -38,8 +39,8 @@ if (! function_exists('get_podcast_metatags')) {
'description' => $podcast->description,
'webFeed' => $podcast->feed_url,
'accessMode' => 'auditory',
'author' => $podcast->owner_name,
'creator' => $podcast->owner_name,
'author' => esc($podcast->owner_name),
'creator' => esc($podcast->owner_name),
'publisher' => $podcast->publisher,
'inLanguage' => $podcast->language_code,
'genre' => $category,
@ -49,25 +50,27 @@ if (! function_exists('get_podcast_metatags')) {
$metatags = new MetaTags();
$metatags
->title(' ' . $podcast->title . " (@{$podcast->handle})" . ' • ' . lang('Podcast.' . $page))
->title(esc($podcast->title) . ' (@' . esc($podcast->handle) . ') • ' . lang('Podcast.' . $page))
->description(htmlspecialchars($podcast->description))
->image((string) $podcast->cover->og_url)
->canonical((string) current_url())
->og('image:width', (string) config('Images')->podcastCoverSizes['og']['width'])
->og('image:height', (string) config('Images')->podcastCoverSizes['og']['height'])
->og('locale', $podcast->language_code)
->og('site_name', service('settings')->get('App.siteName'))
->og('site_name', esc(service('settings')->get('App.siteName')))
->push('link', [
'rel' => 'alternate',
'type' => 'application/activity+json',
'href' => url_to('podcast-activity', $podcast->handle),
'href' => url_to('podcast-activity', esc($podcast->handle)),
]);
if ($podcast->payment_pointer) {
$metatags->meta('monetization', $podcast->payment_pointer);
}
return '<link type="application/rss+xml" rel="alternate" title="' . $podcast->title . '" href="' . $podcast->feed_url . '" />' . PHP_EOL . $metatags->__toString() . PHP_EOL . $schema->__toString();
return '<link type="application/rss+xml" rel="alternate" title="' . esc(
$podcast->title
) . '" href="' . $podcast->feed_url . '" />' . PHP_EOL . $metatags->__toString() . PHP_EOL . $schema->__toString();
}
}
@ -76,8 +79,8 @@ if (! function_exists('get_episode_metatags')) {
{
$schema = new Schema(
new Thing('PodcastEpisode', [
'url' => url_to('episode', $episode->podcast->handle, $episode->slug),
'name' => $episode->title,
'url' => url_to('episode', esc($episode->podcast->handle), $episode->slug),
'name' => esc($episode->title),
'image' => $episode->cover->feed_url,
'description' => $episode->description,
'datePublished' => $episode->published_at->format(DATE_ISO8601),
@ -87,7 +90,7 @@ if (! function_exists('get_episode_metatags')) {
'contentUrl' => $episode->audio->file_url,
]),
'partOfSeries' => new Thing('PodcastSeries', [
'name' => $episode->podcast->title,
'name' => esc($episode->podcast->title),
'url' => $episode->podcast->link,
]),
])
@ -97,10 +100,10 @@ if (! function_exists('get_episode_metatags')) {
$metatags
->title($episode->title)
->description(htmlspecialchars($episode->description))
->description(esc($episode->description))
->image((string) $episode->cover->og_url, 'player')
->canonical($episode->link)
->og('site_name', service('settings')->get('App.siteName'))
->og('site_name', esc(service('settings')->get('App.siteName')))
->og('image:width', (string) config('Images')->podcastCoverSizes['og']['width'])
->og('image:height', (string) config('Images')->podcastCoverSizes['og']['height'])
->og('locale', $episode->podcast->language_code)
@ -109,7 +112,7 @@ if (! function_exists('get_episode_metatags')) {
->meta('article:published_time', $episode->published_at->format(DATE_ISO8601))
->meta('article:modified_time', $episode->updated_at->format(DATE_ISO8601))
->twitter('audio:partner', $episode->podcast->publisher ?? '')
->twitter('audio:artist_name', $episode->podcast->owner_name)
->twitter('audio:artist_name', esc($episode->podcast->owner_name))
->twitter('player', $episode->getEmbedUrl('light'))
->twitter('player:width', (string) config('Embed')->width)
->twitter('player:height', (string) config('Embed')->height)
@ -125,9 +128,11 @@ if (! function_exists('get_episode_metatags')) {
return $metatags->__toString() . PHP_EOL . '<link rel="alternate" type="application/json+oembed" href="' . base_url(
route_to('episode-oembed-json', $episode->podcast->handle, $episode->slug)
) . '" title="' . $episode->title . ' oEmbed json" />' . PHP_EOL . '<link rel="alternate" type="text/xml+oembed" href="' . base_url(
) . '" title="' . esc(
$episode->title
) . ' oEmbed json" />' . PHP_EOL . '<link rel="alternate" type="text/xml+oembed" href="' . base_url(
route_to('episode-oembed-xml', $episode->podcast->handle, $episode->slug)
) . '" title="' . $episode->title . ' oEmbed xml" />' . PHP_EOL . $schema->__toString();
) . '" title="' . esc($episode->title) . ' oEmbed xml" />' . PHP_EOL . $schema->__toString();
}
}
@ -135,7 +140,7 @@ if (! function_exists('get_post_metatags')) {
function get_post_metatags(Post $post): string
{
$socialMediaPosting = new Thing('SocialMediaPosting', [
'@id' => url_to('post', $post->actor->username, $post->id),
'@id' => url_to('post', esc($post->actor->username), $post->id),
'datePublished' => $post->published_at->format(DATE_ISO8601),
'author' => new Thing('Person', [
'name' => $post->actor->display_name,
@ -172,11 +177,11 @@ if (! function_exists('get_post_metatags')) {
->description($post->message)
->image($post->actor->avatar_image_url)
->canonical((string) current_url())
->og('site_name', service('settings')->get('App.siteName'))
->og('site_name', esc(service('settings')->get('App.siteName')))
->push('link', [
'rel' => 'alternate',
'type' => 'application/activity+json',
'href' => url_to('post', $post->actor->username, $post->id),
'href' => url_to('post', esc($post->actor->username), $post->id),
]);
return $metatags->__toString() . PHP_EOL . $schema->__toString();
@ -189,7 +194,7 @@ if (! function_exists('get_episode_comment_metatags')) {
$schema = new Schema(new Thing('SocialMediaPosting', [
'@id' => url_to(
'episode-comment',
$episodeComment->actor->username,
esc($episodeComment->actor->username),
$episodeComment->episode->slug,
$episodeComment->id
),
@ -211,7 +216,7 @@ if (! function_exists('get_episode_comment_metatags')) {
->description($episodeComment->message)
->image($episodeComment->actor->avatar_image_url)
->canonical((string) current_url())
->og('site_name', service('settings')->get('App.siteName'))
->og('site_name', esc(service('settings')->get('App.siteName')))
->push('link', [
'rel' => 'alternate',
'type' => 'application/activity+json',
@ -238,7 +243,7 @@ if (! function_exists('get_follow_metatags')) {
->description($actor->summary)
->image($actor->avatar_image_url)
->canonical((string) current_url())
->og('site_name', service('settings')->get('App.siteName'));
->og('site_name', esc(service('settings')->get('App.siteName')));
return $metatags->__toString();
}
@ -255,7 +260,7 @@ if (! function_exists('get_remote_actions_metatags')) {
->description($post->message)
->image($post->actor->avatar_image_url)
->canonical((string) current_url())
->og('site_name', service('settings')->get('App.siteName'));
->og('site_name', esc(service('settings')->get('App.siteName')));
return $metatags->__toString();
}
@ -266,11 +271,11 @@ if (! function_exists('get_home_metatags')) {
{
$metatags = new MetaTags();
$metatags
->title(service('settings')->get('App.siteName'))
->description(service('settings')->get('App.siteDescription'))
->title(esc(service('settings')->get('App.siteName')))
->description(esc(service('settings')->get('App.siteDescription')))
->image(service('settings')->get('App.siteIcon')['512'])
->canonical((string) current_url())
->og('site_name', service('settings')->get('App.siteName'));
->og('site_name', esc(service('settings')->get('App.siteName')));
return $metatags->__toString();
}
@ -282,14 +287,14 @@ if (! function_exists('get_page_metatags')) {
$metatags = new MetaTags();
$metatags
->title(
$page->title . service('settings')->get('App.siteTitleSeparator') . service(
esc($page->title) . service('settings')->get('App.siteTitleSeparator') . esc(service(
'settings'
)->get('App.siteName')
)->get('App.siteName'))
)
->description(service('settings')->get('App.siteDescription'))
->description(esc(service('settings')->get('App.siteDescription')))
->image(service('settings')->get('App.siteIcon')['512'])
->canonical((string) current_url())
->og('site_name', service('settings')->get('App.siteName'));
->og('site_name', esc(service('settings')->get('App.siteName')));
return $metatags->__toString();
}

View File

@ -55,6 +55,7 @@ if (! function_exists('svg')) {
if ($class) {
$svgContents = str_replace('<svg', '<svg class="' . $class . '"', $svgContents);
}
return $svgContents;
}
}

View File

@ -37,7 +37,7 @@ class CommentObject extends ObjectType
$this->replies = url_to(
'episode-comment-replies',
$comment->actor->username,
esc($comment->actor->username),
$comment->episode->slug,
$comment->id
);

View File

@ -17,8 +17,6 @@ use GdImage;
/**
* TODO: refactor this by splitting process modules into different classes (image generation, subtitles clip, video
* generation)
*
* @phpstan-ignore-next-line
*/
class VideoClipper
{
@ -299,7 +297,7 @@ class VideoClipper
{
$background = $this->generateBackground($this->dimensions['width'], $this->dimensions['height']);
if ($background === null) {
if (! $background instanceof \GdImage) {
return false;
}
@ -384,6 +382,7 @@ class VideoClipper
$this->dimensions['episodeNumbering']['paddingY'],
);
}
$this->addParagraphToImage(
$background,
$this->dimensions['episodeTitle']['x'],
@ -409,6 +408,7 @@ class VideoClipper
if (! $cleanedQuotes) {
return false;
}
imagefilter($cleanedQuotes, IMG_FILTER_CONTRAST, 255);
imagefilter($cleanedQuotes, IMG_FILTER_COLORIZE, ...$this->colors['quotes']);
@ -526,6 +526,7 @@ class VideoClipper
if ($src === false) {
return false;
}
imagecopy($src, $source, 0, 0, 0, 0, $corner, $corner);
imagecopy($src, $source, $corner, 0, $ws - $corner, 0, $corner, $corner);
imagecopy($src, $source, $corner, $corner, $ws - $corner, $hs - $corner, $corner, $corner);
@ -577,6 +578,7 @@ class VideoClipper
if ($dest === false) {
return false;
}
imagealphablending($dest, false);
imagefilledrectangle($dest, 0, 0, $s, $s, $alphacolor);
imagecopyresampled($dest, $img, 0, 0, 0, 0, $s, $s, $ns, $ns);
@ -652,7 +654,7 @@ class VideoClipper
*
* @return array<string, mixed>|false
*/
private function calculateTextBox(int $fontSize, int $fontAngle, string $fontFile, string $text): array | false
private function calculateTextBox(int $fontSize, int $fontAngle, string $fontFile, string $text): array|bool
{
/************
simple function that calculates the *exact* bounding box (single pixel precision).
@ -664,6 +666,7 @@ class VideoClipper
if (! $bbox) {
return false;
}
$minX = min([$bbox[0], $bbox[2], $bbox[4], $bbox[6]]);
$maxX = max([$bbox[0], $bbox[2], $bbox[4], $bbox[6]]);
$minY = min([$bbox[1], $bbox[3], $bbox[5], $bbox[7]]);
@ -755,7 +758,7 @@ class VideoClipper
int $lineWidth,
int $numberOfLines,
int $paragraphIndent = 0,
): array | false {
): array|bool {
// check length of text
$bbox = $this->calculateTextBox($fontsize, 0, $fontPath, $text);
if (! $bbox) {
@ -783,6 +786,7 @@ class VideoClipper
if (! $wordBox) {
return false;
}
$wordWidth = $wordBox['width'];
if (($wordWidth + $length) > $lineWidth) {
@ -791,6 +795,7 @@ class VideoClipper
$lines[$numberOfLines - 1] .= '…';
break;
}
$lines[$lineNumber] = '';
$length = 0;

View File

@ -39,10 +39,11 @@ class PodcastActor extends ActorObject
if ($podcast->category->parent_id !== null) {
$category .= $podcast->category->parent->apple_category . ' ';
}
$category .= $podcast->category->apple_category;
$this->category = $category;
$this->episodes = url_to('podcast-episodes', $podcast->handle);
$this->episodes = url_to('podcast-episodes', esc($podcast->handle));
}
}

View File

@ -60,7 +60,7 @@ class PodcastEpisode extends ObjectType
$this->audio = [
'id' => $episode->audio->file_url,
'type' => 'Audio',
'name' => $episode->title,
'name' => esc($episode->title),
'size' => $episode->audio->file_size,
'duration' => $episode->audio->duration,
'url' => [

View File

@ -27,7 +27,6 @@ class Router extends CodeIgniterRouter
* @param string $uri The URI path to compare against the routes
*
* @return boolean Whether the route was matched or not.
* @throws RedirectException
*/
protected function checkRoutes(string $uri): bool
{
@ -75,6 +74,7 @@ class Router extends CodeIgniterRouter
$this->collection->getRedirectCode($key),
);
}
// Store our locale so CodeIgniter object can
// assign it to the Request.
if (isset($localeSegment)) {
@ -135,6 +135,7 @@ class Router extends CodeIgniterRouter
][$available]['namespace'],
);
}
$val =
$this->collection->getDefaultNamespace() .
$this->directory .

View File

@ -24,7 +24,7 @@ class SimpleRSSElement extends SimpleXMLElement
*
* @return static The addChild method returns a SimpleXMLElement object representing the child added to the XML node.
*/
public function addChildWithCDATA(string $name, string $value = '', ?string $namespace = null)
public function addChildWithCDATA(string $name, string $value = '', ?string $namespace = null): static
{
$newChild = parent::addChild($name, '', $namespace);
@ -52,7 +52,7 @@ class SimpleRSSElement extends SimpleXMLElement
*
* @return static The addChild method returns a SimpleXMLElement object representing the child added to the XML node.
*/
public function addChild($name, $value = null, $namespace = null, $escape = true)
public function addChild($name, $value = null, $namespace = null, $escape = true): static
{
$newChild = parent::addChild($name, '', $namespace);
@ -64,9 +64,11 @@ class SimpleRSSElement extends SimpleXMLElement
if (! $no instanceof DOMDocument) {
return $newChild;
}
if (is_array($value)) {
return $newChild;
}
/** @noRector RecastingRemovalRector */
$node->appendChild($no->createTextNode((string) $value));
}

View File

@ -54,7 +54,6 @@ class TranscriptParser
$lines = explode(PHP_EOL, $this->transcriptContent);
foreach ($lines as $line) {
// @phpstan-ignore-next-line
switch ($state) {
case SRT_STATE_SUBNUMBER:
$subNum = trim($line);
@ -82,8 +81,10 @@ class TranscriptParser
if ($subText !== '') {
$subText .= PHP_EOL . $line;
}
$subText .= $line;
}
break;
}

View File

@ -85,7 +85,6 @@ class View extends CodeIgniterView
$renderVars = $this->renderVars;
$output = (function (): string {
/** @phpstan-ignore-next-line */
extract($this->tempData);
ob_start();
include $this->renderVars['file'];

View File

@ -228,10 +228,10 @@ class ComponentRenderer
if (! file_exists($filePath)) {
return null;
}
$className = service('locator')
->getClassname($filePath);
/** @phpstan-ignore-next-line */
if (! class_exists($className)) {
return null;
}
@ -247,7 +247,6 @@ class ComponentRenderer
private function renderView(string $view, array $data): string
{
return (function (string $view, $data): string {
/** @phpstan-ignore-next-line */
extract($data);
ob_start();
eval('?>' . file_get_contents($view));

View File

@ -52,6 +52,7 @@ class Vite
die("Could not load css manifest: <strong>{$manifestCSSPath}</strong> file not found!");
}
}
$this->manifestCSSData = $cachedManifestCSS;
}
@ -80,6 +81,7 @@ class Vite
die("Could not load manifest: <strong>{$manifestPath}</strong> file not found!");
}
}
$this->manifestData = $cachedManifest;
}

View File

@ -69,6 +69,7 @@ class CategoryModel extends Model
if ($category->parent !== null) {
$result[$category->id] = lang('Podcast.category_options.' . $category->parent->code) . ' ';
}
$result[$category->id] .= lang('Podcast.category_options.' . $category->code);
return $result;
},

View File

@ -73,7 +73,6 @@ class ClipModel extends Model
ConnectionInterface &$db = null,
ValidationInterface $validation = null
) {
// @phpstan-ignore-next-line
switch ($type) {
case 'audio':
$this->returnType = Soundbite::class;
@ -226,6 +225,7 @@ class ClipModel extends Model
cache()
->save($cacheName, $found, DECADE);
}
return $found;
}

View File

@ -94,7 +94,12 @@ class EpisodeCommentModel extends UuidModel
if ($registerActivity) {
// set post id and uri to construct NoteObject
$comment->id = $newCommentId;
$comment->uri = url_to('episode-comment', $comment->actor->username, $comment->episode->slug, $comment->id);
$comment->uri = url_to(
'episode-comment',
esc($comment->actor->username),
$comment->episode->slug,
$comment->id
);
$createActivity = new CreateActivity();
$createActivity
@ -112,7 +117,7 @@ class EpisodeCommentModel extends UuidModel
'queued',
);
$createActivity->set('id', url_to('activity', $comment->actor->username, $activityId));
$createActivity->set('id', url_to('activity', esc($comment->actor->username), $activityId));
model(ActivityModel::class, false)
->update($activityId, [
@ -200,6 +205,7 @@ class EpisodeCommentModel extends UuidModel
$this->uuidUseBytes = false;
return $this->updateBatch($commentsLikesCount, 'id');
}
return 0;
}
@ -234,7 +240,7 @@ class EpisodeCommentModel extends UuidModel
$episode = model(EpisodeModel::class, false)
->find((int) $data['data']['episode_id']);
$data['data']['uri'] = url_to('episode-comment', $actor->username, $episode->slug, $uuid4->toString());
$data['data']['uri'] = url_to('episode-comment', esc($actor->username), $episode->slug, $uuid4->toString());
}
return $data;

View File

@ -115,8 +115,8 @@ class EpisodeModel extends Model
'number' => 'is_natural_no_zero|permit_empty',
'season_number' => 'is_natural_no_zero|permit_empty',
'type' => 'required',
'transcript_remote_url' => 'valid_url|permit_empty',
'chapters_remote_url' => 'valid_url|permit_empty',
'transcript_remote_url' => 'valid_url_strict|permit_empty',
'chapters_remote_url' => 'valid_url_strict|permit_empty',
'published_at' => 'valid_date|permit_empty',
'created_by' => 'required',
'updated_by' => 'required',
@ -215,6 +215,7 @@ class EpisodeModel extends Model
$where['YEAR(published_at)'] = $year;
$where['season_number'] = null;
}
if ($season) {
$where['season_number'] = $season;
}

View File

@ -76,7 +76,7 @@ class LikeModel extends UuidModel
'queued',
);
$likeActivity->set('id', url_to('activity', $actor->username, $activityId));
$likeActivity->set('id', url_to('activity', esc($actor->username), $activityId));
model(ActivityModel::class)
->update($activityId, [
@ -115,7 +115,7 @@ class LikeModel extends UuidModel
$likeActivity = new LikeActivity();
$likeActivity
->set('id', url_to('activity', $actor->username, $activity->id))
->set('id', url_to('activity', esc($actor->username), $activity->id))
->set('actor', $actor->uri)
->set('object', $comment->uri);
@ -134,7 +134,7 @@ class LikeModel extends UuidModel
'queued',
);
$undoActivity->set('id', url_to('activity', $actor->username, $activityId));
$undoActivity->set('id', url_to('activity', esc($actor->username), $activityId));
model(ActivityModel::class)
->update($activityId, [

View File

@ -16,6 +16,7 @@ use App\Entities\Media\Document;
use App\Entities\Media\Image;
use App\Entities\Media\Transcript;
use App\Entities\Media\Video;
use CodeIgniter\Database\BaseResult;
use CodeIgniter\Database\ConnectionInterface;
use CodeIgniter\Model;
use CodeIgniter\Validation\ValidationInterface;
@ -88,7 +89,6 @@ class MediaModel extends Model
ConnectionInterface &$db = null,
ValidationInterface $validation = null
) {
// @phpstan-ignore-next-line
switch ($fileType) {
case 'audio':
$this->returnType = Audio::class;
@ -113,7 +113,7 @@ class MediaModel extends Model
parent::__construct($db, $validation);
}
public function getMediaById(int $mediaId): Document | Audio | Video | Image | Transcript | Chapters
public function getMediaById(int $mediaId): mixed
{
$cacheName = "media#{$mediaId}";
if (! ($found = cache($cacheName))) {
@ -172,7 +172,7 @@ class MediaModel extends Model
return $result;
}
public function deleteMedia(object $media): bool
public function deleteMedia(object $media): bool|BaseResult
{
$media->deleteFile();

View File

@ -389,10 +389,12 @@ class PersonModel extends Model
];
}
}
return $this->db->table('episodes_persons')
->ignore(true)
->insertBatch($data);
}
return 0;
}

View File

@ -60,6 +60,7 @@ class PlatformModel extends Model
cache()
->save('platforms', $found, DECADE);
}
return $found;
}
@ -72,6 +73,7 @@ class PlatformModel extends Model
cache()
->save($cacheName, $found, DECADE);
}
return $found;
}

View File

@ -94,7 +94,7 @@ class PodcastModel extends Model
'language_code' => 'required',
'category_id' => 'required',
'owner_email' => 'required|valid_email',
'new_feed_url' => 'valid_url|permit_empty',
'new_feed_url' => 'valid_url_strict|permit_empty',
'type' => 'required',
'created_by' => 'required',
'updated_by' => 'required',
@ -403,6 +403,7 @@ class PodcastModel extends Model
$secondsToNextUnpublishedEpisode ? $secondsToNextUnpublishedEpisode : DECADE
);
}
return $defaultQuery;
}
@ -504,6 +505,7 @@ class PodcastModel extends Model
(new ActorModel())->update($podcast->actor_id, $podcastActor);
}
}
return $data;
}

View File

@ -11,6 +11,8 @@ class DropdownMenu extends Component
{
public string $id = '';
public string $labelledby;
public string $placement = 'bottom-end';
public string $offsetX = '0';

View File

@ -18,7 +18,10 @@ class FormComponent extends Component
protected bool $readonly = false;
public function __construct($attributes)
/**
* @param array<string, string> $attributes
*/
public function __construct(array $attributes)
{
parent::__construct($attributes);

View File

@ -8,6 +8,8 @@ use ViewComponents\Component;
class ReadMore extends Component
{
public string $id;
public function render(): string
{
$readMoreLabel = lang('Common.read_more');

View File

@ -1,22 +1,19 @@
<?php declare(strict_types=1);
if (session()->has('message')): ?>
<div class="px-4 py-2 mb-4 font-semibold text-green-900 bg-green-200 border border-green-700">
<?= session('message') ?>
</div>
<Alert variant="success" class="mb-4"><?= esc(session('message')) ?></Alert>
<?php endif; ?>
<?php if (session()->has('error')): ?>
<div class="px-4 py-2 mb-4 font-semibold text-red-900 bg-red-200 border border-red-700">
<?= session('error') ?>
</div>
<Alert variant="danger" class="mb-4"><?= esc(session('error')) ?></Alert>
<?php endif; ?>
<?php if (session()->has('errors')): ?>
<ul class="px-4 py-2 mb-4 font-semibold text-red-900 bg-red-200 border border-red-700">
<?php foreach (session('errors') as $error): ?>
<li><?= $error ?></li>
<?php endforeach; ?>
</ul>
<?php endif;
?>
<Alert variant="danger" class="mb-4">
<ul>
<?php foreach (session('errors') as $error): ?>
<li><?= esc($error) ?></li>
<?php endforeach; ?>
</ul>
</Alert>
<?php endif; ?>

View File

@ -6,12 +6,13 @@ use CodeIgniter\CLI\CLI;
// The main Exception
CLI::newLine();
CLI::write('[' . $exception::class . ']', 'light_gray', 'red');
CLI::write('[' . get_class($exception) . ']', 'light_gray', 'red');
CLI::newLine();
CLI::write($message);
CLI::newLine();
CLI::write('at ' . CLI::color(clean_path($exception->getFile()) . ':' . $exception->getLine(), 'green', ), );
CLI::write('at ' . CLI::color(clean_path($exception->getFile()) . ':' . $exception->getLine(), 'green'));
CLI::newLine();
// The backtrace
if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) {
$backtraces = $exception->getTrace();
@ -21,41 +22,42 @@ if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) {
}
foreach ($backtraces as $i => $error) {
$padFile = ' ';
$c = str_pad($i + 1, 3, ' ', STR_PAD_LEFT);
$padFile = ' '; // 4 spaces
$padClass = ' '; // 7 spaces
$c = str_pad((string) ($i + 1), 3, ' ', STR_PAD_LEFT);
if (isset($error['file'])) {
$filepath = clean_path($error['file']) . ':' . $error['line'];
CLI::write($c . $padFile . CLI::color($filepath, 'yellow'));
} else {
CLI::write($c . $padFile . CLI::color('[internal function]', 'yellow'), );
CLI::write($c . $padFile . CLI::color('[internal function]', 'yellow'));
}
$function = '';
if (isset($error['class'])) {
$type =
$error['type'] === '->'
? '()' . $error['type']
: $error['type'];
$function .=
$padClass . $error['class'] . $type . $error['function'];
$type = ($error['type'] === '->') ? '()' . $error['type'] : $error['type'];
$function .= $padClass . $error['class'] . $type . $error['function'];
} elseif (! isset($error['class']) && isset($error['function'])) {
$function .= $padClass . $error['function'];
}
$args = implode(
', ',
array_map(function ($value) {
return match (true) {
is_object($value) => 'Object(' . $value::class . ')',
is_array($value) => $value !== [] ? '[...]' : '[]',
$value === null => 'null',
default => var_export($value, true),
};
}, array_values($error['args'] ?? [])),
);
$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);
}
}, array_values($error['args'] ?? [])));
$function .= '(' . $args . ')';

View File

@ -1,26 +1,21 @@
<?php declare(strict_types=1);
use CodeIgniter\CodeIgniter;
use Config\Services;
$errorId = uniqid('error', true); ?>
$error_id = uniqid('error', true); ?>
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="robots" content="noindex">
<title><?= esc($title) ?></title>
<style type="text/css">
<?= preg_replace('~[\r\n\t ]+~', ' ', file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'debug.css')) ?>
<?= preg_replace('#[\r\n\t ]+#', ' ', file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'debug.css')) ?>
</style>
<script type="text/javascript">
<?= file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'debug.js') ?>
</script>
</head>
<body onload="init()">
<!-- Header -->
@ -29,7 +24,8 @@ $errorId = uniqid('error', true); ?>
<h1><?= esc($title), esc($exception->getCode() ? ' #' . $exception->getCode() : '') ?></h1>
<p>
<?= nl2br(esc($exception->getMessage())) ?>
<a href="https://www.duckduckgo.com/?q=<?= urlencode($title . ' ' . preg_replace('~\'.*\'|".*"~Us', '', $exception->getMessage())) ?>" rel="noreferrer" target="_blank">search &rarr;</a>
<a href="https://www.duckduckgo.com/?q=<?= urlencode($title . ' ' . preg_replace('#\'.*\'|".*"#Us', '', $exception->getMessage())) ?>"
rel="noreferrer" target="_blank">search &rarr;</a>
</p>
</div>
</div>
@ -38,7 +34,7 @@ $errorId = uniqid('error', true); ?>
<div class="container">
<p><b><?= esc(static::cleanPath($file, $line)) ?></b> at line <b><?= esc($line) ?></b></p>
<?php if (is_file($file)): ?>
<?php if (is_file($file)) : ?>
<div class="source">
<?= static::highlightFile($file, $line, 15); ?>
</div>
@ -62,79 +58,79 @@ $errorId = uniqid('error', true); ?>
<div class="content" id="backtrace">
<ol class="trace">
<?php foreach ($trace as $index => $row): ?>
<?php foreach ($trace as $index => $row) : ?>
<li>
<p>
<!-- Trace info -->
<?php if (isset($row['file']) && is_file($row['file'])): ?>
<?php
if (isset($row['function']) && in_array($row['function'], ['include', 'include_once', 'require', 'require_once'], true)) {
echo esc($row['function'] . ' ' . static::cleanPath($row['file']));
} else {
echo esc(static::cleanPath($row['file']) . ' : ' . $row['line']);
}
?>
<?php else: ?>
{PHP internal code}
<li>
<p>
<!-- Trace info -->
<?php if (isset($row['file']) && is_file($row['file'])) :?>
<?php
if (isset($row['function']) && in_array($row['function'], ['include', 'include_once', 'require', 'require_once'], true)) {
echo esc($row['function'] . ' ' . static::cleanPath($row['file']));
} else {
echo esc(static::cleanPath($row['file']) . ' : ' . $row['line']);
}
?>
<?php else : ?>
{PHP internal code}
<?php endif; ?>
<!-- Class/Method -->
<?php if (isset($row['class'])) : ?>
&nbsp;&nbsp;&mdash;&nbsp;&nbsp;<?= esc($row['class'] . $row['type'] . $row['function']) ?>
<?php if (! empty($row['args'])) : ?>
<?php $args_id = $error_id . 'args' . $index ?>
( <a href="#" onclick="return toggle('<?= esc($args_id, 'attr') ?>');">arguments</a> )
<div class="args" id="<?= esc($args_id, 'attr') ?>">
<table cellspacing="0">
<?php
$params = null;
// Reflection by name is not available for closure function
if (substr($row['function'], -1) !== '}') {
$mirror = isset($row['class']) ? new \ReflectionMethod($row['class'], $row['function']) : new \ReflectionFunction($row['function']);
$params = $mirror->getParameters();
}
foreach ($row['args'] as $key => $value) : ?>
<tr>
<td><code><?= esc(isset($params[$key]) ? '$' . $params[$key]->name : "#{$key}") ?></code></td>
<td><pre><?= esc(print_r($value, true)) ?></pre></td>
</tr>
<?php endforeach ?>
</table>
</div>
<?php else : ?>
()
<?php endif; ?>
<?php endif; ?>
<!-- Class/Method -->
<?php if (isset($row['class'])) : ?>
&nbsp;&nbsp;&mdash;&nbsp;&nbsp;<?= esc($row['class'] . $row['type'] . $row['function']) ?>
<?php if (! empty($row['args'])) : ?>
<?php $args_id = $errorId . 'args' . $index ?>
( <a href="#" onclick="return toggle('<?= esc($args_id, 'attr') ?>');">arguments</a> )
<div class="args" id="<?= esc($args_id, 'attr') ?>">
<table cellspacing="0">
<?php if (! isset($row['class']) && isset($row['function'])) : ?>
&nbsp;&nbsp;&mdash;&nbsp;&nbsp; <?= esc($row['function']) ?>()
<?php endif; ?>
</p>
<?php
$params = null;
// Reflection by name is not available for closure function
if (! str_ends_with($row['function'], '}')) {
$mirror = isset($row['class']) ? new ReflectionMethod($row['class'], $row['function']) : new ReflectionFunction($row['function']);
$params = $mirror->getParameters();
}
foreach ($row['args'] as $key => $value): ?>
<tr>
<td><code><?= esc(isset($params[$key]) ? '$' . $params[$key]->name : "#{$key}") ?></code></td>
<td>
<pre><?= esc(print_r($value, true)) ?></pre>
</td>
</tr>
<?php endforeach; ?>
</table>
<!-- Source? -->
<?php if (isset($row['file']) && is_file($row['file']) && isset($row['class'])) : ?>
<div class="source">
<?= static::highlightFile($row['file'], $row['line']) ?>
</div>
<?php else: ?>
()
<?php endif; ?>
<?php endif; ?>
</li>
<?php if (! isset($row['class']) && isset($row['function'])): ?>
&nbsp;&nbsp;&mdash;&nbsp;&nbsp; <?= esc($row['function']) ?>()
<?php endif; ?>
</p>
<!-- Source? -->
<?php if (isset($row['file']) && is_file($row['file']) && isset($row['class'])): ?>
<div class="source">
<?= static::highlightFile($row['file'], $row['line']) ?>
</div>
<?php endif; ?>
</li>
<?php endforeach; ?>
<?php endforeach; ?>
</ol>
</div>
<!-- Server -->
<div class="content" id="server">
<?php foreach (['_SERVER', '_SESSION'] as $var): ?>
<?php if (empty($GLOBALS[$var]) || ! is_array($GLOBALS[$var])) {
continue;
} ?>
<?php foreach (['_SERVER', '_SESSION'] as $var) : ?>
<?php
if (empty($GLOBALS[$var]) || ! is_array($GLOBALS[$var])) {
continue;
} ?>
<h3>$<?= esc($var) ?></h3>
@ -146,26 +142,26 @@ $errorId = uniqid('error', true); ?>
</tr>
</thead>
<tbody>
<?php foreach ($GLOBALS[$var] as $key => $value): ?>
<tr>
<td><?= esc($key) ?></td>
<td>
<?php if (is_string($value)): ?>
<?= esc($value) ?>
<?php else: ?>
<pre><?= esc(print_r($value, true)) ?></pre>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php foreach ($GLOBALS[$var] as $key => $value) : ?>
<tr>
<td><?= esc($key) ?></td>
<td>
<?php if (is_string($value)) : ?>
<?= esc($value) ?>
<?php else: ?>
<pre><?= esc(print_r($value, true)) ?></pre>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endforeach; ?>
<?php endforeach ?>
<!-- Constants -->
<?php $constants = get_defined_constants(true); ?>
<?php if (! empty($constants['user'])): ?>
<?php if (! empty($constants['user'])) : ?>
<h3>Constants</h3>
<table>
@ -176,18 +172,18 @@ $errorId = uniqid('error', true); ?>
</tr>
</thead>
<tbody>
<?php foreach ($constants['user'] as $key => $value): ?>
<tr>
<td><?= esc($key) ?></td>
<td>
<?php if (is_string($value)): ?>
<?= esc($value) ?>
<?php else: ?>
<pre><?= esc(print_r($value, true)) ?></pre>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php foreach ($constants['user'] as $key => $value) : ?>
<tr>
<td><?= esc($key) ?></td>
<td>
<?php if (is_string($value)) : ?>
<?= esc($value) ?>
<?php else: ?>
<pre><?= esc(print_r($value, true)) ?></pre>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
@ -195,7 +191,7 @@ $errorId = uniqid('error', true); ?>
<!-- Request -->
<div class="content" id="request">
<?php $request = Services::request(); ?>
<?php $request = \Config\Services::request(); ?>
<table>
<tbody>
@ -233,10 +229,11 @@ $errorId = uniqid('error', true); ?>
<?php $empty = true; ?>
<?php foreach (['_GET', '_POST', '_COOKIE'] as $var): ?>
<?php if (empty($GLOBALS[$var]) || ! is_array($GLOBALS[$var])) {
continue;
} ?>
<?php foreach (['_GET', '_POST', '_COOKIE'] as $var) : ?>
<?php
if (empty($GLOBALS[$var]) || ! is_array($GLOBALS[$var])) {
continue;
} ?>
<?php $empty = false; ?>
@ -250,24 +247,24 @@ $errorId = uniqid('error', true); ?>
</tr>
</thead>
<tbody>
<?php foreach ($GLOBALS[$var] as $key => $value): ?>
<tr>
<td><?= esc($key) ?></td>
<td>
<?php if (is_string($value)): ?>
<?= esc($value) ?>
<?php else: ?>
<pre><?= esc(print_r($value, true)) ?></pre>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php foreach ($GLOBALS[$var] as $key => $value) : ?>
<tr>
<td><?= esc($key) ?></td>
<td>
<?php if (is_string($value)) : ?>
<?= esc($value) ?>
<?php else: ?>
<pre><?= esc(print_r($value, true)) ?></pre>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endforeach; ?>
<?php endforeach ?>
<?php if ($empty): ?>
<?php if ($empty) : ?>
<div class="alert">
No $_GET, $_POST, or $_COOKIE Information to show.
@ -276,7 +273,7 @@ $errorId = uniqid('error', true); ?>
<?php endif; ?>
<?php $headers = $request->getHeaders(); ?>
<?php if (! empty($headers)): ?>
<?php if (! empty($headers)) : ?>
<h3>Headers</h3>
@ -288,20 +285,22 @@ $errorId = uniqid('error', true); ?>
</tr>
</thead>
<tbody>
<?php foreach ($headers as $value): ?>
<?php if (empty($value)) {
continue;
} ?>
<?php 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 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; ?>
</tbody>
</table>
@ -310,9 +309,9 @@ $errorId = uniqid('error', true); ?>
<!-- Response -->
<?php
$response = Services::response();
$response->setStatusCode(http_response_code());
?>
$response = \Config\Services::response();
$response->setStatusCode(http_response_code());
?>
<div class="content" id="response">
<table>
<tr>
@ -322,8 +321,8 @@ $errorId = uniqid('error', true); ?>
</table>
<?php $headers = $response->getHeaders(); ?>
<?php if ($headers !== []): ?>
<?php natsort($headers); ?>
<?php if (! empty($headers)) : ?>
<?php natsort($headers) ?>
<h3>Headers</h3>
@ -335,12 +334,12 @@ $errorId = uniqid('error', true); ?>
</tr>
</thead>
<tbody>
<?php foreach (array_keys($headers) as $name): ?>
<tr>
<td><?= esc($name, 'html') ?></td>
<td><?= esc($response->getHeaderLine($name), 'html') ?></td>
</tr>
<?php endforeach; ?>
<?php foreach ($headers as $name => $value) : ?>
<tr>
<td><?= esc($name, 'html') ?></td>
<td><?= esc($response->getHeaderLine($name), 'html') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
@ -352,9 +351,9 @@ $errorId = uniqid('error', true); ?>
<?php $files = get_included_files(); ?>
<ol>
<?php foreach ($files as $file): ?>
<li><?= esc(static::cleanPath($file)) ?></li>
<?php endforeach; ?>
<?php foreach ($files as $file) :?>
<li><?= esc(static::cleanPath($file)) ?></li>
<?php endforeach ?>
</ol>
</div>
@ -389,13 +388,12 @@ $errorId = uniqid('error', true); ?>
<p>
Displayed at <?= esc(date('H:i:sa')) ?> &mdash;
PHP: <?= esc(PHP_VERSION) ?> &mdash;
CodeIgniter: <?= esc(CodeIgniter::CI_VERSION) ?>
PHP: <?= esc(PHP_VERSION) ?> &mdash;
CodeIgniter: <?= esc(\CodeIgniter\CodeIgniter::CI_VERSION) ?>
</p>
</div>
</div>
</body>
</html>

View File

@ -7,11 +7,11 @@
"license": "AGPL-3.0-or-later",
"require": {
"php": "^8.0",
"codeigniter4/framework": "^4",
"james-heinrich/getid3": "^2.0.x-dev",
"whichbrowser/parser": "^v2.1.1",
"geoip2/geoip2": "^v2.11.0",
"geoip2/geoip2": "^v2.12.2",
"myth/auth": "dev-develop",
"codeigniter4/codeigniter4": "dev-develop",
"league/commonmark": "^2.2",
"vlucas/phpdotenv": "^v5.3.0",
"league/html-to-markdown": "^v5.0.1",
@ -21,21 +21,18 @@
"phpseclib/phpseclib": "~2.0.30",
"michalsn/codeigniter4-uuid": "dev-develop",
"essence/essence": "^3.5.4",
"codeigniter4/settings": "dev-develop",
"codeigniter4/settings": "^v1.0",
"chrisjean/php-ico": "^1.0",
"melbahja/seo": "^2.0"
},
"require-dev": {
"mikey179/vfsstream": "^v1.6.8",
"phpunit/phpunit": "^9.5.4",
"rector/rector": "^0.11.5",
"captainhook/captainhook": "^5.10.0",
"phpstan/phpstan": "^0.12.85",
"phpstan/extension-installer": "^1.1.0",
"rector/rector-phpstan-rules": "^0.2.9",
"symplify/phpstan-extensions": "^v9.3.12",
"symplify/easy-coding-standard": "^v9.3.12",
"symplify/coding-standard": "^v9.3.12"
"mikey179/vfsstream": "v1.6.10",
"phpunit/phpunit": "^9.5.16",
"captainhook/captainhook": "^5.10.7",
"symplify/easy-coding-standard": "^10.1",
"phpstan/phpstan": "^1.4",
"rector/rector": "^0.12.16",
"symplify/coding-standard": "^10.1"
},
"autoload": {
"psr-4": {
@ -80,14 +77,7 @@
"source": "https://code.castopod.org/adaures/castopod.git",
"discord": "https://castopod.org/discord"
},
"minimum-stability": "dev",
"prefer-stable": true,
"repositories": [
{
"type": "vcs",
"url": "https://github.com/codeigniter4/codeigniter4"
}
],
"config": {
"allow-plugins": {
"phpstan/extension-installer": true

2891
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,7 @@ use Symplify\CodingStandard\Fixer\Naming\StandardizeHereNowDocKeywordFixer;
use Symplify\EasyCodingStandard\ValueObject\Option;
use Symplify\EasyCodingStandard\ValueObject\Set\SetList;
use Symplify\CodingStandard\Fixer\LineLength\LineLengthFixer;
use PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis\AssignmentInConditionSniff;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters = $containerConfigurator->parameters();
@ -19,7 +20,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
__DIR__ . '/public',
]);
$parameters->set(Option::SKIP, [
$parameters->set(Option::SKIP, [
// skip specific generated files
__DIR__ . '/modules/Admin/Language/*/PersonsTaxonomy.php',
@ -39,7 +40,9 @@ return static function (ContainerConfigurator $containerConfigurator): void {
__DIR__ . '/app/Views/*',
__DIR__ . '/modules/**/Views/*',
__DIR__ . '/themes/*',
]
],
AssignmentInConditionSniff::class,
]);
$containerConfigurator->import(SetList::PSR_12);

View File

@ -63,6 +63,7 @@ class ContributorController extends BaseController
public function view(): string
{
$data = [
'podcast' => $this->podcast,
'contributor' => (new UserModel())->getPodcastContributor($this->user->id, $this->podcast->id),
];

View File

@ -294,6 +294,7 @@ class EpisodeController extends BaseController
) {
(new MediaModel())->deleteMedia($this->episode->transcript);
}
$this->episode->transcript_remote_url = $transcriptRemoteUrl === '' ? null : $transcriptRemoteUrl;
}
@ -311,6 +312,7 @@ class EpisodeController extends BaseController
) {
(new MediaModel())->deleteMedia($this->episode->chapters);
}
$this->episode->chapters_remote_url = $chaptersRemoteUrl === '' ? null : $chaptersRemoteUrl;
}

View File

@ -384,6 +384,7 @@ class PodcastController extends BaseController
->withInput()
->with('errors', $mediaModel->errors());
}
(new PodcastModel())->clearCache([
'id' => $this->podcast->id,
]);

View File

@ -77,6 +77,7 @@ class PodcastImportController extends BaseController
->withInput()
->with('errors', $this->validator->getErrors());
}
try {
ini_set('user_agent', 'Castopod/' . CP_VERSION);
$feed = simplexml_load_file($this->request->getPost('imported_feed_url'));
@ -93,6 +94,7 @@ class PodcastImportController extends BaseController
' ⎋</a>',
]);
}
$nsItunes = $feed->channel[0]->children('http://www.itunes.com/dtds/podcast-1.0.dtd');
$nsPodcast = $feed->channel[0]->children(
'https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md',
@ -128,6 +130,7 @@ class PodcastImportController extends BaseController
$nsPodcast->location->attributes()['osm'] === null ? null : (string) $nsPodcast->location->attributes()['osm'],
);
}
$guid = null;
if (property_exists($nsPodcast, 'guid') && $nsPodcast->guid !== null) {
$guid = (string) $nsPodcast->guid;
@ -313,8 +316,10 @@ class PodcastImportController extends BaseController
while (in_array($slug . '-' . $slugNumber, $slugs, true)) {
++$slugNumber;
}
$slug = $slug . '-' . $slugNumber;
}
$slugs[] = $slug;
$itemDescriptionHtml = match ($this->request->getPost('description_field')) {
'content' => (string) $nsContent->encoded,

View File

@ -74,9 +74,11 @@ class PodcastPlatformController extends BaseController
if ($podcastPlatformUrl === null) {
continue;
}
if (! $validation->check($podcastPlatformUrl, 'validate_url')) {
continue;
}
$podcastsPlatformsData[] = [
'platform_slug' => $platformSlug,
'podcast_id' => $this->podcast->id,

View File

@ -81,6 +81,7 @@ class SchedulerController extends Controller
'job_ended_at' => Time::now(),
]);
}
$clipModel->clearVideoClipCache($scheduledClip->id);
} catch (Exception $exception) {
(new ClipModel())->update($scheduledClip->id, [

View File

@ -179,6 +179,7 @@ class SettingsController extends BaseController
(new EpisodeCommentModel())->resetLikesCount();
(new EpisodeCommentModel())->resetRepliesCount();
}
helper('media');
if ($this->request->getPost('rewrite_media') === 'yes') {

View File

@ -144,7 +144,7 @@ class SoundbiteController extends BaseController
{
$soundbite = (new ClipModel())->getSoundbiteById((int) $soundbiteId);
if ($soundbite === null) {
if (! $soundbite instanceof Soundbite) {
throw PageNotFoundException::forPageNotFound();
}

View File

@ -197,7 +197,7 @@ class VideoClipsController extends BaseController
{
$videoClip = (new ClipModel())->getVideoClipById((int) $videoClipId);
if ($videoClip === null) {
if (! $videoClip instanceof VideoClip) {
throw PageNotFoundException::forPageNotFound();
}
@ -214,7 +214,7 @@ class VideoClipsController extends BaseController
{
$videoClip = (new ClipModel())->getVideoClipById((int) $videoClipId);
if ($videoClip === null) {
if (! $videoClip instanceof VideoClip) {
throw PageNotFoundException::forPageNotFound();
}

View File

@ -10,6 +10,7 @@ declare(strict_types=1);
return [
'messages' => [
'actorNotFound' => 'The account could not be found!',
'blockActorSuccess' => '{actor} has been blocked!',
'unblockActorSuccess' => 'Actor has been unblocked!',
'blockDomainSuccess' => '{domain} has been blocked!',

View File

@ -10,6 +10,7 @@ declare(strict_types=1);
return [
'messages' => [
'actorNotFound' => 'Lutilisateur na pu être trouvé!',
'blockActorSuccess' => '{actor} a été bloqué!',
'unblockActorSuccess' => 'Lutilisateur a été débloqué!',
'blockDomainSuccess' => '{domain} a été bloqué!',

View File

@ -9,7 +9,6 @@ declare(strict_types=1);
*/
use AdAures\Ipcat\IpDb;
use CodeIgniter\Router\Exceptions\RouterException;
use Config\Services;
use GeoIp2\Database\Reader;
use Opawg\UserAgentsPhp\UserAgents;
@ -38,8 +37,6 @@ if (! function_exists('base64_url_decode')) {
if (! function_exists('generate_episode_analytics_url')) {
/**
* Builds the episode analytics url that redirects to the audio file url after analytics hit.
*
* @throws RouterException
*/
function generate_episode_analytics_url(
int $podcastId,
@ -124,6 +121,7 @@ if (! function_exists('set_user_session_location')) {
// If things go wrong the show must go on and the user must be able to download the file
} catch (Exception) {
}
$session->set('location', $location);
}
}
@ -147,6 +145,7 @@ if (! function_exists('set_user_session_player')) {
// If things go wrong the show must go on and the user must be able to download the file
} catch (Exception) {
}
if ($playerFound) {
$session->set('player', $playerFound);
} else {
@ -188,9 +187,11 @@ if (! function_exists('set_user_session_browser')) {
} catch (Exception) {
$browserName = '- Could not get browser name -';
}
if ($browserName === '') {
$browserName = '- Could not get browser name -';
}
$session->set('browser', $browserName);
}
}
@ -273,6 +274,7 @@ if (! function_exists('podcast_hit')) {
if ($session->get('denyListIp')) {
$session->get('player')['bot'] = true;
}
//We get the HTTP header field `Range`:
$httpRange = isset($_SERVER['HTTP_RANGE'])
? $_SERVER['HTTP_RANGE']
@ -295,6 +297,7 @@ if (! function_exists('podcast_hit')) {
// If it was never downloaded that means that zero byte were downloaded:
$downloadedBytes = 0;
}
// If the number of downloaded bytes was previously below the 1mn threshold we go on:
// (Otherwise it means that this was already counted, therefore we don't do anything)
if ($downloadedBytes < $bytesThreshold) {
@ -314,6 +317,7 @@ if (! function_exists('podcast_hit')) {
(array_key_exists(0, $parts) ? 0 : (int) $parts[0]);
}
}
// We save the number of downloaded bytes for this user and this episode:
cache()
->save($episodeHashId, $downloadedBytes, $rollingTTL);
@ -339,6 +343,7 @@ if (! function_exists('podcast_hit')) {
} else {
$downloadsByUser = 1;
}
// Listener count is calculated from 00h00 to 23h59:
$midnightTTL = strtotime('tomorrow') - time();
// We save the download count for this user until midnight:

View File

@ -89,6 +89,7 @@ class AnalyticsPodcastByCountryModel extends Model
cache()
->save("{$podcastId}_analytics_podcast_by_country_yearly", $found, 600);
}
return $found;
}
}

View File

@ -62,6 +62,7 @@ class AnalyticsPodcastByPlayerModel extends Model
cache()
->save("{$podcastId}_analytics_podcasts_by_player_by_app_weekly", $found, 600);
}
return $found;
}
@ -90,6 +91,7 @@ class AnalyticsPodcastByPlayerModel extends Model
cache()
->save("{$podcastId}_analytics_podcasts_by_player_by_app_yearly", $found, 600);
}
return $found;
}
@ -119,6 +121,7 @@ class AnalyticsPodcastByPlayerModel extends Model
cache()
->save("{$podcastId}_analytics_podcasts_by_player_by_os_weekly", $found, 600);
}
return $found;
}
@ -147,6 +150,7 @@ class AnalyticsPodcastByPlayerModel extends Model
cache()
->save("{$podcastId}_analytics_podcasts_by_player_by_device_weekly", $found, 600);
}
return $found;
}
@ -175,6 +179,7 @@ class AnalyticsPodcastByPlayerModel extends Model
cache()
->save("{$podcastId}_analytics_podcasts_by_player_bots", $found, 600);
}
return $found;
}
}

View File

@ -64,6 +64,7 @@ class AnalyticsPodcastByRegionModel extends Model
cache()
->save("{$podcastId}_analytics_podcast_by_region_{$locale}", $found, 600);
}
return $found;
}
}

View File

@ -62,6 +62,7 @@ class AnalyticsPodcastByServiceModel extends Model
cache()
->save("{$podcastId}_analytics_podcasts_by_service_weekly", $found, 600);
}
return $found;
}
}

View File

@ -56,6 +56,7 @@ class AnalyticsPodcastModel extends Model
cache()
->save("{$podcastId}_analytics_podcast_by_day", $found, 600);
}
return $found;
}
@ -80,6 +81,7 @@ class AnalyticsPodcastModel extends Model
cache()
->save("{$podcastId}_analytics_podcasts_by_weekday", $found, 600);
}
return $found;
}
@ -102,6 +104,7 @@ class AnalyticsPodcastModel extends Model
cache()
->save("{$podcastId}_analytics_podcast_by_bandwidth", $found, 600);
}
return $found;
}
@ -125,6 +128,7 @@ class AnalyticsPodcastModel extends Model
cache()
->save("{$podcastId}_analytics_podcast_by_month", $found, 600);
}
return $found;
}
@ -149,6 +153,7 @@ class AnalyticsPodcastModel extends Model
cache()
->save("{$podcastId}_analytics_podcast_unique_listeners_by_day", $found, 600);
}
return $found;
}
@ -174,6 +179,7 @@ class AnalyticsPodcastModel extends Model
cache()
->save("{$podcastId}_analytics_podcast_unique_listeners_by_month", $found, 600);
}
return $found;
}
@ -200,6 +206,7 @@ class AnalyticsPodcastModel extends Model
cache()
->save("{$podcastId}_analytics_podcast_listening_time_by_day", $found, 600);
}
return $found;
}
@ -225,6 +232,7 @@ class AnalyticsPodcastModel extends Model
cache()
->save("{$podcastId}_analytics_podcast_listening_time_by_month", $found, 600);
}
return $found;
}
}

View File

@ -59,6 +59,7 @@ class AnalyticsWebsiteByBrowserModel extends Model
cache()
->save("{$podcastId}_analytics_website_by_browser", $found, 600);
}
return $found;
}
}

View File

@ -58,6 +58,7 @@ class AnalyticsWebsiteByEntryPageModel extends Model
cache()
->save("{$podcastId}_analytics_website_by_entry_page", $found, 600);
}
return $found;
}
}

View File

@ -58,6 +58,7 @@ class AnalyticsWebsiteByRefererModel extends Model
cache()
->save("{$podcastId}_analytics_website_by_referer", $found, 600);
}
return $found;
}
@ -84,6 +85,7 @@ class AnalyticsWebsiteByRefererModel extends Model
cache()
->save("{$podcastId}_analytics_website_by_domain_weekly", $found, 600);
}
return $found;
}
@ -110,6 +112,7 @@ class AnalyticsWebsiteByRefererModel extends Model
cache()
->save("{$podcastId}_analytics_website_by_domain_yearly", $found, 600);
}
return $found;
}
}

View File

@ -78,6 +78,7 @@ class PermissionFilter implements FilterInterface
->to($redirectURL)
->with('error', lang('Auth.notEnoughPrivilege'));
}
throw new PermissionException(lang('Auth.notEnoughPrivilege'));
}
}

View File

@ -132,6 +132,7 @@ class ActorController extends Controller
return $this->response->setStatusCode(200)
->setJSON([]);
}
// return not handled undo error (501 = not implemented)
return $this->response->setStatusCode(501)
->setJSON([]);
@ -286,6 +287,7 @@ class ActorController extends Controller
foreach ($paginatedActivity as $activity) {
$orderedItems[] = $activity->payload;
}
$collection = new OrderedCollectionPage($pager, $orderedItems);
}
@ -322,6 +324,7 @@ class ActorController extends Controller
foreach ($paginatedFollowers as $follower) {
$orderedItems[] = $follower->uri;
}
$followersCollection = new OrderedCollectionPage($pager, $orderedItems);
}

View File

@ -12,6 +12,7 @@ namespace Modules\Fediverse\Controllers;
use CodeIgniter\Controller;
use CodeIgniter\HTTP\RedirectResponse;
use Exception;
class BlockController extends Controller
{
@ -23,7 +24,7 @@ class BlockController extends Controller
public function attemptBlockActor(): RedirectResponse
{
$rules = [
'handle' => 'required',
'handle' => 'required|regex_match[/^@?([\w\.\-]+)@([\w\.\-]+)(:[\d]+)?$/]',
];
if (! $this->validate($rules)) {
@ -36,9 +37,9 @@ class BlockController extends Controller
$handle = $this->request->getPost('handle');
if ($parts = split_handle($handle)) {
if (
($actor = get_or_create_actor($parts['username'], $parts['domain'])) === null
) {
try {
$actor = get_or_create_actor($parts['username'], $parts['domain']);
} catch (Exception) {
return redirect()
->back()
->withInput()
@ -78,7 +79,7 @@ class BlockController extends Controller
public function attemptBlockDomain(): RedirectResponse
{
$rules = [
'domain' => 'required',
'domain' => 'required|regex_match[/^[\w\-\.]+[\w]+(:[\d]+)?/]',
];
if (! $this->validate($rules)) {

View File

@ -30,8 +30,7 @@ class NodeInfo2Controller extends Controller
'version' => '1.0',
'server' => [
'baseUrl' => base_url(),
'name' => service('settings')
->get('App.siteName'),
'name' => esc(service('settings') ->get('App.siteName')),
'software' => 'Castopod',
'version' => CP_VERSION,
],

View File

@ -79,7 +79,7 @@ if (! function_exists('accept_follow')) {
$acceptActivity->toJSON(),
);
$acceptActivity->set('id', url_to('activity', $actor->username, $activityId));
$acceptActivity->set('id', url_to('activity', esc($actor->username), $activityId));
$activityModel->update($activityId, [
'payload' => $acceptActivity->toJSON(),
@ -108,6 +108,7 @@ if (! function_exists('send_activity_to_actor')) {
if ($actor->private_key !== null) {
$acceptRequest->sign($actor->public_key_id, $actor->private_key);
}
$acceptRequest->post();
} catch (Exception $exception) {
// log error
@ -283,6 +284,7 @@ if (! function_exists('create_actor_from_uri')) {
$newActor->cover_image_url = $actorPayload->image->url;
$newActor->cover_image_mimetype = $actorPayload->image->mediaType;
}
$newActor->inbox_url = $actorPayload->inbox;
$newActor->outbox_url = property_exists($actorPayload, 'outbox') ? $actorPayload->outbox : null;
$newActor->followers_url = property_exists($actorPayload, 'followers') ? $actorPayload->followers : null;
@ -299,8 +301,6 @@ if (! function_exists('create_actor_from_uri')) {
if (! function_exists('get_current_domain')) {
/**
* Returns instance's domain name
*
* @throws HTTPException
*/
function get_current_domain(): string
{
@ -324,8 +324,6 @@ if (! function_exists('get_message_from_object')) {
* Gets the message from content, if no content key is present, checks for content in contentMap
*
* TODO: store multiple languages, convert markdown
*
* @return string|false
*/
function get_message_from_object(stdClass $object): string | false
{
@ -365,9 +363,10 @@ if (! function_exists('linkify')) {
'http', 'https' => preg_replace_callback(
'~(?:(https?)://([^\s<]+)|(www\.[^\s<]+?\.[^\s<]+))(?<![\.,:])~i',
function (array $match) use ($protocol, &$links) {
if ($match[1]) {
if ($match[1] !== '' && $match[1] !== '0') {
$protocol = $match[1];
}
$link = $match[2] ?: $match[3];
helper('text');

View File

@ -12,7 +12,6 @@ namespace Modules\Fediverse\Models;
use CodeIgniter\Database\BaseResult;
use CodeIgniter\Events\Events;
use CodeIgniter\Model;
use Modules\Fediverse\Entities\BlockedDomain;
class BlockedDomainModel extends BaseModel
@ -64,6 +63,7 @@ class BlockedDomainModel extends BaseModel
cache()
->save($cacheName, $found, DECADE);
}
return $found;
}

View File

@ -75,7 +75,7 @@ class FavouriteModel extends BaseUuidModel
'queued',
);
$likeActivity->set('id', url_to('activity', $actor->username, $activityId));
$likeActivity->set('id', url_to('activity', esc($actor->username), $activityId));
model('ActivityModel', false)
->update($activityId, [
@ -122,7 +122,7 @@ class FavouriteModel extends BaseUuidModel
$likeActivity = new LikeActivity();
$likeActivity
->set('id', url_to('activity', $actor->username, $activity->id))
->set('id', url_to('activity', esc($actor->username), $activity->id))
->set('actor', $actor->uri)
->set('object', $post->uri);
@ -141,7 +141,7 @@ class FavouriteModel extends BaseUuidModel
'queued',
);
$undoActivity->set('id', url_to('activity', $actor->username, $activityId));
$undoActivity->set('id', url_to('activity', esc($actor->username), $activityId));
model('ActivityModel', false)
->update($activityId, [

View File

@ -10,12 +10,9 @@ declare(strict_types=1);
namespace Modules\Fediverse\Models;
use CodeIgniter\Database\Exceptions\DatabaseException;
use CodeIgniter\Events\Events;
use CodeIgniter\I18n\Time;
use CodeIgniter\Model;
use Exception;
use InvalidArgumentException;
use Modules\Fediverse\Activities\FollowActivity;
use Modules\Fediverse\Activities\UndoActivity;
use Modules\Fediverse\Entities\Actor;
@ -48,7 +45,6 @@ class FollowModel extends BaseModel
/**
* @param Actor $actor Actor that is following
* @param Actor $targetActor Actor that is being followed
* @throws DatabaseException
*/
public function addFollower(Actor $actor, Actor $targetActor, bool $registerActivity = true): void
{
@ -83,7 +79,7 @@ class FollowModel extends BaseModel
'queued',
);
$followActivity->set('id', url_to('activity', $actor->username, $activityId));
$followActivity->set('id', url_to('activity', esc($actor->username), $activityId));
model('ActivityModel', false)
->update($activityId, [
@ -105,8 +101,6 @@ class FollowModel extends BaseModel
/**
* @param Actor $actor Actor that is unfollowing
* @param Actor $targetActor Actor that is being unfollowed
* @throws InvalidArgumentException
* @throws DatabaseException
*/
public function removeFollower(Actor $actor, Actor $targetActor, bool $registerActivity = true): void
{
@ -148,7 +142,7 @@ class FollowModel extends BaseModel
'queued',
);
$undoActivity->set('id', url_to('activity', $actor->username, $activityId));
$undoActivity->set('id', url_to('activity', esc($actor->username), $activityId));
model('ActivityModel', false)
->update($activityId, [

View File

@ -286,7 +286,7 @@ class PostModel extends BaseUuidModel
if ($registerActivity) {
// set post id and uri to construct NoteObject
$post->id = $newPostId;
$post->uri = url_to('post', $post->actor->username, $newPostId);
$post->uri = url_to('post', esc($post->actor->username), $newPostId);
$createActivity = new CreateActivity();
$noteObjectClass = config('Fediverse')
@ -306,7 +306,7 @@ class PostModel extends BaseUuidModel
'queued',
);
$createActivity->set('id', url_to('activity', $post->actor->username, $activityId));
$createActivity->set('id', url_to('activity', esc($post->actor->username), $activityId));
model('ActivityModel', false)
->update($activityId, [
@ -419,7 +419,7 @@ class PostModel extends BaseUuidModel
'queued',
);
$deleteActivity->set('id', url_to('activity', $post->actor->username, $activityId));
$deleteActivity->set('id', url_to('activity', esc($post->actor->username), $activityId));
model('ActivityModel', false)
->update($activityId, [
@ -506,7 +506,7 @@ class PostModel extends BaseUuidModel
'queued',
);
$announceActivity->set('id', url_to('activity', $post->actor->username, $activityId));
$announceActivity->set('id', url_to('activity', esc($post->actor->username), $activityId));
model('ActivityModel', false)
->update($activityId, [
@ -721,7 +721,7 @@ class PostModel extends BaseUuidModel
$actor = model('ActorModel', false)
->getActorById((int) $data['data']['actor_id']);
$data['data']['uri'] = url_to('post', $actor->username, $uuid4->toString());
$data['data']['uri'] = url_to('post', esc($actor->username), $uuid4->toString());
}
return $data;

View File

@ -44,7 +44,7 @@ class NoteObject extends ObjectType
$this->inReplyTo = $post->reply_to_post->uri;
}
$this->replies = url_to('post-replies', $post->actor->username, $post->id);
$this->replies = url_to('post-replies', esc($post->actor->username), $post->id);
$this->cc = [$post->actor->followers_url];
}

View File

@ -33,6 +33,7 @@ class OrderedCollectionPage extends OrderedCollectionObject
if ($isFirstPage) {
$this->first = null;
}
if ($isLastPage) {
$this->last = null;
}

View File

@ -356,6 +356,7 @@ class InstallController extends Controller
$replaced = true;
return $keyVal;
}
return $line;
},
$envData

3829
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -27,71 +27,71 @@
"prepare": "is-ci || husky install"
},
"dependencies": {
"@amcharts/amcharts4": "^4.10.23",
"@amcharts/amcharts4": "^4.10.24",
"@amcharts/amcharts4-geodata": "^4.1.22",
"@codemirror/basic-setup": "^0.19.1",
"@codemirror/commands": "^0.19.7",
"@codemirror/commands": "^0.19.8",
"@codemirror/lang-xml": "^0.19.2",
"@codemirror/state": "^0.19.6",
"@codemirror/view": "^0.19.39",
"@floating-ui/dom": "^0.1.10",
"@codemirror/state": "^0.19.9",
"@codemirror/view": "^0.19.45",
"@floating-ui/dom": "^0.3.1",
"@github/clipboard-copy-element": "^1.1.2",
"@github/hotkey": "^1.6.1",
"@github/hotkey": "^2.0.0",
"@github/markdown-toolbar-element": "^2.1.0",
"@github/time-elements": "^3.1.2",
"@tailwindcss/nesting": "^0.0.0-insiders.565cd3e",
"@vime/core": "^5.3.1",
"choices.js": "^10.0.0",
"choices.js": "^10.1.0",
"flatpickr": "^4.6.9",
"leaflet": "^1.7.1",
"leaflet.markercluster": "^1.5.3",
"lit": "^2.1.1",
"marked": "^4.0.10",
"wavesurfer.js": "^5.2.0",
"xml-formatter": "^2.6.0"
"lit": "^2.2.0",
"marked": "^4.0.12",
"wavesurfer.js": "^6.0.2",
"xml-formatter": "^2.6.1"
},
"devDependencies": {
"@commitlint/cli": "^16.0.2",
"@commitlint/config-conventional": "^16.0.0",
"@commitlint/cli": "^16.2.1",
"@commitlint/config-conventional": "^16.2.1",
"@semantic-release/changelog": "^6.0.1",
"@semantic-release/exec": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"@semantic-release/gitlab": "^7.0.4",
"@tailwindcss/forms": "^0.4.0-alpha.2",
"@tailwindcss/line-clamp": "^0.3.1",
"@tailwindcss/typography": "^0.5.0",
"@types/leaflet": "^1.7.8",
"@types/marked": "^4.0.1",
"@types/wavesurfer.js": "^5.2.2",
"@typescript-eslint/eslint-plugin": "^5.10.0",
"@typescript-eslint/parser": "^5.10.0",
"@tailwindcss/typography": "^0.5.2",
"@types/leaflet": "^1.7.9",
"@types/marked": "^4.0.2",
"@types/wavesurfer.js": "^6.0.1",
"@typescript-eslint/eslint-plugin": "^5.12.1",
"@typescript-eslint/parser": "^5.12.1",
"cross-env": "^7.0.3",
"cssnano": "^5.0.15",
"cssnano": "^5.0.17",
"cz-conventional-changelog": "^3.3.0",
"eslint": "^8.7.0",
"eslint-config-prettier": "^8.3.0",
"eslint": "^8.10.0",
"eslint-config-prettier": "^8.4.0",
"eslint-plugin-prettier": "^4.0.0",
"husky": "^7.0.4",
"is-ci": "^3.0.1",
"lint-staged": "^12.2.0",
"lint-staged": "^12.3.4",
"postcss-import": "^14.0.2",
"postcss-nesting": "^10.1.2",
"postcss-preset-env": "^7.2.3",
"postcss-preset-env": "^7.4.1",
"postcss-reporter": "^7.0.5",
"prettier": "2.5.1",
"prettier-plugin-organize-imports": "^2.3.4",
"semantic-release": "^19.0.2",
"stylelint": "^14.2.0",
"stylelint-config-standard": "^24.0.0",
"stylelint": "^14.5.3",
"stylelint-config-standard": "^25.0.0",
"svgo": "^2.8.0",
"tailwindcss": "^3.0.15",
"typescript": "^4.5.4",
"vite": "^2.7.13",
"tailwindcss": "^3.0.23",
"typescript": "^4.6.2",
"vite": "^2.8.6",
"vite-plugin-pwa": "^0.11.13",
"workbox-build": "^6.4.2",
"workbox-core": "^6.4.2",
"workbox-routing": "^6.4.2",
"workbox-strategies": "^6.4.2"
"workbox-build": "^6.5.0",
"workbox-core": "^6.5.0",
"workbox-routing": "^6.5.0",
"workbox-strategies": "^6.5.0"
},
"lint-staged": {
"*.{js,ts,css,md,json}": "prettier --write",

View File

@ -5,35 +5,22 @@ parameters:
- app
- tests
bootstrapFiles:
- vendor/codeigniter4/codeigniter4/system/Test/bootstrap.php
- vendor/codeigniter4/framework/system/Test/bootstrap.php
scanDirectories:
- app/Helpers
- modules/Analytics/Helpers
- modules/Fediverse/Helpers
- vendor/codeigniter4/codeigniter4/system/Helpers
- vendor/codeigniter4/framework/system/Helpers
- vendor/myth/auth/src/Helpers
excludes_analyse:
excludePaths:
- app/Libraries/Router.php
- app/Views/*
- modules/*/Views/*
- themes/*
ignoreErrors:
- '#This property type might be inlined to PHP. Do you have confidence it is correct\? Put it here#'
- '#^Cognitive complexity for#'
- '#^Class cognitive complexity#'
- '#Do not use chained method calls. Put each on separated lines.#'
- '#Do not inherit from abstract class, better use composition#'
- '#Cannot access property [\$a-z_]+ on ((array\|)?object)#'
- '#^Call to an undefined method CodeIgniter\\Database\\BaseBuilder#'
- '#^Call to an undefined method CodeIgniter\\Database\\ConnectionInterface#'
- '#Function \"preg_.*\(\)\" cannot be used/left in the code#'
- '#Function "property_exists\(\)" cannot be used/left in the code#'
- '#Instead of "instanceof/is_a\(\)" use ReflectionProvider service or "\(new ObjectType\(<desired_type\>\)\)\-\>isSuperTypeOf\(<element_type\>\)" for static reflection to work#'
- '#^Call to an undefined method CodeIgniter\\Model#'
- '#^Access to an undefined property App\\Entities\\Media\\Image#'
- '#^Access to an undefined property CodeIgniter\\Database\\BaseBuilder#'
-
message: '#Function "function_exists\(\)" cannot be used/left in the code#'
paths:
- app/Helpers
- app/Common.php
- app/Libraries/ViewComponents/Helpers
- '#^Access to an undefined property CodeIgniter\\Database\\BaseBuilder#'

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
bootstrap="vendor/codeigniter4/codeigniter4/system/Test/bootstrap.php"
bootstrap="vendor/codeigniter4/framework/system/Test/bootstrap.php"
backupGlobals="false"
colors="true"
convertErrorsToExceptions="true"

View File

@ -2,12 +2,11 @@
declare(strict_types=1);
use Rector\CodeQuality\Rector\PropertyFetch\ExplicitMethodCallOverMagicGetSetRector;
use Rector\CodingStyle\Rector\ClassMethod\UnSpreadOperatorRector;
use Rector\CodingStyle\Rector\Encapsed\EncapsedStringsToSprintfRector;
use Rector\CodingStyle\Rector\FuncCall\CallUserFuncArrayToVariadicRector;
use Rector\CodingStyle\Rector\FuncCall\ConsistentPregDelimiterRector;
use Rector\CodingStyle\Rector\FuncCall\VersionCompareFuncCallToConstantRector;
use Rector\CodingStyle\Rector\String_\SplitStringClassConstantToClassConstFetchRector;
use Rector\CodingStyle\Rector\Stmt\NewlineAfterStatementRector;
use Rector\Core\Configuration\Option;
use Rector\Core\ValueObject\PhpVersion;
use Rector\EarlyReturn\Rector\If_\ChangeOrIfContinueToMultiContinueRector;
@ -15,7 +14,6 @@ use Rector\EarlyReturn\Rector\If_\ChangeOrIfReturnToEarlyReturnRector;
use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
use Rector\Php80\Rector\ClassMethod\OptionalParametersAfterRequiredRector;
use Rector\Set\ValueObject\SetList;
use Rector\Transform\Rector\FuncCall\FuncCallToConstFetchRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
@ -24,14 +22,14 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$parameters->set(Option::PATHS, [
__DIR__ . '/app',
__DIR__ . '/modules',
__DIR__ . '/tests',
__DIR__ . '/public',
// __DIR__ . '/modules',
// __DIR__ . '/tests',
// __DIR__ . '/public',
]);
// do you need to include constants, class aliases or custom autoloader? files listed will be executed
$parameters->set(Option::BOOTSTRAP_FILES, [
__DIR__ . '/vendor/codeigniter4/codeigniter4/system/Test/bootstrap.php',
__DIR__ . '/vendor/codeigniter4/framework/system/Test/bootstrap.php',
]);
// Define what rule sets will be applied
@ -46,10 +44,13 @@ return static function (ContainerConfigurator $containerConfigurator): void {
// auto import fully qualified class names
$parameters->set(Option::AUTO_IMPORT_NAMES, true);
// $parameters->set(Option::ENABLE_CACHE, true);
// TODO: add parallel
// $parameters->set(Option::PARALLEL, true);
$parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_80);
$parameters->set(Option::SKIP, [
__DIR__ . '/app/Views/errors/*',
// skip specific generated files
__DIR__ . '/modules/Admin/Language/*/PersonsTaxonomy.php',
@ -57,8 +58,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
ChangeOrIfReturnToEarlyReturnRector::class,
ChangeOrIfContinueToMultiContinueRector::class,
EncapsedStringsToSprintfRector::class,
SplitStringClassConstantToClassConstFetchRector::class,
UnSpreadOperatorRector::class,
ExplicitMethodCallOverMagicGetSetRector::class,
// skip rule in specific directory
StringClassNameToClassConstantRector::class => [
@ -68,6 +69,10 @@ return static function (ContainerConfigurator $containerConfigurator): void {
OptionalParametersAfterRequiredRector::class => [
__DIR__ . '/app/Validation',
],
NewlineAfterStatementRector::class => [
__DIR__ . '/app/Views',
]
]);
// Path to phpstan with extensions, that PHPStan in Rector uses to determine types

View File

@ -24,8 +24,6 @@ declare(strict_types=1);
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* @package CodeIgniter
* @author CodeIgniter Dev Team
* @copyright 2019-2020 CodeIgniter Foundation
* @license https://opensource.org/licenses/MIT MIT License
* @link https://codeigniter.com

View File

@ -34,7 +34,7 @@ final class HealthTest extends CIUnitTestCase
// So if you set app.baseURL in .env, it takes precedence
$config = new App();
$this->assertTrue(
$validation->check($config->baseURL, 'valid_url'),
$validation->check($config->baseURL, 'valid_url_strict'),
'baseURL "' . $config->baseURL . '" in .env is not valid URL'
);
}
@ -45,7 +45,7 @@ final class HealthTest extends CIUnitTestCase
// BaseURL in app/Config/App.php is a valid URL?
$this->assertTrue(
$validation->check($reader->baseURL, 'valid_url'),
$validation->check($reader->baseURL, 'valid_url_strict'),
'baseURL "' . $reader->baseURL . '" in app/Config/App.php is not valid URL'
);
}

View File

@ -1,20 +1,19 @@
<?php declare(strict_types=1);
if (session()->has('message')): ?>
<Alert variant="success" class="mb-4"><?= session('message') ?></Alert>
<Alert variant="success" class="mb-4"><?= esc(session('message')) ?></Alert>
<?php endif; ?>
<?php if (session()->has('error')): ?>
<Alert variant="danger" class="mb-4"><?= session('error') ?></Alert>
<Alert variant="danger" class="mb-4"><?= esc(session('error')) ?></Alert>
<?php endif; ?>
<?php if (session()->has('errors')): ?>
<Alert variant="danger" class="mb-4">
<ul>
<?php foreach (session('errors') as $error): ?>
<li><?= $error ?></li>
<li><?= esc($error) ?></li>
<?php endforeach; ?>
</ul>
</Alert>
<?php endif;
?>
<?php endif; ?>

View File

@ -27,16 +27,17 @@
<?= user()
->podcasts === [] ? '' : '<img src="' . interact_as_actor()->avatar_image_url . '" class="absolute bottom-0 w-4 h-4 border rounded-full -right-1 border-navigation-bg" loading="lazy" />' ?>
</div>
<?= user()->username ?>
<?= esc(user()->username) ?>
<?= icon('caret-down', 'ml-auto text-2xl') ?></button>
<?php
$interactButtons = '';
foreach (user()->podcasts as $userPodcast) {
$checkMark = interact_as_actor_id() === $userPodcast->actor_id ? icon('check', 'ml-2 bg-accent-base text-accent-contrast rounded-full') : '';
$userPodcastTitle = esc($userPodcast->title);
$interactButtons .= <<<CODE_SAMPLE
<button class="inline-flex items-center w-full px-4 py-1 hover:bg-highlight" id="interact-as-actor-{$userPodcast->id}" name="actor_id" value="{$userPodcast->actor_id}">
<div class="inline-flex items-center flex-1 text-sm"><img src="{$userPodcast->cover->tiny_url}" class="w-6 h-6 mr-2 rounded-full" loading="lazy" /><span class="truncate">{$userPodcast->title}</span>{$checkMark}</div>
<div class="inline-flex items-center flex-1 text-sm"><img src="{$userPodcast->cover->tiny_url}" class="w-6 h-6 mr-2 rounded-full" loading="lazy" /><span class="max-w-xs truncate">{$userPodcastTitle}</span>{$checkMark}</div>
</button>
CODE_SAMPLE;
}

View File

@ -11,7 +11,7 @@
<?= lang('User.form.username') ?>
</dt>
<dd class="mt-1 text-sm leading-5">
<?= $user->username ?>
<?= esc($user->username) ?>
</dd>
</div>
<div class="px-4 py-5">

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