fix: remove fediverse prefix to prevent migration error + load routes during podcast import

refactor migration queries to use forge functions
This commit is contained in:
Yassine Doghri 2023-08-26 13:03:01 +00:00
parent 072b3ff61d
commit 7ff1dbe903
63 changed files with 387 additions and 355 deletions

View File

@ -27,7 +27,8 @@ class View extends BaseView
* *
* Examples: { title|esc(js) } { created_on|date(Y-m-d)|esc(attr) } * Examples: { title|esc(js) } { created_on|date(Y-m-d)|esc(attr) }
* *
* @var string[] * @var array<string, string>
* @phpstan-var array<string, callable-string>
*/ */
public $filters = []; public $filters = [];
@ -35,7 +36,8 @@ class View extends BaseView
* Parser Plugins provide a way to extend the functionality provided by the core Parser by creating aliases that * Parser Plugins provide a way to extend the functionality provided by the core Parser by creating aliases that
* will be replaced with any callable. Can be single or tag pair. * will be replaced with any callable. Can be single or tag pair.
* *
* @var string[] * @var array<string, string>
* @phpstan-var array<string, callable-string>
*/ */
public $plugins = []; public $plugins = [];

View File

@ -290,7 +290,7 @@ class EpisodeController extends BaseController
$episodeComments = model(PostModel::class) $episodeComments = model(PostModel::class)
->whereIn('in_reply_to_id', function (BaseBuilder $builder): BaseBuilder { ->whereIn('in_reply_to_id', function (BaseBuilder $builder): BaseBuilder {
return $builder->select('id') return $builder->select('id')
->from(config('Fediverse')->tablesPrefix . 'posts') ->from('fediverse_posts')
->where('episode_id', $this->episode->id); ->where('episode_id', $this->episode->id);
}) })
->where('`published_at` <= UTC_TIMESTAMP()', null, false) ->where('`published_at` <= UTC_TIMESTAMP()', null, false)

View File

@ -195,7 +195,7 @@ class AddPodcasts extends BaseMigration
$this->forge->addUniqueKey('handle'); $this->forge->addUniqueKey('handle');
$this->forge->addUniqueKey('guid'); $this->forge->addUniqueKey('guid');
$this->forge->addUniqueKey('actor_id'); $this->forge->addUniqueKey('actor_id');
$this->forge->addForeignKey('actor_id', config('Fediverse')->tablesPrefix . 'actors', 'id', '', 'CASCADE'); $this->forge->addForeignKey('actor_id', 'fediverse_actors', 'id', '', 'CASCADE');
$this->forge->addForeignKey('cover_id', 'media', 'id'); $this->forge->addForeignKey('cover_id', 'media', 'id');
$this->forge->addForeignKey('banner_id', 'media', 'id', '', 'SET NULL'); $this->forge->addForeignKey('banner_id', 'media', 'id', '', 'SET NULL');
$this->forge->addForeignKey('category_id', 'categories', 'id'); $this->forge->addForeignKey('category_id', 'categories', 'id');

View File

@ -164,10 +164,10 @@ class AddEpisodes extends BaseMigration
// Add Full-Text Search index on title and description_markdown // Add Full-Text Search index on title and description_markdown
$prefix = $this->db->getPrefix(); $prefix = $this->db->getPrefix();
$createQuery = <<<CODE_SAMPLE $createQuery = <<<SQL
ALTER TABLE {$prefix}episodes ALTER TABLE {$prefix}episodes
ADD FULLTEXT(title, description_markdown); ADD FULLTEXT title (title, description_markdown);
CODE_SAMPLE; SQL;
$this->db->query($createQuery); $this->db->query($createQuery);
} }

View File

@ -64,12 +64,9 @@ class AddEpisodeComments extends BaseMigration
], ],
]); ]);
$fediverseTablesPrefix = config('Fediverse')
->tablesPrefix;
$this->forge->addPrimaryKey('id'); $this->forge->addPrimaryKey('id');
$this->forge->addForeignKey('episode_id', 'episodes', 'id', '', 'CASCADE'); $this->forge->addForeignKey('episode_id', 'episodes', 'id', '', 'CASCADE');
$this->forge->addForeignKey('actor_id', $fediverseTablesPrefix . 'actors', 'id', '', 'CASCADE'); $this->forge->addForeignKey('actor_id', 'fediverse_actors', 'id', '', 'CASCADE');
$this->forge->addForeignKey('created_by', 'users', 'id'); $this->forge->addForeignKey('created_by', 'users', 'id');
$this->forge->createTable('episode_comments'); $this->forge->createTable('episode_comments');
} }

View File

@ -27,12 +27,9 @@ class AddLikes extends BaseMigration
], ],
]); ]);
$fediverseTablesPrefix = config('Fediverse')
->tablesPrefix;
$this->forge->addField('`created_at` timestamp NOT NULL DEFAULT current_timestamp()'); $this->forge->addField('`created_at` timestamp NOT NULL DEFAULT current_timestamp()');
$this->forge->addPrimaryKey(['actor_id', 'comment_id']); $this->forge->addPrimaryKey(['actor_id', 'comment_id']);
$this->forge->addForeignKey('actor_id', $fediverseTablesPrefix . 'actors', 'id', '', 'CASCADE'); $this->forge->addForeignKey('actor_id', 'fediverse_actors', 'id', '', 'CASCADE');
$this->forge->addForeignKey('comment_id', 'episode_comments', 'id', '', 'CASCADE'); $this->forge->addForeignKey('comment_id', 'episode_comments', 'id', '', 'CASCADE');
$this->forge->createTable('likes'); $this->forge->createTable('likes');
} }

View File

@ -20,7 +20,7 @@ class AddCreditsView extends BaseMigration
$podcastPersonsTable = $this->db->prefixTable('podcasts_persons'); $podcastPersonsTable = $this->db->prefixTable('podcasts_persons');
$episodePersonsTable = $this->db->prefixTable('episodes_persons'); $episodePersonsTable = $this->db->prefixTable('episodes_persons');
$episodesTable = $this->db->prefixTable('episodes'); $episodesTable = $this->db->prefixTable('episodes');
$createQuery = <<<CODE_SAMPLE $createQuery = <<<SQL
CREATE VIEW `{$viewName}` AS CREATE VIEW `{$viewName}` AS
SELECT `person_group`, `person_id`, `full_name`, `person_role`, `podcast_id`, NULL AS `episode_id` FROM `{$podcastPersonsTable}` SELECT `person_group`, `person_id`, `full_name`, `person_role`, `podcast_id`, NULL AS `episode_id` FROM `{$podcastPersonsTable}`
INNER JOIN `{$personsTable}` INNER JOIN `{$personsTable}`
@ -33,7 +33,7 @@ class AddCreditsView extends BaseMigration
ON (`episode_id`=`{$episodesTable}`.`id`) ON (`episode_id`=`{$episodesTable}`.`id`)
WHERE `{$episodesTable}`.published_at <= UTC_TIMESTAMP() WHERE `{$episodesTable}`.published_at <= UTC_TIMESTAMP()
ORDER BY `person_group`, `full_name`, `person_role`, `podcast_id`, `episode_id`; ORDER BY `person_group`, `full_name`, `person_role`, `podcast_id`, `episode_id`;
CODE_SAMPLE; SQL;
$this->db->query($createQuery); $this->db->query($createQuery);
} }

View File

@ -17,10 +17,8 @@ class AddEpisodeIdToPosts extends BaseMigration
public function up(): void public function up(): void
{ {
$prefix = $this->db->getPrefix(); $prefix = $this->db->getPrefix();
$fediverseTablesPrefix = config('Fediverse')
->tablesPrefix;
$this->forge->addColumn("{$fediverseTablesPrefix}posts", [ $this->forge->addColumn('fediverse_posts', [
'episode_id' => [ 'episode_id' => [
'type' => 'INT', 'type' => 'INT',
'unsigned' => true, 'unsigned' => true,
@ -29,22 +27,22 @@ class AddEpisodeIdToPosts extends BaseMigration
], ],
]); ]);
$alterQuery = <<<CODE_SAMPLE $this->forge->addForeignKey(
ALTER TABLE {$prefix}{$fediverseTablesPrefix}posts 'episode_id',
ADD FOREIGN KEY {$prefix}{$fediverseTablesPrefix}posts_episode_id_foreign(episode_id) REFERENCES {$prefix}episodes(id) ON DELETE CASCADE; 'episodes',
CODE_SAMPLE; 'id',
$this->db->query($alterQuery); '',
'CASCADE',
$prefix . 'fediverse_posts_episode_id_foreign'
);
$this->forge->processIndexes('fediverse_posts');
} }
public function down(): void public function down(): void
{ {
$fediverseTablesPrefix = config('Fediverse') $prefix = $this->db->getPrefix();
->tablesPrefix;
$this->forge->dropForeignKey( $this->forge->dropForeignKey('fediverse_posts', $prefix . 'fediverse_posts_episode_id_foreign');
$fediverseTablesPrefix . 'posts', $this->forge->dropColumn('fediverse_posts', 'episode_id');
$fediverseTablesPrefix . 'posts_episode_id_foreign'
);
$this->forge->dropColumn($fediverseTablesPrefix . 'posts', 'episode_id');
} }
} }

View File

@ -17,10 +17,8 @@ class AddCreatedByToPosts extends BaseMigration
public function up(): void public function up(): void
{ {
$prefix = $this->db->getPrefix(); $prefix = $this->db->getPrefix();
$fediverseTablesPrefix = config('Fediverse')
->tablesPrefix;
$this->forge->addColumn("{$fediverseTablesPrefix}posts", [ $this->forge->addColumn('fediverse_posts', [
'created_by' => [ 'created_by' => [
'type' => 'INT', 'type' => 'INT',
'unsigned' => true, 'unsigned' => true,
@ -29,22 +27,22 @@ class AddCreatedByToPosts extends BaseMigration
], ],
]); ]);
$alterQuery = <<<CODE_SAMPLE $this->forge->addForeignKey(
ALTER TABLE {$prefix}{$fediverseTablesPrefix}posts 'created_by',
ADD FOREIGN KEY {$prefix}{$fediverseTablesPrefix}posts_created_by_foreign(created_by) REFERENCES {$prefix}users(id) ON DELETE CASCADE; 'users',
CODE_SAMPLE; 'id',
$this->db->query($alterQuery); '',
'CASCADE',
$prefix . 'fediverse_posts_created_by_foreign'
);
$this->forge->processIndexes('fediverse_posts');
} }
public function down(): void public function down(): void
{ {
$fediverseTablesPrefix = config('Fediverse') $prefix = $this->db->getPrefix();
->tablesPrefix;
$this->forge->dropForeignKey( $this->forge->dropForeignKey('fediverse_posts', $prefix . 'fediverse_posts_created_by_foreign');
$fediverseTablesPrefix . 'posts', $this->forge->dropColumn('fediverse_posts', 'created_by');
$fediverseTablesPrefix . 'posts_created_by_foreign'
);
$this->forge->dropColumn($fediverseTablesPrefix . 'posts', 'created_by');
} }
} }

