mirror of
https://code.castopod.org/adaures/castopod
synced 2025-04-19 13:01:19 +00:00
fix: replace deletedField with published_at for episodes
- remove delete_at field + soft delete for media and pages - update CodeIgniter4 to 4.2.0 + update all starter files - explicitly use builder() when creating queries from model
This commit is contained in:
parent
dbb4030da4
commit
14d7d07822
@ -293,7 +293,7 @@ class App extends BaseConfig
|
||||
* (empty string) means default SameSite attribute set by browsers (`Lax`)
|
||||
* will be set on cookies. If set to `None`, `$cookieSecure` must also be set.
|
||||
*
|
||||
* @deprecated use Config\Cookie::$samesite property instead.
|
||||
* @deprecated `Config\Cookie` $samesite property is used.
|
||||
*/
|
||||
public string $cookieSameSite = 'Lax';
|
||||
|
||||
|
@ -52,9 +52,9 @@ defined('MINUTE') || define('MINUTE', 60);
|
||||
defined('HOUR') || define('HOUR', 3600);
|
||||
defined('DAY') || define('DAY', 86400);
|
||||
defined('WEEK') || define('WEEK', 604800);
|
||||
defined('MONTH') || define('MONTH', 2592000);
|
||||
defined('YEAR') || define('YEAR', 31536000);
|
||||
defined('DECADE') || define('DECADE', 315360000);
|
||||
defined('MONTH') || define('MONTH', 2_592_000);
|
||||
defined('YEAR') || define('YEAR', 31_536_000);
|
||||
defined('DECADE') || define('DECADE', 315_360_000);
|
||||
|
||||
/*
|
||||
| --------------------------------------------------------------------------
|
||||
@ -91,3 +91,18 @@ defined('EXIT_USER_INPUT') || define('EXIT_USER_INPUT', 7); // invalid user inpu
|
||||
defined('EXIT_DATABASE') || define('EXIT_DATABASE', 8); // database error
|
||||
defined('EXIT__AUTO_MIN') || define('EXIT__AUTO_MIN', 9); // lowest automatically-assigned error code
|
||||
defined('EXIT__AUTO_MAX') || define('EXIT__AUTO_MAX', 125); // highest automatically-assigned error code
|
||||
|
||||
/**
|
||||
* @deprecated Use \CodeIgniter\Events\Events::PRIORITY_LOW instead.
|
||||
*/
|
||||
define('EVENT_PRIORITY_LOW', 200);
|
||||
|
||||
/**
|
||||
* @deprecated Use \CodeIgniter\Events\Events::PRIORITY_NORMAL instead.
|
||||
*/
|
||||
define('EVENT_PRIORITY_NORMAL', 100);
|
||||
|
||||
/**
|
||||
* @deprecated Use \CodeIgniter\Events\Events::PRIORITY_HIGH instead.
|
||||
*/
|
||||
define('EVENT_PRIORITY_HIGH', 10);
|
||||
|
@ -145,4 +145,19 @@ class ContentSecurityPolicy extends BaseConfig
|
||||
* @var string|string[]|null
|
||||
*/
|
||||
public string | array | null $sandbox = null;
|
||||
|
||||
/**
|
||||
* Nonce tag for style
|
||||
*/
|
||||
public string $styleNonceTag = '{csp-style-nonce}';
|
||||
|
||||
/**
|
||||
* Nonce tag for script
|
||||
*/
|
||||
public string $scriptNonceTag = '{csp-script-nonce}';
|
||||
|
||||
/**
|
||||
* Replace nonce tag automatically
|
||||
*/
|
||||
public bool $autoNonce = true;
|
||||
}
|
||||
|
@ -61,8 +61,9 @@ class Database extends Config
|
||||
'database' => ':memory:',
|
||||
'DBDriver' => 'SQLite3',
|
||||
'DBPrefix' => 'db_',
|
||||
// Needed to ensure we're working correctly with prefixes live. DO NOT REMOVE FOR CI DEVS
|
||||
'pConnect' => false,
|
||||
'DBDebug' => ENVIRONMENT !== 'production',
|
||||
'DBDebug' => (ENVIRONMENT !== 'production'),
|
||||
'charset' => 'utf8',
|
||||
'DBCollat' => 'utf8_general_ci',
|
||||
'swapPre' => '',
|
||||
@ -71,6 +72,7 @@ class Database extends Config
|
||||
'strictOn' => false,
|
||||
'failover' => [],
|
||||
'port' => 3306,
|
||||
'foreignKeys' => true,
|
||||
];
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
@ -39,9 +39,7 @@ Events::on('pre_system', function () {
|
||||
ob_end_flush();
|
||||
}
|
||||
|
||||
ob_start(function ($buffer) {
|
||||
return $buffer;
|
||||
});
|
||||
ob_start(static fn ($buffer) => $buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -132,11 +130,11 @@ Events::on('on_post_add', function ($post): void {
|
||||
|
||||
if ($post->episode_id !== null) {
|
||||
if ($isReply) {
|
||||
model(EpisodeModel::class, false)
|
||||
model(EpisodeModel::class, false)->builder()
|
||||
->where('id', $post->episode_id)
|
||||
->increment('comments_count');
|
||||
} else {
|
||||
model(EpisodeModel::class, false)
|
||||
model(EpisodeModel::class, false)->builder()
|
||||
->where('id', $post->episode_id)
|
||||
->increment('posts_count');
|
||||
}
|
||||
@ -161,7 +159,7 @@ Events::on('on_post_remove', function ($post): void {
|
||||
}
|
||||
|
||||
if ($episodeId = $post->episode_id) {
|
||||
model(EpisodeModel::class, false)
|
||||
model(EpisodeModel::class, false)->builder()
|
||||
->where('id', $episodeId)
|
||||
->decrement('posts_count');
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ use CodeIgniter\Config\BaseConfig;
|
||||
class Feature extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* Enable multiple filters for a route or not
|
||||
* Enable multiple filters for a route or not.
|
||||
*
|
||||
* If you enable this:
|
||||
* - CodeIgniter\CodeIgniter::handleRequest() uses:
|
||||
@ -24,4 +24,9 @@ class Feature extends BaseConfig
|
||||
* - CodeIgniter\Router\RouteCollection::getFiltersForRoute(), instead of getFilterForRoute()
|
||||
*/
|
||||
public bool $multipleFilters = false;
|
||||
|
||||
/**
|
||||
* Use improved new auto routing instead of the default legacy version.
|
||||
*/
|
||||
public bool $autoRoutesImproved = false;
|
||||
}
|
||||
|
@ -59,7 +59,10 @@ class Filters extends BaseConfig
|
||||
/**
|
||||
* List of filter aliases that works on a particular HTTP method (GET, POST, etc.).
|
||||
*
|
||||
* Example: 'post' => ['csrf', 'throttle']
|
||||
* Example: 'post' => ['foo', 'bar']
|
||||
*
|
||||
* If you use this, you should disable auto-routing because auto-routing permits any HTTP method to access a
|
||||
* controller. Accessing the controller with a method you don’t expect could bypass the filter.
|
||||
*
|
||||
* @var array<string, string[]>
|
||||
*/
|
||||
|
@ -169,6 +169,7 @@ class Mimes
|
||||
'mj2' => ['image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'],
|
||||
'mjp2' => ['image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'],
|
||||
'png' => ['image/png', 'image/x-png'],
|
||||
'webp' => 'image/webp',
|
||||
'tif' => 'image/tiff',
|
||||
'tiff' => 'image/tiff',
|
||||
'css' => ['text/css', 'text/plain'],
|
||||
|
@ -14,4 +14,15 @@ use CodeIgniter\Config\Publisher as BasePublisher;
|
||||
*/
|
||||
class Publisher extends BasePublisher
|
||||
{
|
||||
/**
|
||||
* A list of allowed destinations with a (pseudo-)regex of allowed files for each destination. Attempts to publish
|
||||
* to directories not in this list will result in a PublisherException. Files that do no fit the pattern will cause
|
||||
* copy/merge to fail.
|
||||
*
|
||||
* @var array<string,string>
|
||||
*/
|
||||
public $restrictions = [
|
||||
ROOTPATH => '*',
|
||||
FCPATH => '#\.(s?css|js|map|html?|xml|json|webmanifest|ttf|eot|woff2?|gif|jpe?g|tiff?|png|webp|bmp|ico|svg)$#i',
|
||||
];
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ $routes = Services::routes();
|
||||
|
||||
// Load the system's routing file first, so that the app and ENVIRONMENT
|
||||
// can override as needed.
|
||||
if (file_exists(SYSTEMPATH . 'Config/Routes.php')) {
|
||||
if (is_file(SYSTEMPATH . 'Config/Routes.php')) {
|
||||
require SYSTEMPATH . 'Config/Routes.php';
|
||||
}
|
||||
|
||||
@ -23,6 +23,11 @@ $routes->setDefaultController('Home');
|
||||
$routes->setDefaultMethod('index');
|
||||
$routes->setTranslateURIDashes(false);
|
||||
$routes->set404Override();
|
||||
|
||||
// The Auto Routing (Legacy) is very dangerous. It is easy to create vulnerable apps
|
||||
// where controller filters or CSRF protection are bypassed.
|
||||
// If you don't want to define all routes, please use the Auto Routing (Improved).
|
||||
// Set `$autoRoutesImproved` to true in `app/Config/Feature.php` and set the following to true.
|
||||
$routes->setAutoRoute(false);
|
||||
|
||||
/**
|
||||
@ -303,6 +308,6 @@ $routes->group('@(:podcastHandle)', function ($routes): void {
|
||||
* You will have access to the $routes object within that file without
|
||||
* needing to reload it.
|
||||
*/
|
||||
if (file_exists(APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php')) {
|
||||
if (is_file(APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php')) {
|
||||
require APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php';
|
||||
}
|
||||
|
@ -83,4 +83,23 @@ class Security extends BaseConfig
|
||||
* Redirect to previous page with error on failure.
|
||||
*/
|
||||
public bool $redirect = true;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* CSRF SameSite
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Setting for CSRF SameSite cookie token.
|
||||
*
|
||||
* Allowed values are: None - Lax - Strict - ''.
|
||||
*
|
||||
* Defaults to `Lax` as recommended in this link:
|
||||
*
|
||||
* @see https://portswigger.net/web-security/csrf/samesite-cookies
|
||||
*
|
||||
* @var string
|
||||
*
|
||||
* @deprecated `Config\Cookie` $samesite property is used.
|
||||
*/
|
||||
public $samesite = 'Lax';
|
||||
}
|
||||
|
@ -6,13 +6,14 @@ namespace Config;
|
||||
|
||||
use App\Validation\FileRules as AppFileRules;
|
||||
use App\Validation\Rules as AppRules;
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
use CodeIgniter\Validation\CreditCardRules;
|
||||
use CodeIgniter\Validation\FileRules;
|
||||
use CodeIgniter\Validation\FormatRules;
|
||||
use CodeIgniter\Validation\Rules;
|
||||
use Myth\Auth\Authentication\Passwords\ValidationRules as PasswordRules;
|
||||
|
||||
class Validation
|
||||
class Validation extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* Stores the classes that contain the rules that are available.
|
||||
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\View as BaseView;
|
||||
use CodeIgniter\View\ViewDecoratorInterface;
|
||||
|
||||
class View extends BaseView
|
||||
{
|
||||
@ -36,4 +37,14 @@ class View extends BaseView
|
||||
* @var string[]
|
||||
*/
|
||||
public $plugins = [];
|
||||
|
||||
/**
|
||||
* View Decorators are class methods that will be run in sequence to have a chance to alter the generated output
|
||||
* just prior to caching the results.
|
||||
*
|
||||
* All classes must implement CodeIgniter\View\ViewDecoratorInterface
|
||||
*
|
||||
* @var class-string<ViewDecoratorInterface>[]
|
||||
*/
|
||||
public array $decorators = [];
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ use ViewThemes\Theme;
|
||||
*
|
||||
* For security be sure to declare any new methods as protected or private.
|
||||
*/
|
||||
class BaseController extends Controller
|
||||
abstract class BaseController extends Controller
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
|
@ -67,10 +67,6 @@ class AddMedia extends Migration
|
||||
'updated_at' => [
|
||||
'type' => 'DATETIME',
|
||||
],
|
||||
'deleted_at' => [
|
||||
'type' => 'DATETIME',
|
||||
'null' => true,
|
||||
],
|
||||
]);
|
||||
|
||||
$this->forge->addKey('id', true);
|
||||
|
@ -45,10 +45,6 @@ class AddPages extends Migration
|
||||
'updated_at' => [
|
||||
'type' => 'DATETIME',
|
||||
],
|
||||
'deleted_at' => [
|
||||
'type' => 'DATETIME',
|
||||
'null' => true,
|
||||
],
|
||||
]);
|
||||
$this->forge->addPrimaryKey('id');
|
||||
$this->forge->createTable('pages');
|
||||
|
@ -136,7 +136,7 @@ class Episode extends Entity
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $dates = ['published_at', 'created_at', 'updated_at', 'deleted_at'];
|
||||
protected $dates = ['published_at', 'created_at', 'updated_at'];
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
|
@ -96,12 +96,13 @@ class EpisodeComment extends UuidEntity
|
||||
throw new RuntimeException('Comment must have an actor_id before getting actor.');
|
||||
}
|
||||
|
||||
if ($this->actor === null) {
|
||||
if (! $this->actor instanceof Actor) {
|
||||
// @phpstan-ignore-next-line
|
||||
$this->actor = model(ActorModel::class, false)
|
||||
->getActorById($this->actor_id);
|
||||
}
|
||||
|
||||
// @phpstan-ignore-next-line
|
||||
return $this->actor;
|
||||
}
|
||||
|
||||
@ -135,7 +136,7 @@ class EpisodeComment extends UuidEntity
|
||||
throw new RuntimeException('Comment is not a reply.');
|
||||
}
|
||||
|
||||
if ($this->reply_to_comment === null) {
|
||||
if (! $this->reply_to_comment instanceof self) {
|
||||
$this->reply_to_comment = model(EpisodeCommentModel::class, false)
|
||||
->getCommentById($this->in_reply_to_id);
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ class BaseMedia extends Entity
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $dates = ['uploaded_at', 'updated_at', 'deleted_at'];
|
||||
protected $dates = ['uploaded_at', 'updated_at'];
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
@ -112,6 +112,6 @@ class BaseMedia extends Entity
|
||||
public function delete(): bool|BaseResult
|
||||
{
|
||||
$mediaModel = new MediaModel();
|
||||
return $mediaModel->delete($this->id, true);
|
||||
return $mediaModel->delete($this->id);
|
||||
}
|
||||
}
|
||||
|
@ -197,12 +197,13 @@ class Podcast extends Entity
|
||||
throw new RuntimeException('Podcast must have an actor_id before getting actor.');
|
||||
}
|
||||
|
||||
if ($this->actor === null) {
|
||||
if (! $this->actor instanceof Actor) {
|
||||
// @phpstan-ignore-next-line
|
||||
$this->actor = model(ActorModel::class, false)
|
||||
->getActorById($this->actor_id);
|
||||
}
|
||||
|
||||
// @phpstan-ignore-next-line
|
||||
return $this->actor;
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,7 @@ class ClipModel extends Model
|
||||
|
||||
public function getRunningVideoClipsCount(): int
|
||||
{
|
||||
$result = $this
|
||||
$result = $this->builder()
|
||||
->select('COUNT(*) as `running_count`')
|
||||
->where([
|
||||
'type' => 'video',
|
||||
@ -146,6 +146,7 @@ class ClipModel extends Model
|
||||
public function doesVideoClipExist(VideoClip $videoClip): int | false
|
||||
{
|
||||
$result = $this->select('id')
|
||||
->builder()
|
||||
->where([
|
||||
'podcast_id' => $videoClip->podcast_id,
|
||||
'episode_id' => $videoClip->episode_id,
|
||||
|
@ -13,6 +13,7 @@ namespace App\Models;
|
||||
use App\Entities\EpisodeComment;
|
||||
use App\Libraries\CommentObject;
|
||||
use CodeIgniter\Database\BaseBuilder;
|
||||
use CodeIgniter\Database\BaseResult;
|
||||
use Michalsn\Uuid\UuidModel;
|
||||
use Modules\Fediverse\Activities\CreateActivity;
|
||||
use Modules\Fediverse\Models\ActivityModel;
|
||||
@ -82,11 +83,11 @@ class EpisodeCommentModel extends UuidModel
|
||||
}
|
||||
|
||||
if ($comment->in_reply_to_id === null) {
|
||||
(new EpisodeModel())
|
||||
(new EpisodeModel())->builder()
|
||||
->where('id', $comment->episode_id)
|
||||
->increment('comments_count');
|
||||
} else {
|
||||
(new self())
|
||||
(new self())->builder()
|
||||
->where('id', service('uuid')->fromString($comment->in_reply_to_id)->getBytes())
|
||||
->increment('replies_count');
|
||||
}
|
||||
@ -146,17 +147,19 @@ class EpisodeCommentModel extends UuidModel
|
||||
public function getEpisodeComments(int $episodeId): array
|
||||
{
|
||||
// TODO: merge with replies from posts linked to episode linked
|
||||
$episodeComments = $this->select('*, 0 as is_from_post')
|
||||
$episodeCommentsBuilder = $this->builder();
|
||||
$episodeComments = $episodeCommentsBuilder->select('*, 0 as is_from_post')
|
||||
->where([
|
||||
'episode_id' => $episodeId,
|
||||
'in_reply_to_id' => null,
|
||||
])
|
||||
->getCompiledSelect();
|
||||
|
||||
$episodePostsReplies = $this->db->table(config('Fediverse')->tablesPrefix . 'posts')
|
||||
->select(
|
||||
'id, uri, episode_id, actor_id, in_reply_to_id, message, message_html, favourites_count as likes_count, replies_count, published_at as created_at, created_by, 1 as is_from_post'
|
||||
)
|
||||
$postModel = new PostModel();
|
||||
$episodePostsRepliesBuilder = $postModel->builder();
|
||||
$episodePostsReplies = $episodePostsRepliesBuilder->select(
|
||||
'id, uri, episode_id, actor_id, in_reply_to_id, message, message_html, favourites_count as likes_count, replies_count, published_at as created_at, created_by, 1 as is_from_post'
|
||||
)
|
||||
->whereIn('in_reply_to_id', function (BaseBuilder $builder) use (&$episodeId): BaseBuilder {
|
||||
return $builder->select('id')
|
||||
->from(config('Fediverse')->tablesPrefix . 'posts')
|
||||
@ -168,6 +171,7 @@ class EpisodeCommentModel extends UuidModel
|
||||
->where('`created_at` <= UTC_TIMESTAMP()', null, false)
|
||||
->getCompiledSelect();
|
||||
|
||||
/** @var BaseResult $allEpisodeComments */
|
||||
$allEpisodeComments = $this->db->query(
|
||||
$episodeComments . ' UNION ' . $episodePostsReplies . ' ORDER BY created_at ASC'
|
||||
);
|
||||
@ -211,7 +215,8 @@ class EpisodeCommentModel extends UuidModel
|
||||
|
||||
public function resetRepliesCount(): int | false
|
||||
{
|
||||
$commentsRepliesCount = $this->select('episode_comments.id, COUNT(*) as `replies_count`')
|
||||
$commentsRepliesCount = $this->builder()
|
||||
->select('episode_comments.id, COUNT(*) as `replies_count`')
|
||||
->join('episode_comments as c2', 'episode_comments.id = c2.in_reply_to_id')
|
||||
->groupBy('episode_comments.id')
|
||||
->get()
|
||||
|
@ -11,6 +11,7 @@ declare(strict_types=1);
|
||||
namespace App\Models;
|
||||
|
||||
use App\Entities\Episode;
|
||||
use CodeIgniter\Database\BaseResult;
|
||||
use CodeIgniter\I18n\Time;
|
||||
use CodeIgniter\Model;
|
||||
|
||||
@ -264,7 +265,8 @@ class EpisodeModel extends Model
|
||||
*/
|
||||
public function getSecondsToNextUnpublishedEpisode(int $podcastId): int | false
|
||||
{
|
||||
$result = $this->select('TIMESTAMPDIFF(SECOND, UTC_TIMESTAMP(), `published_at`) as timestamp_diff')
|
||||
$result = $this->builder()
|
||||
->select('TIMESTAMPDIFF(SECOND, UTC_TIMESTAMP(), `published_at`) as timestamp_diff')
|
||||
->where([
|
||||
'podcast_id' => $podcastId,
|
||||
])
|
||||
@ -280,10 +282,11 @@ class EpisodeModel extends Model
|
||||
|
||||
public function getCurrentSeasonNumber(int $podcastId): ?int
|
||||
{
|
||||
$result = $this->select('MAX(season_number) as current_season_number')
|
||||
$result = $this->builder()
|
||||
->select('MAX(season_number) as current_season_number')
|
||||
->where([
|
||||
'podcast_id' => $podcastId,
|
||||
$this->deletedField => null,
|
||||
'published_at IS NOT' => null,
|
||||
])
|
||||
->get()
|
||||
->getResultArray();
|
||||
@ -293,11 +296,12 @@ class EpisodeModel extends Model
|
||||
|
||||
public function getNextEpisodeNumber(int $podcastId, ?int $seasonNumber): int
|
||||
{
|
||||
$result = $this->select('MAX(number) as next_episode_number')
|
||||
$result = $this->builder()
|
||||
->select('MAX(number) as next_episode_number')
|
||||
->where([
|
||||
'podcast_id' => $podcastId,
|
||||
'season_number' => $seasonNumber,
|
||||
$this->deletedField => null,
|
||||
'published_at IS NOT' => null,
|
||||
])->get()
|
||||
->getResultArray();
|
||||
|
||||
@ -309,13 +313,13 @@ class EpisodeModel extends Model
|
||||
*/
|
||||
public function getPodcastStats(int $podcastId): array
|
||||
{
|
||||
$result = $this->select(
|
||||
'COUNT(DISTINCT season_number) as number_of_seasons, COUNT(*) as number_of_episodes, MIN(published_at) as first_published_at'
|
||||
)
|
||||
$result = $this->builder()
|
||||
->select(
|
||||
'COUNT(DISTINCT season_number) as number_of_seasons, COUNT(*) as number_of_episodes, MIN(published_at) as first_published_at'
|
||||
)
|
||||
->where([
|
||||
'podcast_id' => $podcastId,
|
||||
'published_at IS NOT' => null,
|
||||
$this->deletedField => null,
|
||||
])->get()
|
||||
->getResultArray();
|
||||
|
||||
@ -333,13 +337,16 @@ class EpisodeModel extends Model
|
||||
|
||||
public function resetCommentsCount(): int | false
|
||||
{
|
||||
$episodeCommentsCount = $this->select('episodes.id, COUNT(*) as `comments_count`')
|
||||
$episodeCommentsBuilder = $this->builder();
|
||||
$episodeCommentsCount = $episodeCommentsBuilder->select('episodes.id, COUNT(*) as `comments_count`')
|
||||
->join('episode_comments', 'episodes.id = episode_comments.episode_id')
|
||||
->where('in_reply_to_id', null)
|
||||
->groupBy('episodes.id')
|
||||
->getCompiledSelect();
|
||||
|
||||
$episodePostsRepliesCount = $this
|
||||
$postModel = new PostModel();
|
||||
$episodePostsRepliesBuilder = $postModel->builder();
|
||||
$episodePostsRepliesCount = $episodePostsRepliesBuilder
|
||||
->select('episodes.id, COUNT(*) as `comments_count`')
|
||||
->join(
|
||||
config('Fediverse')
|
||||
@ -350,6 +357,7 @@ class EpisodeModel extends Model
|
||||
->groupBy('episodes.id')
|
||||
->getCompiledSelect();
|
||||
|
||||
/** @var BaseResult $query */
|
||||
$query = $this->db->query(
|
||||
'SELECT `id`, SUM(`comments_count`) as `comments_count` FROM (' . $episodeCommentsCount . ' UNION ALL ' . $episodePostsRepliesCount . ') x GROUP BY `id`'
|
||||
);
|
||||
@ -365,7 +373,8 @@ class EpisodeModel extends Model
|
||||
|
||||
public function resetPostsCount(): int | false
|
||||
{
|
||||
$episodePostsCount = $this->select('episodes.id, COUNT(*) as `posts_count`')
|
||||
$episodePostsCount = $this->builder()
|
||||
->select('episodes.id, COUNT(*) as `posts_count`')
|
||||
->join(
|
||||
config('Fediverse')
|
||||
->tablesPrefix . 'posts',
|
||||
|
@ -56,7 +56,7 @@ class LikeModel extends UuidModel
|
||||
'comment_id' => $comment->id,
|
||||
]);
|
||||
|
||||
(new EpisodeCommentModel())
|
||||
(new EpisodeCommentModel())->builder()
|
||||
->where('id', service('uuid')->fromString($comment->id)->getBytes())
|
||||
->increment('likes_count');
|
||||
|
||||
@ -91,7 +91,7 @@ class LikeModel extends UuidModel
|
||||
{
|
||||
$this->db->transStart();
|
||||
|
||||
(new EpisodeCommentModel())
|
||||
(new EpisodeCommentModel())->builder()
|
||||
->where('id', service('uuid') ->fromString($comment->id) ->getBytes())
|
||||
->decrement('likes_count');
|
||||
|
||||
|
@ -36,7 +36,7 @@ class MediaModel extends Model
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $useSoftDeletes = true;
|
||||
protected $useSoftDeletes = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
@ -121,6 +121,7 @@ class MediaModel extends Model
|
||||
'id' => $mediaId,
|
||||
]);
|
||||
|
||||
/** @var object $result */
|
||||
$result = $builder->first();
|
||||
$mediaClass = $this->returnType;
|
||||
$found = new $mediaClass($result->toArray(false, true));
|
||||
@ -176,7 +177,7 @@ class MediaModel extends Model
|
||||
{
|
||||
$media->deleteFile();
|
||||
|
||||
return $this->delete($media->id, true);
|
||||
return $this->delete($media->id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -38,7 +38,7 @@ class PageModel extends Model
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $useSoftDeletes = true;
|
||||
protected $useSoftDeletes = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
|
@ -110,7 +110,7 @@ class PersonModel extends Model
|
||||
$cacheName = "podcast#{$podcastId}_episode#{$episodeId}_person#{$personId}_roles";
|
||||
|
||||
if (! ($found = cache($cacheName))) {
|
||||
$found = $this
|
||||
$found = $this->builder()
|
||||
->select('episodes_persons.person_group as group, episodes_persons.person_role as role')
|
||||
->join('episodes_persons', 'persons.id = episodes_persons.person_id')
|
||||
->where('persons.id', $personId)
|
||||
@ -122,7 +122,7 @@ class PersonModel extends Model
|
||||
$cacheName = "podcast#{$podcastId}_person#{$personId}_roles";
|
||||
|
||||
if (! ($found = cache($cacheName))) {
|
||||
$found = $this
|
||||
$found = $this->builder()
|
||||
->select('podcasts_persons.person_group as group, podcasts_persons.person_role as role')
|
||||
->join('podcasts_persons', 'persons.id = podcasts_persons.person_id')
|
||||
->where('persons.id', $personId)
|
||||
@ -210,15 +210,15 @@ class PersonModel extends Model
|
||||
{
|
||||
$cacheName = "podcast#{$podcastId}_episode#{$episodeId}_persons";
|
||||
if (! ($found = cache($cacheName))) {
|
||||
$found = $this
|
||||
$this->builder()
|
||||
->select(
|
||||
'persons.*, episodes_persons.podcast_id as podcast_id, episodes_persons.episode_id as episode_id'
|
||||
)
|
||||
->distinct()
|
||||
->join('episodes_persons', 'persons.id = episodes_persons.person_id')
|
||||
->where('episodes_persons.episode_id', $episodeId)
|
||||
->orderby('persons.full_name')
|
||||
->findAll();
|
||||
->orderby('persons.full_name');
|
||||
$found = $this->findAll();
|
||||
|
||||
cache()
|
||||
->save($cacheName, $found, DECADE);
|
||||
@ -234,13 +234,13 @@ class PersonModel extends Model
|
||||
{
|
||||
$cacheName = "podcast#{$podcastId}_persons";
|
||||
if (! ($found = cache($cacheName))) {
|
||||
$found = $this
|
||||
$this->builder()
|
||||
->select('persons.*, podcasts_persons.podcast_id as podcast_id')
|
||||
->distinct()
|
||||
->join('podcasts_persons', 'persons.id=podcasts_persons.person_id')
|
||||
->where('podcasts_persons.podcast_id', $podcastId)
|
||||
->orderby('persons.full_name')
|
||||
->findAll();
|
||||
->orderby('persons.full_name');
|
||||
$found = $this->findAll();
|
||||
|
||||
cache()
|
||||
->save($cacheName, $found, DECADE);
|
||||
|
@ -175,9 +175,10 @@ class PodcastModel extends Model
|
||||
|
||||
$fediverseTablePrefix = $prefix . config('Fediverse')
|
||||
->tablesPrefix;
|
||||
$this->select(
|
||||
'podcasts.*, MAX(' . $fediverseTablePrefix . 'posts.published_at' . ') as max_published_at'
|
||||
)
|
||||
$this->builder()
|
||||
->select(
|
||||
'podcasts.*, MAX(' . $fediverseTablePrefix . 'posts.published_at' . ') as max_published_at'
|
||||
)
|
||||
->join(
|
||||
$fediverseTablePrefix . 'posts',
|
||||
$fediverseTablePrefix . 'posts.actor_id = podcasts.actor_id',
|
||||
@ -302,11 +303,12 @@ class PodcastModel extends Model
|
||||
if (! ($found = cache($cacheName))) {
|
||||
$episodeModel = new EpisodeModel();
|
||||
$found = $episodeModel
|
||||
->builder()
|
||||
->select('YEAR(published_at) as year, count(*) as number_of_episodes')
|
||||
->where([
|
||||
'podcast_id' => $podcastId,
|
||||
'season_number' => null,
|
||||
$episodeModel->deletedField => null,
|
||||
'published_at IS NOT' => null,
|
||||
])
|
||||
->where('`published_at` <= UTC_TIMESTAMP()', null, false)
|
||||
->groupBy('year')
|
||||
@ -338,11 +340,12 @@ class PodcastModel extends Model
|
||||
if (! ($found = cache($cacheName))) {
|
||||
$episodeModel = new EpisodeModel();
|
||||
$found = $episodeModel
|
||||
->builder()
|
||||
->select('season_number, count(*) as number_of_episodes')
|
||||
->where([
|
||||
'podcast_id' => $podcastId,
|
||||
'season_number is not' => null,
|
||||
$episodeModel->deletedField => null,
|
||||
'published_at IS NOT' => null,
|
||||
])
|
||||
->where('`published_at` <= UTC_TIMESTAMP()', null, false)
|
||||
->groupBy('season_number')
|
||||
|
@ -32,7 +32,7 @@ $error_id = uniqid('error', true); ?>
|
||||
|
||||
<!-- Source -->
|
||||
<div class="container">
|
||||
<p><b><?= esc(static::cleanPath($file, $line)) ?></b> at line <b><?= esc($line) ?></b></p>
|
||||
<p><b><?= esc(clean_path($file, $line)) ?></b> at line <b><?= esc($line) ?></b></p>
|
||||
|
||||
<?php if (is_file($file)) : ?>
|
||||
<div class="source">
|
||||
@ -66,9 +66,9 @@ $error_id = uniqid('error', true); ?>
|
||||
<?php if (isset($row['file']) && is_file($row['file'])) :?>
|
||||
<?php
|
||||
if (isset($row['function']) && in_array($row['function'], ['include', 'include_once', 'require', 'require_once'], true)) {
|
||||
echo esc($row['function'] . ' ' . static::cleanPath($row['file']));
|
||||
echo esc($row['function'] . ' ' . clean_path($row['file']));
|
||||
} else {
|
||||
echo esc(static::cleanPath($row['file']) . ' : ' . $row['line']);
|
||||
echo esc(clean_path($row['file']) . ' : ' . $row['line']);
|
||||
}
|
||||
?>
|
||||
<?php else : ?>
|
||||
@ -201,7 +201,7 @@ $error_id = uniqid('error', true); ?>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>HTTP Method</td>
|
||||
<td><?= esc($request->getMethod(true)) ?></td>
|
||||
<td><?= esc(strtoupper($request->getMethod())) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IP Address</td>
|
||||
@ -316,7 +316,7 @@ $error_id = uniqid('error', true); ?>
|
||||
<table>
|
||||
<tr>
|
||||
<td style="width: 15em">Response Status</td>
|
||||
<td><?= esc($response->getStatusCode() . ' - ' . $response->getReason()) ?></td>
|
||||
<td><?= esc($response->getStatusCode() . ' - ' . $response->getReasonPhrase()) ?></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@ -352,7 +352,7 @@ $error_id = uniqid('error', true); ?>
|
||||
|
||||
<ol>
|
||||
<?php foreach ($files as $file) :?>
|
||||
<li><?= esc(static::cleanPath($file)) ?></li>
|
||||
<li><?= esc(clean_path($file)) ?></li>
|
||||
<?php endforeach ?>
|
||||
</ol>
|
||||
</div>
|
||||
|
2
builds
2
builds
@ -39,7 +39,7 @@ if (is_file($file)) {
|
||||
if ($dev) {
|
||||
$array['minimum-stability'] = 'dev';
|
||||
$array['prefer-stable'] = true;
|
||||
$array['repositories'] = $array['repositories'] ?? [];
|
||||
$array['repositories'] ??= [];
|
||||
|
||||
$found = false;
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"require": {
|
||||
"php": "^8.0",
|
||||
"codeigniter4/framework": "^4",
|
||||
"codeigniter4/framework": "^4.2.0",
|
||||
"james-heinrich/getid3": "^2.0.x-dev",
|
||||
"whichbrowser/parser": "^v2.1.1",
|
||||
"geoip2/geoip2": "^v2.12.2",
|
||||
@ -35,10 +35,6 @@
|
||||
"symplify/coding-standard": "^10.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "app",
|
||||
"Config\\": "app/Config"
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"**/Database/Migrations/**"
|
||||
]
|
||||
|
16
composer.lock
generated
16
composer.lock
generated
@ -173,16 +173,16 @@
|
||||
},
|
||||
{
|
||||
"name": "codeigniter4/framework",
|
||||
"version": "v4.1.9",
|
||||
"version": "v4.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/codeigniter4/framework.git",
|
||||
"reference": "4ec623a6b8269dd09f570ab514e5864276bb7f56"
|
||||
"reference": "29f0e9eb2442eba41f4e9832b6695c7584e096e0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/codeigniter4/framework/zipball/4ec623a6b8269dd09f570ab514e5864276bb7f56",
|
||||
"reference": "4ec623a6b8269dd09f570ab514e5864276bb7f56",
|
||||
"url": "https://api.github.com/repos/codeigniter4/framework/zipball/29f0e9eb2442eba41f4e9832b6695c7584e096e0",
|
||||
"reference": "29f0e9eb2442eba41f4e9832b6695c7584e096e0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -190,15 +190,15 @@
|
||||
"ext-intl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"kint-php/kint": "^4.0",
|
||||
"kint-php/kint": "^4.1.1",
|
||||
"laminas/laminas-escaper": "^2.9",
|
||||
"php": "^7.3 || ^8.0",
|
||||
"php": "^7.4 || ^8.0",
|
||||
"psr/log": "^1.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeigniter/coding-standard": "^1.1",
|
||||
"fakerphp/faker": "^1.9",
|
||||
"friendsofphp/php-cs-fixer": "^3.1",
|
||||
"friendsofphp/php-cs-fixer": "3.6.*",
|
||||
"mikey179/vfsstream": "^1.6",
|
||||
"nexusphp/cs-config": "^3.3",
|
||||
"phpunit/phpunit": "^9.1",
|
||||
@ -223,7 +223,7 @@
|
||||
"slack": "https://codeigniterchat.slack.com",
|
||||
"source": "https://github.com/codeigniter4/CodeIgniter4"
|
||||
},
|
||||
"time": "2022-02-26T00:51:52+00:00"
|
||||
"time": "2022-06-03T14:04:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "codeigniter4/settings",
|
||||
|
9
env
9
env
@ -21,12 +21,14 @@
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# app.baseURL = ''
|
||||
# If you have trouble with `.`, you could also use `_`.
|
||||
# app_baseURL = ''
|
||||
# app.forceGlobalSecureRequests = false
|
||||
|
||||
# app.sessionDriver = 'CodeIgniter\Session\Handlers\FileHandler'
|
||||
# app.sessionCookieName = 'ci_session'
|
||||
# app.sessionExpiration = 7200
|
||||
# app.sessionSavePath = NULL
|
||||
# app.sessionSavePath = null
|
||||
# app.sessionMatchIP = false
|
||||
# app.sessionTimeToUpdate = 300
|
||||
# app.sessionRegenerateDestroy = false
|
||||
@ -60,7 +62,7 @@
|
||||
# contentsecuritypolicy.scriptSrc = 'self'
|
||||
# contentsecuritypolicy.styleSrc = 'self'
|
||||
# contentsecuritypolicy.imageSrc = 'self'
|
||||
# contentsecuritypolicy.base_uri = null
|
||||
# contentsecuritypolicy.baseURI = null
|
||||
# contentsecuritypolicy.childSrc = null
|
||||
# contentsecuritypolicy.connectSrc = 'self'
|
||||
# contentsecuritypolicy.fontSrc = null
|
||||
@ -73,6 +75,9 @@
|
||||
# contentsecuritypolicy.reportURI = null
|
||||
# contentsecuritypolicy.sandbox = false
|
||||
# contentsecuritypolicy.upgradeInsecureRequests = false
|
||||
# contentsecuritypolicy.styleNonceTag = '{csp-style-nonce}'
|
||||
# contentsecuritypolicy.scriptNonceTag = '{csp-script-nonce}'
|
||||
# contentsecuritypolicy.autoNonce = true
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# COOKIE
|
||||
|
@ -19,7 +19,7 @@ use ViewThemes\Theme;
|
||||
* For security be sure to declare any new methods as protected or private.
|
||||
*/
|
||||
|
||||
class BaseController extends Controller
|
||||
abstract class BaseController extends Controller
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
|
@ -513,11 +513,15 @@ class PodcastController extends BaseController
|
||||
'type' => 'cover',
|
||||
'file' => $this->podcast->cover,
|
||||
],
|
||||
];
|
||||
|
||||
if ($this->podcast->banner_id !== null) {
|
||||
$podcastMediaList[] =
|
||||
[
|
||||
'type' => 'banner',
|
||||
'file' => $this->podcast->banner,
|
||||
],
|
||||
];
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($podcastMediaList as $podcastMedia) {
|
||||
if ($podcastMedia['file'] !== null && ! $podcastMedia['file']->delete()) {
|
||||
@ -566,6 +570,15 @@ class PodcastController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->podcast->actor_id === interact_as_actor_id()) {
|
||||
//set interact to the most recently created podcast actor
|
||||
$mostRecentPodcast = (new PodcastModel())->orderBy('created_at', 'desc')
|
||||
->first();
|
||||
if ($mostRecentPodcast !== null) {
|
||||
set_interact_as_actor($mostRecentPodcast->actor_id);
|
||||
}
|
||||
}
|
||||
|
||||
$db->transComplete();
|
||||
|
||||
//delete podcast media files and folder
|
||||
|
@ -19,8 +19,5 @@ parameters:
|
||||
- themes/*
|
||||
ignoreErrors:
|
||||
- '#Cannot access property [\$a-z_]+ on ((array\|)?object)#'
|
||||
- '#^Call to an undefined method CodeIgniter\\Database\\BaseBuilder#'
|
||||
- '#^Call to an undefined method CodeIgniter\\Database\\ConnectionInterface#'
|
||||
- '#^Call to an undefined method CodeIgniter\\Model#'
|
||||
- '#^Access to an undefined property App\\Entities\\Media\\Image#'
|
||||
- '#^Access to an undefined property CodeIgniter\\Database\\BaseBuilder#'
|
@ -2,11 +2,16 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use CodeIgniter\Config\DotEnv;
|
||||
use Config\Paths;
|
||||
use Config\Services;
|
||||
|
||||
// Path to the front controller (this file)
|
||||
define('FCPATH', __DIR__ . DIRECTORY_SEPARATOR);
|
||||
|
||||
// Ensure the current directory is pointing to the front controller's directory
|
||||
chdir(FCPATH);
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------
|
||||
* BOOTSTRAP THE APPLICATION
|
||||
@ -16,20 +21,34 @@ define('FCPATH', __DIR__ . DIRECTORY_SEPARATOR);
|
||||
* and fires up an environment-specific bootstrapping.
|
||||
*/
|
||||
|
||||
// Ensure the current directory is pointing to the front controller's directory
|
||||
chdir(__DIR__);
|
||||
|
||||
// Load our paths config file
|
||||
// This is the line that might need to be changed, depending on your folder structure.
|
||||
$pathsConfig = FCPATH . '../app/Config/Paths.php';
|
||||
// ^^^ Change this if you move your application folder
|
||||
require realpath($pathsConfig) ?: $pathsConfig;
|
||||
require FCPATH . '../app/Config/Paths.php';
|
||||
// ^^^ Change this line if you move your application folder
|
||||
|
||||
$paths = new Paths();
|
||||
|
||||
// Location of the framework bootstrap file.
|
||||
$bootstrap = rtrim($paths->systemDirectory, '\\/ ') . DIRECTORY_SEPARATOR . 'bootstrap.php';
|
||||
$app = require realpath($bootstrap) ?: $bootstrap;
|
||||
require rtrim($paths->systemDirectory, '\\/ ') . DIRECTORY_SEPARATOR . 'bootstrap.php';
|
||||
|
||||
// Load environment settings from .env files into $_SERVER and $_ENV
|
||||
require_once SYSTEMPATH . 'Config/DotEnv.php';
|
||||
(new DotEnv(ROOTPATH))->load();
|
||||
|
||||
/*
|
||||
* ---------------------------------------------------------------
|
||||
* GRAB OUR CODEIGNITER INSTANCE
|
||||
* ---------------------------------------------------------------
|
||||
*
|
||||
* The CodeIgniter class contains the core functionality to make
|
||||
* the application run, and does all of the dirty work to get
|
||||
* the pieces all working together.
|
||||
*/
|
||||
|
||||
$app = Services::codeigniter();
|
||||
$app->initialize();
|
||||
$context = is_cli() ? 'php-cli' : 'web';
|
||||
$app->setContext($context);
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------
|
||||
@ -38,4 +57,5 @@ $app = require realpath($bootstrap) ?: $bootstrap;
|
||||
* Now that everything is setup, it's time to actually fire
|
||||
* up the engines and make this app do its thang.
|
||||
*/
|
||||
|
||||
$app->run();
|
||||
|
73
spark
73
spark
@ -1,6 +1,15 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/*
|
||||
* --------------------------------------------------------------------
|
||||
* CodeIgniter command-line tools
|
||||
@ -12,8 +21,28 @@
|
||||
* this class mainly acts as a passthru to the framework itself.
|
||||
*/
|
||||
|
||||
// Refuse to run when called from php-cgi
|
||||
if (strpos(PHP_SAPI, 'cgi') === 0) {
|
||||
exit("The cli tool is not supported when running php-cgi. It needs php-cli to function!\n\n");
|
||||
}
|
||||
|
||||
// We want errors to be shown when using it from the CLI.
|
||||
error_reporting(-1);
|
||||
ini_set('display_errors', '1');
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*
|
||||
* @deprecated No longer in use. `CodeIgniter` has `$context` property.
|
||||
*/
|
||||
define('SPARKED', true);
|
||||
|
||||
// Path to the front controller
|
||||
define('FCPATH', __DIR__ . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR);
|
||||
|
||||
// Ensure the current directory is pointing to the front controller's directory
|
||||
chdir(FCPATH);
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------
|
||||
* BOOTSTRAP THE APPLICATION
|
||||
@ -23,47 +52,39 @@ define('SPARKED', true);
|
||||
* and fires up an environment-specific bootstrapping.
|
||||
*/
|
||||
|
||||
// Refuse to run when called from php-cgi
|
||||
if (strpos(PHP_SAPI, 'cgi') === 0)
|
||||
{
|
||||
die("The cli tool is not supported when running php-cgi. It needs php-cli to function!\n\n");
|
||||
}
|
||||
|
||||
// Path to the front controller
|
||||
define('FCPATH', __DIR__ . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR);
|
||||
|
||||
// Load our paths config file
|
||||
$pathsConfig = 'app/Config/Paths.php';
|
||||
// This is the line that might need to be changed, depending on your folder structure.
|
||||
require FCPATH . '../app/Config/Paths.php';
|
||||
// ^^^ Change this line if you move your application folder
|
||||
require realpath($pathsConfig) ?: $pathsConfig;
|
||||
|
||||
$paths = new Config\Paths();
|
||||
|
||||
// Ensure the current directory is pointing to the front controller's directory
|
||||
chdir(FCPATH);
|
||||
// Location of the framework bootstrap file.
|
||||
require rtrim($paths->systemDirectory, '\\/ ') . DIRECTORY_SEPARATOR . 'bootstrap.php';
|
||||
|
||||
$bootstrap = rtrim($paths->systemDirectory, '\\/ ') . DIRECTORY_SEPARATOR . 'bootstrap.php';
|
||||
$app = require realpath($bootstrap) ?: $bootstrap;
|
||||
// Load environment settings from .env files into $_SERVER and $_ENV
|
||||
require_once SYSTEMPATH . 'Config/DotEnv.php';
|
||||
(new CodeIgniter\Config\DotEnv(ROOTPATH))->load();
|
||||
|
||||
// Grab our CodeIgniter
|
||||
$app = Config\Services::codeigniter();
|
||||
$app->initialize();
|
||||
$app->setContext('spark');
|
||||
|
||||
// Grab our Console
|
||||
$console = new CodeIgniter\CLI\Console($app);
|
||||
|
||||
// We want errors to be shown when using it from the CLI.
|
||||
error_reporting(-1);
|
||||
ini_set('display_errors', '1');
|
||||
|
||||
// Show basic information before we do anything else.
|
||||
if (is_int($suppress = array_search('--no-header', $_SERVER['argv'], true)))
|
||||
{
|
||||
unset($_SERVER['argv'][$suppress]); // @codeCoverageIgnore
|
||||
$suppress = true;
|
||||
if (is_int($suppress = array_search('--no-header', $_SERVER['argv'], true))) {
|
||||
unset($_SERVER['argv'][$suppress]); // @codeCoverageIgnore
|
||||
$suppress = true;
|
||||
}
|
||||
|
||||
$console->showHeader($suppress);
|
||||
|
||||
// fire off the command in the main framework.
|
||||
$response = $console->run();
|
||||
if ($response->getStatusCode() >= 300)
|
||||
{
|
||||
exit($response->getStatusCode());
|
||||
|
||||
if ($response->getStatusCode() >= 300) {
|
||||
exit($response->getStatusCode());
|
||||
}
|
||||
|
@ -8,12 +8,12 @@ test your application. Those details can be found in the documentation.
|
||||
## Resources
|
||||
|
||||
- [CodeIgniter 4 User Guide on Testing](https://codeigniter4.github.io/userguide/testing/index.html)
|
||||
- [PHPUnit docs](https://phpunit.readthedocs.io/en/8.5/index.html)
|
||||
- [PHPUnit docs](https://phpunit.de/documentation.html)
|
||||
|
||||
## Requirements
|
||||
|
||||
It is recommended to use the latest version of PHPUnit. At the time of this
|
||||
writing we are running version 8.5.13. Support for this has been built into the
|
||||
writing we are running version 9.x. Support for this has been built into the
|
||||
**composer.json** file that ships with CodeIgniter and can easily be installed
|
||||
via [Composer](https://getcomposer.org/) if you don't already have it installed
|
||||
globally.
|
||||
@ -34,7 +34,8 @@ A number of the tests use a running database. In order to set up the database
|
||||
edit the details for the `tests` group in **app/Config/Database.php** or
|
||||
**phpunit.xml**. Make sure that you provide a database engine that is currently
|
||||
running on your machine. More details on a test database setup are in the
|
||||
_Docs>>Testing>>Testing Your Database_ section of the documentation.
|
||||
[Testing Your Database](https://codeigniter4.github.io/userguide/testing/database.html)
|
||||
section of the documentation.
|
||||
|
||||
If you want to run the tests without using live database you can exclude
|
||||
@DatabaseLive group. Or make a copy of **phpunit.dist.xml** - call it
|
||||
@ -48,6 +49,10 @@ the main directory.
|
||||
|
||||
> ./phpunit
|
||||
|
||||
If you are using Windows, use the following command.
|
||||
|
||||
> vendor\bin\phpunit
|
||||
|
||||
You can limit tests to those within a single test directory by specifying the
|
||||
directory name after phpunit.
|
||||
|
||||
|
@ -44,13 +44,13 @@
|
||||
<?php endif; ?>
|
||||
|
||||
<nav class="flex items-center justify-between h-10 col-start-2 text-white bg-header">
|
||||
<a href="<?= route_to('podcast-episodes', esc($podcast->handle)) ?>" class="flex items-center flex-1 h-full min-w-0 px-2 gap-x-2 focus:ring-accent focus:ring-inset" title="<?= lang('Episode.back_to_episodes', [
|
||||
<a href="<?= route_to('podcast-episodes', esc($podcast->handle)) ?>" class="flex items-center h-full min-w-0 px-2 gap-x-2 focus:ring-accent focus:ring-inset" title="<?= lang('Episode.back_to_episodes', [
|
||||
'podcast' => esc($podcast->title),
|
||||
]) ?>">
|
||||
<?= icon('arrow-left', 'text-lg') ?>
|
||||
<div class="flex items-center flex-1 min-w-0 gap-x-2">
|
||||
<?= icon('arrow-left', 'text-lg flex-shrink-0') ?>
|
||||
<div class="flex items-center min-w-0 gap-x-2">
|
||||
<img class="w-8 h-8 rounded-full" src="<?= $episode->podcast->cover->tiny_url ?>" alt="<?= esc($episode->podcast->title) ?>" loading="lazy" />
|
||||
<div class="flex flex-col flex-1 overflow-hidden">
|
||||
<div class="flex flex-col overflow-hidden">
|
||||
<span class="text-sm font-semibold leading-none truncate"><?= esc($episode->podcast->title) ?></span>
|
||||
<span class="text-xs"><?= lang('Podcast.followers', [
|
||||
'numberOfFollowers' => $podcast->actor->followers_count,
|
||||
|
@ -1,6 +1,6 @@
|
||||
<footer>
|
||||
<?php if (can_user_interact()): ?>
|
||||
<form action="<?= route_to('episode-comment-attempt-like', esc(interact_as_actor()->username), esc($comment->episode->slug), $comment->id) ?>" method="POST" class="flex items-center gap-x-4">
|
||||
<form action="<?= route_to('episode-comment-attempt-like', esc($comment->episode->podcast->handle), esc($comment->episode->slug), $comment->id) ?>" method="POST" class="flex items-center gap-x-4">
|
||||
<?= csrf_field() ?>
|
||||
<button type="submit" name="action" class="inline-flex items-center hover:underline group" title="<?= lang(
|
||||
'Comment.likes',
|
||||
|
@ -1,6 +1,6 @@
|
||||
<footer>
|
||||
<?php if (can_user_interact()): ?>
|
||||
<form action="<?= route_to('episode-comment-attempt-like', esc(interact_as_actor()->username), esc($reply->episode->slug), $reply->id) ?>" method="POST" class="flex items-center gap-x-4">
|
||||
<form action="<?= route_to('episode-comment-attempt-like', esc($reply->episode->podcast->handle), esc($reply->episode->slug), $reply->id) ?>" method="POST" class="flex items-center gap-x-4">
|
||||
<?= csrf_field() ?>
|
||||
|
||||
<button type="submit" name="action" class="inline-flex items-center hover:underline group" title="<?= lang(
|
||||
|
Loading…
x
Reference in New Issue
Block a user