Compare commits
4 Commits
64be3f8a44
...
964723cd52
Author | SHA1 | Date |
---|---|---|
Yassine Doghri | 964723cd52 | |
Yassine Doghri | 0acc1363f0 | |
Yassine Doghri | f9a939471d | |
Yassine Doghri | 303a900f66 |
|
@ -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>"
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ class App extends BaseConfig
|
|||
* DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!!
|
||||
*
|
||||
*/
|
||||
public string $permittedURIChars = 'a-z 0-9~%.:_\-';
|
||||
public string $permittedURIChars = 'a-z 0-9~%.:_\-@';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
|
|
|
@ -49,6 +49,7 @@ class Autoload extends AutoloadConfig
|
|||
'Modules\Install' => ROOTPATH . 'modules/Install/',
|
||||
'Modules\Media' => ROOTPATH . 'modules/Media/',
|
||||
'Modules\MediaClipper' => ROOTPATH . 'modules/MediaClipper/',
|
||||
'Modules\Platforms' => ROOTPATH . 'modules/Platforms/',
|
||||
'Modules\PodcastImport' => ROOTPATH . 'modules/PodcastImport/',
|
||||
'Modules\PremiumPodcasts' => ROOTPATH . 'modules/PremiumPodcasts/',
|
||||
'Modules\Update' => ROOTPATH . 'modules/Update/',
|
||||
|
@ -61,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
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Config;
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,7 +15,6 @@ use CodeIgniter\Router\RouteCollection;
|
|||
$routes->addPlaceholder('podcastHandle', '[a-zA-Z0-9\_]{1,32}');
|
||||
$routes->addPlaceholder('slug', '[a-zA-Z0-9\-]{1,128}');
|
||||
$routes->addPlaceholder('base64', '[A-Za-z0-9\.\_]+\-{0,2}');
|
||||
$routes->addPlaceholder('platformType', '\bpodcasting|\bsocial|\bfunding');
|
||||
$routes->addPlaceholder('postAction', '\bfavourite|\breblog|\breply');
|
||||
$routes->addPlaceholder('embedTheme', '\blight|\bdark|\blight-transparent|\bdark-transparent');
|
||||
$routes->addPlaceholder(
|
||||
|
|
|
@ -29,6 +29,7 @@ class Routing extends BaseRouting
|
|||
ROOTPATH . 'modules/Auth/Config/Routes.php',
|
||||
ROOTPATH . 'modules/Fediverse/Config/Routes.php',
|
||||
ROOTPATH . 'modules/Install/Config/Routes.php',
|
||||
ROOTPATH . 'modules/Platforms/Config/Routes.php',
|
||||
ROOTPATH . 'modules/PodcastImport/Config/Routes.php',
|
||||
ROOTPATH . 'modules/PremiumPodcasts/Config/Routes.php',
|
||||
];
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright 2020 Ad Aures
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Models\PlatformModel;
|
||||
use CodeIgniter\Controller;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
|
||||
/*
|
||||
* Provide public access to all platforms so that they can be exported
|
||||
*/
|
||||
class PlatformController extends Controller
|
||||
{
|
||||
public function index(): ResponseInterface
|
||||
{
|
||||
$model = new PlatformModel();
|
||||
|
||||
return $this->response->setJSON($model->getPlatforms());
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Database\Migrations;
|
||||
|
||||
use CodeIgniter\Database\Migration;
|
||||
|
||||
class RefactorPlatforms extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
$this->forge->addField([
|
||||
'id' => [
|
||||
'type' => 'INT',
|
||||
'unsigned' => true,
|
||||
'auto_increment' => true,
|
||||
],
|
||||
'podcast_id' => [
|
||||
'type' => 'INT',
|
||||
'unsigned' => true,
|
||||
],
|
||||
'type' => [
|
||||
'type' => 'ENUM',
|
||||
'constraint' => ['podcasting', 'social', 'funding'],
|
||||
'after' => 'podcast_id',
|
||||
],
|
||||
'slug' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 32,
|
||||
],
|
||||
'link_url' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 512,
|
||||
],
|
||||
'account_id' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 128,
|
||||
'null' => true,
|
||||
],
|
||||
'is_visible' => [
|
||||
'type' => 'TINYINT',
|
||||
'constraint' => 1,
|
||||
'default' => 0,
|
||||
],
|
||||
]);
|
||||
|
||||
$this->forge->addPrimaryKey('id');
|
||||
$this->forge->addForeignKey('podcast_id', 'podcasts', 'id', '', 'CASCADE', 'platforms_podcast_id_foreign');
|
||||
$this->forge->addUniqueKey(['podcast_id', 'type', 'slug']);
|
||||
$this->forge->createTable('platforms_temp');
|
||||
|
||||
$platformsData = $this->db->table('podcasts_platforms')
|
||||
->select('podcasts_platforms.*, type')
|
||||
->join('platforms', 'platforms.slug = podcasts_platforms.platform_slug')
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
$data = [];
|
||||
foreach ($platformsData as $platformData) {
|
||||
$data[] = [
|
||||
'podcast_id' => $platformData['podcast_id'],
|
||||
'type' => $platformData['type'],
|
||||
'slug' => $platformData['platform_slug'],
|
||||
'link_url' => $platformData['link_url'],
|
||||
'account_id' => $platformData['account_id'],
|
||||
'is_visible' => $platformData['is_visible'],
|
||||
];
|
||||
}
|
||||
|
||||
if ($data !== []) {
|
||||
$this->db->table('platforms_temp')
|
||||
->insertBatch($data);
|
||||
}
|
||||
|
||||
$this->forge->dropTable('platforms');
|
||||
|
||||
$this->forge->dropTable('podcasts_platforms');
|
||||
|
||||
$this->forge->renameTable('platforms_temp', 'platforms');
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
// delete platforms
|
||||
$this->forge->dropTable('platforms');
|
||||
|
||||
// recreate platforms and podcasts_platforms tables
|
||||
$this->forge->addField([
|
||||
'slug' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 32,
|
||||
],
|
||||
'type' => [
|
||||
'type' => 'ENUM',
|
||||
'constraint' => ['podcasting', 'social', 'funding'],
|
||||
],
|
||||
'label' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 32,
|
||||
],
|
||||
'home_url' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 255,
|
||||
],
|
||||
'submit_url' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 512,
|
||||
'null' => true,
|
||||
],
|
||||
]);
|
||||
$this->forge->addField('`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP()');
|
||||
$this->forge->addField(
|
||||
'`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP() ON UPDATE CURRENT_TIMESTAMP()'
|
||||
);
|
||||
$this->forge->addPrimaryKey('slug');
|
||||
$this->forge->createTable('platforms');
|
||||
|
||||
$this->forge->addField([
|
||||
'podcast_id' => [
|
||||
'type' => 'INT',
|
||||
'unsigned' => true,
|
||||
],
|
||||
'platform_slug' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 32,
|
||||
],
|
||||
'link_url' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 512,
|
||||
],
|
||||
'account_id' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 128,
|
||||
'null' => true,
|
||||
],
|
||||
'is_visible' => [
|
||||
'type' => 'TINYINT',
|
||||
'constraint' => 1,
|
||||
'default' => 0,
|
||||
],
|
||||
'is_on_embed' => [
|
||||
'type' => 'TINYINT',
|
||||
'constraint' => 1,
|
||||
'default' => 0,
|
||||
],
|
||||
]);
|
||||
|
||||
$this->forge->addPrimaryKey(['podcast_id', 'platform_slug']);
|
||||
$this->forge->addForeignKey('podcast_id', 'podcasts', 'id', '', 'CASCADE');
|
||||
$this->forge->addForeignKey('platform_slug', 'platforms', 'slug', 'CASCADE');
|
||||
$this->forge->createTable('podcasts_platforms');
|
||||
}
|
||||
}
|
|
@ -20,6 +20,5 @@ class AppSeeder extends Seeder
|
|||
{
|
||||
$this->call('CategorySeeder');
|
||||
$this->call('LanguageSeeder');
|
||||
$this->call('PlatformSeeder');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ class DevSeeder extends Seeder
|
|||
{
|
||||
$this->call('CategorySeeder');
|
||||
$this->call('LanguageSeeder');
|
||||
$this->call('PlatformSeeder');
|
||||
$this->call('DevSuperadminSeeder');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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[] = [
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ use App\Models\ActorModel;
|
|||
use App\Models\CategoryModel;
|
||||
use App\Models\EpisodeModel;
|
||||
use App\Models\PersonModel;
|
||||
use App\Models\PlatformModel;
|
||||
use CodeIgniter\Entity\Entity;
|
||||
use CodeIgniter\Files\File;
|
||||
use CodeIgniter\HTTP\Files\UploadedFile;
|
||||
|
@ -32,6 +31,8 @@ use League\CommonMark\MarkdownConverter;
|
|||
use Modules\Auth\Models\UserModel;
|
||||
use Modules\Media\Entities\Image;
|
||||
use Modules\Media\Models\MediaModel;
|
||||
use Modules\Platforms\Entities\Platform;
|
||||
use Modules\Platforms\Models\PlatformModel;
|
||||
use Modules\PremiumPodcasts\Entities\Subscription;
|
||||
use Modules\PremiumPodcasts\Models\SubscriptionModel;
|
||||
use RuntimeException;
|
||||
|
@ -528,7 +529,7 @@ class Podcast extends Entity
|
|||
}
|
||||
|
||||
if ($this->podcasting_platforms === null) {
|
||||
$this->podcasting_platforms = (new PlatformModel())->getPodcastPlatforms($this->id, 'podcasting');
|
||||
$this->podcasting_platforms = (new PlatformModel())->getPlatforms($this->id, 'podcasting');
|
||||
}
|
||||
|
||||
return $this->podcasting_platforms;
|
||||
|
@ -546,7 +547,7 @@ class Podcast extends Entity
|
|||
}
|
||||
|
||||
if ($this->social_platforms === null) {
|
||||
$this->social_platforms = (new PlatformModel())->getPodcastPlatforms($this->id, 'social');
|
||||
$this->social_platforms = (new PlatformModel())->getPlatforms($this->id, 'social');
|
||||
}
|
||||
|
||||
return $this->social_platforms;
|
||||
|
@ -564,7 +565,7 @@ class Podcast extends Entity
|
|||
}
|
||||
|
||||
if ($this->funding_platforms === null) {
|
||||
$this->funding_platforms = (new PlatformModel())->getPodcastPlatforms($this->id, 'funding');
|
||||
$this->funding_platforms = (new PlatformModel())->getPlatforms($this->id, 'funding');
|
||||
}
|
||||
|
||||
return $this->funding_platforms;
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,205 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Class PlatformModel Model for platforms table in database
|
||||
*
|
||||
* @copyright 2020 Ad Aures
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Entities\Platform;
|
||||
use CodeIgniter\Model;
|
||||
use Config\App;
|
||||
|
||||
class PlatformModel extends Model
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'platforms';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $primaryKey = 'slug';
|
||||
|
||||
/**
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $allowedFields = ['slug', 'type', 'label', 'home_url', 'submit_url'];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $returnType = Platform::class;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $useSoftDeletes = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $useTimestamps = false;
|
||||
|
||||
/**
|
||||
* @return Platform[]
|
||||
*/
|
||||
public function getPlatforms(): array
|
||||
{
|
||||
if (! ($found = cache('platforms'))) {
|
||||
$baseUrl = rtrim(config(App::class)->baseURL, '/');
|
||||
$found = $this->select(
|
||||
"*, CONCAT('{$baseUrl}/assets/images/platforms/',`type`,'/',`slug`,'.svg') as icon",
|
||||
)->findAll();
|
||||
cache()
|
||||
->save('platforms', $found, DECADE);
|
||||
}
|
||||
|
||||
return $found;
|
||||
}
|
||||
|
||||
public function getPlatform(string $slug): ?Platform
|
||||
{
|
||||
$cacheName = "platform-{$slug}";
|
||||
if (! ($found = cache($cacheName))) {
|
||||
$found = $this->where('slug', $slug)
|
||||
->first();
|
||||
cache()
|
||||
->save($cacheName, $found, DECADE);
|
||||
}
|
||||
|
||||
return $found;
|
||||
}
|
||||
|
||||
public function createPlatform(
|
||||
string $slug,
|
||||
string $type,
|
||||
string $label,
|
||||
string $homeUrl,
|
||||
string $submitUrl = null
|
||||
): bool {
|
||||
$data = [
|
||||
'slug' => $slug,
|
||||
'type' => $type,
|
||||
'label' => $label,
|
||||
'home_url' => $homeUrl,
|
||||
'submit_url' => $submitUrl,
|
||||
];
|
||||
|
||||
return $this->insert($data, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Platform[]
|
||||
*/
|
||||
public function getPlatformsWithLinks(int $podcastId, string $platformType): array
|
||||
{
|
||||
if (
|
||||
! ($found = cache("podcast#{$podcastId}_platforms_{$platformType}_withLinks"))
|
||||
) {
|
||||
$found = $this->select(
|
||||
'platforms.*, podcasts_platforms.link_url, podcasts_platforms.account_id, podcasts_platforms.is_visible, podcasts_platforms.is_on_embed',
|
||||
)
|
||||
->join(
|
||||
'podcasts_platforms',
|
||||
"podcasts_platforms.platform_slug = platforms.slug AND podcasts_platforms.podcast_id = {$podcastId}",
|
||||
'left',
|
||||
)
|
||||
->where('platforms.type', $platformType)
|
||||
->findAll();
|
||||
|
||||
cache()
|
||||
->save("podcast#{$podcastId}_platforms_{$platformType}_withLinks", $found, DECADE);
|
||||
}
|
||||
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Platform[]
|
||||
*/
|
||||
public function getPodcastPlatforms(int $podcastId, string $platformType): array
|
||||
{
|
||||
$cacheName = "podcast#{$podcastId}_platforms_{$platformType}";
|
||||
if (! ($found = cache($cacheName))) {
|
||||
$found = $this->select(
|
||||
'platforms.*, podcasts_platforms.link_url, podcasts_platforms.account_id, podcasts_platforms.is_visible, podcasts_platforms.is_on_embed',
|
||||
)
|
||||
->join('podcasts_platforms', 'podcasts_platforms.platform_slug = platforms.slug')
|
||||
->where('podcasts_platforms.podcast_id', $podcastId)
|
||||
->where('platforms.type', $platformType)
|
||||
->findAll();
|
||||
|
||||
cache()
|
||||
->save($cacheName, $found, DECADE);
|
||||
}
|
||||
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $podcastsPlatformsData
|
||||
*
|
||||
* @return int|false Number of rows inserted or FALSE on failure
|
||||
*/
|
||||
public function savePodcastPlatforms(
|
||||
int $podcastId,
|
||||
string $platformType,
|
||||
array $podcastsPlatformsData
|
||||
): int | false {
|
||||
$this->clearCache($podcastId);
|
||||
|
||||
$podcastsPlatformsTable = $this->db->prefixTable('podcasts_platforms');
|
||||
$platformsTable = $this->db->prefixTable('platforms');
|
||||
|
||||
$deleteJoinQuery = <<<SQL
|
||||
DELETE {$podcastsPlatformsTable}
|
||||
FROM {$podcastsPlatformsTable}
|
||||
INNER JOIN {$platformsTable} ON {$platformsTable}.slug = {$podcastsPlatformsTable}.platform_slug
|
||||
WHERE `podcast_id` = ? AND `type` = ?
|
||||
SQL;
|
||||
|
||||
$this->db->query($deleteJoinQuery, [$podcastId, $platformType]);
|
||||
|
||||
if ($podcastsPlatformsData === []) {
|
||||
// no rows inserted
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $this->db
|
||||
->table('podcasts_platforms')
|
||||
->insertBatch($podcastsPlatformsData);
|
||||
}
|
||||
|
||||
public function removePodcastPlatform(int $podcastId, string $platformSlug): bool | string
|
||||
{
|
||||
$this->clearCache($podcastId);
|
||||
|
||||
return $this->db->table('podcasts_platforms')
|
||||
->delete([
|
||||
'podcast_id' => $podcastId,
|
||||
'platform_slug' => $platformSlug,
|
||||
]);
|
||||
}
|
||||
|
||||
public function clearCache(int $podcastId): void
|
||||
{
|
||||
cache()->deleteMatching("podcast#{$podcastId}_platforms_*");
|
||||
|
||||
// delete localized podcast page cache
|
||||
cache()
|
||||
->deleteMatching("page_podcast#{$podcastId}*");
|
||||
// delete post and episode comments pages cache
|
||||
cache()
|
||||
->deleteMatching('page_post*');
|
||||
cache()
|
||||
->deleteMatching('page_episode#*');
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M0,0H24V24H0Z" fill="none" />
|
||||
<path d="M13.3,13.07a1.91,1.91,0,0,1,.48,0,2.51,2.51,0,0,1,1.88,1.63c.08.26.19.66.26.93l.09.37h.38a5.49,5.49,0,0,0,5.38-5.46c0-4.52-3.77-8.28-9.54-8.28A10.16,10.16,0,0,0,5.9,4.56,10,10,0,0,0,2.23,12.3a10.64,10.64,0,0,0,.28,2.35,10,10,0,0,0,9.72,7.65,11.07,11.07,0,0,0,1.3-.08A10,10,0,0,0,20,18.6l.1-.12-.2-.73-.38.1a14.32,14.32,0,0,1-3.72.48,14.14,14.14,0,0,1-3-.31,2.51,2.51,0,0,1-1.87-1.65,2.51,2.51,0,0,1,.48-2.44A2.5,2.5,0,0,1,13.3,13.07ZM12.23,3.24c5.34,0,8.6,3.42,8.6,7.34a4.55,4.55,0,0,1-4.1,4.5c-.06-.22-.13-.45-.18-.62a15.25,15.25,0,0,0-9.36-9.7A8.79,8.79,0,0,1,12.23,3.24ZM3.17,12.3A9,9,0,0,1,6.3,5.45a14.35,14.35,0,0,1,8.38,7A3.26,3.26,0,0,0,14,12.2a15.86,15.86,0,0,0-3.17-.33,15.65,15.65,0,0,0-7.51,1.94A9.33,9.33,0,0,1,3.17,12.3Zm9.44,6.64a15.9,15.9,0,0,0,3.19.33,16.77,16.77,0,0,0,2.46-.19,9,9,0,0,1-4.6,2.17,13.91,13.91,0,0,1-1.73-2.52A3.83,3.83,0,0,0,12.61,18.94Zm-.07,2.42h-.31a8.82,8.82,0,0,1-4.16-1A14.24,14.24,0,0,1,9.88,16a4.07,4.07,0,0,0,.16.71A15.68,15.68,0,0,0,12.54,21.36Zm-1.83-8a15.6,15.6,0,0,0-3.48,6.55,9,9,0,0,1-3.72-5.1,14.27,14.27,0,0,1,7.29-1.95h.44A3.57,3.57,0,0,0,10.71,13.31Z" />
|
||||
</svg>
|
Before Width: | Height: | Size: 1.2 KiB |
|
@ -1,4 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M0,0H24V24H0Z" fill="none" />
|
||||
<path d="M3.22,10.08A1.24,1.24,0,0,0,2,11.31V12.7a1.22,1.22,0,0,0,2.44,0h0V11.31A1.17,1.17,0,0,0,3.22,10.08Zm17.56,0a1.23,1.23,0,0,0-1.22,1.23V12.7a1.21,1.21,0,0,0,1.22,1.22A1.22,1.22,0,0,0,22,12.7h0V11.31A1.24,1.24,0,0,0,20.78,10.08ZM7.56,14.2a1.24,1.24,0,0,0-1.23,1.23v1.4a1.23,1.23,0,0,0,2.45,0h0v-1.4A1.17,1.17,0,0,0,7.56,14.2ZM7.56,6A1.24,1.24,0,0,0,6.33,7.24V11.7h0a1.23,1.23,0,0,0,2.45,0h0V7.24A1.17,1.17,0,0,0,7.56,6Zm8.88,0a1.24,1.24,0,0,0-1.22,1.23V8.63a1.23,1.23,0,0,0,2.45,0h0V7.24A1.24,1.24,0,0,0,16.44,6ZM12,2a1.23,1.23,0,0,0-1.22,1.23V4.62a1.22,1.22,0,1,0,2.44,0h0V3.23A1.23,1.23,0,0,0,12,2Zm0,16.16a1.22,1.22,0,0,0-1.22,1.22v1.4a1.22,1.22,0,1,0,2.44,0h0v-1.4A1.26,1.26,0,0,0,12,18.16Zm4.44-7a1.23,1.23,0,0,0-1.22,1.22v4.47a1.23,1.23,0,0,0,2.45,0h0V12.36A1.23,1.23,0,0,0,16.44,11.14ZM13.22,8.41a1.22,1.22,0,0,0-2.44,0h0v7.36h0a1.22,1.22,0,1,0,2.44,0h0V8.41Z" />
|
||||
</svg>
|
Before Width: | Height: | Size: 991 B |
|
@ -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;
|
||||
|
||||
|
|
|
@ -28,17 +28,18 @@ class Button extends Component
|
|||
public function render(): string
|
||||
{
|
||||
$baseClass =
|
||||
'gap-x-2 flex-shrink-0 inline-flex items-center justify-center font-semibold shadow-xs rounded-full focus:ring-accent';
|
||||
'gap-x-2 flex-shrink-0 inline-flex items-center justify-center font-semibold rounded-full focus:ring-accent';
|
||||
|
||||
$variantClass = [
|
||||
'default' => 'text-black bg-gray-300 hover:bg-gray-400',
|
||||
'primary' => 'text-accent-contrast bg-accent-base hover:bg-accent-hover',
|
||||
'secondary' => 'border-2 border-accent-base text-accent-base bg-white hover:border-accent-hover hover:text-accent-hover',
|
||||
'success' => 'text-white bg-pine-500 hover:bg-pine-800',
|
||||
'danger' => 'text-white bg-red-600 hover:bg-red-700',
|
||||
'warning' => 'text-black bg-yellow-500 hover:bg-yellow-600',
|
||||
'info' => 'text-white bg-blue-500 hover:bg-blue-600',
|
||||
'disabled' => 'text-black bg-gray-300 cursor-not-allowed',
|
||||
'default' => 'shadow-sm text-black bg-gray-300 hover:bg-gray-400',
|
||||
'primary' => 'shadow-sm text-accent-contrast bg-accent-base hover:bg-accent-hover',
|
||||
'secondary' => 'shadow-sm border-2 border-accent-base text-accent-base bg-white hover:border-accent-hover hover:text-accent-hover',
|
||||
'success' => 'shadow-sm text-white bg-pine-500 hover:bg-pine-800',
|
||||
'danger' => 'shadow-sm text-white bg-red-600 hover:bg-red-700',
|
||||
'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 = [
|
||||
|
|
2
builds
2
builds
|
@ -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');
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"php": "^8.1",
|
||||
"adaures/ipcat-php": "^v1.0.0",
|
||||
"adaures/podcast-persons-taxonomy": "^v1.0.1",
|
||||
"aws/aws-sdk-php": "^3.305.0",
|
||||
"aws/aws-sdk-php": "^3.305.1",
|
||||
"chrisjean/php-ico": "^1.0.4",
|
||||
"cocur/slugify": "^v4.5.1",
|
||||
"codeigniter4/framework": "v4.5.1",
|
||||
|
@ -35,10 +35,10 @@
|
|||
"mikey179/vfsstream": "^v1.6.11",
|
||||
"phpstan/extension-installer": "^1.3.1",
|
||||
"phpstan/phpstan": "^1.10.67",
|
||||
"phpunit/phpunit": "^10.5.19",
|
||||
"phpunit/phpunit": "^10.5.20",
|
||||
"rector/rector": "^1.0.4",
|
||||
"symplify/coding-standard": "^12.0.7",
|
||||
"symplify/easy-coding-standard": "^12.0.13"
|
||||
"symplify/coding-standard": "^12.1.4",
|
||||
"symplify/easy-coding-standard": "^12.1.14"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -303,11 +303,8 @@ You may skip this section if you go through the install wizard (go to
|
|||
# Populates all Languages
|
||||
php spark db:seed LanguageSeeder
|
||||
|
||||
# Populates all podcasts platforms
|
||||
php spark db:seed PlatformSeeder
|
||||
|
||||
# Adds a superadmin with [admin@castopod.local / castopod] credentials
|
||||
php spark db:seed PlatformSeeder
|
||||
php spark db:seed DevSuperadminSeeder
|
||||
```
|
||||
|
||||
3. (optionnal) Populate the database with test data:
|
||||
|
|
30
ecs.php
30
ecs.php
|
@ -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',
|
||||
],
|
||||
]);
|
||||
};
|
||||
|
|
|
@ -511,48 +511,6 @@ $routes->group(
|
|||
});
|
||||
});
|
||||
});
|
||||
$routes->group('platforms', static function ($routes): void {
|
||||
$routes->get(
|
||||
'/',
|
||||
'PodcastPlatformController::platforms/$1/podcasting',
|
||||
[
|
||||
'as' => 'platforms-podcasting',
|
||||
'filter' => 'permission:podcast#.manage-platforms',
|
||||
],
|
||||
);
|
||||
$routes->get(
|
||||
'social',
|
||||
'PodcastPlatformController::platforms/$1/social',
|
||||
[
|
||||
'as' => 'platforms-social',
|
||||
'filter' => 'permission:podcast#.manage-platforms',
|
||||
],
|
||||
);
|
||||
$routes->get(
|
||||
'funding',
|
||||
'PodcastPlatformController::platforms/$1/funding',
|
||||
[
|
||||
'as' => 'platforms-funding',
|
||||
'filter' => 'permission:podcast#.manage-platforms',
|
||||
],
|
||||
);
|
||||
$routes->post(
|
||||
'save/(:platformType)',
|
||||
'PodcastPlatformController::attemptPlatformsUpdate/$1/$2',
|
||||
[
|
||||
'as' => 'platforms-save',
|
||||
'filter' => 'permission:podcast#.manage-platforms',
|
||||
],
|
||||
);
|
||||
$routes->get(
|
||||
'(:slug)/podcast-platform-remove',
|
||||
'PodcastPlatformController::removePodcastPlatform/$1/$2',
|
||||
[
|
||||
'as' => 'podcast-platform-remove',
|
||||
'filter' => 'permission:podcast#.manage-platforms',
|
||||
],
|
||||
);
|
||||
});
|
||||
// Podcast notifications
|
||||
$routes->group('notifications', static function ($routes): void {
|
||||
$routes->get('/', 'NotificationController::list/$1', [
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
use CodeIgniter\Router\RouteCollection;
|
||||
use Modules\Analytics\Config\Analytics;
|
||||
|
||||
/**
|
||||
|
|
|
@ -85,14 +85,10 @@ if (! function_exists('set_user_session_location')) {
|
|||
$city = $cityReader->city(client_ip());
|
||||
|
||||
$location = [
|
||||
'countryCode' => $city->country->isoCode === null
|
||||
? 'N/A'
|
||||
: $city->country->isoCode,
|
||||
'regionCode' => $city->subdivisions[0]->isoCode === null
|
||||
? 'N/A'
|
||||
: $city->subdivisions[0]->isoCode,
|
||||
'latitude' => round($city->location->latitude, 3),
|
||||
'longitude' => round($city->location->longitude, 3),
|
||||
'countryCode' => $city->country->isoCode ?? 'N/A',
|
||||
'regionCode' => $city->subdivisions[0]->isoCode ?? 'N/A',
|
||||
'latitude' => round($city->location->latitude, 3),
|
||||
'longitude' => round($city->location->longitude, 3),
|
||||
];
|
||||
// If things go wrong the show must go on and the user must be able to download the file
|
||||
} catch (Exception) {
|
||||
|
@ -179,9 +175,7 @@ if (! function_exists('set_user_session_referer')) {
|
|||
{
|
||||
$session = Services::session();
|
||||
|
||||
$newreferer = isset($_SERVER['HTTP_REFERER'])
|
||||
? $_SERVER['HTTP_REFERER']
|
||||
: '- Direct -';
|
||||
$newreferer = $_SERVER['HTTP_REFERER'] ?? '- Direct -';
|
||||
$newreferer =
|
||||
parse_url((string) $newreferer, PHP_URL_HOST) ===
|
||||
parse_url(current_url(false), PHP_URL_HOST)
|
||||
|
@ -250,9 +244,7 @@ if (! function_exists('podcast_hit')) {
|
|||
}
|
||||
|
||||
//We get the HTTP header field `Range`:
|
||||
$httpRange = isset($_SERVER['HTTP_RANGE'])
|
||||
? $_SERVER['HTTP_RANGE']
|
||||
: null;
|
||||
$httpRange = $_SERVER['HTTP_RANGE'] ?? null;
|
||||
|
||||
$salt = config(Analytics::class)
|
||||
->salt;
|
||||
|
|
|
@ -65,7 +65,7 @@ class EpisodeController extends Controller
|
|||
return $this->failNotFound('Episode not found');
|
||||
}
|
||||
|
||||
return $this->respond($this->mapEpisode($episode));
|
||||
return $this->respond(static::mapEpisode($episode));
|
||||
}
|
||||
|
||||
protected static function mapEpisode(Episode $episode): Episode
|
||||
|
|
|
@ -36,7 +36,7 @@ class ApiFilter implements FilterInterface
|
|||
}
|
||||
|
||||
$authHeader = $request->getHeaderLine('Authorization');
|
||||
if (substr($authHeader, 0, 6) !== 'Basic ') {
|
||||
if (! str_starts_with($authHeader, 'Basic ')) {
|
||||
$response->setStatusCode(401);
|
||||
|
||||
return $response;
|
||||
|
@ -44,7 +44,7 @@ class ApiFilter implements FilterInterface
|
|||
|
||||
$auth_token = base64_decode(substr($authHeader, 6), true);
|
||||
|
||||
list($username, $password) = explode(':', (string) $auth_token);
|
||||
[$username, $password] = explode(':', (string) $auth_token);
|
||||
|
||||
if (! ($username === $restApiConfig->basicAuthUsername && $password === $restApiConfig->basicAuthPassword)) {
|
||||
$response->setStatusCode(401);
|
||||
|
|
|
@ -11,8 +11,6 @@ use Psr\Log\LoggerInterface;
|
|||
use ViewThemes\Theme;
|
||||
|
||||
/**
|
||||
* Class ActionController
|
||||
*
|
||||
* A generic controller to handle Authentication Actions.
|
||||
*/
|
||||
class ActionController extends ShieldActionController
|
||||
|
|
|
@ -22,7 +22,7 @@ class ContributorController extends BaseController
|
|||
{
|
||||
protected Podcast $podcast;
|
||||
|
||||
protected ?User $contributor;
|
||||
protected ?User $contributor = null;
|
||||
|
||||
public function _remap(string $method, string ...$params): mixed
|
||||
{
|
||||
|
|
|
@ -8,8 +8,6 @@ use CodeIgniter\Controller;
|
|||
use CodeIgniter\HTTP\RedirectResponse;
|
||||
|
||||
/**
|
||||
* Class ActionController
|
||||
*
|
||||
* A generic controller to handle Authentication Actions.
|
||||
*/
|
||||
class InteractController extends Controller
|
||||
|
|
|
@ -11,8 +11,6 @@ use Psr\Log\LoggerInterface;
|
|||
use ViewThemes\Theme;
|
||||
|
||||
/**
|
||||
* Class RegisterController
|
||||
*
|
||||
* Handles displaying registration form, and handling actual registration flow.
|
||||
*/
|
||||
class RegisterController extends ShieldRegisterController
|
||||
|
|
|
@ -22,7 +22,7 @@ use Modules\Auth\Models\UserModel;
|
|||
|
||||
class UserController extends BaseController
|
||||
{
|
||||
protected ?User $user;
|
||||
protected ?User $user = null;
|
||||
|
||||
public function _remap(string $method, string ...$params): mixed
|
||||
{
|
||||
|
|
|
@ -102,9 +102,10 @@ if (! function_exists('add_podcast_group')) {
|
|||
if (! function_exists('get_instance_group')) {
|
||||
function get_instance_group(User $user): ?string
|
||||
{
|
||||
$instanceGroups = array_filter($user->getGroups() ?? [], static function ($group): bool {
|
||||
return ! str_starts_with($group, 'podcast#');
|
||||
});
|
||||
$instanceGroups = array_filter(
|
||||
$user->getGroups() ?? [],
|
||||
static fn ($group): bool => ! str_starts_with((string) $group, 'podcast#')
|
||||
);
|
||||
|
||||
if ($instanceGroups === []) {
|
||||
return null;
|
||||
|
@ -138,9 +139,10 @@ if (! function_exists('set_instance_group')) {
|
|||
if (! function_exists('get_podcast_group')) {
|
||||
function get_podcast_group(User $user, int $podcastId, bool $removePrefix = true): ?string
|
||||
{
|
||||
$podcastGroups = array_filter($user->getGroups() ?? [], static function ($group) use ($podcastId): bool {
|
||||
return str_starts_with($group, "podcast#{$podcastId}-");
|
||||
});
|
||||
$podcastGroups = array_filter(
|
||||
$user->getGroups() ?? [],
|
||||
static fn ($group): bool => str_starts_with((string) $group, "podcast#{$podcastId}-")
|
||||
);
|
||||
|
||||
if ($podcastGroups === []) {
|
||||
return null;
|
||||
|
@ -180,9 +182,10 @@ if (! function_exists('get_podcast_groups')) {
|
|||
*/
|
||||
function get_user_podcast_ids(User $user): array
|
||||
{
|
||||
$podcastGroups = array_filter($user->getGroups() ?? [], static function ($group): bool {
|
||||
return str_starts_with($group, 'podcast#');
|
||||
});
|
||||
$podcastGroups = array_filter(
|
||||
$user->getGroups() ?? [],
|
||||
static fn ($group): bool => str_starts_with((string) $group, 'podcast#')
|
||||
);
|
||||
|
||||
$userPodcastIds = [];
|
||||
// extract all podcast ids from groups
|
||||
|
|
|
@ -42,9 +42,7 @@ abstract class AbstractObject
|
|||
}
|
||||
|
||||
// removes all NULL, FALSE and Empty Strings but leaves 0 (zero) values
|
||||
return array_filter($array, static function ($value): bool {
|
||||
return $value !== null && $value !== false && $value !== '';
|
||||
});
|
||||
return array_filter($array, static fn ($value): bool => $value !== null && $value !== false && $value !== '');
|
||||
}
|
||||
|
||||
public function toJSON(): string
|
||||
|
|
|
@ -175,12 +175,10 @@ if (! function_exists('create_preview_card_from_url')) {
|
|||
|
||||
// Check that, at least, the url and title are set
|
||||
$newPreviewCard = new PreviewCard([
|
||||
'url' => $mediaUrl,
|
||||
'title' => $media['title'] ?? '',
|
||||
'description' => $media['description'] ?? '',
|
||||
'type' => isset($typeMapping[$media['type']])
|
||||
? $typeMapping[$media['type']]
|
||||
: 'link',
|
||||
'url' => $mediaUrl,
|
||||
'title' => $media['title'] ?? '',
|
||||
'description' => $media['description'] ?? '',
|
||||
'type' => $typeMapping[$media['type']] ?? 'link',
|
||||
'author_name' => $media['author_name'] ?? null,
|
||||
'author_url' => $media['author_url'] ?? null,
|
||||
'provider_name' => $media['provider_name'] ?? '',
|
||||
|
|
|
@ -134,7 +134,7 @@ class PostModel extends UuidModel
|
|||
$secondsToNextUnpublishedPost = $this->getSecondsToNextUnpublishedPosts($actorId);
|
||||
|
||||
cache()
|
||||
->save($cacheName, $found, $secondsToNextUnpublishedPost ? $secondsToNextUnpublishedPost : DECADE);
|
||||
->save($cacheName, $found, $secondsToNextUnpublishedPost ?: DECADE);
|
||||
}
|
||||
|
||||
return $found;
|
||||
|
|
|
@ -160,9 +160,7 @@ class InstallController extends Controller
|
|||
|
||||
if (! $this->validate($rules)) {
|
||||
return redirect()
|
||||
->to(
|
||||
(host_url() === null ? config(App::class) ->baseURL : host_url()) . config(Install::class)->gateway
|
||||
)
|
||||
->to((host_url() ?? config(App::class) ->baseURL) . config(Install::class)->gateway)
|
||||
->withInput()
|
||||
->with('errors', $this->validator->getErrors());
|
||||
}
|
||||
|
|
|
@ -85,16 +85,12 @@ class Transcript extends BaseMedia
|
|||
}
|
||||
|
||||
$transcript_format = $this->file->getExtension();
|
||||
switch ($transcript_format) {
|
||||
case 'vtt':
|
||||
$transcriptJson = $transcriptParser->loadString($transcriptContent)
|
||||
->parseVtt();
|
||||
break;
|
||||
case 'srt':
|
||||
default:
|
||||
$transcriptJson = $transcriptParser->loadString($transcriptContent)
|
||||
->parseSrt();
|
||||
}
|
||||
$transcriptJson = match ($transcript_format) {
|
||||
'vtt' => $transcriptParser->loadString($transcriptContent)
|
||||
->parseVtt(),
|
||||
default => $transcriptParser->loadString($transcriptContent)
|
||||
->parseSrt(),
|
||||
};
|
||||
|
||||
$tempFilePath = WRITEPATH . 'uploads/' . $this->file->getRandomName();
|
||||
file_put_contents($tempFilePath, $transcriptJson);
|
||||
|
|
|
@ -543,9 +543,9 @@ class VideoClipper
|
|||
|
||||
# find unique color
|
||||
do {
|
||||
$r = rand(0, 255);
|
||||
$g = rand(0, 255);
|
||||
$b = rand(0, 255);
|
||||
$r = random_int(0, 255);
|
||||
$g = random_int(0, 255);
|
||||
$b = random_int(0, 255);
|
||||
} while (imagecolorexact($src, $r, $g, $b) < 0);
|
||||
|
||||
$ns = $s * $q;
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\PremiumPodcasts\Config;
|
||||
|
||||
use CodeIgniter\Router\RouteCollection;
|
||||
use Modules\Admin\Config\Admin;
|
||||
|
||||
$routes->addPlaceholder('platformType', '\bpodcasting|\bsocial|\bfunding');
|
||||
|
||||
/** @var RouteCollection $routes */
|
||||
|
||||
// Admin routes for subscriptions
|
||||
$routes->group(
|
||||
config(Admin::class)
|
||||
->gateway,
|
||||
[
|
||||
'namespace' => 'Modules\Platforms\Controllers',
|
||||
],
|
||||
static function ($routes): void {
|
||||
$routes->group('podcasts/(:num)/platforms', static function ($routes): void {
|
||||
$routes->get(
|
||||
'/',
|
||||
'PlatformController::platforms/$1/podcasting',
|
||||
[
|
||||
'as' => 'platforms-podcasting',
|
||||
'filter' => 'permission:podcast#.manage-platforms',
|
||||
],
|
||||
);
|
||||
$routes->get(
|
||||
'social',
|
||||
'PlatformController::platforms/$1/social',
|
||||
[
|
||||
'as' => 'platforms-social',
|
||||
'filter' => 'permission:podcast#.manage-platforms',
|
||||
],
|
||||
);
|
||||
$routes->get(
|
||||
'funding',
|
||||
'PlatformController::platforms/$1/funding',
|
||||
[
|
||||
'as' => 'platforms-funding',
|
||||
'filter' => 'permission:podcast#.manage-platforms',
|
||||
],
|
||||
);
|
||||
$routes->post(
|
||||
'save/(:platformType)',
|
||||
'PlatformController::attemptPlatformsUpdate/$1/$2',
|
||||
[
|
||||
'as' => 'platforms-save',
|
||||
'filter' => 'permission:podcast#.manage-platforms',
|
||||
],
|
||||
);
|
||||
$routes->get(
|
||||
'(:platformType)/(:slug)/podcast-platform-remove',
|
||||
'PlatformController::removePlatform/$1/$2/$3',
|
||||
[
|
||||
'as' => 'podcast-platform-remove',
|
||||
'filter' => 'permission:podcast#.manage-platforms',
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
|
@ -8,18 +8,19 @@ declare(strict_types=1);
|
|||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace Modules\Admin\Controllers;
|
||||
namespace Modules\Platforms\Controllers;
|
||||
|
||||
use App\Entities\Podcast;
|
||||
use App\Models\PlatformModel;
|
||||
use App\Models\PodcastModel;
|
||||
use CodeIgniter\Exceptions\PageNotFoundException;
|
||||
use CodeIgniter\HTTP\RedirectResponse;
|
||||
use Config\Services;
|
||||
use Modules\Admin\Controllers\BaseController;
|
||||
use Modules\Platforms\Models\PlatformModel;
|
||||
|
||||
class PodcastPlatformController extends BaseController
|
||||
class PlatformController extends BaseController
|
||||
{
|
||||
protected ?Podcast $podcast;
|
||||
protected Podcast $podcast;
|
||||
|
||||
public function _remap(string $method, string ...$params): mixed
|
||||
{
|
||||
|
@ -28,18 +29,20 @@ class PodcastPlatformController extends BaseController
|
|||
}
|
||||
|
||||
if (
|
||||
($this->podcast = (new PodcastModel())->getPodcastById((int) $params[0])) instanceof Podcast
|
||||
! ($podcast = (new PodcastModel())->getPodcastById((int) $params[0])) instanceof Podcast
|
||||
) {
|
||||
unset($params[0]);
|
||||
return $this->{$method}(...$params);
|
||||
throw PageNotFoundException::forPageNotFound();
|
||||
}
|
||||
|
||||
throw PageNotFoundException::forPageNotFound();
|
||||
$this->podcast = $podcast;
|
||||
|
||||
unset($params[0]);
|
||||
return $this->{$method}(...$params);
|
||||
}
|
||||
|
||||
public function index(): string
|
||||
{
|
||||
return view('podcast/platforms\dashboard');
|
||||
return view('podcast/platforms/dashboard');
|
||||
}
|
||||
|
||||
public function platforms(string $platformType): string
|
||||
|
@ -49,7 +52,7 @@ class PodcastPlatformController extends BaseController
|
|||
$data = [
|
||||
'podcast' => $this->podcast,
|
||||
'platformType' => $platformType,
|
||||
'platforms' => (new PlatformModel())->getPlatformsWithLinks($this->podcast->id, $platformType),
|
||||
'platforms' => (new PlatformModel())->getPlatformsWithData($this->podcast->id, $platformType),
|
||||
];
|
||||
|
||||
replace_breadcrumb_params([
|
||||
|
@ -64,8 +67,7 @@ class PodcastPlatformController extends BaseController
|
|||
$platformModel = new PlatformModel();
|
||||
$validation = Services::validation();
|
||||
|
||||
$podcastsPlatformsData = [];
|
||||
|
||||
$platformsData = [];
|
||||
foreach (
|
||||
$this->request->getPost('platforms')
|
||||
as $platformSlug => $podcastPlatform
|
||||
|
@ -80,30 +82,27 @@ class PodcastPlatformController extends BaseController
|
|||
}
|
||||
|
||||
$podcastPlatformAccountId = trim((string) $podcastPlatform['account_id']);
|
||||
$podcastsPlatformsData[] = [
|
||||
'platform_slug' => $platformSlug,
|
||||
'podcast_id' => $this->podcast->id,
|
||||
'link_url' => $podcastPlatformUrl,
|
||||
'account_id' => $podcastPlatformAccountId === '' ? null : $podcastPlatformAccountId,
|
||||
'is_visible' => array_key_exists('visible', $podcastPlatform) &&
|
||||
$platformsData[] = [
|
||||
'podcast_id' => $this->podcast->id,
|
||||
'type' => $platformType,
|
||||
'slug' => $platformSlug,
|
||||
'link_url' => $podcastPlatformUrl,
|
||||
'account_id' => $podcastPlatformAccountId === '' ? null : $podcastPlatformAccountId,
|
||||
'is_visible' => array_key_exists('visible', $podcastPlatform) &&
|
||||
$podcastPlatform['visible'] === 'yes',
|
||||
'is_on_embed' => array_key_exists(
|
||||
'on_embed',
|
||||
$podcastPlatform
|
||||
) && $podcastPlatform['on_embed'] === 'yes',
|
||||
];
|
||||
}
|
||||
|
||||
$platformModel->savePodcastPlatforms($this->podcast->id, $platformType, $podcastsPlatformsData);
|
||||
$platformModel->savePlatforms($this->podcast->id, $platformType, $platformsData);
|
||||
|
||||
return redirect()
|
||||
->back()
|
||||
->with('message', lang('Platforms.messages.updateSuccess'));
|
||||
}
|
||||
|
||||
public function removePodcastPlatform(string $platformSlug): RedirectResponse
|
||||
public function removePlatform(string $platformType, string $platformSlug): RedirectResponse
|
||||
{
|
||||
(new PlatformModel())->removePodcastPlatform($this->podcast->id, $platformSlug);
|
||||
(new PlatformModel())->removePlatform($this->podcast->id, $platformType, $platformSlug);
|
||||
|
||||
return redirect()
|
||||
->back()
|
|
@ -8,20 +8,20 @@ declare(strict_types=1);
|
|||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace App\Entities;
|
||||
namespace Modules\Platforms\Entities;
|
||||
|
||||
use CodeIgniter\Entity\Entity;
|
||||
|
||||
/**
|
||||
* @property int $podcast_id
|
||||
* @property string $slug
|
||||
* @property string $type
|
||||
* @property string $label
|
||||
* @property string $link_url
|
||||
* @property string|null $account_id
|
||||
* @property bool $is_visible
|
||||
* @property string $home_url
|
||||
* @property string|null $submit_url
|
||||
* @property string|null $link_url
|
||||
* @property string|null $account_id
|
||||
* @property bool|null $is_visible
|
||||
* @property bool|null $is_on_embed
|
||||
*/
|
||||
class Platform extends Entity
|
||||
{
|
||||
|
@ -29,14 +29,14 @@ class Platform extends Entity
|
|||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'slug' => 'string',
|
||||
'type' => 'string',
|
||||
'label' => 'string',
|
||||
'home_url' => 'string',
|
||||
'submit_url' => '?string',
|
||||
'link_url' => '?string',
|
||||
'account_id' => '?string',
|
||||
'is_visible' => '?boolean',
|
||||
'is_on_embed' => '?boolean',
|
||||
'podcast_id' => 'int',
|
||||
'slug' => 'string',
|
||||
'type' => 'string',
|
||||
'label' => 'string',
|
||||
'link_url' => 'string',
|
||||
'account_id' => '?string',
|
||||
'is_visible' => 'boolean',
|
||||
'home_url' => 'string',
|
||||
'submit_url' => '?string',
|
||||
];
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Class PlatformModel Model for platforms table in database
|
||||
*
|
||||
* @copyright 2024 Ad Aures
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||
* @link https://castopod.org/
|
||||
*/
|
||||
|
||||
namespace Modules\Platforms\Models;
|
||||
|
||||
use CodeIgniter\Model;
|
||||
use Modules\Platforms\Entities\Platform;
|
||||
use Modules\Platforms\Platforms;
|
||||
|
||||
class PlatformModel extends Model
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'platforms';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $primaryKey = 'id';
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $allowedFields = ['podcast_id', 'type', 'slug', 'link_url', 'account_id', 'is_visible'];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $returnType = Platform::class;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $useSoftDeletes = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $useTimestamps = false;
|
||||
|
||||
/**
|
||||
* @return Platform[]
|
||||
*/
|
||||
public function getPlatformsWithData(int $podcastId, string $platformType): array
|
||||
{
|
||||
$cacheName = "podcast#{$podcastId}_platforms_{$platformType}_withData";
|
||||
if (! ($found = cache($cacheName))) {
|
||||
$platforms = new Platforms();
|
||||
|
||||
$found = $this->getPlatforms($podcastId, $platformType);
|
||||
$platformsData = $platforms->getPlatformsByType($platformType);
|
||||
|
||||
$knownSlugs = [];
|
||||
foreach ($found as $podcastPlatform) {
|
||||
$knownSlugs[] = $podcastPlatform->slug;
|
||||
}
|
||||
|
||||
foreach ($platformsData as $slug => $platform) {
|
||||
if (! in_array($slug, $knownSlugs, true)) {
|
||||
$found[] = new Platform([
|
||||
'podcast_id' => $podcastId,
|
||||
'slug' => $slug,
|
||||
'type' => $platformType,
|
||||
'label' => $platform['label'],
|
||||
'home_url' => $platform['home_url'],
|
||||
'submit_url' => $platform['submit_url'],
|
||||
'link_url' => '',
|
||||
'account_id' => null,
|
||||
'is_visible' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
cache()
|
||||
->save($cacheName, $found, DECADE);
|
||||
}
|
||||
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Platform[]
|
||||
*/
|
||||
public function getPlatforms(int $podcastId, string $platformType): array
|
||||
{
|
||||
$cacheName = "podcast#{$podcastId}_platforms_{$platformType}";
|
||||
if (! ($found = cache($cacheName))) {
|
||||
$platforms = new Platforms();
|
||||
|
||||
/** @var Platform[] $found */
|
||||
$found = $this
|
||||
->where('podcast_id', $podcastId)
|
||||
->where('type', $platformType)
|
||||
->orderBy('slug')
|
||||
->findAll();
|
||||
|
||||
foreach ($found as $platform) {
|
||||
$platformData = $platforms->findPlatformBySlug($platformType, $platform->slug);
|
||||
|
||||
if ($platformData === null) {
|
||||
// delete platform, it does not correspond to any existing one
|
||||
$this->delete($platform->id);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$platform->type = $platformType;
|
||||
$platform->label = $platformData['label'];
|
||||
$platform->home_url = $platformData['home_url'];
|
||||
$platform->submit_url = $platformData['submit_url'];
|
||||
}
|
||||
|
||||
cache()
|
||||
->save($cacheName, $found, DECADE);
|
||||
}
|
||||
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|false Number of rows inserted or FALSE on failure
|
||||
*/
|
||||
public function savePlatforms(int $podcastId, string $platformType, array $data): int | false
|
||||
{
|
||||
$this->clearCache($podcastId);
|
||||
|
||||
$platforms = new Platforms();
|
||||
|
||||
$platformsData = $platforms->getPlatformsByType($platformType);
|
||||
|
||||
$this->builder()
|
||||
->whereIn('slug', array_keys($platformsData))
|
||||
->delete();
|
||||
|
||||
if ($data === []) {
|
||||
// no rows inserted
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $this->insertBatch($data);
|
||||
}
|
||||
|
||||
public function removePlatform(int $podcastId, string $platformType, string $platformSlug): bool | string
|
||||
{
|
||||
$this->clearCache($podcastId);
|
||||
|
||||
return $this->builder()
|
||||
->delete([
|
||||
'podcast_id' => $podcastId,
|
||||
'type' => $platformType,
|
||||
'slug' => $platformSlug,
|
||||
]);
|
||||
}
|
||||
|
||||
public function clearCache(int $podcastId): void
|
||||
{
|
||||
cache()->deleteMatching("podcast#{$podcastId}_platforms_*");
|
||||
|
||||
// delete localized podcast page cache
|
||||
cache()
|
||||
->deleteMatching("page_podcast#{$podcastId}*");
|
||||
// delete post and episode comments pages cache
|
||||
cache()
|
||||
->deleteMatching('page_post*');
|
||||
cache()
|
||||
->deleteMatching('page_episode#*');
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -8,11 +8,9 @@ use AdAures\PodcastPersonsTaxonomy\ReversedTaxonomy;
|
|||
use App\Entities\Episode;
|
||||
use App\Entities\Location;
|
||||
use App\Entities\Person;
|
||||
use App\Entities\Platform;
|
||||
use App\Entities\Podcast;
|
||||
use App\Models\EpisodeModel;
|
||||
use App\Models\PersonModel;
|
||||
use App\Models\PlatformModel;
|
||||
use App\Models\PodcastModel;
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
|
@ -23,6 +21,8 @@ use Exception;
|
|||
use League\HTMLToMarkdown\HtmlConverter;
|
||||
use Modules\Auth\Config\AuthGroups;
|
||||
use Modules\Auth\Models\UserModel;
|
||||
use Modules\Platforms\Models\PlatformModel;
|
||||
use Modules\Platforms\Platforms;
|
||||
use Modules\PodcastImport\Entities\PodcastImportTask;
|
||||
use Modules\PodcastImport\Entities\TaskStatus;
|
||||
use PodcastFeed\PodcastFeed;
|
||||
|
@ -52,9 +52,9 @@ class PodcastImport extends BaseCommand
|
|||
|
||||
$importQueue = get_import_tasks();
|
||||
|
||||
$currentImport = current(array_filter($importQueue, static function ($task): bool {
|
||||
return $task->status === TaskStatus::Running;
|
||||
}));
|
||||
$currentImport = current(
|
||||
array_filter($importQueue, static fn ($task): bool => $task->status === TaskStatus::Running)
|
||||
);
|
||||
|
||||
if ($currentImport instanceof PodcastImportTask) {
|
||||
$currentImport->syncWithProcess();
|
||||
|
@ -68,9 +68,7 @@ class PodcastImport extends BaseCommand
|
|||
}
|
||||
|
||||
// Get the next queued import
|
||||
$queuedImports = array_filter($importQueue, static function ($task): bool {
|
||||
return $task->status === TaskStatus::Queued;
|
||||
});
|
||||
$queuedImports = array_filter($importQueue, static fn ($task): bool => $task->status === TaskStatus::Queued);
|
||||
$nextImport = end($queuedImports);
|
||||
|
||||
if (! $nextImport instanceof PodcastImportTask) {
|
||||
|
@ -392,27 +390,32 @@ class PodcastImport extends BaseCommand
|
|||
],
|
||||
];
|
||||
|
||||
$platforms = new Platforms();
|
||||
$platformModel = new PlatformModel();
|
||||
foreach ($platformTypes as $platformType) {
|
||||
$podcastsPlatformsData = [];
|
||||
$platformsData = [];
|
||||
$currPlatformStep = 1; // for progress
|
||||
CLI::write($platformType['name'] . ' - ' . $platformType['count'] . ' elements');
|
||||
foreach ($platformType['elements'] as $platform) {
|
||||
CLI::showProgress($currPlatformStep++, $platformType['count']);
|
||||
$platformLabel = $platform->getAttribute('platform');
|
||||
$platformSlug = slugify((string) $platformLabel);
|
||||
if ($platformModel->getPlatform($platformSlug) instanceof Platform) {
|
||||
$podcastsPlatformsData[] = [
|
||||
'platform_slug' => $platformSlug,
|
||||
'podcast_id' => $this->podcast->id,
|
||||
'link_url' => $platform->getAttribute($platformType['account_url_key']),
|
||||
'account_id' => $platform->getAttribute($platformType['account_id_key']),
|
||||
'is_visible' => false,
|
||||
];
|
||||
$platformSlug = $platform->getAttribute('platform');
|
||||
$platformData = $platforms->findPlatformBySlug($platformType['name'], $platformSlug);
|
||||
|
||||
if ($platformData === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$platformsData[] = [
|
||||
'podcast_id' => $this->podcast->id,
|
||||
'type' => $platformType['name'],
|
||||
'slug' => $platformSlug,
|
||||
'link_url' => $platform->getAttribute($platformType['account_url_key']),
|
||||
'account_id' => $platform->getAttribute($platformType['account_id_key']),
|
||||
'is_visible' => false,
|
||||
];
|
||||
}
|
||||
|
||||
$platformModel->savePodcastPlatforms($this->podcast->id, $platformType['name'], $podcastsPlatformsData);
|
||||
$platformModel->savePlatforms($this->podcast->id, $platformType['name'], $platformsData);
|
||||
CLI::showProgress(false);
|
||||
}
|
||||
}
|
||||
|
@ -522,9 +525,7 @@ class PodcastImport extends BaseCommand
|
|||
->get()
|
||||
->getResultArray();
|
||||
|
||||
return array_map(static function (array $element) {
|
||||
return $element['guid'];
|
||||
}, $result);
|
||||
return array_map(static fn (array $element) => $element['guid'], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,11 +26,10 @@ if (! function_exists('get_import_tasks')) {
|
|||
}
|
||||
|
||||
if ($podcastHandle !== null) {
|
||||
$podcastImportsQueue = array_filter($podcastImportsQueue, static function ($importTask) use (
|
||||
$podcastHandle
|
||||
): bool {
|
||||
return $importTask->handle === $podcastHandle;
|
||||
});
|
||||
$podcastImportsQueue = array_filter(
|
||||
$podcastImportsQueue,
|
||||
static fn ($importTask): bool => $importTask->handle === $podcastHandle
|
||||
);
|
||||
}
|
||||
|
||||
usort($podcastImportsQueue, static function (PodcastImportTask $a, PodcastImportTask $b): int {
|
||||
|
|
60
package.json
60
package.json
|
@ -30,16 +30,16 @@
|
|||
"dependencies": {
|
||||
"@amcharts/amcharts4": "^4.10.38",
|
||||
"@amcharts/amcharts4-geodata": "^4.1.28",
|
||||
"@codemirror/commands": "^6.3.3",
|
||||
"@codemirror/lang-xml": "^6.0.2",
|
||||
"@codemirror/commands": "^6.5.0",
|
||||
"@codemirror/lang-xml": "^6.1.0",
|
||||
"@codemirror/language": "^6.10.1",
|
||||
"@codemirror/state": "^6.4.1",
|
||||
"@codemirror/view": "^6.24.1",
|
||||
"@codemirror/view": "^6.26.3",
|
||||
"@floating-ui/dom": "^1.6.3",
|
||||
"@github/clipboard-copy-element": "^1.3.0",
|
||||
"@github/hotkey": "^3.1.0",
|
||||
"@github/markdown-toolbar-element": "^2.2.1",
|
||||
"@github/relative-time-element": "^4.3.1",
|
||||
"@github/markdown-toolbar-element": "^2.2.3",
|
||||
"@github/relative-time-element": "^4.4.0",
|
||||
"@tailwindcss/nesting": "0.0.0-insiders.565cd3e",
|
||||
"@vime/core": "^5.4.1",
|
||||
"choices.js": "^10.2.0",
|
||||
|
@ -47,28 +47,28 @@
|
|||
"flatpickr": "^4.6.13",
|
||||
"leaflet": "^1.9.4",
|
||||
"leaflet.markercluster": "^1.5.3",
|
||||
"lit": "^3.1.2",
|
||||
"marked": "^12.0.0",
|
||||
"wavesurfer.js": "^7.7.3",
|
||||
"lit": "^3.1.3",
|
||||
"marked": "^12.0.2",
|
||||
"wavesurfer.js": "^7.7.11",
|
||||
"xml-formatter": "^3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^19.0.3",
|
||||
"@commitlint/config-conventional": "^19.0.3",
|
||||
"@csstools/css-tokenizer": "^2.2.3",
|
||||
"@commitlint/cli": "^19.3.0",
|
||||
"@commitlint/config-conventional": "^19.2.2",
|
||||
"@csstools/css-tokenizer": "^2.2.4",
|
||||
"@semantic-release/changelog": "^6.0.3",
|
||||
"@semantic-release/exec": "^6.0.3",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"@semantic-release/gitlab": "^13.0.3",
|
||||
"@tailwindcss/forms": "^0.5.7",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@types/leaflet": "^1.9.8",
|
||||
"@typescript-eslint/eslint-plugin": "^7.1.0",
|
||||
"@typescript-eslint/parser": "^7.1.0",
|
||||
"@tailwindcss/typography": "^0.5.12",
|
||||
"@types/leaflet": "^1.9.12",
|
||||
"@typescript-eslint/eslint-plugin": "^7.7.1",
|
||||
"@typescript-eslint/parser": "^7.7.1",
|
||||
"all-contributors-cli": "^6.26.1",
|
||||
"commitizen": "^4.3.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"cssnano": "^6.0.3",
|
||||
"cssnano": "^6.1.2",
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
|
@ -76,25 +76,25 @@
|
|||
"husky": "^9.0.11",
|
||||
"is-ci": "^3.0.1",
|
||||
"lint-staged": "^15.2.2",
|
||||
"postcss": "^8.4.35",
|
||||
"postcss-import": "^16.0.1",
|
||||
"postcss-nesting": "^12.0.4",
|
||||
"postcss-preset-env": "^9.4.0",
|
||||
"postcss": "^8.4.38",
|
||||
"postcss-import": "^16.1.0",
|
||||
"postcss-nesting": "^12.1.2",
|
||||
"postcss-preset-env": "^9.5.9",
|
||||
"postcss-reporter": "^7.1.0",
|
||||
"prettier": "3.2.5",
|
||||
"prettier-plugin-organize-imports": "^3.2.4",
|
||||
"semantic-release": "^23.0.2",
|
||||
"stylelint": "^16.2.1",
|
||||
"semantic-release": "^23.0.8",
|
||||
"stylelint": "^16.4.0",
|
||||
"stylelint-config-standard": "^36.0.0",
|
||||
"svgo": "^3.2.0",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5.3.3",
|
||||
"vite": "^5.1.4",
|
||||
"vite-plugin-pwa": "^0.19.2",
|
||||
"workbox-build": "^7.0.0",
|
||||
"workbox-core": "^7.0.0",
|
||||
"workbox-routing": "^7.0.0",
|
||||
"workbox-strategies": "^7.0.0"
|
||||
"tailwindcss": "^3.4.3",
|
||||
"typescript": "^5.4.5",
|
||||
"vite": "^5.2.10",
|
||||
"vite-plugin-pwa": "^0.19.8",
|
||||
"workbox-build": "^7.1.0",
|
||||
"workbox-core": "^7.1.0",
|
||||
"workbox-routing": "^7.1.0",
|
||||
"workbox-strategies": "^7.1.0"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,ts,css,md,json}": "prettier --write",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<phpunit
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
|
||||
bootstrap="system/Test/bootstrap.php"
|
||||
bootstrap="vendor/codeigniter4/framework/system/Test/bootstrap.php"
|
||||
backupGlobals="false"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
colors="true"
|
||||
|
@ -57,13 +57,4 @@
|
|||
<env name="database.tests.DBPrefix" value="tests_"/>
|
||||
<env name="restapi.enabled" value="true"/>
|
||||
</php>
|
||||
<source>
|
||||
<include>
|
||||
<directory suffix=".php">./app</directory>
|
||||
</include>
|
||||
<exclude>
|
||||
<directory suffix=".php">./app/Views</directory>
|
||||
<file>./app/Config/Routes.php</file>
|
||||
</exclude>
|
||||
</source>
|
||||
</phpunit>
|
||||
|
|
14089
pnpm-lock.yaml
14089
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
47
rector.php
47
rector.php
|
@ -2,6 +2,7 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\CodeQuality\Rector\ClassMethod\ExplicitReturnNullRector;
|
||||
use Rector\CodingStyle\Rector\Encapsed\EncapsedStringsToSprintfRector;
|
||||
use Rector\CodingStyle\Rector\Stmt\NewlineAfterStatementRector;
|
||||
use Rector\CodingStyle\Rector\String_\SymplifyQuoteEscapeRector;
|
||||
|
@ -12,31 +13,22 @@ use Rector\EarlyReturn\Rector\If_\ChangeAndIfToEarlyReturnRector;
|
|||
use Rector\EarlyReturn\Rector\If_\ChangeOrIfContinueToMultiContinueRector;
|
||||
use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
|
||||
use Rector\Php71\Rector\FuncCall\RemoveExtraParametersRector;
|
||||
use Rector\Set\ValueObject\SetList;
|
||||
use Rector\ValueObject\PhpVersion;
|
||||
|
||||
return static function (RectorConfig $rectorConfig): void {
|
||||
$rectorConfig->paths([__DIR__ . '/app', __DIR__ . '/modules', __DIR__ . '/tests', __DIR__ . '/public']);
|
||||
|
||||
// do you need to include constants, class aliases or custom autoloader? files listed will be executed
|
||||
$rectorConfig->bootstrapFiles([__DIR__ . '/vendor/codeigniter4/framework/system/Test/bootstrap.php']);
|
||||
|
||||
// Define what rule sets will be applied
|
||||
$rectorConfig->sets([
|
||||
SetList::PHP_81,
|
||||
SetList::TYPE_DECLARATION,
|
||||
SetList::CODE_QUALITY,
|
||||
SetList::CODING_STYLE,
|
||||
SetList::EARLY_RETURN,
|
||||
SetList::DEAD_CODE,
|
||||
]);
|
||||
|
||||
// auto import fully qualified class names
|
||||
$rectorConfig->importNames();
|
||||
|
||||
$rectorConfig->phpVersion(PhpVersion::PHP_81);
|
||||
|
||||
$rectorConfig->skip([
|
||||
return RectorConfig::configure()
|
||||
->withPaths([__DIR__ . '/app', __DIR__ . '/modules', __DIR__ . '/tests', __DIR__ . '/public'])
|
||||
->withBootstrapFiles([__DIR__ . '/vendor/codeigniter4/framework/system/Test/bootstrap.php'])
|
||||
->withPhpVersion(PhpVersion::PHP_81)
|
||||
->withPhpSets(php81: true)
|
||||
->withPreparedSets(
|
||||
typeDeclarations: true,
|
||||
codeQuality: true,
|
||||
codingStyle: true,
|
||||
earlyReturn: true,
|
||||
deadCode: true,
|
||||
)
|
||||
->withImportNames(true, true, true, true)
|
||||
->withSkip([
|
||||
// .mp3 files were somehow processed by rector, so skip all media files
|
||||
__DIR__ . '/public/media/*',
|
||||
|
||||
|
@ -50,6 +42,7 @@ return static function (RectorConfig $rectorConfig): void {
|
|||
EncapsedStringsToSprintfRector::class,
|
||||
RemoveExtraParametersRector::class,
|
||||
UnwrapFutureCompatibleIfPhpVersionRector::class,
|
||||
ExplicitReturnNullRector::class,
|
||||
|
||||
// skip rule in specific directory
|
||||
StringClassNameToClassConstantRector::class => [
|
||||
|
@ -65,9 +58,5 @@ return static function (RectorConfig $rectorConfig): void {
|
|||
],
|
||||
|
||||
ChangeAndIfToEarlyReturnRector::class => [__DIR__ . '/modules/Install/Controllers/InstallController.php'],
|
||||
]);
|
||||
|
||||
// Path to phpstan with extensions, that PHPStan in Rector uses to determine types
|
||||
$rectorConfig->phpstanConfig(__DIR__ . '/phpstan.neon');
|
||||
$rectorConfig->phpstanConfigs(['vendor/codeigniter/phpstan-codeigniter/extension.neon']);
|
||||
};
|
||||
])
|
||||
->withPHPStanConfigs([__DIR__ . '/phpstan.neon', 'vendor/codeigniter/phpstan-codeigniter/extension.neon']);
|
||||
|
|
2
spark
2
spark
|
@ -1,6 +1,8 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
|
|
|
@ -117,6 +117,7 @@ module.exports = {
|
|||
cards: "repeat(auto-fill, minmax(14rem, 1fr))",
|
||||
latestEpisodes: "repeat(5, 1fr)",
|
||||
colorButtons: "repeat(auto-fill, minmax(4rem, 1fr))",
|
||||
platforms: "repeat(auto-fill, minmax(18rem, 1fr))",
|
||||
},
|
||||
gridTemplateRows: {
|
||||
admin: "40px 1fr",
|
||||
|
|
|
@ -36,8 +36,6 @@ namespace Tests\Support\Libraries;
|
|||
use Config\App;
|
||||
|
||||
/**
|
||||
* Class ConfigReader
|
||||
*
|
||||
* An extension of BaseConfig that prevents the constructor from loading external values. Used to read actual local
|
||||
* values from a config file.
|
||||
*/
|
||||
|
|
|
@ -14,9 +14,10 @@
|
|||
|
||||
<?= $this->section('content') ?>
|
||||
|
||||
<form id="platforms-form" action="<?= route_to('platforms-save', $podcast->id, $platformType) ?>" method="POST" class="flex flex-col max-w-md gap-y-8">
|
||||
<form id="platforms-form" action="<?= route_to('platforms-save', $podcast->id, $platformType) ?>" method="POST" class="grid w-full gap-4 lg:gap-8 grid-cols-platforms">
|
||||
<?= csrf_field() ?>
|
||||
|
||||
|
||||
<?php foreach ($platforms as $platform): ?>
|
||||
|
||||
<div class="relative flex-col items-start p-4 rounded-lg bg-elevated border-3 <?= $platform->link_url ? 'border-accent-base' : 'border-subtle' ?>">
|
||||
|
@ -24,7 +25,8 @@
|
|||
route_to(
|
||||
'podcast-platform-remove',
|
||||
$podcast->id,
|
||||
esc($platform->slug),
|
||||
$platform->type,
|
||||
$platform->slug,
|
||||
),
|
||||
icon('delete-bin', 'mx-auto'),
|
||||
[
|
||||
|
@ -36,7 +38,7 @@
|
|||
],
|
||||
)
|
||||
: '' ?>
|
||||
<div class="flex items-center gap-x-4">
|
||||
<div class="flex items-center gap-x-2">
|
||||
<?= icon(
|
||||
esc($platform->slug),
|
||||
'text-skin-muted text-4xl',
|
||||
|
@ -45,29 +47,15 @@
|
|||
<h2 class="text-xl font-semibold"><?= $platform->label ?></h2>
|
||||
</div>
|
||||
<div class="flex flex-col flex-1 mt-4">
|
||||
<div class="inline-flex ml-12 gap-x-2">
|
||||
<?= anchor($platform->home_url, icon('external-link', 'mx-auto') . lang('Platforms.website'), [
|
||||
'class' => 'gap-x-1 flex-shrink-0 inline-flex items-center justify-center font-semibold shadow-xs rounded-full focus:ring-accent px-2 py-1 text-sm border-2 border-accent-base text-accent-base bg-white hover:border-accent-hover hover:text-accent-hover',
|
||||
'target' => '_blank',
|
||||
'rel' => 'noopener noreferrer',
|
||||
'data-tooltip' => 'bottom',
|
||||
'title' => lang('Platforms.home_url', [
|
||||
<div class="inline-flex ml-8 -mt-6 gap-x-1">
|
||||
<Button uri="<?= $platform->home_url ?>" variant="link" size="small" target="_blank" rel="noopener noreferrer" title="<?= lang('Platforms.home_url', [
|
||||
'platformName' => $platform->label,
|
||||
]) ?>" data-tooltip="bottom"><?= lang('Platforms.website') ?></Button>
|
||||
<?php if ($platform->submit_url !== null): ?>
|
||||
<Button uri="<?= $platform->submit_url ?>" variant="link" size="small" target="_blank" rel="noopener noreferrer" title="<?= lang('Platforms.submit_url', [
|
||||
'platformName' => $platform->label,
|
||||
]),
|
||||
]) ?>
|
||||
<?= $platform->submit_url ? anchor(
|
||||
$platform->submit_url,
|
||||
icon('add') . lang('Platforms.register'),
|
||||
[
|
||||
'class' => 'gap-x-1 flex-shrink-0 inline-flex items-center justify-center font-semibold shadow-xs rounded-full focus:ring-accent px-2 py-1 text-sm border-2 border-accent-base text-accent-base bg-white hover:border-accent-hover hover:text-accent-hover',
|
||||
'target' => '_blank',
|
||||
'rel' => 'noopener noreferrer',
|
||||
'data-tooltip' => 'bottom',
|
||||
'title' => lang('Platforms.submit_url', [
|
||||
'platformName' => $platform->label,
|
||||
]),
|
||||
]
|
||||
) : '' ?>
|
||||
]) ?>" data-tooltip="bottom"><?= lang('Platforms.register') ?></Button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<fieldset>
|
||||
<Forms.Field
|
||||
|
|
Loading…
Reference in New Issue