View File

@ -10,23 +10,23 @@ class AddFullTextSearchIndexes extends BaseMigration
{ {
$prefix = $this->db->getPrefix(); $prefix = $this->db->getPrefix();
$createQuery = <<<CODE_SAMPLE $createQuery = <<<SQL
ALTER TABLE {$prefix}episodes DROP INDEX IF EXISTS title; ALTER TABLE {$prefix}episodes DROP INDEX IF EXISTS title;
CODE_SAMPLE; SQL;
$this->db->query($createQuery); $this->db->query($createQuery);
$createQuery = <<<CODE_SAMPLE $createQuery = <<<SQL
ALTER TABLE {$prefix}episodes ALTER TABLE {$prefix}episodes
ADD FULLTEXT episodes_search (title, description_markdown, slug, location_name); ADD FULLTEXT episodes_search (title, description_markdown, slug, location_name);
CODE_SAMPLE; SQL;
$this->db->query($createQuery); $this->db->query($createQuery);
$createQuery = <<<CODE_SAMPLE $createQuery = <<<SQL
ALTER TABLE {$prefix}podcasts ALTER TABLE {$prefix}podcasts
ADD FULLTEXT podcasts_search (title, description_markdown, handle, location_name); ADD FULLTEXT podcasts_search (title, description_markdown, handle, location_name);
CODE_SAMPLE; SQL;
$this->db->query($createQuery); $this->db->query($createQuery);
} }
@ -35,17 +35,17 @@ class AddFullTextSearchIndexes extends BaseMigration
{ {
$prefix = $this->db->getPrefix(); $prefix = $this->db->getPrefix();
$createQuery = <<<CODE_SAMPLE $createQuery = <<<SQL
ALTER TABLE {$prefix}episodes ALTER TABLE {$prefix}episodes
DROP INDEX IF EXISTS episodes_search; DROP INDEX IF EXISTS episodes_search;
CODE_SAMPLE; SQL;
$this->db->query($createQuery); $this->db->query($createQuery);
$createQuery = <<<CODE_SAMPLE $createQuery = <<<SQL
ALTER TABLE {$prefix}podcasts ALTER TABLE {$prefix}podcasts
DROP INDEX IF EXISTS podcasts_search; DROP INDEX IF EXISTS podcasts_search;
CODE_SAMPLE; SQL;
$this->db->query($createQuery); $this->db->query($createQuery);
} }

View File

@ -57,7 +57,8 @@ class BaseClip extends Entity
protected ?float $end_time = null; protected ?float $end_time = null;
/** /**
* @var string[] * @var array<int, string>
* @phpstan-var list<string>
*/ */
protected $dates = ['created_at', 'updated_at', 'job_started_at', 'job_ended_at']; protected $dates = ['created_at', 'updated_at', 'job_started_at', 'job_ended_at'];

View File

@ -144,7 +144,8 @@ class Episode extends Entity
protected ?string $publication_status = null; protected ?string $publication_status = null;
/** /**
* @var string[] * @var array<int, string>
* @phpstan-var list<string>
*/ */
protected $dates = ['published_at', 'created_at', 'updated_at']; protected $dates = ['published_at', 'created_at', 'updated_at'];

View File

@ -51,7 +51,8 @@ class EpisodeComment extends UuidEntity
protected bool $has_replies = false; protected bool $has_replies = false;
/** /**
* @var string[] * @var array<int, string>
* @phpstan-var list<string>
*/ */
protected $dates = ['created_at']; protected $dates = ['created_at'];

View File

@ -166,7 +166,8 @@ class Podcast extends Entity
protected ?string $publication_status = null; protected ?string $publication_status = null;
/** /**
* @var string[] * @var array<int, string>
* @phpstan-var list<string>
*/ */
protected $dates = ['published_at', 'created_at', 'updated_at']; protected $dates = ['published_at', 'created_at', 'updated_at'];

View File

