mirror of
https://code.castopod.org/adaures/castopod
synced 2025-06-06 18:31:05 +00:00
feat: add cache to ActivityPub sql queries + cache activity and note pages
- authenticated pages are not cached - add AnalyticsTrait to register a podcast webpage hit across mutliple controllers - set actor_id as unique in podcasts table - fix issues with preview card not appearing - update codeigniter4-uuid
This commit is contained in:
parent
54b84f9684
commit
2d297f45b3
@ -81,6 +81,10 @@ Events::on('on_note_add', function ($note) {
|
|||||||
->where('id', $note->episode_id)
|
->where('id', $note->episode_id)
|
||||||
->increment('notes_total');
|
->increment('notes_total');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Removing all of the podcast pages is a bit overkill, but works perfectly
|
||||||
|
// same for other events below
|
||||||
|
cache()->deleteMatching("page_podcast#{$note->actor->podcast->id}*");
|
||||||
});
|
});
|
||||||
|
|
||||||
Events::on('on_note_remove', function ($note) {
|
Events::on('on_note_remove', function ($note) {
|
||||||
@ -97,6 +101,9 @@ Events::on('on_note_remove', function ($note) {
|
|||||||
->where('id', $note->episode_id)
|
->where('id', $note->episode_id)
|
||||||
->decrement('favourites_total', $note->favourites_count);
|
->decrement('favourites_total', $note->favourites_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cache()->deleteMatching("page_podcast#{$note->actor->podcast->id}*");
|
||||||
|
cache()->deleteMatching("page_note#{$note->id}*");
|
||||||
});
|
});
|
||||||
|
|
||||||
Events::on('on_note_reblog', function ($actor, $note) {
|
Events::on('on_note_reblog', function ($actor, $note) {
|
||||||
@ -109,10 +116,18 @@ Events::on('on_note_reblog', function ($actor, $note) {
|
|||||||
->where('id', $episodeId)
|
->where('id', $episodeId)
|
||||||
->increment('notes_total');
|
->increment('notes_total');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cache()->deleteMatching("page_podcast#{$note->actor->podcast->id}*");
|
||||||
|
cache()->deleteMatching("page_note#{$note->id}*");
|
||||||
|
|
||||||
|
if ($actor->is_podcast) {
|
||||||
|
cache()->deleteMatching("page_podcast#{$actor->podcast->id}*");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Events::on('on_note_undo_reblog', function ($reblogNote) {
|
Events::on('on_note_undo_reblog', function ($reblogNote) {
|
||||||
if ($episodeId = $reblogNote->reblog_of_note->episode_id) {
|
$note = $reblogNote->reblog_of_note;
|
||||||
|
if ($episodeId = $note->episode_id) {
|
||||||
model('EpisodeModel')
|
model('EpisodeModel')
|
||||||
->where('id', $episodeId)
|
->where('id', $episodeId)
|
||||||
->decrement('reblogs_total');
|
->decrement('reblogs_total');
|
||||||
@ -121,6 +136,29 @@ Events::on('on_note_undo_reblog', function ($reblogNote) {
|
|||||||
->where('id', $episodeId)
|
->where('id', $episodeId)
|
||||||
->decrement('notes_total');
|
->decrement('notes_total');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cache()->deleteMatching("page_podcast#{$note->actor->podcast->id}*");
|
||||||
|
cache()->deleteMatching("page_note#{$note->id}*");
|
||||||
|
|
||||||
|
if ($reblogNote->actor->is_podcast) {
|
||||||
|
cache()->deleteMatching(
|
||||||
|
"page_podcast#{$reblogNote->actor->podcast->id}*",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Events::on('on_note_reply', function ($reply) {
|
||||||
|
$note = $reply->reply_to_note;
|
||||||
|
|
||||||
|
cache()->deleteMatching("page_podcast#{$note->actor->podcast->id}*");
|
||||||
|
cache()->deleteMatching("page_note#{$note->id}*");
|
||||||
|
});
|
||||||
|
|
||||||
|
Events::on('on_reply_remove', function ($reply) {
|
||||||
|
$note = $reply->reply_to_note;
|
||||||
|
|
||||||
|
cache()->deleteMatching("page_podcast#{$note->actor->podcast->id}*");
|
||||||
|
cache()->deleteMatching("page_note#{$note->id}*");
|
||||||
});
|
});
|
||||||
|
|
||||||
Events::on('on_note_favourite', function ($actor, $note) {
|
Events::on('on_note_favourite', function ($actor, $note) {
|
||||||
@ -129,6 +167,13 @@ Events::on('on_note_favourite', function ($actor, $note) {
|
|||||||
->where('id', $note->episode_id)
|
->where('id', $note->episode_id)
|
||||||
->increment('favourites_total');
|
->increment('favourites_total');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cache()->deleteMatching("page_podcast#{$actor->podcast->id}*");
|
||||||
|
cache()->deleteMatching("page_note#{$note->id}*");
|
||||||
|
|
||||||
|
if ($note->in_reply_to_id) {
|
||||||
|
cache()->deleteMatching("page_note#{$note->in_reply_to_id}*");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Events::on('on_note_undo_favourite', function ($actor, $note) {
|
Events::on('on_note_undo_favourite', function ($actor, $note) {
|
||||||
@ -137,4 +182,31 @@ Events::on('on_note_undo_favourite', function ($actor, $note) {
|
|||||||
->where('id', $note->episode_id)
|
->where('id', $note->episode_id)
|
||||||
->decrement('favourites_total');
|
->decrement('favourites_total');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cache()->deleteMatching("page_podcast#{$actor->podcast->id}*");
|
||||||
|
cache()->deleteMatching("page_note#{$note->id}*");
|
||||||
|
|
||||||
|
if ($note->in_reply_to_id) {
|
||||||
|
cache()->deleteMatching("page_note#{$note->in_reply_to_id}*");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Events::on('on_block_actor', function ($actorId) {
|
||||||
|
cache()->deleteMatching('page_podcast*');
|
||||||
|
cache()->deleteMatching('page_note*');
|
||||||
|
});
|
||||||
|
|
||||||
|
Events::on('on_unblock_actor', function ($actorId) {
|
||||||
|
cache()->deleteMatching('page_podcast*');
|
||||||
|
cache()->deleteMatching('page_note*');
|
||||||
|
});
|
||||||
|
|
||||||
|
Events::on('on_block_domain', function ($domainName) {
|
||||||
|
cache()->deleteMatching('page_podcast*');
|
||||||
|
cache()->deleteMatching('page_note*');
|
||||||
|
});
|
||||||
|
|
||||||
|
Events::on('on_unblock_domain', function ($domainName) {
|
||||||
|
cache()->deleteMatching('page_podcast*');
|
||||||
|
cache()->deleteMatching('page_note*');
|
||||||
});
|
});
|
||||||
|
@ -8,15 +8,32 @@
|
|||||||
|
|
||||||
namespace App\Controllers;
|
namespace App\Controllers;
|
||||||
|
|
||||||
|
use Analytics\AnalyticsTrait;
|
||||||
|
|
||||||
class Actor extends \ActivityPub\Controllers\ActorController
|
class Actor extends \ActivityPub\Controllers\ActorController
|
||||||
{
|
{
|
||||||
|
use AnalyticsTrait;
|
||||||
|
|
||||||
public function follow()
|
public function follow()
|
||||||
{
|
{
|
||||||
|
// Prevent analytics hit when authenticated
|
||||||
|
if (!can_user_interact()) {
|
||||||
|
$this->registerPodcastWebpageHit($this->actor->podcast->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$cacheName = "page_podcast@{$this->actor->username}_follow";
|
||||||
|
if (!($cachedView = cache($cacheName))) {
|
||||||
helper(['form', 'components', 'svg']);
|
helper(['form', 'components', 'svg']);
|
||||||
$data = [
|
$data = [
|
||||||
'actor' => $this->actor,
|
'actor' => $this->actor,
|
||||||
];
|
];
|
||||||
|
|
||||||
return view('podcast/follow', $data);
|
return view('podcast/follow', $data, [
|
||||||
|
'cache' => DECADE,
|
||||||
|
'cache_name' => $cacheName,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cachedView;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ class BaseController extends Controller
|
|||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $helpers = ['auth', 'analytics', 'svg', 'components', 'misc'];
|
protected $helpers = ['auth', 'svg', 'components', 'misc'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
@ -47,15 +47,5 @@ class BaseController extends Controller
|
|||||||
// Preload any models, libraries, etc, here.
|
// Preload any models, libraries, etc, here.
|
||||||
//--------------------------------------------------------------------
|
//--------------------------------------------------------------------
|
||||||
// E.g.: $this->session = \Config\Services::session();
|
// E.g.: $this->session = \Config\Services::session();
|
||||||
|
|
||||||
set_user_session_deny_list_ip();
|
|
||||||
set_user_session_browser();
|
|
||||||
set_user_session_referer();
|
|
||||||
set_user_session_entry_page();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function triggerWebpageHit($podcastId)
|
|
||||||
{
|
|
||||||
webpage_hit($podcastId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,15 @@
|
|||||||
|
|
||||||
namespace App\Controllers;
|
namespace App\Controllers;
|
||||||
|
|
||||||
|
use Analytics\AnalyticsTrait;
|
||||||
use App\Models\EpisodeModel;
|
use App\Models\EpisodeModel;
|
||||||
use App\Models\PodcastModel;
|
use App\Models\PodcastModel;
|
||||||
use SimpleXMLElement;
|
use SimpleXMLElement;
|
||||||
|
|
||||||
class Episode extends BaseController
|
class Episode extends BaseController
|
||||||
{
|
{
|
||||||
|
use AnalyticsTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \App\Entities\Podcast
|
* @var \App\Entities\Podcast
|
||||||
*/
|
*/
|
||||||
@ -44,10 +47,15 @@ class Episode extends BaseController
|
|||||||
|
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
self::triggerWebpageHit($this->podcast->id);
|
// Prevent analytics hit when authenticated
|
||||||
|
if (!can_user_interact()) {
|
||||||
|
$this->registerPodcastWebpageHit($this->episode->podcast_id);
|
||||||
|
}
|
||||||
|
|
||||||
$locale = service('request')->getLocale();
|
$locale = service('request')->getLocale();
|
||||||
$cacheName = "page_podcast#{$this->podcast->id}_episode{$this->episode->id}_{$locale}";
|
$cacheName =
|
||||||
|
"page_podcast#{$this->podcast->id}_episode#{$this->episode->id}_{$locale}" .
|
||||||
|
(can_user_interact() ? '_authenticated' : '');
|
||||||
|
|
||||||
if (!($cachedView = cache($cacheName))) {
|
if (!($cachedView = cache($cacheName))) {
|
||||||
helper('persons');
|
helper('persons');
|
||||||
@ -69,13 +77,7 @@ class Episode extends BaseController
|
|||||||
|
|
||||||
if (can_user_interact()) {
|
if (can_user_interact()) {
|
||||||
helper('form');
|
helper('form');
|
||||||
// The page cache is set to a decade so it is deleted manually upon podcast update
|
return view('podcast/episode_authenticated', $data);
|
||||||
return view('podcast/episode_authenticated', $data, [
|
|
||||||
'cache' => $secondsToNextUnpublishedEpisode
|
|
||||||
? $secondsToNextUnpublishedEpisode
|
|
||||||
: DECADE,
|
|
||||||
'cache_name' => $cacheName . '_authenticated',
|
|
||||||
]);
|
|
||||||
} else {
|
} else {
|
||||||
// The page cache is set to a decade so it is deleted manually upon podcast update
|
// The page cache is set to a decade so it is deleted manually upon podcast update
|
||||||
return view('podcast/episode', $data, [
|
return view('podcast/episode', $data, [
|
||||||
@ -94,7 +96,10 @@ class Episode extends BaseController
|
|||||||
{
|
{
|
||||||
header('Content-Security-Policy: frame-ancestors https://* http://*');
|
header('Content-Security-Policy: frame-ancestors https://* http://*');
|
||||||
|
|
||||||
self::triggerWebpageHit($this->episode->podcast_id);
|
// Prevent analytics hit when authenticated
|
||||||
|
if (!can_user_interact()) {
|
||||||
|
$this->registerPodcastWebpageHit($this->episode->podcast_id);
|
||||||
|
}
|
||||||
|
|
||||||
$session = \Config\Services::session();
|
$session = \Config\Services::session();
|
||||||
$session->start();
|
$session->start();
|
||||||
@ -107,7 +112,7 @@ class Episode extends BaseController
|
|||||||
|
|
||||||
$locale = service('request')->getLocale();
|
$locale = service('request')->getLocale();
|
||||||
|
|
||||||
$cacheName = "page_podcast#{$this->podcast->id}_episode{$this->episode->id}_embeddable_player_{$theme}_{$locale}";
|
$cacheName = "page_podcast#{$this->podcast->id}_episode#{$this->episode->id}_embeddable_player_{$theme}_{$locale}";
|
||||||
|
|
||||||
if (!($cachedView = cache($cacheName))) {
|
if (!($cachedView = cache($cacheName))) {
|
||||||
$theme = EpisodeModel::$themes[$theme];
|
$theme = EpisodeModel::$themes[$theme];
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
namespace App\Controllers;
|
namespace App\Controllers;
|
||||||
|
|
||||||
|
use Analytics\AnalyticsTrait;
|
||||||
use App\Models\EpisodeModel;
|
use App\Models\EpisodeModel;
|
||||||
use App\Models\PodcastModel;
|
use App\Models\PodcastModel;
|
||||||
use CodeIgniter\HTTP\URI;
|
use CodeIgniter\HTTP\URI;
|
||||||
@ -15,6 +16,8 @@ use CodeIgniter\I18n\Time;
|
|||||||
|
|
||||||
class Note extends \ActivityPub\Controllers\NoteController
|
class Note extends \ActivityPub\Controllers\NoteController
|
||||||
{
|
{
|
||||||
|
use AnalyticsTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \App\Entities\Podcast
|
* @var \App\Entities\Podcast
|
||||||
*/
|
*/
|
||||||
@ -47,6 +50,22 @@ class Note extends \ActivityPub\Controllers\NoteController
|
|||||||
|
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
|
// Prevent analytics hit when authenticated
|
||||||
|
if (!can_user_interact()) {
|
||||||
|
$this->registerPodcastWebpageHit($this->podcast->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$cacheName = implode(
|
||||||
|
'_',
|
||||||
|
array_filter([
|
||||||
|
'page',
|
||||||
|
"note#{$this->note->id}",
|
||||||
|
service('request')->getLocale(),
|
||||||
|
can_user_interact() ? '_authenticated' : null,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!($cachedView = cache($cacheName))) {
|
||||||
helper('persons');
|
helper('persons');
|
||||||
$persons = [];
|
$persons = [];
|
||||||
construct_person_array($this->podcast->persons, $persons);
|
construct_person_array($this->podcast->persons, $persons);
|
||||||
@ -63,10 +82,16 @@ class Note extends \ActivityPub\Controllers\NoteController
|
|||||||
helper('form');
|
helper('form');
|
||||||
return view('podcast/note_authenticated', $data);
|
return view('podcast/note_authenticated', $data);
|
||||||
} else {
|
} else {
|
||||||
return view('podcast/note', $data);
|
return view('podcast/note', $data, [
|
||||||
|
'cache' => DECADE,
|
||||||
|
'cache_name' => $cacheName,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $cachedView;
|
||||||
|
}
|
||||||
|
|
||||||
public function attemptCreate()
|
public function attemptCreate()
|
||||||
{
|
{
|
||||||
$rules = [
|
$rules = [
|
||||||
@ -198,6 +223,22 @@ class Note extends \ActivityPub\Controllers\NoteController
|
|||||||
|
|
||||||
public function remoteAction($action)
|
public function remoteAction($action)
|
||||||
{
|
{
|
||||||
|
// Prevent analytics hit when authenticated
|
||||||
|
if (!can_user_interact()) {
|
||||||
|
$this->registerPodcastWebpageHit($this->podcast->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$cacheName = implode(
|
||||||
|
'_',
|
||||||
|
array_filter([
|
||||||
|
'page',
|
||||||
|
"note#{$this->note->id}",
|
||||||
|
"remote_{$action}",
|
||||||
|
service('request')->getLocale(),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!($cachedView = cache($cacheName))) {
|
||||||
$data = [
|
$data = [
|
||||||
'podcast' => $this->podcast,
|
'podcast' => $this->podcast,
|
||||||
'actor' => $this->actor,
|
'actor' => $this->actor,
|
||||||
@ -207,6 +248,12 @@ class Note extends \ActivityPub\Controllers\NoteController
|
|||||||
|
|
||||||
helper('form');
|
helper('form');
|
||||||
|
|
||||||
return view('podcast/note_remote_action', $data);
|
return view('podcast/note_remote_action', $data, [
|
||||||
|
'cache' => DECADE,
|
||||||
|
'cache_name' => $cacheName,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cachedView;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ class Page extends BaseController
|
|||||||
$locale = service('request')->getLocale();
|
$locale = service('request')->getLocale();
|
||||||
$allPodcasts = (new PodcastModel())->findAll();
|
$allPodcasts = (new PodcastModel())->findAll();
|
||||||
|
|
||||||
$cacheName = "paĝe_credits_{$locale}";
|
$cacheName = "page_credits_{$locale}";
|
||||||
if (!($found = cache($cacheName))) {
|
if (!($found = cache($cacheName))) {
|
||||||
$page = new \App\Entities\Page([
|
$page = new \App\Entities\Page([
|
||||||
'title' => lang('Person.credits', [], $locale),
|
'title' => lang('Person.credits', [], $locale),
|
||||||
|
@ -8,12 +8,15 @@
|
|||||||
|
|
||||||
namespace App\Controllers;
|
namespace App\Controllers;
|
||||||
|
|
||||||
|
use Analytics\AnalyticsTrait;
|
||||||
use App\Models\EpisodeModel;
|
use App\Models\EpisodeModel;
|
||||||
use App\Models\PodcastModel;
|
use App\Models\PodcastModel;
|
||||||
use App\Models\NoteModel;
|
use App\Models\NoteModel;
|
||||||
|
|
||||||
class Podcast extends BaseController
|
class Podcast extends BaseController
|
||||||
{
|
{
|
||||||
|
use AnalyticsTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \App\Entities\Podcast|null
|
* @var \App\Entities\Podcast|null
|
||||||
*/
|
*/
|
||||||
@ -37,15 +40,30 @@ class Podcast extends BaseController
|
|||||||
|
|
||||||
public function activity()
|
public function activity()
|
||||||
{
|
{
|
||||||
self::triggerWebpageHit($this->podcast->id);
|
// Prevent analytics hit when authenticated
|
||||||
|
if (!can_user_interact()) {
|
||||||
|
$this->registerPodcastWebpageHit($this->podcast->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$cacheName = implode(
|
||||||
|
'_',
|
||||||
|
array_filter([
|
||||||
|
'page',
|
||||||
|
"podcast#{$this->podcast->id}",
|
||||||
|
'activity',
|
||||||
|
service('request')->getLocale(),
|
||||||
|
can_user_interact() ? '_authenticated' : null,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!($cachedView = cache($cacheName))) {
|
||||||
helper('persons');
|
helper('persons');
|
||||||
$persons = [];
|
$persons = [];
|
||||||
construct_person_array($this->podcast->persons, $persons);
|
construct_person_array($this->podcast->persons, $persons);
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'podcast' => $this->podcast,
|
'podcast' => $this->podcast,
|
||||||
'notes' => (new NoteModel())->getActorNotes(
|
'notes' => (new NoteModel())->getActorPublishedNotes(
|
||||||
$this->podcast->actor_id,
|
$this->podcast->actor_id,
|
||||||
),
|
),
|
||||||
'persons' => $persons,
|
'persons' => $persons,
|
||||||
@ -56,13 +74,22 @@ class Podcast extends BaseController
|
|||||||
helper('form');
|
helper('form');
|
||||||
return view('podcast/activity_authenticated', $data);
|
return view('podcast/activity_authenticated', $data);
|
||||||
} else {
|
} else {
|
||||||
return view('podcast/activity', $data);
|
return view('podcast/activity', $data, [
|
||||||
|
'cache' => DECADE,
|
||||||
|
'cache_name' => $cacheName,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $cachedView;
|
||||||
|
}
|
||||||
|
|
||||||
public function episodes()
|
public function episodes()
|
||||||
{
|
{
|
||||||
self::triggerWebpageHit($this->podcast->id);
|
// Prevent analytics hit when authenticated
|
||||||
|
if (!can_user_interact()) {
|
||||||
|
$this->registerPodcastWebpageHit($this->podcast->id);
|
||||||
|
}
|
||||||
|
|
||||||
$yearQuery = $this->request->getGet('year');
|
$yearQuery = $this->request->getGet('year');
|
||||||
$seasonQuery = $this->request->getGet('season');
|
$seasonQuery = $this->request->getGet('season');
|
||||||
@ -85,14 +112,15 @@ class Podcast extends BaseController
|
|||||||
array_filter([
|
array_filter([
|
||||||
'page',
|
'page',
|
||||||
"podcast#{$this->podcast->id}",
|
"podcast#{$this->podcast->id}",
|
||||||
|
'episodes',
|
||||||
$yearQuery ? 'year' . $yearQuery : null,
|
$yearQuery ? 'year' . $yearQuery : null,
|
||||||
$seasonQuery ? 'season' . $seasonQuery : null,
|
$seasonQuery ? 'season' . $seasonQuery : null,
|
||||||
service('request')->getLocale(),
|
service('request')->getLocale(),
|
||||||
can_user_interact() ? '_interact' : '',
|
can_user_interact() ? '_authenticated' : null,
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!($found = cache($cacheName))) {
|
if (!($cachedView = cache($cacheName))) {
|
||||||
// Build navigation array
|
// Build navigation array
|
||||||
$podcastModel = new PodcastModel();
|
$podcastModel = new PodcastModel();
|
||||||
$years = $podcastModel->getYears($this->podcast->id);
|
$years = $podcastModel->getYears($this->podcast->id);
|
||||||
@ -171,14 +199,9 @@ class Podcast extends BaseController
|
|||||||
|
|
||||||
// if user is logged in then send to the authenticated episodes view
|
// if user is logged in then send to the authenticated episodes view
|
||||||
if (can_user_interact()) {
|
if (can_user_interact()) {
|
||||||
$found = view('podcast/episodes_authenticated', $data, [
|
return view('podcast/episodes_authenticated', $data);
|
||||||
'cache' => $secondsToNextUnpublishedEpisode
|
|
||||||
? $secondsToNextUnpublishedEpisode
|
|
||||||
: DECADE,
|
|
||||||
'cache_name' => $cacheName,
|
|
||||||
]);
|
|
||||||
} else {
|
} else {
|
||||||
$found = view('podcast/episodes', $data, [
|
return view('podcast/episodes', $data, [
|
||||||
'cache' => $secondsToNextUnpublishedEpisode
|
'cache' => $secondsToNextUnpublishedEpisode
|
||||||
? $secondsToNextUnpublishedEpisode
|
? $secondsToNextUnpublishedEpisode
|
||||||
: DECADE,
|
: DECADE,
|
||||||
@ -187,6 +210,6 @@ class Podcast extends BaseController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $found;
|
return $cachedView;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -187,7 +187,9 @@ class AddPodcasts extends Migration
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$this->forge->addPrimaryKey('id');
|
$this->forge->addPrimaryKey('id');
|
||||||
|
// TODO: remove name in favor of username from actor
|
||||||
$this->forge->addUniqueKey('name');
|
$this->forge->addUniqueKey('name');
|
||||||
|
$this->forge->addUniqueKey('actor_id');
|
||||||
$this->forge->addForeignKey(
|
$this->forge->addForeignKey(
|
||||||
'actor_id',
|
'actor_id',
|
||||||
'activitypub_actors',
|
'activitypub_actors',
|
||||||
|
46
app/Entities/Actor.php
Normal file
46
app/Entities/Actor.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright 2020 Podlibre
|
||||||
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||||
|
* @link https://castopod.org/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Entities;
|
||||||
|
|
||||||
|
use App\Models\PodcastModel;
|
||||||
|
|
||||||
|
class Actor extends \ActivityPub\Entities\Actor
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var App\Entities\Podcast|null
|
||||||
|
*/
|
||||||
|
protected $podcast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
protected $is_podcast;
|
||||||
|
|
||||||
|
public function getIsPodcast()
|
||||||
|
{
|
||||||
|
return !empty($this->podcast);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPodcast()
|
||||||
|
{
|
||||||
|
if (empty($this->id)) {
|
||||||
|
throw new \RuntimeException(
|
||||||
|
'Actor must be created before getting associated podcast.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($this->podcast)) {
|
||||||
|
$this->podcast = (new PodcastModel())->getPodcastByActorId(
|
||||||
|
$this->id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->podcast;
|
||||||
|
}
|
||||||
|
}
|
@ -31,7 +31,7 @@ class Category extends Entity
|
|||||||
$parentId = $this->attributes['parent_id'];
|
$parentId = $this->attributes['parent_id'];
|
||||||
|
|
||||||
return $parentId != 0
|
return $parentId != 0
|
||||||
? (new CategoryModel())->findParent($parentId)
|
? (new CategoryModel())->getCategoryById($parentId)
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,7 +143,7 @@ class Podcast extends Entity
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (empty($this->actor)) {
|
if (empty($this->actor)) {
|
||||||
$this->actor = (new ActorModel())->getActorById($this->actor_id);
|
$this->actor = model('ActorModel')->getActorById($this->actor_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->actor;
|
return $this->actor;
|
||||||
@ -254,7 +254,9 @@ class Podcast extends Entity
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (empty($this->category)) {
|
if (empty($this->category)) {
|
||||||
$this->category = (new CategoryModel())->find($this->category_id);
|
$this->category = (new CategoryModel())->getCategoryById(
|
||||||
|
$this->category_id,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->category;
|
return $this->category;
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
* @link https://castopod.org/
|
* @link https://castopod.org/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use ActivityPub\Models\ActorModel;
|
|
||||||
use CodeIgniter\Database\Exceptions\DataException;
|
use CodeIgniter\Database\Exceptions\DataException;
|
||||||
use Config\Services;
|
use Config\Services;
|
||||||
|
|
||||||
@ -68,7 +67,7 @@ if (!function_exists('interact_as_actor')) {
|
|||||||
|
|
||||||
$session = session();
|
$session = session();
|
||||||
if ($session->has('interact_as_actor_id')) {
|
if ($session->has('interact_as_actor_id')) {
|
||||||
return (new ActorModel())->getActorById(
|
return model('ActorModel')->getActorById(
|
||||||
$session->get('interact_as_actor_id'),
|
$session->get('interact_as_actor_id'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use CodeIgniter\Files\File;
|
use CodeIgniter\Files\File;
|
||||||
use CodeIgniter\HTTP\Exceptions\HTTPException;
|
|
||||||
use CodeIgniter\HTTP\ResponseInterface;
|
use CodeIgniter\HTTP\ResponseInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,8 +41,6 @@ function save_media($file, $folder, $mediaName)
|
|||||||
*/
|
*/
|
||||||
function download_file($fileUrl)
|
function download_file($fileUrl)
|
||||||
{
|
{
|
||||||
var_dump($fileUrl);
|
|
||||||
|
|
||||||
$client = \Config\Services::curlrequest();
|
$client = \Config\Services::curlrequest();
|
||||||
|
|
||||||
$response = $client->get($fileUrl, [
|
$response = $client->get($fileUrl, [
|
||||||
|
@ -30,4 +30,11 @@ class ActivityPub extends BaseConfig
|
|||||||
|
|
||||||
public $defaultCoverImagePath = 'assets/images/cover-default.jpg';
|
public $defaultCoverImagePath = 'assets/images/cover-default.jpg';
|
||||||
public $defaultCoverImageMimetype = 'image/jpeg';
|
public $defaultCoverImageMimetype = 'image/jpeg';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
* Cache options
|
||||||
|
* --------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
public $cachePrefix = 'ap_';
|
||||||
}
|
}
|
||||||
|
@ -44,11 +44,21 @@ class Note extends UuidEntity
|
|||||||
*/
|
*/
|
||||||
protected $preview_card;
|
protected $preview_card;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
protected $has_preview_card;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \ActivityPub\Entities\Note[]
|
* @var \ActivityPub\Entities\Note[]
|
||||||
*/
|
*/
|
||||||
protected $replies;
|
protected $replies;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
protected $has_replies;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \ActivityPub\Entities\Note[]
|
* @var \ActivityPub\Entities\Note[]
|
||||||
*/
|
*/
|
||||||
@ -106,6 +116,18 @@ class Note extends UuidEntity
|
|||||||
return $this->preview_card;
|
return $this->preview_card;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getHasPreviewCard()
|
||||||
|
{
|
||||||
|
return !empty($this->getPreviewCard()) ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIsReply()
|
||||||
|
{
|
||||||
|
$this->is_reply = $this->in_reply_to_id !== null;
|
||||||
|
|
||||||
|
return $this->is_reply;
|
||||||
|
}
|
||||||
|
|
||||||
public function getReplies()
|
public function getReplies()
|
||||||
{
|
{
|
||||||
if (empty($this->id)) {
|
if (empty($this->id)) {
|
||||||
@ -121,11 +143,9 @@ class Note extends UuidEntity
|
|||||||
return $this->replies;
|
return $this->replies;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getIsReply()
|
public function getHasReplies()
|
||||||
{
|
{
|
||||||
$this->is_reply = $this->in_reply_to_id !== null;
|
return !empty($this->getReplies()) ? true : false;
|
||||||
|
|
||||||
return $this->is_reply;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getReplyToNote()
|
public function getReplyToNote()
|
||||||
@ -152,11 +172,7 @@ class Note extends UuidEntity
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (empty($this->reblogs)) {
|
if (empty($this->reblogs)) {
|
||||||
$this->reblogs = model('NoteModel')->getNoteReblogs(
|
$this->reblogs = model('NoteModel')->getNoteReblogs($this->id);
|
||||||
service('uuid')
|
|
||||||
->fromString($this->id)
|
|
||||||
->getBytes(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->reblogs;
|
return $this->reblogs;
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
namespace ActivityPub\Models;
|
namespace ActivityPub\Models;
|
||||||
|
|
||||||
|
use Michalsn\Uuid\UuidModel;
|
||||||
|
|
||||||
class ActivityModel extends UuidModel
|
class ActivityModel extends UuidModel
|
||||||
{
|
{
|
||||||
protected $table = 'activitypub_activities';
|
protected $table = 'activitypub_activities';
|
||||||
@ -34,7 +36,15 @@ class ActivityModel extends UuidModel
|
|||||||
|
|
||||||
public function getActivityById($activityId)
|
public function getActivityById($activityId)
|
||||||
{
|
{
|
||||||
return $this->find($activityId);
|
$cacheName =
|
||||||
|
config('ActivityPub')->cachePrefix . "activity#{$activityId}";
|
||||||
|
if (!($found = cache($cacheName))) {
|
||||||
|
$found = $this->find($activityId);
|
||||||
|
|
||||||
|
cache()->save($cacheName, $found, DECADE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $found;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
namespace ActivityPub\Models;
|
namespace ActivityPub\Models;
|
||||||
|
|
||||||
|
use CodeIgniter\Events\Events;
|
||||||
use CodeIgniter\Model;
|
use CodeIgniter\Model;
|
||||||
|
|
||||||
class ActorModel extends Model
|
class ActorModel extends Model
|
||||||
@ -42,7 +43,14 @@ class ActorModel extends Model
|
|||||||
|
|
||||||
public function getActorById($id)
|
public function getActorById($id)
|
||||||
{
|
{
|
||||||
return $this->find($id);
|
$cacheName = config('ActivityPub')->cachePrefix . "actor#{$id}";
|
||||||
|
if (!($found = cache($cacheName))) {
|
||||||
|
$found = $this->find($id);
|
||||||
|
|
||||||
|
cache()->save($cacheName, $found, DECADE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $found;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -76,31 +84,51 @@ class ActorModel extends Model
|
|||||||
|
|
||||||
public function getActorByUri($actorUri)
|
public function getActorByUri($actorUri)
|
||||||
{
|
{
|
||||||
return $this->where('uri', $actorUri)->first();
|
$hashedActorUri = md5($actorUri);
|
||||||
|
$cacheName =
|
||||||
|
config('ActivityPub')->cachePrefix . "actor@{$hashedActorUri}";
|
||||||
|
if (!($found = cache($cacheName))) {
|
||||||
|
$found = $this->where('uri', $actorUri)->first();
|
||||||
|
|
||||||
|
cache()->save($cacheName, $found, DECADE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $found;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFollowers($actorId)
|
public function getFollowers($actorId)
|
||||||
{
|
{
|
||||||
return $this->join(
|
$cacheName =
|
||||||
|
config('ActivityPub')->cachePrefix . "actor#{$actorId}_followers";
|
||||||
|
if (!($found = cache($cacheName))) {
|
||||||
|
$found = $this->join(
|
||||||
'activitypub_follows',
|
'activitypub_follows',
|
||||||
'activitypub_follows.actor_id = id',
|
'activitypub_follows.actor_id = id',
|
||||||
'inner',
|
'inner',
|
||||||
)
|
)
|
||||||
->where('activitypub_follows.target_actor_id', $actorId)
|
->where('activitypub_follows.target_actor_id', $actorId)
|
||||||
->findAll();
|
->findAll();
|
||||||
|
|
||||||
|
cache()->save($cacheName, $found, DECADE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $found;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if an actor is blocked using its uri
|
* Check if an existing actor is blocked using its uri.
|
||||||
|
* Returns FALSE if the actor doesn't exist
|
||||||
*
|
*
|
||||||
* @param mixed $actorUri
|
* @param mixed $actorUri
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function isActorBlocked($actorUri)
|
public function isActorBlocked($actorUri)
|
||||||
{
|
{
|
||||||
return $this->where(['uri' => $actorUri, 'is_blocked' => true])->first()
|
if ($actor = $this->getActorByUri($actorUri)) {
|
||||||
? true
|
return $actor->is_blocked;
|
||||||
: false;
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -110,16 +138,35 @@ class ActorModel extends Model
|
|||||||
*/
|
*/
|
||||||
public function getBlockedActors()
|
public function getBlockedActors()
|
||||||
{
|
{
|
||||||
return $this->where('is_blocked', 1)->findAll();
|
$cacheName = config('ActivityPub')->cachePrefix . 'blocked_actors';
|
||||||
|
if (!($found = cache($cacheName))) {
|
||||||
|
$found = $this->where('is_blocked', 1)->findAll();
|
||||||
|
|
||||||
|
cache()->save($cacheName, $found, DECADE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $found;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function blockActor($actorId)
|
public function blockActor($actorId)
|
||||||
{
|
{
|
||||||
|
$prefix = config('ActivityPub')->cachePrefix;
|
||||||
|
cache()->delete($prefix . 'blocked_actors');
|
||||||
|
cache()->deleteMatching($prefix . '*replies');
|
||||||
|
|
||||||
|
Events::trigger('on_block_actor', $actorId);
|
||||||
|
|
||||||
$this->update($actorId, ['is_blocked' => 1]);
|
$this->update($actorId, ['is_blocked' => 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function unblockActor($actorId)
|
public function unblockActor($actorId)
|
||||||
{
|
{
|
||||||
|
$prefix = config('ActivityPub')->cachePrefix;
|
||||||
|
cache()->delete($prefix . 'blocked_actors');
|
||||||
|
cache()->deleteMatching($prefix . '*replies');
|
||||||
|
|
||||||
|
Events::trigger('on_unblock_actor', $actorId);
|
||||||
|
|
||||||
$this->update($actorId, ['is_blocked' => 0]);
|
$this->update($actorId, ['is_blocked' => 0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
namespace ActivityPub\Models;
|
namespace ActivityPub\Models;
|
||||||
|
|
||||||
|
use CodeIgniter\Events\Events;
|
||||||
use CodeIgniter\Model;
|
use CodeIgniter\Model;
|
||||||
|
|
||||||
class BlockedDomainModel extends Model
|
class BlockedDomainModel extends Model
|
||||||
@ -30,20 +31,41 @@ class BlockedDomainModel extends Model
|
|||||||
*/
|
*/
|
||||||
public function getBlockedDomains()
|
public function getBlockedDomains()
|
||||||
{
|
{
|
||||||
return $this->findAll();
|
$cacheName = config('ActivityPub')->cachePrefix . 'blocked_domains';
|
||||||
|
if (!($found = cache($cacheName))) {
|
||||||
|
$found = $this->findAll();
|
||||||
|
|
||||||
|
cache()->save($cacheName, $found, DECADE);
|
||||||
|
}
|
||||||
|
return $found;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isDomainBlocked($domain)
|
public function isDomainBlocked($domain)
|
||||||
{
|
{
|
||||||
if ($this->find($domain)) {
|
$hashedDomain = md5($domain);
|
||||||
return true;
|
$cacheName =
|
||||||
|
config('ActivityPub')->cachePrefix .
|
||||||
|
"domain#{$hashedDomain}_isBlocked";
|
||||||
|
if (!($found = cache($cacheName))) {
|
||||||
|
$found = $this->find($domain) ? true : false;
|
||||||
|
|
||||||
|
cache()->save($cacheName, $found, DECADE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return $found;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function blockDomain($name)
|
public function blockDomain($name)
|
||||||
{
|
{
|
||||||
|
$hashedDomain = md5($name);
|
||||||
|
$prefix = config('ActivityPub')->cachePrefix;
|
||||||
|
cache()->delete($prefix . "domain#{$hashedDomain}_isBlocked");
|
||||||
|
cache()->delete($prefix . 'blocked_domains');
|
||||||
|
|
||||||
|
cache()->deleteMatching($prefix . '*replies');
|
||||||
|
|
||||||
|
Events::trigger('on_block_domain', $name);
|
||||||
|
|
||||||
$this->db->transStart();
|
$this->db->transStart();
|
||||||
|
|
||||||
// set all actors from the domain as blocked
|
// set all actors from the domain as blocked
|
||||||
@ -63,6 +85,15 @@ class BlockedDomainModel extends Model
|
|||||||
|
|
||||||
public function unblockDomain($name)
|
public function unblockDomain($name)
|
||||||
{
|
{
|
||||||
|
$hashedDomain = md5($name);
|
||||||
|
$prefix = config('ActivityPub')->cachePrefix;
|
||||||
|
cache()->delete($prefix . "domain#{$hashedDomain}_isBlocked");
|
||||||
|
cache()->delete($prefix . 'blocked_domains');
|
||||||
|
|
||||||
|
cache()->deleteMatching($prefix . '*replies');
|
||||||
|
|
||||||
|
Events::trigger('on_unblock_domain', $name);
|
||||||
|
|
||||||
$this->db->transStart();
|
$this->db->transStart();
|
||||||
// unblock all actors from the domain
|
// unblock all actors from the domain
|
||||||
model('ActorModel')
|
model('ActorModel')
|
||||||
|
@ -11,6 +11,7 @@ namespace ActivityPub\Models;
|
|||||||
use ActivityPub\Activities\LikeActivity;
|
use ActivityPub\Activities\LikeActivity;
|
||||||
use ActivityPub\Activities\UndoActivity;
|
use ActivityPub\Activities\UndoActivity;
|
||||||
use CodeIgniter\Events\Events;
|
use CodeIgniter\Events\Events;
|
||||||
|
use Michalsn\Uuid\UuidModel;
|
||||||
|
|
||||||
class FavouriteModel extends UuidModel
|
class FavouriteModel extends UuidModel
|
||||||
{
|
{
|
||||||
@ -49,6 +50,19 @@ class FavouriteModel extends UuidModel
|
|||||||
)
|
)
|
||||||
->increment('favourites_count');
|
->increment('favourites_count');
|
||||||
|
|
||||||
|
$prefix = config('ActivityPub')->cachePrefix;
|
||||||
|
$hashedNoteUri = md5($note->uri);
|
||||||
|
cache()->delete($prefix . "note#{$note->id}");
|
||||||
|
cache()->delete($prefix . "note@{$hashedNoteUri}");
|
||||||
|
cache()->delete($prefix . "actor#{$actor->id}_published_notes");
|
||||||
|
|
||||||
|
if ($note->in_reply_to_id) {
|
||||||
|
cache()->delete($prefix . "note#{$note->in_reply_to_id}_replies");
|
||||||
|
cache()->delete(
|
||||||
|
$prefix . "note#{$note->in_reply_to_id}_replies_withBlocked",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Events::trigger('on_note_favourite', $actor, $note);
|
Events::trigger('on_note_favourite', $actor, $note);
|
||||||
|
|
||||||
if ($registerActivity) {
|
if ($registerActivity) {
|
||||||
@ -91,6 +105,19 @@ class FavouriteModel extends UuidModel
|
|||||||
)
|
)
|
||||||
->decrement('favourites_count');
|
->decrement('favourites_count');
|
||||||
|
|
||||||
|
$prefix = config('ActivityPub')->cachePrefix;
|
||||||
|
$hashedNoteUri = md5($note->uri);
|
||||||
|
cache()->delete($prefix . "note#{$note->id}");
|
||||||
|
cache()->delete($prefix . "note@{$hashedNoteUri}");
|
||||||
|
cache()->delete($prefix . "actor#{$actor->id}_published_notes");
|
||||||
|
|
||||||
|
if ($note->in_reply_to_id) {
|
||||||
|
cache()->delete($prefix . "note#{$note->in_reply_to_id}_replies");
|
||||||
|
cache()->delete(
|
||||||
|
$prefix . "note#{$note->in_reply_to_id}_replies_withBlocked",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$this->table('activitypub_favourites')
|
$this->table('activitypub_favourites')
|
||||||
->where([
|
->where([
|
||||||
'actor_id' => $actor->id,
|
'actor_id' => $actor->id,
|
||||||
|
@ -27,9 +27,8 @@ class FollowModel extends Model
|
|||||||
protected $updatedField = null;
|
protected $updatedField = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* @param \ActivityPub\Entities\Actor $actor Actor that is following
|
||||||
* @param \ActivityPub\Entities\Actor $actor
|
* @param \ActivityPub\Entities\Actor $targetActor Actor that is being followed
|
||||||
* @param \ActivityPub\Entities\Actor $targetActor
|
|
||||||
* @param bool $registerActivity
|
* @param bool $registerActivity
|
||||||
* @return void
|
* @return void
|
||||||
* @throws DatabaseException
|
* @throws DatabaseException
|
||||||
@ -49,6 +48,14 @@ class FollowModel extends Model
|
|||||||
->where('id', $targetActor->id)
|
->where('id', $targetActor->id)
|
||||||
->increment('followers_count');
|
->increment('followers_count');
|
||||||
|
|
||||||
|
cache()->delete(
|
||||||
|
config('ActivityPub')->cachePrefix . "actor#{$targetActor->id}",
|
||||||
|
);
|
||||||
|
cache()->delete(
|
||||||
|
config('ActivityPub')->cachePrefix .
|
||||||
|
"actor#{$targetActor->id}_followers",
|
||||||
|
);
|
||||||
|
|
||||||
if ($registerActivity) {
|
if ($registerActivity) {
|
||||||
$followActivity = new FollowActivity();
|
$followActivity = new FollowActivity();
|
||||||
|
|
||||||
@ -85,8 +92,8 @@ class FollowModel extends Model
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \ActivityPub\Entities\Actor $actor
|
* @param \ActivityPub\Entities\Actor $actor Actor that is unfollowing
|
||||||
* @param \ActivityPub\Entities\Actor $targetActor
|
* @param \ActivityPub\Entities\Actor $targetActor Actor that is being unfollowed
|
||||||
* @return void
|
* @return void
|
||||||
* @throws InvalidArgumentException
|
* @throws InvalidArgumentException
|
||||||
* @throws DatabaseException
|
* @throws DatabaseException
|
||||||
@ -108,6 +115,14 @@ class FollowModel extends Model
|
|||||||
->where('id', $targetActor->id)
|
->where('id', $targetActor->id)
|
||||||
->decrement('followers_count');
|
->decrement('followers_count');
|
||||||
|
|
||||||
|
cache()->delete(
|
||||||
|
config('ActivityPub')->cachePrefix . "actor#{$targetActor->id}",
|
||||||
|
);
|
||||||
|
cache()->delete(
|
||||||
|
config('ActivityPub')->cachePrefix .
|
||||||
|
"actor#{$targetActor->id}_followers",
|
||||||
|
);
|
||||||
|
|
||||||
if ($registerActivity) {
|
if ($registerActivity) {
|
||||||
$undoActivity = new UndoActivity();
|
$undoActivity = new UndoActivity();
|
||||||
// get follow activity from database
|
// get follow activity from database
|
||||||
|
@ -17,6 +17,7 @@ use ActivityPub\Objects\TombstoneObject;
|
|||||||
use CodeIgniter\Events\Events;
|
use CodeIgniter\Events\Events;
|
||||||
use CodeIgniter\HTTP\URI;
|
use CodeIgniter\HTTP\URI;
|
||||||
use CodeIgniter\I18n\Time;
|
use CodeIgniter\I18n\Time;
|
||||||
|
use Michalsn\Uuid\UuidModel;
|
||||||
|
|
||||||
class NoteModel extends UuidModel
|
class NoteModel extends UuidModel
|
||||||
{
|
{
|
||||||
@ -54,12 +55,28 @@ class NoteModel extends UuidModel
|
|||||||
|
|
||||||
public function getNoteById($noteId)
|
public function getNoteById($noteId)
|
||||||
{
|
{
|
||||||
return $this->find($noteId);
|
$cacheName = config('ActivityPub')->cachePrefix . "note#{$noteId}";
|
||||||
|
if (!($found = cache($cacheName))) {
|
||||||
|
$found = $this->find($noteId);
|
||||||
|
|
||||||
|
cache()->save($cacheName, $found, DECADE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $found;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getNoteByUri($noteUri)
|
public function getNoteByUri($noteUri)
|
||||||
{
|
{
|
||||||
return $this->where('uri', $noteUri)->first();
|
$hashedNoteUri = md5($noteUri);
|
||||||
|
$cacheName =
|
||||||
|
config('ActivityPub')->cachePrefix . "note@{$hashedNoteUri}";
|
||||||
|
if (!($found = cache($cacheName))) {
|
||||||
|
$found = $this->where('uri', $noteUri)->first();
|
||||||
|
|
||||||
|
cache()->save($cacheName, $found, DECADE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $found;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -67,15 +84,24 @@ class NoteModel extends UuidModel
|
|||||||
*
|
*
|
||||||
* @return \ActivityPub\Entities\Note[]
|
* @return \ActivityPub\Entities\Note[]
|
||||||
*/
|
*/
|
||||||
public function getActorNotes($actorId)
|
public function getActorPublishedNotes($actorId)
|
||||||
{
|
{
|
||||||
return $this->where([
|
$cacheName =
|
||||||
|
config('ActivityPub')->cachePrefix .
|
||||||
|
"actor#{$actorId}_published_notes";
|
||||||
|
if (!($found = cache($cacheName))) {
|
||||||
|
$found = $this->where([
|
||||||
'actor_id' => $actorId,
|
'actor_id' => $actorId,
|
||||||
'in_reply_to_id' => null,
|
'in_reply_to_id' => null,
|
||||||
])
|
])
|
||||||
->where('`published_at` <= NOW()', null, false)
|
->where('`published_at` <= NOW()', null, false)
|
||||||
->orderBy('published_at', 'DESC')
|
->orderBy('published_at', 'DESC')
|
||||||
->findAll();
|
->findAll();
|
||||||
|
|
||||||
|
cache()->save($cacheName, $found, DECADE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $found;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -88,6 +114,12 @@ class NoteModel extends UuidModel
|
|||||||
*/
|
*/
|
||||||
public function getNoteReplies($noteId, $withBlocked = false)
|
public function getNoteReplies($noteId, $withBlocked = false)
|
||||||
{
|
{
|
||||||
|
$cacheName =
|
||||||
|
config('ActivityPub')->cachePrefix .
|
||||||
|
"note#{$noteId}_replies" .
|
||||||
|
($withBlocked ? '_withBlocked' : '');
|
||||||
|
|
||||||
|
if (!($found = cache($cacheName))) {
|
||||||
if (!$withBlocked) {
|
if (!$withBlocked) {
|
||||||
$this->select('activitypub_notes.*')
|
$this->select('activitypub_notes.*')
|
||||||
->join(
|
->join(
|
||||||
@ -100,14 +132,16 @@ class NoteModel extends UuidModel
|
|||||||
|
|
||||||
$this->where(
|
$this->where(
|
||||||
'in_reply_to_id',
|
'in_reply_to_id',
|
||||||
service('uuid')
|
$this->uuid->fromString($noteId)->getBytes(),
|
||||||
->fromString($noteId)
|
|
||||||
->getBytes(),
|
|
||||||
)
|
)
|
||||||
->where('`published_at` <= NOW()', null, false)
|
->where('`published_at` <= NOW()', null, false)
|
||||||
->orderBy('published_at', 'ASC');
|
->orderBy('published_at', 'ASC');
|
||||||
|
$found = $this->findAll();
|
||||||
|
|
||||||
return $this->findAll();
|
cache()->save($cacheName, $found, DECADE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $found;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -115,16 +149,28 @@ class NoteModel extends UuidModel
|
|||||||
*/
|
*/
|
||||||
public function getNoteReblogs($noteId)
|
public function getNoteReblogs($noteId)
|
||||||
{
|
{
|
||||||
return $this->where('reblog_of_id', $noteId)
|
$cacheName =
|
||||||
|
config('ActivityPub')->cachePrefix . "note#{$noteId}_reblogs";
|
||||||
|
|
||||||
|
if (!($found = cache($cacheName))) {
|
||||||
|
$found = $this->where(
|
||||||
|
'reblog_of_id',
|
||||||
|
$this->uuid->fromString($noteId)->getBytes(),
|
||||||
|
)
|
||||||
->where('`published_at` <= NOW()', null, false)
|
->where('`published_at` <= NOW()', null, false)
|
||||||
->orderBy('published_at', 'ASC')
|
->orderBy('published_at', 'ASC')
|
||||||
->findAll();
|
->findAll();
|
||||||
|
|
||||||
|
cache()->save($cacheName, $found, DECADE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $found;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addPreviewCard($noteId, $previewCardId)
|
public function addPreviewCard($noteId, $previewCardId)
|
||||||
{
|
{
|
||||||
return $this->db->table('activitypub_notes_preview_cards')->insert([
|
return $this->db->table('activitypub_notes_preview_cards')->insert([
|
||||||
'note_id' => $noteId,
|
'note_id' => $this->uuid->fromString($noteId)->getBytes(),
|
||||||
'preview_card_id' => $previewCardId,
|
'preview_card_id' => $previewCardId,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -169,10 +215,6 @@ class NoteModel extends UuidModel
|
|||||||
// problem when linking note to preview card
|
// problem when linking note to preview card
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->db->transComplete();
|
|
||||||
|
|
||||||
return $newNoteId;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,17 +222,19 @@ class NoteModel extends UuidModel
|
|||||||
->where('id', $note->actor_id)
|
->where('id', $note->actor_id)
|
||||||
->increment('notes_count');
|
->increment('notes_count');
|
||||||
|
|
||||||
|
$cachePrefix = config('ActivityPub')->cachePrefix;
|
||||||
|
cache()->delete($cachePrefix . "actor#{$note->actor_id}");
|
||||||
|
cache()->delete(
|
||||||
|
$cachePrefix . "actor#{$note->actor_id}_published_notes",
|
||||||
|
);
|
||||||
|
|
||||||
Events::trigger('on_note_add', $note);
|
Events::trigger('on_note_add', $note);
|
||||||
|
|
||||||
if ($registerActivity) {
|
if ($registerActivity) {
|
||||||
$noteUuid = service('uuid')
|
|
||||||
->fromBytes($newNoteId)
|
|
||||||
->toString();
|
|
||||||
|
|
||||||
// set note id and uri to construct NoteObject
|
// set note id and uri to construct NoteObject
|
||||||
$note->id = $noteUuid;
|
$note->id = $newNoteId;
|
||||||
$note->uri = base_url(
|
$note->uri = base_url(
|
||||||
route_to('note', $note->actor->username, $noteUuid),
|
route_to('note', $note->actor->username, $newNoteId),
|
||||||
);
|
);
|
||||||
|
|
||||||
$createActivity = new CreateActivity();
|
$createActivity = new CreateActivity();
|
||||||
@ -203,7 +247,7 @@ class NoteModel extends UuidModel
|
|||||||
'Create',
|
'Create',
|
||||||
$note->actor_id,
|
$note->actor_id,
|
||||||
null,
|
null,
|
||||||
$noteUuid,
|
$newNoteId,
|
||||||
$createActivity->toJSON(),
|
$createActivity->toJSON(),
|
||||||
$note->published_at,
|
$note->published_at,
|
||||||
'queued',
|
'queued',
|
||||||
@ -234,7 +278,7 @@ class NoteModel extends UuidModel
|
|||||||
$scheduledActivity = model('ActivityModel')
|
$scheduledActivity = model('ActivityModel')
|
||||||
->where([
|
->where([
|
||||||
'type' => 'Create',
|
'type' => 'Create',
|
||||||
'note_id' => service('uuid')
|
'note_id' => $this->uuid
|
||||||
->fromString($updatedNote->id)
|
->fromString($updatedNote->id)
|
||||||
->getBytes(),
|
->getBytes(),
|
||||||
])
|
])
|
||||||
@ -253,6 +297,12 @@ class NoteModel extends UuidModel
|
|||||||
// update note
|
// update note
|
||||||
$updateResult = $this->update($updatedNote->id, $updatedNote);
|
$updateResult = $this->update($updatedNote->id, $updatedNote);
|
||||||
|
|
||||||
|
// Clear note cache
|
||||||
|
$prefix = config('ActivityPub')->cachePrefix;
|
||||||
|
$hashedNoteUri = md5($updatedNote->uri);
|
||||||
|
cache()->delete($prefix . "note#{$updatedNote->id}");
|
||||||
|
cache()->delete($prefix . "note@{$hashedNoteUri}");
|
||||||
|
|
||||||
$this->db->transComplete();
|
$this->db->transComplete();
|
||||||
|
|
||||||
return $updateResult;
|
return $updateResult;
|
||||||
@ -268,32 +318,61 @@ class NoteModel extends UuidModel
|
|||||||
{
|
{
|
||||||
$this->db->transStart();
|
$this->db->transStart();
|
||||||
|
|
||||||
|
$cachePrefix = config('ActivityPub')->cachePrefix;
|
||||||
|
|
||||||
model('ActorModel')
|
model('ActorModel')
|
||||||
->where('id', $note->actor_id)
|
->where('id', $note->actor_id)
|
||||||
->decrement('notes_count');
|
->decrement('notes_count');
|
||||||
|
cache()->delete($cachePrefix . "actor#{$note->actor_id}");
|
||||||
|
cache()->delete(
|
||||||
|
$cachePrefix . "actor#{$note->actor_id}_published_notes",
|
||||||
|
);
|
||||||
|
|
||||||
if ($note->in_reply_to_id) {
|
if ($note->in_reply_to_id) {
|
||||||
// Note to remove is a reply
|
// Note to remove is a reply
|
||||||
model('NoteModel')
|
model('NoteModel')
|
||||||
->where(
|
->where(
|
||||||
'id',
|
'id',
|
||||||
service('uuid')
|
$this->uuid->fromString($note->in_reply_to_id)->getBytes(),
|
||||||
->fromString($note->in_reply_to_id)
|
|
||||||
->getBytes(),
|
|
||||||
)
|
)
|
||||||
->decrement('replies_count');
|
->decrement('replies_count');
|
||||||
|
|
||||||
|
$replyToNote = $note->reply_to_note;
|
||||||
|
cache()->delete($cachePrefix . "note#{$replyToNote->id}");
|
||||||
|
cache()->delete($cachePrefix . "note@{$replyToNote->uri}");
|
||||||
|
cache()->delete($cachePrefix . "note#{$replyToNote->id}_replies");
|
||||||
|
cache()->delete(
|
||||||
|
$cachePrefix . "note#{$replyToNote->id}_replies_withBlocked",
|
||||||
|
);
|
||||||
|
|
||||||
|
Events::trigger('on_reply_remove', $note);
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove all reblogs
|
// remove all note reblogs
|
||||||
foreach ($note->reblogs as $reblog) {
|
foreach ($note->reblogs as $reblog) {
|
||||||
$this->removeNote($reblog);
|
$this->removeNote($reblog);
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove all replies
|
// remove all note replies
|
||||||
foreach ($note->replies as $reply) {
|
foreach ($note->replies as $reply) {
|
||||||
$this->removeNote($reply);
|
$this->removeNote($reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($note->preview_card) {
|
||||||
|
// check that preview card in no longer used elsewhere before deleting it
|
||||||
|
if (
|
||||||
|
$this->db
|
||||||
|
->table('activitypub_notes_preview_cards')
|
||||||
|
->where('preview_card_id', $note->preview_card->id)
|
||||||
|
->countAll() <= 1
|
||||||
|
) {
|
||||||
|
model('PreviewCardModel')->deletePreviewCard(
|
||||||
|
$note->preview_card->id,
|
||||||
|
$note->preview_card->url,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Events::trigger('on_note_remove', $note);
|
Events::trigger('on_note_remove', $note);
|
||||||
|
|
||||||
if ($registerActivity) {
|
if ($registerActivity) {
|
||||||
@ -326,6 +405,15 @@ class NoteModel extends UuidModel
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear note + replies / reblogs + actor and its published notes
|
||||||
|
$hashedNoteUri = md5($note->uri);
|
||||||
|
cache()->delete($cachePrefix . "note#{$note->id}");
|
||||||
|
cache()->delete($cachePrefix . "note@{$hashedNoteUri}");
|
||||||
|
cache()->delete($cachePrefix . "note#{$note->id}_replies");
|
||||||
|
cache()->delete($cachePrefix . "note#{$note->id}_replies_withBlocked");
|
||||||
|
cache()->delete($cachePrefix . "note#{$note->id}_reblogs");
|
||||||
|
cache()->delete($cachePrefix . "note#{$note->id}_preview_card");
|
||||||
|
|
||||||
$result = model('NoteModel', false)->delete($note->id);
|
$result = model('NoteModel', false)->delete($note->id);
|
||||||
|
|
||||||
$this->db->transComplete();
|
$this->db->transComplete();
|
||||||
@ -349,12 +437,19 @@ class NoteModel extends UuidModel
|
|||||||
model('NoteModel')
|
model('NoteModel')
|
||||||
->where(
|
->where(
|
||||||
'id',
|
'id',
|
||||||
service('uuid')
|
$this->uuid->fromString($reply->in_reply_to_id)->getBytes(),
|
||||||
->fromString($reply->in_reply_to_id)
|
|
||||||
->getBytes(),
|
|
||||||
)
|
)
|
||||||
->increment('replies_count');
|
->increment('replies_count');
|
||||||
|
|
||||||
|
$prefix = config('ActivityPub')->cachePrefix;
|
||||||
|
$hashedNoteUri = md5($reply->reply_to_note->uri);
|
||||||
|
cache()->delete($prefix . "note#{$reply->in_reply_to_id}");
|
||||||
|
cache()->delete($prefix . "note@{$hashedNoteUri}");
|
||||||
|
cache()->delete($prefix . "note#{$reply->in_reply_to_id}_replies");
|
||||||
|
cache()->delete(
|
||||||
|
$prefix . "note#{$reply->in_reply_to_id}_replies_withBlocked",
|
||||||
|
);
|
||||||
|
|
||||||
Events::trigger('on_note_reply', $reply);
|
Events::trigger('on_note_reply', $reply);
|
||||||
|
|
||||||
$this->db->transComplete();
|
$this->db->transComplete();
|
||||||
@ -385,15 +480,19 @@ class NoteModel extends UuidModel
|
|||||||
->where('id', $actor->id)
|
->where('id', $actor->id)
|
||||||
->increment('notes_count');
|
->increment('notes_count');
|
||||||
|
|
||||||
|
$prefix = config('ActivityPub')->cachePrefix;
|
||||||
|
cache()->delete($prefix . "actor#{$note->actor_id}");
|
||||||
|
cache()->delete($prefix . "actor#{$note->actor_id}_published_notes");
|
||||||
|
|
||||||
model('NoteModel')
|
model('NoteModel')
|
||||||
->where(
|
->where('id', $this->uuid->fromString($note->id)->getBytes())
|
||||||
'id',
|
|
||||||
service('uuid')
|
|
||||||
->fromString($note->id)
|
|
||||||
->getBytes(),
|
|
||||||
)
|
|
||||||
->increment('reblogs_count');
|
->increment('reblogs_count');
|
||||||
|
|
||||||
|
$hashedNoteUri = md5($note->uri);
|
||||||
|
cache()->delete($prefix . "note#{$note->id}");
|
||||||
|
cache()->delete($prefix . "note@{$hashedNoteUri}");
|
||||||
|
cache()->delete($prefix . "note#{$note->id}_reblogs");
|
||||||
|
|
||||||
Events::trigger('on_note_reblog', $actor, $note);
|
Events::trigger('on_note_reblog', $actor, $note);
|
||||||
|
|
||||||
if ($registerActivity) {
|
if ($registerActivity) {
|
||||||
@ -438,15 +537,26 @@ class NoteModel extends UuidModel
|
|||||||
->where('id', $reblogNote->actor_id)
|
->where('id', $reblogNote->actor_id)
|
||||||
->decrement('notes_count');
|
->decrement('notes_count');
|
||||||
|
|
||||||
|
$cachePrefix = config('ActivityPub')->cachePrefix;
|
||||||
|
cache()->delete($cachePrefix . "actor#{$reblogNote->actor_id}");
|
||||||
|
cache()->delete(
|
||||||
|
$cachePrefix . "actor#{$reblogNote->actor_id}_published_notes",
|
||||||
|
);
|
||||||
|
|
||||||
model('NoteModel')
|
model('NoteModel')
|
||||||
->where(
|
->where(
|
||||||
'id',
|
'id',
|
||||||
service('uuid')
|
$this->uuid->fromString($reblogNote->reblog_of_id)->getBytes(),
|
||||||
->fromString($reblogNote->reblog_of_id)
|
|
||||||
->getBytes(),
|
|
||||||
)
|
)
|
||||||
->decrement('reblogs_count');
|
->decrement('reblogs_count');
|
||||||
|
|
||||||
|
$hashedReblogNoteUri = md5($reblogNote->uri);
|
||||||
|
$hashedNoteUri = md5($reblogNote->reblog_of_note->uri);
|
||||||
|
cache()->delete($cachePrefix . "note#{$reblogNote->id}");
|
||||||
|
cache()->delete($cachePrefix . "note@{$hashedReblogNoteUri}");
|
||||||
|
cache()->delete($cachePrefix . "note#{$reblogNote->reblog_of_id}");
|
||||||
|
cache()->delete($cachePrefix . "note@{$hashedNoteUri}");
|
||||||
|
|
||||||
Events::trigger('on_note_undo_reblog', $reblogNote);
|
Events::trigger('on_note_undo_reblog', $reblogNote);
|
||||||
|
|
||||||
if ($registerActivity) {
|
if ($registerActivity) {
|
||||||
@ -456,7 +566,7 @@ class NoteModel extends UuidModel
|
|||||||
->where([
|
->where([
|
||||||
'type' => 'Announce',
|
'type' => 'Announce',
|
||||||
'actor_id' => $reblogNote->actor_id,
|
'actor_id' => $reblogNote->actor_id,
|
||||||
'note_id' => service('uuid')
|
'note_id' => $this->uuid
|
||||||
->fromString($reblogNote->reblog_of_id)
|
->fromString($reblogNote->reblog_of_id)
|
||||||
->getBytes(),
|
->getBytes(),
|
||||||
])
|
])
|
||||||
@ -516,7 +626,7 @@ class NoteModel extends UuidModel
|
|||||||
if (
|
if (
|
||||||
!($reblogNote = $this->where([
|
!($reblogNote = $this->where([
|
||||||
'actor_id' => $actor->id,
|
'actor_id' => $actor->id,
|
||||||
'reblog_of_id' => service('uuid')
|
'reblog_of_id' => $this->uuid
|
||||||
->fromString($note->id)
|
->fromString($note->id)
|
||||||
->getBytes(),
|
->getBytes(),
|
||||||
])->first())
|
])->first())
|
||||||
@ -529,9 +639,8 @@ class NoteModel extends UuidModel
|
|||||||
|
|
||||||
protected function setNoteId($data)
|
protected function setNoteId($data)
|
||||||
{
|
{
|
||||||
$uuid4 = service('uuid')->uuid4();
|
$uuid4 = $this->uuid->{$this->uuidVersion}();
|
||||||
$data['id'] = $uuid4->toString();
|
$data['data']['id'] = $uuid4->toString();
|
||||||
$data['data']['id'] = $uuid4->getBytes();
|
|
||||||
|
|
||||||
if (!isset($data['data']['uri'])) {
|
if (!isset($data['data']['uri'])) {
|
||||||
$actor = model('ActorModel')->getActorById(
|
$actor = model('ActorModel')->getActorById(
|
||||||
|
@ -35,12 +35,24 @@ class PreviewCardModel extends Model
|
|||||||
|
|
||||||
public function getPreviewCardFromUrl($url)
|
public function getPreviewCardFromUrl($url)
|
||||||
{
|
{
|
||||||
return $this->where('url', $url)->first();
|
$hashedPreviewCardUrl = md5($url);
|
||||||
|
$cacheName =
|
||||||
|
config('ActivityPub')->cachePrefix .
|
||||||
|
"preview_card@{$hashedPreviewCardUrl}";
|
||||||
|
if (!($found = cache($cacheName))) {
|
||||||
|
$found = $this->where('url', $url)->first();
|
||||||
|
cache()->save($cacheName, $found, DECADE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $found;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getNotePreviewCard($noteId)
|
public function getNotePreviewCard($noteId)
|
||||||
{
|
{
|
||||||
return $this->join(
|
$cacheName =
|
||||||
|
config('ActivityPub')->cachePrefix . "note#{$noteId}_preview_card";
|
||||||
|
if (!($found = cache($cacheName))) {
|
||||||
|
$found = $this->join(
|
||||||
'activitypub_notes_preview_cards',
|
'activitypub_notes_preview_cards',
|
||||||
'activitypub_notes_preview_cards.preview_card_id = id',
|
'activitypub_notes_preview_cards.preview_card_id = id',
|
||||||
'inner',
|
'inner',
|
||||||
@ -52,5 +64,21 @@ class PreviewCardModel extends Model
|
|||||||
->getBytes(),
|
->getBytes(),
|
||||||
)
|
)
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
|
cache()->save($cacheName, $found, DECADE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $found;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deletePreviewCard($id, $url)
|
||||||
|
{
|
||||||
|
$hashedPreviewCardUrl = md5($url);
|
||||||
|
cache()->delete(
|
||||||
|
config('ActivityPub')->cachePrefix .
|
||||||
|
"preview_card@{$hashedPreviewCardUrl}",
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->delete($id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,206 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @copyright 2021 Podlibre
|
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
||||||
* @link https://castopod.org/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace ActivityPub\Models;
|
|
||||||
|
|
||||||
use CodeIgniter\Database\Exceptions\DataException;
|
|
||||||
use stdClass;
|
|
||||||
|
|
||||||
class UuidModel extends \Michalsn\Uuid\UuidModel
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* This insert overwrite is added as a means to FIX some bugs
|
|
||||||
* from the extended Uuid package. See: https://github.com/michalsn/codeigniter4-uuid/issues/2
|
|
||||||
*
|
|
||||||
* Inserts data into the current table. If an object is provided,
|
|
||||||
* it will attempt to convert it to an array.
|
|
||||||
*
|
|
||||||
* @param array|object $data
|
|
||||||
* @param boolean $returnID Whether insert ID should be returned or not.
|
|
||||||
*
|
|
||||||
* @return BaseResult|integer|string|false
|
|
||||||
* @throws \ReflectionException
|
|
||||||
*/
|
|
||||||
public function insert($data = null, bool $returnID = true)
|
|
||||||
{
|
|
||||||
$escape = null;
|
|
||||||
|
|
||||||
$this->insertID = 0;
|
|
||||||
|
|
||||||
if (empty($data)) {
|
|
||||||
$data = $this->tempData['data'] ?? null;
|
|
||||||
$escape = $this->tempData['escape'] ?? null;
|
|
||||||
$this->tempData = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($data)) {
|
|
||||||
throw DataException::forEmptyDataset('insert');
|
|
||||||
}
|
|
||||||
|
|
||||||
// If $data is using a custom class with public or protected
|
|
||||||
// properties representing the table elements, we need to grab
|
|
||||||
// them as an array.
|
|
||||||
if (is_object($data) && !$data instanceof stdClass) {
|
|
||||||
$data = static::classToArray(
|
|
||||||
$data,
|
|
||||||
$this->primaryKey,
|
|
||||||
$this->dateFormat,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it's still a stdClass, go ahead and convert to
|
|
||||||
// an array so doProtectFields and other model methods
|
|
||||||
// don't have to do special checks.
|
|
||||||
if (is_object($data)) {
|
|
||||||
$data = (array) $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($data)) {
|
|
||||||
throw DataException::forEmptyDataset('insert');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate data before saving.
|
|
||||||
if ($this->skipValidation === false) {
|
|
||||||
if ($this->cleanRules()->validate($data) === false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must be called first so we don't
|
|
||||||
// strip out created_at values.
|
|
||||||
$data = $this->doProtectFields($data);
|
|
||||||
|
|
||||||
// Set created_at and updated_at with same time
|
|
||||||
$date = $this->setDate();
|
|
||||||
|
|
||||||
if (
|
|
||||||
$this->useTimestamps &&
|
|
||||||
!empty($this->createdField) &&
|
|
||||||
!array_key_exists($this->createdField, $data)
|
|
||||||
) {
|
|
||||||
$data[$this->createdField] = $date;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
$this->useTimestamps &&
|
|
||||||
!empty($this->updatedField) &&
|
|
||||||
!array_key_exists($this->updatedField, $data)
|
|
||||||
) {
|
|
||||||
$data[$this->updatedField] = $date;
|
|
||||||
}
|
|
||||||
|
|
||||||
$eventData = ['data' => $data];
|
|
||||||
if ($this->tempAllowCallbacks) {
|
|
||||||
$eventData = $this->trigger('beforeInsert', $eventData);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Require non empty primaryKey when
|
|
||||||
// not using auto-increment feature
|
|
||||||
if (
|
|
||||||
!$this->useAutoIncrement &&
|
|
||||||
empty($eventData['data'][$this->primaryKey])
|
|
||||||
) {
|
|
||||||
throw DataException::forEmptyPrimaryKey('insert');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($this->uuidFields)) {
|
|
||||||
foreach ($this->uuidFields as $field) {
|
|
||||||
if ($field === $this->primaryKey) {
|
|
||||||
$this->uuidTempData[
|
|
||||||
$field
|
|
||||||
] = $this->uuid->{$this->uuidVersion}();
|
|
||||||
|
|
||||||
if ($this->uuidUseBytes === true) {
|
|
||||||
$this->builder()->set(
|
|
||||||
$field,
|
|
||||||
$this->uuidTempData[$field]->getBytes(),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$this->builder()->set(
|
|
||||||
$field,
|
|
||||||
$this->uuidTempData[$field]->toString(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (
|
|
||||||
$this->uuidUseBytes === true &&
|
|
||||||
!empty($eventData['data'][$field])
|
|
||||||
) {
|
|
||||||
$this->uuidTempData[$field] = $this->uuid->fromString(
|
|
||||||
$eventData['data'][$field],
|
|
||||||
);
|
|
||||||
$this->builder()->set(
|
|
||||||
$field,
|
|
||||||
$this->uuidTempData[$field]->getBytes(),
|
|
||||||
);
|
|
||||||
unset($eventData['data'][$field]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must use the set() method to ensure objects get converted to arrays
|
|
||||||
$result = $this->builder()
|
|
||||||
->set($eventData['data'], '', $escape)
|
|
||||||
->insert();
|
|
||||||
|
|
||||||
// If insertion succeeded then save the insert ID
|
|
||||||
if ($result) {
|
|
||||||
if (
|
|
||||||
!$this->useAutoIncrement ||
|
|
||||||
isset($eventData['data'][$this->primaryKey])
|
|
||||||
) {
|
|
||||||
$this->insertID = $eventData['data'][$this->primaryKey];
|
|
||||||
} else {
|
|
||||||
if (in_array($this->primaryKey, $this->uuidFields)) {
|
|
||||||
$this->insertID = $this->uuidTempData[
|
|
||||||
$this->primaryKey
|
|
||||||
]->toString();
|
|
||||||
} else {
|
|
||||||
$this->insertID = $this->db->insertID();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup data before event trigger
|
|
||||||
if (!empty($this->uuidFields) && $this->uuidUseBytes === true) {
|
|
||||||
foreach ($this->uuidFields as $field) {
|
|
||||||
if (
|
|
||||||
$field === $this->primaryKey ||
|
|
||||||
empty($this->uuidTempData[$field])
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$eventData['data'][$field] = $this->uuidTempData[
|
|
||||||
$field
|
|
||||||
]->toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$eventData = [
|
|
||||||
'id' => $this->insertID,
|
|
||||||
'data' => $eventData['data'],
|
|
||||||
'result' => $result,
|
|
||||||
];
|
|
||||||
if ($this->tempAllowCallbacks) {
|
|
||||||
// Trigger afterInsert events with the inserted data and new ID
|
|
||||||
$this->trigger('afterInsert', $eventData);
|
|
||||||
}
|
|
||||||
$this->tempAllowCallbacks = $this->allowCallbacks;
|
|
||||||
|
|
||||||
// If insertion failed, get out of here
|
|
||||||
if (!$result) {
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise return the insertID, if requested.
|
|
||||||
return $returnID ? $this->insertID : $result;
|
|
||||||
}
|
|
||||||
}
|
|
51
app/Libraries/Analytics/AnalyticsTrait.php
Normal file
51
app/Libraries/Analytics/AnalyticsTrait.php
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright 2021 Podlibre
|
||||||
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||||
|
* @link https://castopod.org/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Analytics;
|
||||||
|
|
||||||
|
trait AnalyticsTrait
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param integer $podcastId
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function registerPodcastWebpageHit($podcastId)
|
||||||
|
{
|
||||||
|
helper('analytics');
|
||||||
|
|
||||||
|
set_user_session_deny_list_ip();
|
||||||
|
set_user_session_browser();
|
||||||
|
set_user_session_referer();
|
||||||
|
set_user_session_entry_page();
|
||||||
|
|
||||||
|
$session = \Config\Services::session();
|
||||||
|
$session->start();
|
||||||
|
|
||||||
|
if (!$session->get('denyListIp')) {
|
||||||
|
$db = \Config\Database::connect();
|
||||||
|
|
||||||
|
$referer = $session->get('referer');
|
||||||
|
$domain = empty(parse_url($referer, PHP_URL_HOST))
|
||||||
|
? '- Direct -'
|
||||||
|
: parse_url($referer, PHP_URL_HOST);
|
||||||
|
parse_str(parse_url($referer, PHP_URL_QUERY), $queries);
|
||||||
|
$keywords = empty($queries['q']) ? null : $queries['q'];
|
||||||
|
|
||||||
|
$procedureName = $db->prefixTable('analytics_website');
|
||||||
|
$db->query("call $procedureName(?,?,?,?,?,?)", [
|
||||||
|
$podcastId,
|
||||||
|
$session->get('browser'),
|
||||||
|
$session->get('entryPage'),
|
||||||
|
$referer,
|
||||||
|
$domain,
|
||||||
|
$keywords,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -258,40 +258,6 @@ if (!function_exists('set_user_session_entry_page')) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!function_exists('webpage_hit')) {
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param integer $podcastId
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
function webpage_hit($podcastId)
|
|
||||||
{
|
|
||||||
$session = \Config\Services::session();
|
|
||||||
$session->start();
|
|
||||||
|
|
||||||
if (!$session->get('denyListIp')) {
|
|
||||||
$db = \Config\Database::connect();
|
|
||||||
|
|
||||||
$referer = $session->get('referer');
|
|
||||||
$domain = empty(parse_url($referer, PHP_URL_HOST))
|
|
||||||
? '- Direct -'
|
|
||||||
: parse_url($referer, PHP_URL_HOST);
|
|
||||||
parse_str(parse_url($referer, PHP_URL_QUERY), $queries);
|
|
||||||
$keywords = empty($queries['q']) ? null : $queries['q'];
|
|
||||||
|
|
||||||
$procedureName = $db->prefixTable('analytics_website');
|
|
||||||
$db->query("call $procedureName(?,?,?,?,?,?)", [
|
|
||||||
$podcastId,
|
|
||||||
$session->get('browser'),
|
|
||||||
$session->get('entryPage'),
|
|
||||||
$referer,
|
|
||||||
$domain,
|
|
||||||
$keywords,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('podcast_hit')) {
|
if (!function_exists('podcast_hit')) {
|
||||||
/**
|
/**
|
||||||
* Counting podcast episode downloads for analytic purposes
|
* Counting podcast episode downloads for analytic purposes
|
||||||
|
14
app/Models/ActorModel.php
Normal file
14
app/Models/ActorModel.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright 2021 Podlibre
|
||||||
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||||
|
* @link https://castopod.org/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
class ActorModel extends \ActivityPub\Models\ActorModel
|
||||||
|
{
|
||||||
|
protected $returnType = \App\Entities\Actor::class;
|
||||||
|
}
|
@ -27,9 +27,9 @@ class CategoryModel extends Model
|
|||||||
|
|
||||||
protected $useTimestamps = false;
|
protected $useTimestamps = false;
|
||||||
|
|
||||||
public function findParent($parentId)
|
public function getCategoryById($id)
|
||||||
{
|
{
|
||||||
return $this->find($parentId);
|
return $this->find($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCategoryOptions()
|
public function getCategoryOptions()
|
||||||
|
@ -139,6 +139,7 @@ class EpisodeModel extends Model
|
|||||||
|
|
||||||
public function getEpisodeById($episodeId)
|
public function getEpisodeById($episodeId)
|
||||||
{
|
{
|
||||||
|
// TODO: episode id should be a composite key. The cache should include podcast_id.
|
||||||
$cacheName = "podcast_episode#{$episodeId}";
|
$cacheName = "podcast_episode#{$episodeId}";
|
||||||
if (!($found = cache($cacheName))) {
|
if (!($found = cache($cacheName))) {
|
||||||
$builder = $this->where([
|
$builder = $this->where([
|
||||||
@ -153,19 +154,16 @@ class EpisodeModel extends Model
|
|||||||
return $found;
|
return $found;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPublishedEpisodeById($episodeId, $podcastId = null)
|
public function getPublishedEpisodeById($podcastId, $episodeId)
|
||||||
{
|
{
|
||||||
$cacheName = "podcast_episode#{$episodeId}_published";
|
$cacheName = "podcast#{$podcastId}_episode#{$episodeId}_published";
|
||||||
if (!($found = cache($cacheName))) {
|
if (!($found = cache($cacheName))) {
|
||||||
$builder = $this->where([
|
$found = $this->where([
|
||||||
'id' => $episodeId,
|
'id' => $episodeId,
|
||||||
])->where('`published_at` <= NOW()', null, false);
|
])
|
||||||
|
->where('podcast_id', $podcastId)
|
||||||
if ($podcastId) {
|
->where('`published_at` <= NOW()', null, false)
|
||||||
$builder->where('podcast_id', $podcastId);
|
->first();
|
||||||
}
|
|
||||||
|
|
||||||
$found = $builder->first();
|
|
||||||
|
|
||||||
cache()->save($cacheName, $found, DECADE);
|
cache()->save($cacheName, $found, DECADE);
|
||||||
}
|
}
|
||||||
@ -287,11 +285,17 @@ class EpisodeModel extends Model
|
|||||||
// delete model requests cache
|
// delete model requests cache
|
||||||
cache()->delete("podcast#{$episode->podcast_id}_episodes");
|
cache()->delete("podcast#{$episode->podcast_id}_episodes");
|
||||||
|
|
||||||
cache()->deleteMatching("podcast_episode#{$episode->id}*");
|
cache()->delete("podcast_episode#{$episode->id}");
|
||||||
|
cache()->deleteMatching(
|
||||||
|
"podcast#{$episode->podcast_id}_episode#{$episode->id}*",
|
||||||
|
);
|
||||||
cache()->delete(
|
cache()->delete(
|
||||||
"podcast#{$episode->podcast_id}_episode@{$episode->slug}",
|
"podcast#{$episode->podcast_id}_episode@{$episode->slug}",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
cache()->deleteMatching(
|
||||||
|
"page_podcast#{$episode->podcast_id}_activity*",
|
||||||
|
);
|
||||||
cache()->deleteMatching(
|
cache()->deleteMatching(
|
||||||
"page_podcast#{$episode->podcast_id}_episode#{$episode->id}_*",
|
"page_podcast#{$episode->podcast_id}_episode#{$episode->id}_*",
|
||||||
);
|
);
|
||||||
@ -300,12 +304,12 @@ class EpisodeModel extends Model
|
|||||||
if ($episode->season_number) {
|
if ($episode->season_number) {
|
||||||
cache()->deleteMatching("podcast#{$episode->podcast_id}_season*");
|
cache()->deleteMatching("podcast#{$episode->podcast_id}_season*");
|
||||||
cache()->deleteMatching(
|
cache()->deleteMatching(
|
||||||
"page_podcast#{$episode->podcast_id}_season*",
|
"page_podcast#{$episode->podcast_id}_episodes_season*",
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
cache()->deleteMatching("podcast#{$episode->podcast_id}_year*");
|
cache()->deleteMatching("podcast#{$episode->podcast_id}_year*");
|
||||||
cache()->deleteMatching(
|
cache()->deleteMatching(
|
||||||
"page_podcast#{$episode->podcast_id}_year*",
|
"page_podcast#{$episode->podcast_id}_episodes_year*",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,9 +38,9 @@ class EpisodePersonModel extends Model
|
|||||||
protected $afterInsert = ['clearCache'];
|
protected $afterInsert = ['clearCache'];
|
||||||
protected $beforeDelete = ['clearCache'];
|
protected $beforeDelete = ['clearCache'];
|
||||||
|
|
||||||
public function getEpisodePersons($episodeId)
|
public function getEpisodePersons($podcastId, $episodeId)
|
||||||
{
|
{
|
||||||
$cacheName = "podcast_episode#{$episodeId}_persons";
|
$cacheName = "podcast#{$podcastId}_episode#{$episodeId}_persons";
|
||||||
if (!($found = cache($cacheName))) {
|
if (!($found = cache($cacheName))) {
|
||||||
$found = $this->select('episodes_persons.*')
|
$found = $this->select('episodes_persons.*')
|
||||||
->where('episode_id', $episodeId)
|
->where('episode_id', $episodeId)
|
||||||
@ -124,7 +124,6 @@ class EpisodePersonModel extends Model
|
|||||||
$episodeId = $person->episode_id;
|
$episodeId = $person->episode_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
cache()->delete("podcast_episode#{$episodeId}_persons");
|
|
||||||
(new EpisodeModel())->clearCache(['id' => $episodeId]);
|
(new EpisodeModel())->clearCache(['id' => $episodeId]);
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @copyright 2020 Podlibre
|
* @copyright 2021 Podlibre
|
||||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||||
* @link https://castopod.org/
|
* @link https://castopod.org/
|
||||||
*/
|
*/
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use ActivityPub\Models\ActorModel;
|
|
||||||
use CodeIgniter\HTTP\URI;
|
use CodeIgniter\HTTP\URI;
|
||||||
use CodeIgniter\Model;
|
use CodeIgniter\Model;
|
||||||
use phpseclib\Crypt\RSA;
|
use phpseclib\Crypt\RSA;
|
||||||
@ -74,7 +73,7 @@ class PodcastModel extends Model
|
|||||||
protected $validationMessages = [];
|
protected $validationMessages = [];
|
||||||
|
|
||||||
protected $beforeInsert = ['createPodcastActor'];
|
protected $beforeInsert = ['createPodcastActor'];
|
||||||
protected $afterInsert = ['setAvatarImageUrl'];
|
protected $afterInsert = ['setActorAvatar'];
|
||||||
protected $afterUpdate = ['updatePodcastActor'];
|
protected $afterUpdate = ['updatePodcastActor'];
|
||||||
|
|
||||||
// clear cache before update if by any chance, the podcast name changes, so will the podcast link
|
// clear cache before update if by any chance, the podcast name changes, so will the podcast link
|
||||||
@ -104,6 +103,18 @@ class PodcastModel extends Model
|
|||||||
return $found;
|
return $found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getPodcastByActorId($actorId)
|
||||||
|
{
|
||||||
|
$cacheName = "podcast_actor#{$actorId}";
|
||||||
|
if (!($found = cache($cacheName))) {
|
||||||
|
$found = $this->where('actor_id', $actorId)->first();
|
||||||
|
|
||||||
|
cache()->save($cacheName, $found, DECADE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $found;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all the podcasts a given user is contributing to
|
* Gets all the podcasts a given user is contributing to
|
||||||
*
|
*
|
||||||
@ -300,25 +311,6 @@ class PodcastModel extends Model
|
|||||||
return $defaultQuery;
|
return $defaultQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function clearCache(array $data)
|
|
||||||
{
|
|
||||||
$podcast = (new PodcastModel())->getPodcastById(
|
|
||||||
is_array($data['id']) ? $data['id'][0] : $data['id'],
|
|
||||||
);
|
|
||||||
|
|
||||||
// delete cache all podcast pages
|
|
||||||
cache()->deleteMatching("page_podcast#{$podcast->id}_*");
|
|
||||||
|
|
||||||
// delete model requests cache, includes feed / query / episode lists, etc.
|
|
||||||
cache()->deleteMatching("podcast#{$podcast->id}*");
|
|
||||||
cache()->delete("podcast@{$podcast->name}");
|
|
||||||
|
|
||||||
// clear cache for every credit page
|
|
||||||
cache()->deleteMatching('page_credits_*');
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an actor linked to the podcast
|
* Creates an actor linked to the podcast
|
||||||
* (Triggered before insert)
|
* (Triggered before insert)
|
||||||
@ -359,16 +351,18 @@ class PodcastModel extends Model
|
|||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function setAvatarImageUrl($data)
|
protected function setActorAvatar($data)
|
||||||
{
|
{
|
||||||
$podcast = (new PodcastModel())->getPodcastById(
|
$podcast = (new PodcastModel())->getPodcastById(
|
||||||
is_array($data['id']) ? $data['id'][0] : $data['id'],
|
is_array($data['id']) ? $data['id'][0] : $data['id'],
|
||||||
);
|
);
|
||||||
|
|
||||||
$podcast->actor->avatar_image_url = $podcast->image->thumbnail_url;
|
$podcastActor = (new ActorModel())->find($podcast->actor_id);
|
||||||
$podcast->actor->avatar_image_mimetype = $podcast->image_mimetype;
|
|
||||||
|
|
||||||
(new ActorModel())->update($podcast->actor->id, $podcast->actor);
|
$podcastActor->avatar_image_url = $podcast->image->thumbnail_url;
|
||||||
|
$podcastActor->avatar_image_mimetype = $podcast->image_mimetype;
|
||||||
|
|
||||||
|
(new ActorModel())->update($podcast->actor_id, $podcastActor);
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
@ -380,7 +374,7 @@ class PodcastModel extends Model
|
|||||||
);
|
);
|
||||||
|
|
||||||
$actorModel = new ActorModel();
|
$actorModel = new ActorModel();
|
||||||
$actor = $actorModel->find($podcast->actor_id);
|
$actor = $actorModel->getActorById($podcast->actor_id);
|
||||||
|
|
||||||
// update values
|
// update values
|
||||||
$actor->display_name = $podcast->title;
|
$actor->display_name = $podcast->title;
|
||||||
@ -394,4 +388,28 @@ class PodcastModel extends Model
|
|||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function clearCache(array $data)
|
||||||
|
{
|
||||||
|
$podcast = (new PodcastModel())->getPodcastById(
|
||||||
|
is_array($data['id']) ? $data['id'][0] : $data['id'],
|
||||||
|
);
|
||||||
|
|
||||||
|
// delete cache all podcast pages
|
||||||
|
cache()->deleteMatching("page_podcast#{$podcast->id}*");
|
||||||
|
|
||||||
|
// delete all cache for podcast actor
|
||||||
|
cache()->deleteMatching(
|
||||||
|
config('ActivityPub')->cachePrefix . "actor#{$podcast->actor_id}*",
|
||||||
|
);
|
||||||
|
|
||||||
|
// delete model requests cache, includes feed / query / episode lists, etc.
|
||||||
|
cache()->deleteMatching("podcast#{$podcast->id}*");
|
||||||
|
cache()->delete("podcast@{$podcast->name}");
|
||||||
|
|
||||||
|
// clear cache for every credit page
|
||||||
|
cache()->deleteMatching('page_credits_*');
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ class SoundbiteModel extends Model
|
|||||||
*/
|
*/
|
||||||
public function getEpisodeSoundbites(int $podcastId, int $episodeId): array
|
public function getEpisodeSoundbites(int $podcastId, int $episodeId): array
|
||||||
{
|
{
|
||||||
$cacheName = "podcast_episode#{$episodeId}_soundbites";
|
$cacheName = "podcast#{$podcastId}_episode#{$episodeId}_soundbites";
|
||||||
if (!($found = cache($cacheName))) {
|
if (!($found = cache($cacheName))) {
|
||||||
$found = $this->where([
|
$found = $this->where([
|
||||||
'episode_id' => $episodeId,
|
'episode_id' => $episodeId,
|
||||||
@ -77,7 +77,9 @@ class SoundbiteModel extends Model
|
|||||||
: $data['id']['episode_id'],
|
: $data['id']['episode_id'],
|
||||||
);
|
);
|
||||||
|
|
||||||
cache()->delete("podcast_episode#{$episode->id}_soundbites");
|
cache()->delete(
|
||||||
|
"podcast#{$episode->podcast_id}_episode#{$episode->id}_soundbites",
|
||||||
|
);
|
||||||
|
|
||||||
// delete cache for rss feed
|
// delete cache for rss feed
|
||||||
cache()->deleteMatching("podcast#{$episode->podcast_id}_feed*");
|
cache()->deleteMatching("podcast#{$episode->podcast_id}_feed*");
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
<?= view('podcast/_partials/episode_card', [
|
<?= view('podcast/_partials/episode_card', [
|
||||||
'episode' => $note->episode,
|
'episode' => $note->episode,
|
||||||
]) ?>
|
]) ?>
|
||||||
<?php elseif ($note->preview_card_id): ?>
|
<?php elseif ($note->has_preview_card): ?>
|
||||||
<?= view('podcast/_partials/preview_card', [
|
<?= view('podcast/_partials/preview_card', [
|
||||||
'preview_card' => $note->preview_card,
|
'preview_card' => $note->preview_card,
|
||||||
]) ?>
|
]) ?>
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
<?= view('podcast/_partials/episode_card', [
|
<?= view('podcast/_partials/episode_card', [
|
||||||
'episode' => $note->episode,
|
'episode' => $note->episode,
|
||||||
]) ?>
|
]) ?>
|
||||||
<?php elseif ($note->preview_card_id): ?>
|
<?php elseif ($note->has_preview_card): ?>
|
||||||
<?= view('podcast/_partials/preview_card', [
|
<?= view('podcast/_partials/preview_card', [
|
||||||
'preview_card' => $note->preview_card,
|
'preview_card' => $note->preview_card,
|
||||||
]) ?>
|
]) ?>
|
||||||
|
@ -15,7 +15,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<?php if ($note->has_replies): ?>
|
||||||
<?php foreach ($note->replies as $reply): ?>
|
<?php foreach ($note->replies as $reply): ?>
|
||||||
<?= view('podcast/_partials/reply', ['reply' => $reply]) ?>
|
<?= view('podcast/_partials/reply', ['reply' => $reply]) ?>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
@ -39,7 +39,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<?= form_close() ?>
|
<?= form_close() ?>
|
||||||
|
|
||||||
|
<?php if ($note->has_replies): ?>
|
||||||
<?php foreach ($note->replies as $reply): ?>
|
<?php foreach ($note->replies as $reply): ?>
|
||||||
<?= view('podcast/_partials/reply_authenticated', ['reply' => $reply]) ?>
|
<?= view('podcast/_partials/reply_authenticated', [
|
||||||
|
'reply' => $reply,
|
||||||
|
]) ?>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
<?= view('podcast/_partials/episode_card', [
|
<?= view('podcast/_partials/episode_card', [
|
||||||
'episode' => $note->episode,
|
'episode' => $note->episode,
|
||||||
]) ?>
|
]) ?>
|
||||||
<?php elseif ($note->preview_card_id): ?>
|
<?php elseif ($note->has_preview_card): ?>
|
||||||
<?= view('podcast/_partials/preview_card', [
|
<?= view('podcast/_partials/preview_card', [
|
||||||
'preview_card' => $note->preview_card,
|
'preview_card' => $note->preview_card,
|
||||||
]) ?>
|
]) ?>
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
<?= view('podcast/_partials/episode_card', [
|
<?= view('podcast/_partials/episode_card', [
|
||||||
'episode' => $note->episode,
|
'episode' => $note->episode,
|
||||||
]) ?>
|
]) ?>
|
||||||
<?php elseif ($note->preview_card_id): ?>
|
<?php elseif ($note->has_preview_card): ?>
|
||||||
<?= view('podcast/_partials/preview_card', [
|
<?= view('podcast/_partials/preview_card', [
|
||||||
'preview_card' => $note->preview_card,
|
'preview_card' => $note->preview_card,
|
||||||
]) ?>
|
]) ?>
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
><?= lang('Common.mediumDate', [$reply->published_at]) ?></time>
|
><?= lang('Common.mediumDate', [$reply->published_at]) ?></time>
|
||||||
</header>
|
</header>
|
||||||
<p class="mb-2 note-content"><?= $reply->message_html ?></p>
|
<p class="mb-2 note-content"><?= $reply->message_html ?></p>
|
||||||
<?php if ($reply->preview_card_id): ?>
|
<?php if ($reply->has_preview_card): ?>
|
||||||
<?= view('podcast/_partials/preview_card', [
|
<?= view('podcast/_partials/preview_card', [
|
||||||
'preview_card' => $reply->preview_card,
|
'preview_card' => $reply->preview_card,
|
||||||
]) ?>
|
]) ?>
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
><?= lang('Common.mediumDate', [$reply->created_at]) ?></time>
|
><?= lang('Common.mediumDate', [$reply->created_at]) ?></time>
|
||||||
</header>
|
</header>
|
||||||
<p class="mb-2 note-content"><?= $reply->message_html ?></p>
|
<p class="mb-2 note-content"><?= $reply->message_html ?></p>
|
||||||
<?php if ($reply->preview_card_id): ?>
|
<?php if ($reply->has_preview_card): ?>
|
||||||
<?= view('podcast/_partials/preview_card', [
|
<?= view('podcast/_partials/preview_card', [
|
||||||
'preview_card' => $reply->preview_card,
|
'preview_card' => $reply->preview_card,
|
||||||
]) ?>
|
]) ?>
|
||||||
|
@ -12,14 +12,14 @@
|
|||||||
"geoip2/geoip2": "^v2.11.0",
|
"geoip2/geoip2": "^v2.11.0",
|
||||||
"myth/auth": "dev-develop",
|
"myth/auth": "dev-develop",
|
||||||
"codeigniter4/codeigniter4": "dev-develop",
|
"codeigniter4/codeigniter4": "dev-develop",
|
||||||
"league/commonmark": "^1.5.7",
|
"league/commonmark": "^1.6.0",
|
||||||
"vlucas/phpdotenv": "^v5.3.0",
|
"vlucas/phpdotenv": "^v5.3.0",
|
||||||
"league/html-to-markdown": "^4.10",
|
"league/html-to-markdown": "^4.10",
|
||||||
"opawg/user-agents-php": "^v1.0",
|
"opawg/user-agents-php": "^v1.0",
|
||||||
"podlibre/ipcat": "^v1.0",
|
"podlibre/ipcat": "^v1.0",
|
||||||
"podlibre/podcast-namespace": "^v1.0.6",
|
"podlibre/podcast-namespace": "^v1.0.6",
|
||||||
"phpseclib/phpseclib": "~2.0.30",
|
"phpseclib/phpseclib": "~2.0.30",
|
||||||
"michalsn/codeigniter4-uuid": "^1.0@beta",
|
"michalsn/codeigniter4-uuid": "^v1.0.0",
|
||||||
"essence/essence": "^3.5.4"
|
"essence/essence": "^3.5.4"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
61
composer.lock
generated
61
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "b5d726bdc7252c80c0fd5a6f53de1948",
|
"content-hash": "f370b196462e2ca2ff3e2df9627f2ba4",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "brick/math",
|
"name": "brick/math",
|
||||||
@ -68,12 +68,12 @@
|
|||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/codeigniter4/CodeIgniter4.git",
|
"url": "https://github.com/codeigniter4/CodeIgniter4.git",
|
||||||
"reference": "dfbc85af9ef408a6654cce6a462c8fdde3ee2446"
|
"reference": "8b2e7c29043977fac378c37690cc951a715c2bd5"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/codeigniter4/CodeIgniter4/zipball/dfbc85af9ef408a6654cce6a462c8fdde3ee2446",
|
"url": "https://api.github.com/repos/codeigniter4/CodeIgniter4/zipball/8b2e7c29043977fac378c37690cc951a715c2bd5",
|
||||||
"reference": "dfbc85af9ef408a6654cce6a462c8fdde3ee2446",
|
"reference": "8b2e7c29043977fac378c37690cc951a715c2bd5",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -91,10 +91,10 @@
|
|||||||
"fakerphp/faker": "^1.9",
|
"fakerphp/faker": "^1.9",
|
||||||
"mikey179/vfsstream": "^1.6",
|
"mikey179/vfsstream": "^1.6",
|
||||||
"nexusphp/tachycardia": "^1.0",
|
"nexusphp/tachycardia": "^1.0",
|
||||||
"phpstan/phpstan": "0.12.84",
|
"phpstan/phpstan": "0.12.85",
|
||||||
"phpunit/phpunit": "^9.1",
|
"phpunit/phpunit": "^9.1",
|
||||||
"predis/predis": "^1.1",
|
"predis/predis": "^1.1",
|
||||||
"rector/rector": "0.10.6",
|
"rector/rector": "0.10.17",
|
||||||
"squizlabs/php_codesniffer": "^3.3"
|
"squizlabs/php_codesniffer": "^3.3"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
@ -139,7 +139,7 @@
|
|||||||
"slack": "https://codeigniterchat.slack.com",
|
"slack": "https://codeigniterchat.slack.com",
|
||||||
"issues": "https://github.com/codeigniter4/CodeIgniter4/issues"
|
"issues": "https://github.com/codeigniter4/CodeIgniter4/issues"
|
||||||
},
|
},
|
||||||
"time": "2021-04-20T08:40:30+00:00"
|
"time": "2021-05-03T08:32:21+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "composer/ca-bundle",
|
"name": "composer/ca-bundle",
|
||||||
@ -823,16 +823,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "league/commonmark",
|
"name": "league/commonmark",
|
||||||
"version": "1.5.8",
|
"version": "1.6.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/thephpleague/commonmark.git",
|
"url": "https://github.com/thephpleague/commonmark.git",
|
||||||
"reference": "08fa59b8e4e34ea8a773d55139ae9ac0e0aecbaf"
|
"reference": "19a9673b833cc37770439097b381d86cd125bfe8"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/08fa59b8e4e34ea8a773d55139ae9ac0e0aecbaf",
|
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/19a9673b833cc37770439097b381d86cd125bfe8",
|
||||||
"reference": "08fa59b8e4e34ea8a773d55139ae9ac0e0aecbaf",
|
"reference": "19a9673b833cc37770439097b381d86cd125bfe8",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -920,7 +920,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-03-28T18:51:39+00:00"
|
"time": "2021-05-01T19:00:49+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "league/html-to-markdown",
|
"name": "league/html-to-markdown",
|
||||||
@ -1125,16 +1125,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "michalsn/codeigniter4-uuid",
|
"name": "michalsn/codeigniter4-uuid",
|
||||||
"version": "v1.0.0-beta3",
|
"version": "v1.0.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/michalsn/codeigniter4-uuid.git",
|
"url": "https://github.com/michalsn/codeigniter4-uuid.git",
|
||||||
"reference": "568aba8f315199b6cc87e76b8441cd03a2bba5b4"
|
"reference": "c8bbd961401015307bc72f6f6aa93509ffac1d5f"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/michalsn/codeigniter4-uuid/zipball/568aba8f315199b6cc87e76b8441cd03a2bba5b4",
|
"url": "https://api.github.com/repos/michalsn/codeigniter4-uuid/zipball/c8bbd961401015307bc72f6f6aa93509ffac1d5f",
|
||||||
"reference": "568aba8f315199b6cc87e76b8441cd03a2bba5b4",
|
"reference": "c8bbd961401015307bc72f6f6aa93509ffac1d5f",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -1172,9 +1172,9 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/michalsn/codeigniter4-uuid/issues",
|
"issues": "https://github.com/michalsn/codeigniter4-uuid/issues",
|
||||||
"source": "https://github.com/michalsn/codeigniter4-uuid/tree/v1.0.0-beta3"
|
"source": "https://github.com/michalsn/codeigniter4-uuid/tree/v1.0.1"
|
||||||
},
|
},
|
||||||
"time": "2021-04-02T11:08:18+00:00"
|
"time": "2021-05-03T12:47:44+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "myth/auth",
|
"name": "myth/auth",
|
||||||
@ -1182,12 +1182,12 @@
|
|||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/lonnieezell/myth-auth.git",
|
"url": "https://github.com/lonnieezell/myth-auth.git",
|
||||||
"reference": "eff9805d7f1d27326f14875b53ff4b3d2a6b72ee"
|
"reference": "2b42da1884745eec22ac10f7941a4f9350576a86"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/lonnieezell/myth-auth/zipball/eff9805d7f1d27326f14875b53ff4b3d2a6b72ee",
|
"url": "https://api.github.com/repos/lonnieezell/myth-auth/zipball/2b42da1884745eec22ac10f7941a4f9350576a86",
|
||||||
"reference": "eff9805d7f1d27326f14875b53ff4b3d2a6b72ee",
|
"reference": "2b42da1884745eec22ac10f7941a4f9350576a86",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -1248,7 +1248,7 @@
|
|||||||
"type": "patreon"
|
"type": "patreon"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-04-12T22:34:12+00:00"
|
"time": "2021-05-02T05:32:03+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "opawg/user-agents-php",
|
"name": "opawg/user-agents-php",
|
||||||
@ -1584,16 +1584,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "psr/log",
|
"name": "psr/log",
|
||||||
"version": "1.1.3",
|
"version": "1.1.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/php-fig/log.git",
|
"url": "https://github.com/php-fig/log.git",
|
||||||
"reference": "0f73288fd15629204f9d42b7055f72dacbe811fc"
|
"reference": "d49695b909c3b7628b6289db5479a1c204601f11"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
|
"url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
|
||||||
"reference": "0f73288fd15629204f9d42b7055f72dacbe811fc",
|
"reference": "d49695b909c3b7628b6289db5479a1c204601f11",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -1617,7 +1617,7 @@
|
|||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "PHP-FIG",
|
"name": "PHP-FIG",
|
||||||
"homepage": "http://www.php-fig.org/"
|
"homepage": "https://www.php-fig.org/"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "Common interface for logging libraries",
|
"description": "Common interface for logging libraries",
|
||||||
@ -1628,9 +1628,9 @@
|
|||||||
"psr-3"
|
"psr-3"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/php-fig/log/tree/1.1.3"
|
"source": "https://github.com/php-fig/log/tree/1.1.4"
|
||||||
},
|
},
|
||||||
"time": "2020-03-23T09:12:05+00:00"
|
"time": "2021-05-03T11:20:27+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ramsey/collection",
|
"name": "ramsey/collection",
|
||||||
@ -4314,8 +4314,7 @@
|
|||||||
"stability-flags": {
|
"stability-flags": {
|
||||||
"james-heinrich/getid3": 20,
|
"james-heinrich/getid3": 20,
|
||||||
"myth/auth": 20,
|
"myth/auth": 20,
|
||||||
"codeigniter4/codeigniter4": 20,
|
"codeigniter4/codeigniter4": 20
|
||||||
"michalsn/codeigniter4-uuid": 10
|
|
||||||
},
|
},
|
||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
"prefer-lowest": false,
|
"prefer-lowest": false,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user