@ -10,11 +10,17 @@ use CodeIgniter\HTTP\ResponseInterface;
class AllowCorsFilter implements FilterInterface class AllowCorsFilter implements FilterInterface
{ {
/**
* @param string[]|null $arguments
*/
public function before(RequestInterface $request, $arguments = null): void public function before(RequestInterface $request, $arguments = null): void
{ {
// Do something here // Do something here
} }
/**
* @param string[]|null $arguments
*/
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null): void public function after(RequestInterface $request, ResponseInterface $response, $arguments = null): void
{ {
if (! $response->hasHeader('Cache-Control')) { if (! $response->hasHeader('Cache-Control')) {

View File

@ -206,7 +206,7 @@ if (! function_exists('publication_status_banner')) {
$bannerDisclaimer = lang('Podcast.publication_status_banner.draft_mode'); $bannerDisclaimer = lang('Podcast.publication_status_banner.draft_mode');
$bannerText = lang('Podcast.publication_status_banner.scheduled', [ $bannerText = lang('Podcast.publication_status_banner.scheduled', [
'publication_date' => local_datetime($publicationDate), 'publication_date' => local_datetime($publicationDate),
], null, false); ]);
$linkRoute = route_to('podcast-publish_edit', $podcastId); $linkRoute = route_to('podcast-publish_edit', $podcastId);
$linkLabel = lang('Podcast.publish_edit'); $linkLabel = lang('Podcast.publish_edit');
break; break;
@ -492,10 +492,10 @@ if (! function_exists('category_label')) {
{ {
$categoryLabel = ''; $categoryLabel = '';
if ($category->parent_id !== null) { if ($category->parent_id !== null) {
$categoryLabel .= lang('Podcast.category_options.' . $category->parent->code, [], null, false) . ' '; $categoryLabel .= lang('Podcast.category_options.' . $category->parent->code) . ' ';
} }
return $categoryLabel . lang('Podcast.category_options.' . $category->code, [], null, false); return $categoryLabel . lang('Podcast.category_options.' . $category->code);
} }
} }

View File

@ -14,18 +14,25 @@ declare(strict_types=1);
namespace App\Libraries; namespace App\Libraries;
use Closure;
use CodeIgniter\Router\RouteCollection as CodeIgniterRouteCollection; use CodeIgniter\Router\RouteCollection as CodeIgniterRouteCollection;
class RouteCollection extends CodeIgniterRouteCollection class RouteCollection extends CodeIgniterRouteCollection
{ {
/**
* The current hostname from $_SERVER['HTTP_HOST']
*/
private ?string $httpHost = null;
/** /**
* Does the heavy lifting of creating an actual route. You must specify * Does the heavy lifting of creating an actual route. You must specify
* the request method(s) that this route will work for. They can be separated * the request method(s) that this route will work for. They can be separated
* by a pipe character "|" if there is more than one. * by a pipe character "|" if there is more than one.
* *
* @param array|Closure|string $to * @param array<int, mixed>|Closure|string $to
* @param array<string, mixed> $options
*/ */
protected function create(string $verb, string $from, $to, ?array $options = null) protected function create(string $verb, string $from, $to, ?array $options = null): void
{ {
$overwrite = false; $overwrite = false;
$prefix = $this->group === null ? '' : $this->group . '/'; $prefix = $this->group === null ? '' : $this->group . '/';
@ -81,8 +88,8 @@ class RouteCollection extends CodeIgniterRouteCollection
// Get a constant string to work with. // Get a constant string to work with.
$to = preg_replace('/(\$\d+)/', '$X', $to); $to = preg_replace('/(\$\d+)/', '$X', $to);
for ($i = (int) $options['offset'] + 1; $i < (int) $options['offset'] + 7; $i++) { for ($i = (int) $options['offset'] + 1; $i < (int) $options['offset'] + 7; ++$i) {
$to = preg_replace_callback('/\$X/', static fn ($m) => '$' . $i, $to, 1); $to = preg_replace_callback('/\$X/', static fn ($m): string => '$' . $i, $to, 1);
} }
} }
@ -97,7 +104,7 @@ class RouteCollection extends CodeIgniterRouteCollection
// If no namespace found, add the default namespace // If no namespace found, add the default namespace
if (strpos($to, '\\') === false || strpos($to, '\\') > 0) { if (strpos($to, '\\') === false || strpos($to, '\\') > 0) {
$namespace = $options['namespace'] ?? $this->defaultNamespace; $namespace = $options['namespace'] ?? $this->defaultNamespace;
$to = trim($namespace, '\\') . '\\' . $to; $to = trim((string) $namespace, '\\') . '\\' . $to;
} }
// Always ensure that we escape our namespace so we're not pointing to // Always ensure that we escape our namespace so we're not pointing to
// \CodeIgniter\Routes\Controller::method. // \CodeIgniter\Routes\Controller::method.
@ -134,4 +141,140 @@ class RouteCollection extends CodeIgniterRouteCollection
$this->routes['*'][$name]['redirect'] = $options['redirect']; $this->routes['*'][$name]['redirect'] = $options['redirect'];
} }
} }
/**
* Compares the hostname passed in against the current hostname
* on this page request.
*
* @param string $hostname Hostname in route options
*/
private function checkHostname($hostname): bool
{
// CLI calls can't be on hostname.
if ($this->httpHost === null) {
return false;
}
return strtolower($this->httpHost) === strtolower($hostname);
}
/**
* @param array<int, mixed> $to
*
* @return string|array<int, mixed>
*/
private function processArrayCallableSyntax(string $from, array $to): string | array
{
// [classname, method]
// eg, [Home::class, 'index']
if (is_callable($to, true, $callableName)) {
// If the route has placeholders, add params automatically.
$params = $this->getMethodParams($from);
return '\\' . $callableName . $params;
}
// [[classname, method], params]
// eg, [[Home::class, 'index'], '$1/$2']
if (
isset($to[0], $to[1])
&& is_callable($to[0], true, $callableName)
&& is_string($to[1])
) {
return '\\' . $callableName . '/' . $to[1];
}
return $to;
}
/**
* Compares the subdomain(s) passed in against the current subdomain
* on this page request.
*
* @param string|string[] $subdomains
*/
private function checkSubdomains($subdomains): bool
{
// CLI calls can't be on subdomain.
if ($this->httpHost === null) {
return false;
}
if ($this->currentSubdomain === null) {
$this->currentSubdomain = $this->determineCurrentSubdomain();
}
if (! is_array($subdomains)) {
$subdomains = [$subdomains];
}
// Routes can be limited to any sub-domain. In that case, though,
// it does require a sub-domain to be present.
if (! empty($this->currentSubdomain) && in_array('*', $subdomains, true)) {
return true;
}
return in_array($this->currentSubdomain, $subdomains, true);
}
/**
* Returns the method param string like `/$1/$2` for placeholders
*/
private function getMethodParams(string $from): string
{
preg_match_all('/\(.+?\)/', $from, $matches);
$count = is_countable($matches[0]) ? count($matches[0]) : 0;
$params = '';
for ($i = 1; $i <= $count; ++$i) {
$params .= '/$' . $i;
}
return $params;
}
/**
* Examines the HTTP_HOST to get the best match for the subdomain. It
* won't be perfect, but should work for our needs.
*
* It's especially not perfect since it's possible to register a domain
* with a period (.) as part of the domain name.
*
* @return false|string the subdomain
*/
private function determineCurrentSubdomain()
{
// We have to ensure that a scheme exists
// on the URL else parse_url will mis-interpret
// 'host' as the 'path'.
$url = $this->httpHost;
if (strpos($url, 'http') !== 0) {
$url = 'http://' . $url;
}
$parsedUrl = parse_url($url);
$host = explode('.', $parsedUrl['host']);
if ($host[0] === 'www') {
unset($host[0]);
}
// Get rid of any domains, which will be the last
unset($host[count($host) - 1]);
// Account for .co.uk, .co.nz, etc. domains
if (end($host) === 'co') {
$host = array_slice($host, 0, -1);
}
// If we only have 1 part left, then we don't have a sub-domain.
if (count($host) === 1) {
// Set it to false so we don't make it back here again.
return false;
}
return array_shift($host);
}
} }

View File

@ -82,7 +82,7 @@ class ComponentRenderer
$matches[name] = tag name $matches[name] = tag name
$matches[attributes] = array of attribute string (class="foo") $matches[attributes] = array of attribute string (class="foo")
*/ */
return preg_replace_callback($pattern, function ($match): string { return preg_replace_callback($pattern, function (array $match): string {
$view = $this->locateView($match['name']); $view = $this->locateView($match['name']);
$attributes = $this->parseAttributes($match['attributes']); $attributes = $this->parseAttributes($match['attributes']);
@ -104,7 +104,7 @@ class ComponentRenderer
$matches[attributes] = string of tag attributes (class="foo") $matches[attributes] = string of tag attributes (class="foo")
$matches[slot] = the content inside the tags $matches[slot] = the content inside the tags
*/ */
return preg_replace_callback($pattern, function ($match): string { return preg_replace_callback($pattern, function (array $match): string {
$view = $this->locateView($match['name']); $view = $this->locateView($match['name']);
$attributes = $this->parseAttributes($match['attributes']); $attributes = $this->parseAttributes($match['attributes']);
$attributes['slot'] = $match['slot']; $attributes['slot'] = $match['slot'];

View File

@ -94,13 +94,13 @@ class Vite
private function getHtmlTag(string $assetUrl, string $type): string private function getHtmlTag(string $assetUrl, string $type): string
{ {
return match ($type) { return match ($type) {
'css' => <<<CODE_SAMPLE 'css' => <<<HTML
<link rel="stylesheet" href="{$assetUrl}"/> <link rel="stylesheet" href="{$assetUrl}"/>
CODE_SAMPLE HTML
, ,
'js' => <<<CODE_SAMPLE 'js' => <<<HTML
<script type="module" src="{$assetUrl}"></script> <script type="module" src="{$assetUrl}"></script>
CODE_SAMPLE HTML
, ,
default => '', default => '',
}; };

View File

@ -67,15 +67,10 @@ class CategoryModel extends Model
static function (array $result, Category $category): array { static function (array $result, Category $category): array {
$result[$category->id] = ''; $result[$category->id] = '';
if ($category->parent instanceof Category) { if ($category->parent instanceof Category) {
$result[$category->id] = lang( $result[$category->id] = lang('Podcast.category_options.' . $category->parent->code) . ' ';
'Podcast.category_options.' . $category->parent->code,
[],
null,
false
) . ' ';
} }
$result[$category->id] .= lang('Podcast.category_options.' . $category->code, [], null, false); $result[$category->id] .= lang('Podcast.category_options.' . $category->code);
return $result; return $result;
}, },
[], [],

View File

@ -216,7 +216,7 @@ class EpisodeCommentModel extends UuidModel
) )
->whereIn('in_reply_to_id', static function (BaseBuilder $builder) use (&$episodeId): BaseBuilder { ->whereIn('in_reply_to_id', static function (BaseBuilder $builder) use (&$episodeId): BaseBuilder {
return $builder->select('id') return $builder->select('id')
->from(config('Fediverse')->tablesPrefix . 'posts') ->from('fediverse_posts')
->where([ ->where([
'episode_id' => $episodeId, 'episode_id' => $episodeId,
'in_reply_to_id' => null, 'in_reply_to_id' => null,

View File

@ -382,13 +382,11 @@ class EpisodeModel extends UuidModel
->groupBy('episode_id') ->groupBy('episode_id')
->getCompiledSelect(); ->getCompiledSelect();
$postsTable = config('Fediverse')
->tablesPrefix . 'posts';
$episodePostsRepliesCount = (new PostModel())->builder() $episodePostsRepliesCount = (new PostModel())->builder()
->select($postsTable . '.episode_id as episode_id, COUNT(*) as `comments_count`') ->select('fediverse_posts.episode_id as episode_id, COUNT(*) as `comments_count`')
->join($postsTable . ' as fp', $postsTable . '.id = fp.in_reply_to_id') ->join('fediverse_posts as fp', 'fediverse_posts.id = fp.in_reply_to_id')
->where($postsTable . '.in_reply_to_id', null) ->where('fediverse_posts.in_reply_to_id', null)
->groupBy($postsTable . '.episode_id') ->groupBy('fediverse_posts.episode_id')
->getCompiledSelect(); ->getCompiledSelect();
/** @var BaseResult $query */ /** @var BaseResult $query */
@ -409,11 +407,7 @@ class EpisodeModel extends UuidModel
{ {
$episodePostsCount = $this->builder() $episodePostsCount = $this->builder()
->select('episodes.id, COUNT(*) as `posts_count`') ->select('episodes.id, COUNT(*) as `posts_count`')
->join( ->join('fediverse_posts', 'episodes.id = fediverse_posts.episode_id')
config('Fediverse')
->tablesPrefix . 'posts',
'episodes.id = ' . config('Fediverse')->tablesPrefix . 'posts.episode_id'
)
->where('in_reply_to_id', null) ->where('in_reply_to_id', null)
->groupBy('episodes.id') ->groupBy('episodes.id')
->get() ->get()

View File

@ -145,7 +145,7 @@ class PersonModel extends Model
$this->select('`id`, `full_name`') $this->select('`id`, `full_name`')
->orderBy('`full_name`', 'ASC') ->orderBy('`full_name`', 'ASC')
->findAll(), ->findAll(),
static function ($result, $person) { static function (array $result, $person): array {
$result[$person->id] = $person->full_name; $result[$person->id] = $person->full_name;
return $result; return $result;
}, },

View File

@ -158,12 +158,12 @@ class PlatformModel extends Model
$podcastsPlatformsTable = $this->db->prefixTable('podcasts_platforms'); $podcastsPlatformsTable = $this->db->prefixTable('podcasts_platforms');
$platformsTable = $this->db->prefixTable('platforms'); $platformsTable = $this->db->prefixTable('platforms');
$deleteJoinQuery = <<<CODE_SAMPLE $deleteJoinQuery = <<<SQL
DELETE {$podcastsPlatformsTable} DELETE {$podcastsPlatformsTable}
FROM {$podcastsPlatformsTable} FROM {$podcastsPlatformsTable}
INNER JOIN {$platformsTable} ON {$platformsTable}.slug = {$podcastsPlatformsTable}.platform_slug INNER JOIN {$platformsTable} ON {$platformsTable}.slug = {$podcastsPlatformsTable}.platform_slug
WHERE `podcast_id` = ? AND `type` = ? WHERE `podcast_id` = ? AND `type` = ?
CODE_SAMPLE; SQL;
$this->db->query($deleteJoinQuery, [$podcastId, $platformType]); $this->db->query($deleteJoinQuery, [$podcastId, $platformType]);

View File

@ -84,6 +84,7 @@ class PodcastModel extends Model
* @var array<string, string> * @var array<string, string>
*/ */
protected $validationRules = [ protected $validationRules = [
'id' => 'permit_empty|is_natural_no_zero',
'title' => 'required', 'title' => 'required',
'handle' => 'required|regex_match[/^[a-zA-Z0-9\_]{1,32}$/]|is_unique[podcasts.handle,id,{id}]', 'handle' => 'required|regex_match[/^[a-zA-Z0-9\_]{1,32}$/]|is_unique[podcasts.handle,id,{id}]',
'description_markdown' => 'required', 'description_markdown' => 'required',
@ -174,23 +175,15 @@ class PodcastModel extends Model
$prefix = $this->db->getPrefix(); $prefix = $this->db->getPrefix();
if ($orderBy === 'activity') { if ($orderBy === 'activity') {
$fediverseTablePrefix = $prefix . config('Fediverse')
->tablesPrefix;
$this->builder() $this->builder()
->select( ->select('podcasts.*, MAX(`' . $prefix . 'fediverse_posts`.`published_at`) as max_published_at')
'podcasts.*, MAX(' . $fediverseTablePrefix . 'posts.published_at' . ') as max_published_at' ->join('fediverse_posts', 'fediverse_posts.actor_id = podcasts.actor_id', 'left')
)
->join(
$fediverseTablePrefix . 'posts',
$fediverseTablePrefix . 'posts.actor_id = podcasts.actor_id',
'left'
)
->groupStart() ->groupStart()
->where( ->where(
'`' . $fediverseTablePrefix . 'posts`.`published_at` <= UTC_TIMESTAMP()', '`' . $prefix . 'fediverse_posts`.`published_at` <= UTC_TIMESTAMP()',
null, null,
false false
)->orWhere($fediverseTablePrefix . 'posts.published_at', null) )->orWhere('fediverse_posts.published_at', null)
->groupEnd() ->groupEnd()
->groupBy('podcasts.actor_id') ->groupBy('podcasts.actor_id')
->orderBy('max_published_at', 'DESC'); ->orderBy('max_published_at', 'DESC');

View File

@ -58,8 +58,8 @@ class PostModel extends FediversePostModel
public function setEpisodeIdForRepliesOfEpisodePosts(): int | false public function setEpisodeIdForRepliesOfEpisodePosts(): int | false
{ {
// make sure that posts in reply to episode activities have an episode id // make sure that posts in reply to episode activities have an episode id
$postsToUpdate = $this->db->table(config('Fediverse')->tablesPrefix . 'posts as p1') $postsToUpdate = $this->db->table('fediverse_posts as p1')
->join(config('Fediverse')->tablesPrefix . 'posts as p2', 'p1.id = p2.in_reply_to_id') ->join('fediverse_posts as p2', 'p1.id = p2.in_reply_to_id')
->select('p2.id, p1.episode_id') ->select('p2.id, p1.episode_id')
->where([ ->where([
'p2.in_reply_to_id IS NOT' => null, 'p2.in_reply_to_id IS NOT' => null,

View File

@ -82,7 +82,7 @@ class RolesDoc extends BaseCommand
$pattern, $pattern,
['role', 'description', 'permissions'], ['role', 'description', 'permissions'],
$authGroups->instanceGroups, $authGroups->instanceGroups,
static function ($table, $key, $value) use ($instanceMatrix): void { static function ($table, $key, array $value) use ($instanceMatrix): void {
$table->addRow($value['title'], $value['description'], implode(', ', $instanceMatrix[$key])); $table->addRow($value['title'], $value['description'], implode(', ', $instanceMatrix[$key]));
} }
); );
@ -109,7 +109,7 @@ class RolesDoc extends BaseCommand
$pattern, $pattern,
['role', 'description', 'permissions'], ['role', 'description', 'permissions'],
$authGroups->podcastGroups, $authGroups->podcastGroups,
static function ($table, $key, $value) use ($podcastMatrix): void { static function ($table, $key, array $value) use ($podcastMatrix): void {
$table->addRow($value['title'], $value['description'], implode(', ', $podcastMatrix[$key])); $table->addRow($value['title'], $value['description'], implode(', ', $podcastMatrix[$key]));
} }
); );

View File

@ -83,7 +83,7 @@ class ContributorController extends BaseController
$users = (new UserModel())->findAll(); $users = (new UserModel())->findAll();
$contributorOptions = array_reduce( $contributorOptions = array_reduce(
$users, $users,
static function ($result, $user) { static function (array $result, $user): array {
$result[$user->id] = $user->username; $result[$user->id] = $user->username;
return $result; return $result;
}, },
@ -94,7 +94,7 @@ class ContributorController extends BaseController
$roleOptions = []; $roleOptions = [];
array_walk( array_walk(
$roles, $roles,
static function ($role, $key) use (&$roleOptions): array { static function (string $role, $key) use (&$roleOptions): array {
$roleOptions[$role] = lang('Auth.podcast_groups.' . $role . '.title'); $roleOptions[$role] = lang('Auth.podcast_groups.' . $role . '.title');
return $roleOptions; return $roleOptions;
}, },
@ -137,7 +137,7 @@ class ContributorController extends BaseController
$roleOptions = []; $roleOptions = [];
array_walk( array_walk(
$roles, $roles,
static function ($role) use (&$roleOptions): array { static function (string $role) use (&$roleOptions): array {
$roleOptions[$role] = lang('Auth.podcast_groups.' . $role . '.title'); $roleOptions[$role] = lang('Auth.podcast_groups.' . $role . '.title');
return $roleOptions; return $roleOptions;
}, },

View File

@ -66,7 +66,7 @@ class UserController extends BaseController
$roleOptions = []; $roleOptions = [];
array_walk( array_walk(
$roles, $roles,
static function ($role, $key) use (&$roleOptions): array { static function (array $role, $key) use (&$roleOptions): array {
$roleOptions[$key] = $role['title']; $roleOptions[$key] = $role['title'];
return $roleOptions; return $roleOptions;
}, },
@ -172,7 +172,7 @@ class UserController extends BaseController
$roleOptions = []; $roleOptions = [];
array_walk( array_walk(
$roles, $roles,
static function ($role, $key) use (&$roleOptions): array { static function (array $role, $key) use (&$roleOptions): array {
$roleOptions[$key] = $role['title']; $roleOptions[$key] = $role['title'];
return $roleOptions; return $roleOptions;
}, },

View File

@ -38,8 +38,6 @@ class Fediverse extends BaseConfig
public string $defaultCoverImageMimetype = 'image/jpeg'; public string $defaultCoverImageMimetype = 'image/jpeg';
public string $tablesPrefix = 'fediverse_';
/** /**
* -------------------------------------------------------------------- * --------------------------------------------------------------------
* Cache options * Cache options

View File

@ -291,14 +291,11 @@ class ActorController extends Controller
public function followers(): ResponseInterface public function followers(): ResponseInterface
{ {
$tablesPrefix = config('Fediverse')
->tablesPrefix;
// get followers for a specific actor // get followers for a specific actor
$followers = model('ActorModel', false) $followers = model('ActorModel', false)
->join($tablesPrefix . 'follows', $tablesPrefix . 'follows.actor_id = id', 'inner') ->join('fediverse_follows', 'fediverse_follows.actor_id = id', 'inner')
->where($tablesPrefix . 'follows.target_actor_id', $this->actor->id) ->where('fediverse_follows.target_actor_id', $this->actor->id)
->orderBy($tablesPrefix . 'follows.created_at', 'DESC'); ->orderBy('fediverse_follows.created_at', 'DESC');
$pageNumber = (int) $this->request->getGet('page'); $pageNumber = (int) $this->request->getGet('page');

View File

@ -113,11 +113,11 @@ class AddActors extends BaseMigration
$this->forge->addPrimaryKey('id'); $this->forge->addPrimaryKey('id');
$this->forge->addUniqueKey('uri'); $this->forge->addUniqueKey('uri');
$this->forge->addUniqueKey(['username', 'domain']); $this->forge->addUniqueKey(['username', 'domain']);
$this->forge->createTable(config('Fediverse')->tablesPrefix . 'actors'); $this->forge->createTable('fediverse_actors');
} }
public function down(): void public function down(): void
{ {
$this->forge->dropTable(config('Fediverse')->tablesPrefix . 'actors'); $this->forge->dropTable('fediverse_actors');
} }
} }

View File

@ -75,21 +75,18 @@ class AddPosts extends BaseMigration
], ],
]); ]);
$tablesPrefix = config('Fediverse')
->tablesPrefix;
$this->forge->addPrimaryKey('id'); $this->forge->addPrimaryKey('id');
$this->forge->addUniqueKey('uri'); $this->forge->addUniqueKey('uri');
// FIXME: an actor must reblog a post only once // FIXME: an actor must reblog a post only once
// $this->forge->addUniqueKey(['actor_id', 'reblog_of_id']); // $this->forge->addUniqueKey(['actor_id', 'reblog_of_id']);
$this->forge->addForeignKey('actor_id', $tablesPrefix . 'actors', 'id', '', 'CASCADE'); $this->forge->addForeignKey('actor_id', 'fediverse_actors', 'id', '', 'CASCADE');
$this->forge->addForeignKey('in_reply_to_id', $tablesPrefix . 'posts', 'id', '', 'CASCADE'); $this->forge->addForeignKey('in_reply_to_id', 'fediverse_posts', 'id', '', 'CASCADE');
$this->forge->addForeignKey('reblog_of_id', $tablesPrefix . 'posts', 'id', '', 'CASCADE'); $this->forge->addForeignKey('reblog_of_id', 'fediverse_posts', 'id', '', 'CASCADE');
$this->forge->createTable($tablesPrefix . 'posts'); $this->forge->createTable('fediverse_posts');
} }
public function down(): void public function down(): void
{ {
$this->forge->dropTable(config('Fediverse')->tablesPrefix . 'posts'); $this->forge->dropTable('fediverse_posts');
} }
} }

View File

@ -58,18 +58,15 @@ class AddActivities extends BaseMigration
], ],
]); ]);
$tablesPrefix = config('Fediverse')
->tablesPrefix;
$this->forge->addPrimaryKey('id'); $this->forge->addPrimaryKey('id');
$this->forge->addForeignKey('actor_id', $tablesPrefix . 'actors', 'id', '', 'CASCADE'); $this->forge->addForeignKey('actor_id', 'fediverse_actors', 'id', '', 'CASCADE');
$this->forge->addForeignKey('target_actor_id', $tablesPrefix . 'actors', 'id', '', 'CASCADE'); $this->forge->addForeignKey('target_actor_id', 'fediverse_actors', 'id', '', 'CASCADE');
$this->forge->addForeignKey('post_id', $tablesPrefix . 'posts', 'id', '', 'CASCADE'); $this->forge->addForeignKey('post_id', 'fediverse_posts', 'id', '', 'CASCADE');
$this->forge->createTable($tablesPrefix . 'activities'); $this->forge->createTable('fediverse_activities');
} }
public function down(): void public function down(): void
{ {
$this->forge->dropTable(config('Fediverse')->tablesPrefix . 'activities'); $this->forge->dropTable('fediverse_activities');
} }
} }

View File

@ -29,18 +29,15 @@ class AddFavourites extends BaseMigration
], ],
]); ]);
$tablesPrefix = config('Fediverse')
->tablesPrefix;
$this->forge->addField('`created_at` timestamp NOT NULL DEFAULT current_timestamp()'); $this->forge->addField('`created_at` timestamp NOT NULL DEFAULT current_timestamp()');
$this->forge->addPrimaryKey(['actor_id', 'post_id']); $this->forge->addPrimaryKey(['actor_id', 'post_id']);
$this->forge->addForeignKey('actor_id', $tablesPrefix . 'actors', 'id', '', 'CASCADE'); $this->forge->addForeignKey('actor_id', 'fediverse_actors', 'id', '', 'CASCADE');
$this->forge->addForeignKey('post_id', $tablesPrefix . 'posts', 'id', '', 'CASCADE'); $this->forge->addForeignKey('post_id', 'fediverse_posts', 'id', '', 'CASCADE');
$this->forge->createTable($tablesPrefix . 'favourites'); $this->forge->createTable('fediverse_favourites');
} }
public function down(): void public function down(): void
{ {
$this->forge->dropTable(config('Fediverse')->tablesPrefix . 'favourites'); $this->forge->dropTable('fediverse_favourites');
} }
} }

View File

@ -31,18 +31,15 @@ class AddFollowers extends BaseMigration
], ],
]); ]);
$tablesPrefix = config('Fediverse')
->tablesPrefix;
$this->forge->addField('`created_at` timestamp NOT NULL DEFAULT current_timestamp()'); $this->forge->addField('`created_at` timestamp NOT NULL DEFAULT current_timestamp()');
$this->forge->addPrimaryKey(['actor_id', 'target_actor_id']); $this->forge->addPrimaryKey(['actor_id', 'target_actor_id']);
$this->forge->addForeignKey('actor_id', $tablesPrefix . 'actors', 'id', '', 'CASCADE'); $this->forge->addForeignKey('actor_id', 'fediverse_actors', 'id', '', 'CASCADE');
$this->forge->addForeignKey('target_actor_id', $tablesPrefix . 'actors', 'id', '', 'CASCADE'); $this->forge->addForeignKey('target_actor_id', 'fediverse_actors', 'id', '', 'CASCADE');
$this->forge->createTable($tablesPrefix . 'follows'); $this->forge->createTable('fediverse_follows');
} }
public function down(): void public function down(): void
{ {
$this->forge->dropTable(config('Fediverse')->tablesPrefix . 'follows'); $this->forge->dropTable('fediverse_follows');
} }
} }

View File

@ -75,11 +75,11 @@ class AddPreviewCards extends BaseMigration
$this->forge->addPrimaryKey('id'); $this->forge->addPrimaryKey('id');
$this->forge->addUniqueKey('url'); $this->forge->addUniqueKey('url');
$this->forge->createTable(config('Fediverse')->tablesPrefix . 'preview_cards'); $this->forge->createTable('fediverse_preview_cards');
} }
public function down(): void public function down(): void
{ {
$this->forge->dropTable(config('Fediverse')->tablesPrefix . 'preview_cards'); $this->forge->dropTable('fediverse_preview_cards');
} }
} }

View File

@ -29,17 +29,14 @@ class AddPostsPreviewCards extends BaseMigration
], ],
]); ]);
$tablesPrefix = config('Fediverse')
->tablesPrefix;
$this->forge->addPrimaryKey(['post_id', 'preview_card_id']); $this->forge->addPrimaryKey(['post_id', 'preview_card_id']);
$this->forge->addForeignKey('post_id', $tablesPrefix . 'posts', 'id', '', 'CASCADE'); $this->forge->addForeignKey('post_id', 'fediverse_posts', 'id', '', 'CASCADE');
$this->forge->addForeignKey('preview_card_id', $tablesPrefix . 'preview_cards', 'id', '', 'CASCADE'); $this->forge->addForeignKey('preview_card_id', 'fediverse_preview_cards', 'id', '', 'CASCADE');
$this->forge->createTable($tablesPrefix . 'posts_preview_cards'); $this->forge->createTable('fediverse_posts_preview_cards');
} }
public function down(): void public function down(): void
{ {
$this->forge->dropTable(config('Fediverse')->tablesPrefix . 'posts_preview_cards'); $this->forge->dropTable('fediverse_posts_preview_cards');
} }
} }

View File

@ -28,11 +28,11 @@ class AddBlockedDomains extends BaseMigration
], ],
]); ]);
$this->forge->addPrimaryKey('name'); $this->forge->addPrimaryKey('name');
$this->forge->createTable(config('Fediverse')->tablesPrefix . 'blocked_domains'); $this->forge->createTable('fediverse_blocked_domains');
} }
public function down(): void public function down(): void
{ {
$this->forge->dropTable(config('Fediverse')->tablesPrefix . 'blocked_domains'); $this->forge->dropTable('fediverse_blocked_domains');
} }
} }

View File

@ -55,19 +55,16 @@ class AddNotifications extends BaseMigration
], ],
]); ]);
$tablesPrefix = config('Fediverse')
->tablesPrefix;
$this->forge->addPrimaryKey('id'); $this->forge->addPrimaryKey('id');
$this->forge->addForeignKey('actor_id', $tablesPrefix . 'actors', 'id', '', 'CASCADE'); $this->forge->addForeignKey('actor_id', 'fediverse_actors', 'id', '', 'CASCADE');
$this->forge->addForeignKey('target_actor_id', $tablesPrefix . 'actors', 'id', '', 'CASCADE'); $this->forge->addForeignKey('target_actor_id', 'fediverse_actors', 'id', '', 'CASCADE');
$this->forge->addForeignKey('post_id', $tablesPrefix . 'posts', 'id', '', 'CASCADE'); $this->forge->addForeignKey('post_id', 'fediverse_posts', 'id', '', 'CASCADE');
$this->forge->addForeignKey('activity_id', $tablesPrefix . 'activities', 'id', '', 'CASCADE'); $this->forge->addForeignKey('activity_id', 'fediverse_activities', 'id', '', 'CASCADE');
$this->forge->createTable($tablesPrefix . 'notifications'); $this->forge->createTable('fediverse_notifications');
} }
public function down(): void public function down(): void
{ {
$this->forge->dropTable(config('Fediverse')->tablesPrefix . 'notifications'); $this->forge->dropTable('fediverse_notifications');
} }
} }

View File

@ -408,7 +408,7 @@ if (! function_exists('linkify')) {
), ),
'handle' => preg_replace_callback( 'handle' => preg_replace_callback(
'~(?<!\w)@(?<username>\w++)(?:@(?<domain>(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]))?~', '~(?<!\w)@(?<username>\w++)(?:@(?<domain>(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]))?~',
static function ($match) use (&$links) { static function (array $match) use (&$links) {
// check if host is set and look for actor in database // check if host is set and look for actor in database
if (isset($match['host'])) { if (isset($match['host'])) {
if ( if (
@ -489,7 +489,7 @@ if (! function_exists('linkify')) {
// Insert all links // Insert all links
return preg_replace_callback( return preg_replace_callback(
'~<(\d+)>~', '~<(\d+)>~',
static function ($match) use (&$links) { static function (array $match) use (&$links): string {
return $links[$match[1] - 1]; return $links[$match[1] - 1];
}, },
$text, $text,

View File

@ -13,14 +13,15 @@ namespace Modules\Fediverse\Models;
use CodeIgniter\Database\BaseResult; use CodeIgniter\Database\BaseResult;
use CodeIgniter\I18n\Time; use CodeIgniter\I18n\Time;
use DateTimeInterface; use DateTimeInterface;
use Michalsn\Uuid\UuidModel;
use Modules\Fediverse\Entities\Activity; use Modules\Fediverse\Entities\Activity;
class ActivityModel extends BaseUuidModel class ActivityModel extends UuidModel
{ {
/** /**
* @var string * @var string
*/ */
protected $table = 'activities'; protected $table = 'fediverse_activities';
/** /**
* @var string * @var string

View File

@ -11,14 +11,15 @@ declare(strict_types=1);
namespace Modules\Fediverse\Models; namespace Modules\Fediverse\Models;
use CodeIgniter\Events\Events; use CodeIgniter\Events\Events;
use CodeIgniter\Model;
use Modules\Fediverse\Entities\Actor; use Modules\Fediverse\Entities\Actor;
class ActorModel extends BaseModel class ActorModel extends Model
{ {
/** /**
* @var string * @var string
*/ */
protected $table = 'actors'; protected $table = 'fediverse_actors';
/** /**
* @var string[] * @var string[]
@ -119,10 +120,8 @@ class ActorModel extends BaseModel
config('Fediverse') config('Fediverse')
->cachePrefix . "actor#{$actorId}_followers"; ->cachePrefix . "actor#{$actorId}_followers";
if (! ($found = cache($cacheName))) { if (! ($found = cache($cacheName))) {
$tablesPrefix = config('Fediverse') $found = $this->join('fediverse_follows', 'fediverse_follows.actor_id = id', 'inner')
->tablesPrefix; ->where('fediverse_follows.target_actor_id', $actorId)
$found = $this->join($tablesPrefix . 'follows', $tablesPrefix . 'follows.actor_id = id', 'inner')
->where($tablesPrefix . 'follows.target_actor_id', $actorId)
->findAll(); ->findAll();
cache() cache()
@ -225,28 +224,27 @@ class ActorModel extends BaseModel
->cachePrefix . 'blocked_actors'; ->cachePrefix . 'blocked_actors';
if (! ($found = cache($cacheName))) { if (! ($found = cache($cacheName))) {
$tablePrefix = config('Database') $tablePrefix = config('Database')
->default['DBPrefix'] . config('Fediverse') ->default['DBPrefix'];
->tablesPrefix;
$result = $this->select('COUNT(DISTINCT `cp_fediverse_actors`.`id`) as `total_active_actors`', false) $result = $this->select('COUNT(DISTINCT `cp_fediverse_actors`.`id`) as `total_active_actors`', false)
->join( ->join(
$tablePrefix . 'posts', $tablePrefix . 'fediverse_posts',
$tablePrefix . 'actors.id = ' . $tablePrefix . 'posts.actor_id', $tablePrefix . 'fediverse_actors.id = ' . $tablePrefix . 'fediverse_posts.actor_id',
'left outer' 'left outer'
) )
->join( ->join(
$tablePrefix . 'favourites', $tablePrefix . 'fediverse_favourites',
$tablePrefix . 'actors.id = ' . $tablePrefix . 'favourites.actor_id', $tablePrefix . 'fediverse_actors.id = ' . $tablePrefix . 'fediverse_favourites.actor_id',
'left outer' 'left outer'
) )
->where($tablePrefix . 'actors.domain', get_current_domain()) ->where($tablePrefix . 'actors.domain', get_current_domain())
->groupStart() ->groupStart()
->where( ->where(
"`{$tablePrefix}posts`.`created_at` >= UTC_TIMESTAMP() - INTERVAL {$lastNumberOfMonths} month", "`{$tablePrefix}fediverse_posts`.`created_at` >= UTC_TIMESTAMP() - INTERVAL {$lastNumberOfMonths} month",
null, null,
false false
) )
->orWhere( ->orWhere(
"`{$tablePrefix}favourites`.`created_at` >= UTC_TIMESTAMP() - INTERVAL {$lastNumberOfMonths} month", "`{$tablePrefix}fediverse_favourites`.`created_at` >= UTC_TIMESTAMP() - INTERVAL {$lastNumberOfMonths} month",
null, null,
false false
) )
@ -265,12 +263,8 @@ class ActorModel extends BaseModel
public function resetFollowersCount(): int | false public function resetFollowersCount(): int | false
{ {
$tablePrefix = config('Fediverse') $actorsFollowersCount = $this->db->table('fediverse_follows')
->tablesPrefix; ->select('target_actor_id as id, COUNT(*) as `followers_count`')
$actorsFollowersCount = $this->db->table($tablePrefix . 'follows')->select(
'target_actor_id as id, COUNT(*) as `followers_count`'
)
->groupBy('id') ->groupBy('id')
->get() ->get()
->getResultArray(); ->getResultArray();
@ -284,10 +278,7 @@ class ActorModel extends BaseModel
public function resetPostsCount(): int | false public function resetPostsCount(): int | false
{ {
$tablePrefix = config('Fediverse') $actorsFollowersCount = $this->db->table($tablePrefix . 'fediverse_posts')->select(
->tablesPrefix;
$actorsFollowersCount = $this->db->table($tablePrefix . 'posts')->select(
'actor_id as id, COUNT(*) as `posts_count`' 'actor_id as id, COUNT(*) as `posts_count`'
) )
->where([ ->where([

View File

@ -1,26 +0,0 @@
<?php
declare(strict_types=1);
namespace Modules\Fediverse\Models;
use CodeIgniter\Database\ConnectionInterface;
use CodeIgniter\Model;
use CodeIgniter\Validation\ValidationInterface;
class BaseModel extends Model
{
/**
* Model constructor.
*
* @param ConnectionInterface|null $db DB Connection
* @param ValidationInterface|null $validation Validation
*/
public function __construct(ConnectionInterface &$db = null, ValidationInterface $validation = null)
{
parent::__construct($db, $validation);
$this->table = config('Fediverse')
->tablesPrefix . $this->table;
}
}

View File

@ -1,20 +0,0 @@
<?php
declare(strict_types=1);
namespace Modules\Fediverse\Models;
use CodeIgniter\Database\ConnectionInterface;
use CodeIgniter\Validation\ValidationInterface;
use Michalsn\Uuid\UuidModel;
class BaseUuidModel extends UuidModel
{
public function __construct(ConnectionInterface &$db = null, ValidationInterface $validation = null)
{
parent::__construct($db, $validation);
$this->table = config('Fediverse')
->tablesPrefix . $this->table;
}
}

View File

@ -12,14 +12,15 @@ namespace Modules\Fediverse\Models;
use CodeIgniter\Database\BaseResult; use CodeIgniter\Database\BaseResult;
use CodeIgniter\Events\Events; use CodeIgniter\Events\Events;
use CodeIgniter\Model;
use Modules\Fediverse\Entities\BlockedDomain; use Modules\Fediverse\Entities\BlockedDomain;
class BlockedDomainModel extends BaseModel class BlockedDomainModel extends Model
{ {
/** /**
* @var string * @var string
*/ */
protected $table = 'blocked_domains'; protected $table = 'fediverse_blocked_domains';
/** /**
* @var string * @var string

View File

@ -11,18 +11,19 @@ declare(strict_types=1);
namespace Modules\Fediverse\Models; namespace Modules\Fediverse\Models;
use CodeIgniter\Events\Events; use CodeIgniter\Events\Events;
use Michalsn\Uuid\UuidModel;
use Modules\Fediverse\Activities\LikeActivity; use Modules\Fediverse\Activities\LikeActivity;
use Modules\Fediverse\Activities\UndoActivity; use Modules\Fediverse\Activities\UndoActivity;
use Modules\Fediverse\Entities\Actor; use Modules\Fediverse\Entities\Actor;
use Modules\Fediverse\Entities\Favourite; use Modules\Fediverse\Entities\Favourite;
use Modules\Fediverse\Entities\Post; use Modules\Fediverse\Entities\Post;
class FavouriteModel extends BaseUuidModel class FavouriteModel extends UuidModel
{ {
/** /**
* @var string * @var string
*/ */
protected $table = 'favourites'; protected $table = 'fediverse_favourites';
/** /**
* @var string[] * @var string[]

View File

@ -12,18 +12,19 @@ namespace Modules\Fediverse\Models;
use CodeIgniter\Events\Events; use CodeIgniter\Events\Events;
use CodeIgniter\I18n\Time; use CodeIgniter\I18n\Time;
use CodeIgniter\Model;
use Exception; use Exception;
use Modules\Fediverse\Activities\FollowActivity; use Modules\Fediverse\Activities\FollowActivity;
use Modules\Fediverse\Activities\UndoActivity; use Modules\Fediverse\Activities\UndoActivity;
use Modules\Fediverse\Entities\Actor; use Modules\Fediverse\Entities\Actor;
use Modules\Fediverse\Entities\Follow; use Modules\Fediverse\Entities\Follow;
class FollowModel extends BaseModel class FollowModel extends Model
{ {
/** /**
* @var string * @var string
*/ */
protected $table = 'follows'; protected $table = 'fediverse_follows';
/** /**
* @var string[] * @var string[]

View File

@ -10,14 +10,15 @@ declare(strict_types=1);
namespace Modules\Fediverse\Models; namespace Modules\Fediverse\Models;
use Michalsn\Uuid\UuidModel;
use Modules\Fediverse\Entities\Notification; use Modules\Fediverse\Entities\Notification;
class NotificationModel extends BaseUuidModel class NotificationModel extends UuidModel
{ {
/** /**
* @var string * @var string
*/ */
protected $table = 'notifications'; protected $table = 'fediverse_notifications';
/** /**
* @var string * @var string

View File

@ -15,6 +15,7 @@ use CodeIgniter\Events\Events;
use CodeIgniter\HTTP\URI; use CodeIgniter\HTTP\URI;
use CodeIgniter\I18n\Time; use CodeIgniter\I18n\Time;
use Exception; use Exception;
use Michalsn\Uuid\UuidModel;
use Modules\Fediverse\Activities\AnnounceActivity; use Modules\Fediverse\Activities\AnnounceActivity;
use Modules\Fediverse\Activities\CreateActivity; use Modules\Fediverse\Activities\CreateActivity;
use Modules\Fediverse\Activities\DeleteActivity; use Modules\Fediverse\Activities\DeleteActivity;
@ -23,12 +24,12 @@ use Modules\Fediverse\Entities\Actor;
use Modules\Fediverse\Entities\Post; use Modules\Fediverse\Entities\Post;
use Modules\Fediverse\Objects\TombstoneObject; use Modules\Fediverse\Objects\TombstoneObject;
class PostModel extends BaseUuidModel class PostModel extends UuidModel
{ {
/** /**
* @var string * @var string
*/ */
protected $table = 'posts'; protected $table = 'fediverse_posts';
/** /**
* @var string * @var string
@ -172,16 +173,10 @@ class PostModel extends BaseUuidModel
($withBlocked ? '_withBlocked' : ''); ($withBlocked ? '_withBlocked' : '');
if (! ($found = cache($cacheName))) { if (! ($found = cache($cacheName))) {
$tablesPrefix = config('Fediverse')
->tablesPrefix;
if (! $withBlocked) { if (! $withBlocked) {
$this->select($tablesPrefix . 'posts.*') $this->select('fediverse_posts.*')
->join( ->join('fediverse_actors', 'fediverse_actors.id = fediverse_posts.actor_id', 'inner')
$tablesPrefix . 'actors', ->where('fediverse_actors.is_blocked', 0);
$tablesPrefix . 'actors.id = ' . $tablesPrefix . 'posts.actor_id',
'inner'
)
->where($tablesPrefix . 'actors.is_blocked', 0);
} }
$this->where('in_reply_to_id', $this->uuid->fromString($postId) ->getBytes()) $this->where('in_reply_to_id', $this->uuid->fromString($postId) ->getBytes())
@ -222,7 +217,7 @@ class PostModel extends BaseUuidModel
public function addPreviewCard(string $postId, int $previewCardId): bool public function addPreviewCard(string $postId, int $previewCardId): bool
{ {
return $this->db->table(config('Fediverse')->tablesPrefix . 'posts_preview_cards') return $this->db->table('fediverse_posts_preview_cards')
->insert([ ->insert([
'post_id' => $this->uuid->fromString($postId) 'post_id' => $this->uuid->fromString($postId)
->getBytes(), ->getBytes(),
@ -370,7 +365,7 @@ class PostModel extends BaseUuidModel
if ( if (
$post->preview_card && $post->preview_card &&
$this->db $this->db
->table(config('Fediverse')->tablesPrefix . 'posts_preview_cards') ->table('fediverse_posts_preview_cards')
->where('preview_card_id', $post->preview_card->id) ->where('preview_card_id', $post->preview_card->id)
->countAll() <= 1 ->countAll() <= 1
) { ) {
@ -599,11 +594,9 @@ class PostModel extends BaseUuidModel
$cacheName = config('Fediverse') $cacheName = config('Fediverse')
->cachePrefix . 'blocked_actors'; ->cachePrefix . 'blocked_actors';
if (! ($found = cache($cacheName))) { if (! ($found = cache($cacheName))) {
$tablePrefix = config('Fediverse')
->tablesPrefix;
$result = $this->select('COUNT(*) as total_local_posts') $result = $this->select('COUNT(*) as total_local_posts')
->join($tablePrefix . 'actors', $tablePrefix . 'actors.id = ' . $tablePrefix . 'posts.actor_id') ->join('fediverse_actors', 'fediverse_actors.id = fediverse_posts.actor_id')
->where($tablePrefix . 'actors.domain', get_current_domain()) ->where('fediverse_actors.domain', get_current_domain())
->where('`published_at` <= UTC_TIMESTAMP()', null, false) ->where('`published_at` <= UTC_TIMESTAMP()', null, false)
->get() ->get()
->getResultArray(); ->getResultArray();
@ -619,12 +612,8 @@ class PostModel extends BaseUuidModel
public function resetFavouritesCount(): int | false public function resetFavouritesCount(): int | false
{ {
$tablePrefix = config('Fediverse') $postsFavouritesCount = $this->db->table('fediverse_favourites')
->tablesPrefix; ->select('post_id as id, COUNT(*) as `favourites_count`')
$postsFavouritesCount = $this->db->table($tablePrefix . 'favourites')->select(
'post_id as id, COUNT(*) as `favourites_count`'
)
->groupBy('id') ->groupBy('id')
->get() ->get()
->getResultArray(); ->getResultArray();
@ -639,12 +628,9 @@ class PostModel extends BaseUuidModel
public function resetReblogsCount(): int | false public function resetReblogsCount(): int | false
{ {
$tablePrefix = config('Fediverse') $postsReblogsCount = $this->select('fediverse_posts.id, COUNT(*) as `replies_count`')
->tablesPrefix; ->join('fediverse_posts as p2', 'fediverse_posts.id = p2.reblog_of_id')
->groupBy('fediverse_posts.id')
$postsReblogsCount = $this->select($tablePrefix . 'posts.id, COUNT(*) as `replies_count`')
->join($tablePrefix . 'posts as p2', $tablePrefix . 'posts.id = p2.reblog_of_id')
->groupBy($tablePrefix . 'posts.id')
->get() ->get()
->getResultArray(); ->getResultArray();
@ -658,12 +644,9 @@ class PostModel extends BaseUuidModel
public function resetRepliesCount(): int | false public function resetRepliesCount(): int | false
{ {
$tablePrefix = config('Fediverse') $postsRepliesCount = $this->select('fediverse_posts.id, COUNT(*) as `replies_count`')
->tablesPrefix; ->join('fediverse_posts as p2', 'fediverse_posts.id = p2.in_reply_to_id')
->groupBy('fediverse_posts.id')
$postsRepliesCount = $this->select($tablePrefix . 'posts.id, COUNT(*) as `replies_count`')
->join($tablePrefix . 'posts as p2', $tablePrefix . 'posts.id = p2.in_reply_to_id')
->groupBy($tablePrefix . 'posts.id')
->get() ->get()
->getResultArray(); ->getResultArray();

View File

@ -11,14 +11,15 @@ declare(strict_types=1);
namespace Modules\Fediverse\Models; namespace Modules\Fediverse\Models;
use CodeIgniter\Database\BaseResult; use CodeIgniter\Database\BaseResult;
use CodeIgniter\Model;
use Modules\Fediverse\Entities\PreviewCard; use Modules\Fediverse\Entities\PreviewCard;
class PreviewCardModel extends BaseModel class PreviewCardModel extends Model
{ {
/** /**
* @var string * @var string
*/ */
protected $table = 'preview_cards'; protected $table = 'fediverse_preview_cards';
/** /**
* @var string[] * @var string[]
@ -75,11 +76,9 @@ class PreviewCardModel extends BaseModel
config('Fediverse') config('Fediverse')
->cachePrefix . "post#{$postId}_preview_card"; ->cachePrefix . "post#{$postId}_preview_card";
if (! ($found = cache($cacheName))) { if (! ($found = cache($cacheName))) {
$tablesPrefix = config('Fediverse')
->tablesPrefix;
$found = $this->join( $found = $this->join(
$tablesPrefix . 'posts_preview_cards', 'fediverse_posts_preview_cards',
$tablesPrefix . 'posts_preview_cards.preview_card_id = id', 'fediverse_posts_preview_cards.preview_card_id = id',
'inner', 'inner',
) )
->where('post_id', service('uuid') ->fromString($postId) ->getBytes()) ->where('post_id', service('uuid') ->fromString($postId) ->getBytes())

View File

@ -243,8 +243,6 @@ class InstallController extends Controller
{ {
$migrate = Services::migrations(); $migrate = Services::migrations();
$migrate->setNamespace('CodeIgniter\Settings')
->latest();
$migrate->setNamespace(null) $migrate->setNamespace(null)
->latest(); ->latest();
} }

View File

@ -18,7 +18,7 @@ if (! function_exists('media_url')) {
$relativePath = implode('/', $relativePath); $relativePath = implode('/', $relativePath);
} }
$uri = new URI(rtrim((string) config(Media::class)->baseURL, '/') . '/' . ltrim($relativePath)); $uri = new URI(rtrim(config(Media::class)->baseURL, '/') . '/' . ltrim($relativePath));
return URI::createURIString( return URI::createURIString(
$scheme ?? $uri->getScheme(), $scheme ?? $uri->getScheme(),

View File

@ -18,6 +18,7 @@ use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\CLI; use CodeIgniter\CLI\CLI;
use CodeIgniter\I18n\Time; use CodeIgniter\I18n\Time;
use CodeIgniter\Shield\Entities\User; use CodeIgniter\Shield\Entities\User;
use Config\Services;
use Exception; use Exception;
use League\HTMLToMarkdown\HtmlConverter; use League\HTMLToMarkdown\HtmlConverter;
use Modules\Auth\Models\UserModel; use Modules\Auth\Models\UserModel;
@ -95,6 +96,9 @@ class PodcastImport extends BaseCommand
public function run(array $params): void public function run(array $params): void
{ {
// FIXME: getting named routes doesn't work from v4.3 anymore, so loading all routes before importing
Services::routes()->loadRoutes();
$this->init(); $this->init();
try { try {
@ -503,7 +507,7 @@ class PodcastImport extends BaseCommand
->get() ->get()
->getResultArray(); ->getResultArray();
return array_map(static function ($element) { return array_map(static function (array $element) {
return $element['guid']; return $element['guid'];
}, $result); }, $result);
} }

View File

@ -28,8 +28,6 @@ class DatabaseUpdate extends BaseCommand
{ {
$migrate = Services::migrations(); $migrate = Services::migrations();
$migrate->setNamespace('CodeIgniter\Settings')
->latest();
$migrate->setNamespace(null) $migrate->setNamespace(null)
->latest(); ->latest();
} }

View File

@ -16,14 +16,14 @@ class AddIsPublishedOnHubsToPodcasts extends BaseMigration
{ {
public function up(): void public function up(): void
{ {
$prefix = $this->db->getPrefix(); $this->forge->addColumn('podcasts', [
'is_published_on_hubs' => [
$createQuery = <<<CODE_SAMPLE 'type' => 'BOOLEAN',
ALTER TABLE {$prefix}podcasts 'null' => false,
ADD COLUMN `is_published_on_hubs` BOOLEAN NOT NULL DEFAULT 0 AFTER `custom_rss`; 'default' => 0,
CODE_SAMPLE; 'after' => 'custom_rss',
],
$this->db->query($createQuery); ]);
} }
public function down(): void public function down(): void

View File

@ -16,14 +16,14 @@ class AddIsPublishedOnHubsToEpisodes extends BaseMigration
{ {
public function up(): void public function up(): void
{ {
$prefix = $this->db->getPrefix(); $this->forge->addColumn('episodes', [
'is_published_on_hubs' => [
$createQuery = <<<CODE_SAMPLE 'type' => 'BOOLEAN',
ALTER TABLE {$prefix}episodes 'null' => false,
ADD COLUMN `is_published_on_hubs` BOOLEAN NOT NULL DEFAULT 0 AFTER `custom_rss`; 'default' => 0,
CODE_SAMPLE; 'after' => 'custom_rss',
],
$this->db->query($createQuery); ]);
} }
public function down(): void public function down(): void

View File

@ -2,7 +2,6 @@
declare(strict_types=1); declare(strict_types=1);
use Rector\CodeQuality\Rector\PropertyFetch\ExplicitMethodCallOverMagicGetSetRector;
use Rector\CodingStyle\Rector\ClassMethod\UnSpreadOperatorRector; use Rector\CodingStyle\Rector\ClassMethod\UnSpreadOperatorRector;
use Rector\CodingStyle\Rector\Encapsed\EncapsedStringsToSprintfRector; use Rector\CodingStyle\Rector\Encapsed\EncapsedStringsToSprintfRector;
use Rector\CodingStyle\Rector\Stmt\NewlineAfterStatementRector; use Rector\CodingStyle\Rector\Stmt\NewlineAfterStatementRector;
@ -13,7 +12,6 @@ use Rector\DeadCode\Rector\If_\UnwrapFutureCompatibleIfPhpVersionRector;
use Rector\DeadCode\Rector\Stmt\RemoveUnreachableStatementRector; use Rector\DeadCode\Rector\Stmt\RemoveUnreachableStatementRector;
use Rector\EarlyReturn\Rector\If_\ChangeAndIfToEarlyReturnRector; use Rector\EarlyReturn\Rector\If_\ChangeAndIfToEarlyReturnRector;
use Rector\EarlyReturn\Rector\If_\ChangeOrIfContinueToMultiContinueRector; use Rector\EarlyReturn\Rector\If_\ChangeOrIfContinueToMultiContinueRector;
use Rector\EarlyReturn\Rector\If_\ChangeOrIfReturnToEarlyReturnRector;
use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector; use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
use Rector\Php71\Rector\FuncCall\RemoveExtraParametersRector; use Rector\Php71\Rector\FuncCall\RemoveExtraParametersRector;
use Rector\Set\ValueObject\SetList; use Rector\Set\ValueObject\SetList;
@ -49,11 +47,9 @@ return static function (RectorConfig $rectorConfig): void {
__DIR__ . '/modules/Admin/Language/*/PersonsTaxonomy.php', __DIR__ . '/modules/Admin/Language/*/PersonsTaxonomy.php',
// skip rules from used sets // skip rules from used sets
ChangeOrIfReturnToEarlyReturnRector::class,
ChangeOrIfContinueToMultiContinueRector::class, ChangeOrIfContinueToMultiContinueRector::class,
EncapsedStringsToSprintfRector::class, EncapsedStringsToSprintfRector::class,
UnSpreadOperatorRector::class, UnSpreadOperatorRector::class,
ExplicitMethodCallOverMagicGetSetRector::class,
RemoveExtraParametersRector::class, RemoveExtraParametersRector::class,
UnwrapFutureCompatibleIfPhpVersionRector::class, UnwrapFutureCompatibleIfPhpVersionRector::class,

View File

@ -31,9 +31,9 @@ $userPodcasts = get_podcasts_user_can_interact_with(auth()->user()); ?>
$items = [ $items = [
[ [
'type' => 'html', 'type' => 'html',
'content' => esc(<<<CODE_SAMPLE 'content' => esc(<<<HTML
<span class="px-4 my-2 text-xs font-semibold tracking-wider uppercase text-skin-muted">{$notificationsTitle}</span> <span class="px-4 my-2 text-xs font-semibold tracking-wider uppercase text-skin-muted">{$notificationsTitle}</span>
CODE_SAMPLE), HTML),
], ],
]; ];
@ -45,7 +45,7 @@ if ($userPodcasts !== []) {
$items[] = [ $items[] = [
'type' => 'link', 'type' => 'link',
'title' => <<<CODE_SAMPLE 'title' => <<<HTML
<div class="inline-flex items-center flex-1 text-sm align-middle"> <div class="inline-flex items-center flex-1 text-sm align-middle">
<div class="relative"> <div class="relative">
<img src="{$userPodcast->cover->tiny_url}" class="w-6 h-6 mr-2 rounded-full" loading="lazy" /> <img src="{$userPodcast->cover->tiny_url}" class="w-6 h-6 mr-2 rounded-full" loading="lazy" />
@ -53,7 +53,7 @@ if ($userPodcasts !== []) {
</div> </div>
<span class="max-w-xs truncate">{$userPodcastTitle}</span> <span class="max-w-xs truncate">{$userPodcastTitle}</span>
</div> </div>
CODE_SAMPLE HTML
, ,
'uri' => route_to('notification-list', $userPodcast->id), 'uri' => route_to('notification-list', $userPodcast->id),
]; ];
@ -62,9 +62,9 @@ if ($userPodcasts !== []) {
$noNotificationsText = lang('Notifications.no_notifications'); $noNotificationsText = lang('Notifications.no_notifications');
$items[] = [ $items[] = [
'type' => 'html', 'type' => 'html',
'content' => esc(<<<CODE_SAMPLE 'content' => esc(<<<HTML
<span class="mx-4 my-2 text-sm italic text-center text-skin-muted">{$noNotificationsText}</span> <span class="mx-4 my-2 text-sm italic text-center text-skin-muted">{$noNotificationsText}</span>
CODE_SAMPLE), HTML),
]; ];
} }
?> ?>
@ -90,11 +90,11 @@ foreach ($userPodcasts as $userPodcast) {
$checkMark = interact_as_actor_id() === $userPodcast->actor_id ? icon('check', 'ml-2 bg-accent-base text-accent-contrast rounded-full') : ''; $checkMark = interact_as_actor_id() === $userPodcast->actor_id ? icon('check', 'ml-2 bg-accent-base text-accent-contrast rounded-full') : '';
$userPodcastTitle = esc($userPodcast->title); $userPodcastTitle = esc($userPodcast->title);
$interactButtons .= <<<CODE_SAMPLE $interactButtons .= <<<HTML
<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}"> <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="max-w-xs truncate">{$userPodcastTitle}</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> </button>
CODE_SAMPLE; HTML;
} }
$interactAsText = lang('Common.choose_interact'); $interactAsText = lang('Common.choose_interact');
@ -126,7 +126,7 @@ if ($userPodcasts !== []) {
$menuItems = array_merge([ $menuItems = array_merge([
[ [
'type' => 'html', 'type' => 'html',
'content' => esc(<<<CODE_SAMPLE 'content' => esc(<<<HTML
<nav class="flex flex-col py-2 whitespace-nowrap"> <nav class="flex flex-col py-2 whitespace-nowrap">
<span class="px-4 mb-2 text-xs font-semibold tracking-wider uppercase text-skin-muted">{$interactAsText}</span> <span class="px-4 mb-2 text-xs font-semibold tracking-wider uppercase text-skin-muted">{$interactAsText}</span>
<form action="{$interactAsRoute}" method="POST" class="flex flex-col"> <form action="{$interactAsRoute}" method="POST" class="flex flex-col">
@ -134,7 +134,7 @@ if ($userPodcasts !== []) {
{$interactButtons} {$interactButtons}
</form> </form>
</nav> </nav>
CODE_SAMPLE), HTML),
], ],
[ [
'type' => 'separator', 'type' => 'separator',

View File

@ -66,9 +66,9 @@ if ($episode->published_at === null) {
$title = lang('Episode.messages.unpublishBeforeDeleteTip'); $title = lang('Episode.messages.unpublishBeforeDeleteTip');
$items[] = [ $items[] = [
'type' => 'html', 'type' => 'html',
'content' => esc(<<<CODE_SAMPLE 'content' => esc(<<<HTML
<span class="inline-flex items-center px-4 py-1 font-semibold text-gray-400 cursor-not-allowed" data-tooltip="bottom" title="{$title}">{$icon}{$label}</span> <span class="inline-flex items-center px-4 py-1 font-semibold text-gray-400 cursor-not-allowed" data-tooltip="bottom" title="{$title}">{$icon}{$label}</span>
CODE_SAMPLE), HTML),
]; ];
} ?> } ?>
<DropdownMenu id="more-dropdown-<?= $episode->id ?>-menu" labelledby="more-dropdown-<?= $episode->id ?>" offsetY="-32" items="<?= esc(json_encode($items)) ?>" /> <DropdownMenu id="more-dropdown-<?= $episode->id ?>-menu" labelledby="more-dropdown-<?= $episode->id ?>" offsetY="-32" items="<?= esc(json_encode($items)) ?>" />

View File

@ -149,9 +149,9 @@ data_table(
$title = lang('Episode.messages.unpublishBeforeDeleteTip'); $title = lang('Episode.messages.unpublishBeforeDeleteTip');
$items[] = [ $items[] = [
'type' => 'html', 'type' => 'html',
'content' => esc(<<<CODE_SAMPLE 'content' => esc(<<<HTML
<span class="inline-flex items-center px-4 py-1 font-semibold text-gray-400 cursor-not-allowed" data-tooltip="bottom" title="{$title}">{$icon}<span class="ml-2">{$label}</span></span> <span class="inline-flex items-center px-4 py-1 font-semibold text-gray-400 cursor-not-allowed" data-tooltip="bottom" title="{$title}">{$icon}<span class="ml-2">{$label}</span></span>
CODE_SAMPLE), HTML),
]; ];
} }
return '<button id="more-dropdown-' . $episode->id . '" type="button" class="inline-flex items-center p-1 rounded-full focus:ring-accent" data-dropdown="button" data-dropdown-target="more-dropdown-' . $episode->id . '-menu" aria-haspopup="true" aria-expanded="false">' . return '<button id="more-dropdown-' . $episode->id . '" type="button" class="inline-flex items-center p-1 rounded-full focus:ring-accent" data-dropdown="button" data-dropdown-target="more-dropdown-' . $episode->id . '-menu" aria-haspopup="true" aria-expanded="false">' .

View File

@ -31,15 +31,15 @@
? '' ? ''
: '@' . esc($notification->actor->domain)); : '@' . esc($notification->actor->domain));
$actorUsernameHtml = <<<CODE_SAMPLE $actorUsernameHtml = <<<HTML
<strong class="break-all">{$actorUsername}</strong> <strong class="break-all">{$actorUsername}</strong>
CODE_SAMPLE; HTML;
$targetActorUsername = '@' . esc($notification->target_actor->username); $targetActorUsername = '@' . esc($notification->target_actor->username);
$targetActorUsernameHtml = <<<CODE_SAMPLE $targetActorUsernameHtml = <<<HTML
<strong class="break-all">{$targetActorUsername}</strong> <strong class="break-all">{$targetActorUsername}</strong>
CODE_SAMPLE; HTML;
$notificationTitle = match ($notification->type) { $notificationTitle = match ($notification->type) {
'reply' => lang('Notifications.reply', [ 'reply' => lang('Notifications.reply', [

View File

@ -26,9 +26,9 @@ $userPodcasts = get_podcasts_user_can_interact_with(auth()->user()); ?>
$items = [ $items = [
[ [
'type' => 'html', 'type' => 'html',
'content' => esc(<<<CODE_SAMPLE 'content' => esc(<<<HTML
<span class="px-4 my-2 text-xs font-semibold tracking-wider uppercase text-skin-muted">{$notificationsTitle}</span> <span class="px-4 my-2 text-xs font-semibold tracking-wider uppercase text-skin-muted">{$notificationsTitle}</span>
CODE_SAMPLE), HTML),
], ],
]; ];
@ -40,7 +40,7 @@ if ($userPodcasts !== []) {
$items[] = [ $items[] = [
'type' => 'link', 'type' => 'link',
'title' => <<<CODE_SAMPLE 'title' => <<<HTML
<div class="inline-flex items-center flex-1 text-sm align-middle"> <div class="inline-flex items-center flex-1 text-sm align-middle">
<div class="relative"> <div class="relative">
<img src="{$userPodcast->cover->tiny_url}" class="w-6 h-6 mr-2 rounded-full" loading="lazy" /> <img src="{$userPodcast->cover->tiny_url}" class="w-6 h-6 mr-2 rounded-full" loading="lazy" />
@ -48,7 +48,7 @@ if ($userPodcasts !== []) {
</div> </div>
<span class="max-w-xs truncate">{$userPodcastTitle}</span> <span class="max-w-xs truncate">{$userPodcastTitle}</span>
</div> </div>
CODE_SAMPLE HTML
, ,
'uri' => route_to('notification-list', $userPodcast->id), 'uri' => route_to('notification-list', $userPodcast->id),
]; ];
@ -57,9 +57,9 @@ if ($userPodcasts !== []) {
$noNotificationsText = lang('Notifications.no_notifications'); $noNotificationsText = lang('Notifications.no_notifications');
$items[] = [ $items[] = [
'type' => 'html', 'type' => 'html',
'content' => esc(<<<CODE_SAMPLE 'content' => esc(<<<HTML
<span class="mx-4 my-2 text-sm italic text-center text-skin-muted">{$noNotificationsText}</span> <span class="mx-4 my-2 text-sm italic text-center text-skin-muted">{$noNotificationsText}</span>
CODE_SAMPLE), HTML),
]; ];
} }
?> ?>
@ -90,11 +90,11 @@ foreach ($userPodcasts as $userPodcast) {
$checkMark = interact_as_actor_id() === $userPodcast->actor_id ? icon('check', 'ml-2 bg-accent-base text-accent-contrast rounded-full') : ''; $checkMark = interact_as_actor_id() === $userPodcast->actor_id ? icon('check', 'ml-2 bg-accent-base text-accent-contrast rounded-full') : '';
$userPodcastTitle = esc($userPodcast->title); $userPodcastTitle = esc($userPodcast->title);
$interactButtons .= <<<CODE_SAMPLE $interactButtons .= <<<HTML
<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}"> <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="max-w-xs truncate">{$userPodcastTitle}</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> </button>
CODE_SAMPLE; HTML;
} }
} }
@ -127,7 +127,7 @@ if ($userPodcasts !== []) {
$menuItems = array_merge([ $menuItems = array_merge([
[ [
'type' => 'html', 'type' => 'html',
'content' => esc(<<<CODE_SAMPLE 'content' => esc(<<<HTML
<nav class="flex flex-col py-2 whitespace-nowrap"> <nav class="flex flex-col py-2 whitespace-nowrap">
<span class="px-4 mb-2 text-xs font-semibold tracking-wider uppercase text-skin-muted">{$interactAsText}</span> <span class="px-4 mb-2 text-xs font-semibold tracking-wider uppercase text-skin-muted">{$interactAsText}</span>
<form action="{$interactAsRoute}" method="POST" class="flex flex-col"> <form action="{$interactAsRoute}" method="POST" class="flex flex-col">
@ -135,7 +135,7 @@ if ($userPodcasts !== []) {
{$interactButtons} {$interactButtons}
</form> </form>
</nav> </nav>
CODE_SAMPLE), HTML),
], ],
[ [
'type' => 'separator', 'type' => 'separator',