Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • alpha
  • beta
  • develop
  • docs/fix-readme
  • docs/update-vitepress
  • draft/rss-feed
  • feat/dashboard
  • feat/episodes-page-ux
  • feat/generator-user-agent
  • feat/headliner
  • feat/new-languages
  • feat/plugins
  • fix/federation
  • fix/forms-ux
  • i18n
  • main
  • next
  • refactor/transcripts
  • v1.0.0
  • v1.0.0-alpha.1
  • v1.0.0-alpha.10
  • v1.0.0-alpha.11
  • v1.0.0-alpha.12
  • v1.0.0-alpha.13
  • v1.0.0-alpha.14
  • v1.0.0-alpha.15
  • v1.0.0-alpha.16
  • v1.0.0-alpha.17
  • v1.0.0-alpha.18
  • v1.0.0-alpha.19
  • v1.0.0-alpha.2
  • v1.0.0-alpha.20
  • v1.0.0-alpha.21
  • v1.0.0-alpha.22
  • v1.0.0-alpha.23
  • v1.0.0-alpha.24
  • v1.0.0-alpha.25
  • v1.0.0-alpha.26
  • v1.0.0-alpha.27
  • v1.0.0-alpha.28
  • v1.0.0-alpha.29
  • v1.0.0-alpha.3
  • v1.0.0-alpha.30
  • v1.0.0-alpha.31
  • v1.0.0-alpha.32
  • v1.0.0-alpha.33
  • v1.0.0-alpha.34
  • v1.0.0-alpha.35
  • v1.0.0-alpha.36
  • v1.0.0-alpha.37
  • v1.0.0-alpha.38
  • v1.0.0-alpha.39
  • v1.0.0-alpha.4
  • v1.0.0-alpha.40
  • v1.0.0-alpha.41
  • v1.0.0-alpha.42
  • v1.0.0-alpha.43
  • v1.0.0-alpha.44
  • v1.0.0-alpha.45
  • v1.0.0-alpha.46
  • v1.0.0-alpha.47
  • v1.0.0-alpha.48
  • v1.0.0-alpha.49
  • v1.0.0-alpha.5
  • v1.0.0-alpha.50
  • v1.0.0-alpha.51
  • v1.0.0-alpha.52
  • v1.0.0-alpha.53
  • v1.0.0-alpha.54
  • v1.0.0-alpha.55
  • v1.0.0-alpha.56
  • v1.0.0-alpha.57
  • v1.0.0-alpha.58
  • v1.0.0-alpha.59
  • v1.0.0-alpha.6
  • v1.0.0-alpha.60
  • v1.0.0-alpha.61
  • v1.0.0-alpha.62
  • v1.0.0-alpha.63
  • v1.0.0-alpha.64
  • v1.0.0-alpha.65
  • v1.0.0-alpha.66
  • v1.0.0-alpha.67
  • v1.0.0-alpha.68
  • v1.0.0-alpha.69
  • v1.0.0-alpha.7
  • v1.0.0-alpha.70
  • v1.0.0-alpha.71
  • v1.0.0-alpha.72
  • v1.0.0-alpha.73
  • v1.0.0-alpha.74
  • v1.0.0-alpha.75
  • v1.0.0-alpha.76
  • v1.0.0-alpha.77
  • v1.0.0-alpha.78
  • v1.0.0-alpha.79
  • v1.0.0-alpha.8
  • v1.0.0-alpha.80
  • v1.0.0-alpha.9
  • v1.0.0-beta.1
  • v1.0.0-beta.10
  • v1.0.0-beta.11
  • v1.0.0-beta.12
  • v1.0.0-beta.13
  • v1.0.0-beta.14
  • v1.0.0-beta.15
  • v1.0.0-beta.16
  • v1.0.0-beta.17
  • v1.0.0-beta.18
  • v1.0.0-beta.19
  • v1.0.0-beta.2
  • v1.0.0-beta.20
  • v1.0.0-beta.21
  • v1.0.0-beta.22
  • v1.0.0-beta.23
  • v1.0.0-beta.24
  • v1.0.0-beta.3
  • v1.0.0-beta.4
118 results

Target

Select target project
  • adaures/castopod
  • mkljczk/castopod-host
  • spaetz/castopod-host
  • PatrykMis/castopod
  • jonas/castopod
  • ajeremias/castopod
  • misuzu/castopod
  • KrzysztofDomanczyk/castopod
  • Behel/castopod
  • nebulon/castopod
  • ewen/castopod
  • NeoluxConsulting/castopod
  • nateritter/castopod-og
  • prcutler/castopod
14 results
Select Git revision
  • alpha
  • beta
  • develop
  • docs/fix-readme
  • docs/update-vitepress
  • draft/rss-feed
  • feat/dashboard
  • feat/episodes-page-ux
  • feat/generator-user-agent
  • feat/headliner
  • feat/new-languages
  • feat/plugins
  • fix/federation
  • fix/forms-ux
  • i18n
  • main
  • next
  • refactor/transcripts
  • v1.0.0
  • v1.0.0-alpha.1
  • v1.0.0-alpha.10
  • v1.0.0-alpha.11
  • v1.0.0-alpha.12
  • v1.0.0-alpha.13
  • v1.0.0-alpha.14
  • v1.0.0-alpha.15
  • v1.0.0-alpha.16
  • v1.0.0-alpha.17
  • v1.0.0-alpha.18
  • v1.0.0-alpha.19
  • v1.0.0-alpha.2
  • v1.0.0-alpha.20
  • v1.0.0-alpha.21
  • v1.0.0-alpha.22
  • v1.0.0-alpha.23
  • v1.0.0-alpha.24
  • v1.0.0-alpha.25
  • v1.0.0-alpha.26
  • v1.0.0-alpha.27
  • v1.0.0-alpha.28
  • v1.0.0-alpha.29
  • v1.0.0-alpha.3
  • v1.0.0-alpha.30
  • v1.0.0-alpha.31
  • v1.0.0-alpha.32
  • v1.0.0-alpha.33
  • v1.0.0-alpha.34
  • v1.0.0-alpha.35
  • v1.0.0-alpha.36
  • v1.0.0-alpha.37
  • v1.0.0-alpha.38
  • v1.0.0-alpha.39
  • v1.0.0-alpha.4
  • v1.0.0-alpha.40
  • v1.0.0-alpha.41
  • v1.0.0-alpha.42
  • v1.0.0-alpha.43
  • v1.0.0-alpha.44
  • v1.0.0-alpha.45
  • v1.0.0-alpha.46
  • v1.0.0-alpha.47
  • v1.0.0-alpha.48
  • v1.0.0-alpha.49
  • v1.0.0-alpha.5
  • v1.0.0-alpha.50
  • v1.0.0-alpha.51
  • v1.0.0-alpha.52
  • v1.0.0-alpha.53
  • v1.0.0-alpha.54
  • v1.0.0-alpha.55
  • v1.0.0-alpha.56
  • v1.0.0-alpha.57
  • v1.0.0-alpha.58
  • v1.0.0-alpha.59
  • v1.0.0-alpha.6
  • v1.0.0-alpha.60
  • v1.0.0-alpha.61
  • v1.0.0-alpha.62
  • v1.0.0-alpha.63
  • v1.0.0-alpha.64
  • v1.0.0-alpha.65
  • v1.0.0-alpha.66
  • v1.0.0-alpha.67
  • v1.0.0-alpha.68
  • v1.0.0-alpha.69
  • v1.0.0-alpha.7
  • v1.0.0-alpha.70
  • v1.0.0-alpha.71
  • v1.0.0-alpha.72
  • v1.0.0-alpha.73
  • v1.0.0-alpha.74
  • v1.0.0-alpha.75
  • v1.0.0-alpha.76
  • v1.0.0-alpha.77
  • v1.0.0-alpha.78
  • v1.0.0-alpha.79
  • v1.0.0-alpha.8
  • v1.0.0-alpha.80
  • v1.0.0-alpha.9
  • v1.0.0-beta.1
  • v1.0.0-beta.10
  • v1.0.0-beta.11
  • v1.0.0-beta.12
  • v1.0.0-beta.13
  • v1.0.0-beta.14
  • v1.0.0-beta.15
  • v1.0.0-beta.16
  • v1.0.0-beta.17
  • v1.0.0-beta.18
  • v1.0.0-beta.19
  • v1.0.0-beta.2
  • v1.0.0-beta.20
  • v1.0.0-beta.21
  • v1.0.0-beta.22
  • v1.0.0-beta.23
  • v1.0.0-beta.24
  • v1.0.0-beta.3
  • v1.0.0-beta.4
118 results
Show changes
Showing
with 551 additions and 630 deletions
......@@ -7,8 +7,6 @@
*/
return [
'not_in_protected_slugs' =>
'Le champ {field} est en conflit avec une des routes (admin, auth ou install).',
'min_dims' =>
'{field} n’est pas une image ou n’a pas la taille minimale requise.',
'is_image_squared' =>
......
......@@ -30,4 +30,11 @@ class ActivityPub extends BaseConfig
public $defaultCoverImagePath = 'assets/images/cover-default.jpg';
public $defaultCoverImageMimetype = 'image/jpeg';
/**
* --------------------------------------------------------------------
* Cache options
* --------------------------------------------------------------------
*/
public $cachePrefix = 'ap_';
}
......@@ -44,11 +44,21 @@ class Note extends UuidEntity
*/
protected $preview_card;
/**
* @var boolean
*/
protected $has_preview_card;
/**
* @var \ActivityPub\Entities\Note[]
*/
protected $replies;
/**
* @var boolean
*/
protected $has_replies;
/**
* @var \ActivityPub\Entities\Note[]
*/
......@@ -106,6 +116,18 @@ class Note extends UuidEntity
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()
{
if (empty($this->id)) {
......@@ -121,11 +143,9 @@ class Note extends UuidEntity
return $this->replies;
}
public function getIsReply()
public function getHasReplies()
{
$this->is_reply = $this->in_reply_to_id !== null;
return $this->is_reply;
return !empty($this->getReplies()) ? true : false;
}
public function getReplyToNote()
......@@ -152,11 +172,7 @@ class Note extends UuidEntity
}
if (empty($this->reblogs)) {
$this->reblogs = model('NoteModel')->getNoteReblogs(
service('uuid')
->fromString($this->id)
->getBytes(),
);
$this->reblogs = model('NoteModel')->getNoteReblogs($this->id);
}
return $this->reblogs;
......
......@@ -8,6 +8,8 @@
namespace ActivityPub\Models;
use Michalsn\Uuid\UuidModel;
class ActivityModel extends UuidModel
{
protected $table = 'activitypub_activities';
......@@ -34,7 +36,15 @@ class ActivityModel extends UuidModel
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;
use CodeIgniter\Events\Events;
use CodeIgniter\Model;
class ActorModel extends Model
......@@ -42,7 +43,14 @@ class ActorModel extends Model
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)
{
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)
{
return $this->join(
'activitypub_follows',
'activitypub_follows.actor_id = id',
'inner',
)
->where('activitypub_follows.target_actor_id', $actorId)
->findAll();
$cacheName =
config('ActivityPub')->cachePrefix . "actor#{$actorId}_followers";
if (!($found = cache($cacheName))) {
$found = $this->join(
'activitypub_follows',
'activitypub_follows.actor_id = id',
'inner',
)
->where('activitypub_follows.target_actor_id', $actorId)
->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
* @return boolean
*/
public function isActorBlocked($actorUri)
{
return $this->where(['uri' => $actorUri, 'is_blocked' => true])->first()
? true
: false;
if ($actor = $this->getActorByUri($actorUri)) {
return $actor->is_blocked;
}
return false;
}
/**
......@@ -110,16 +138,35 @@ class ActorModel extends Model
*/
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)
{
$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]);
}
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]);
}
}
......@@ -8,6 +8,7 @@
namespace ActivityPub\Models;
use CodeIgniter\Events\Events;
use CodeIgniter\Model;
class BlockedDomainModel extends Model
......@@ -30,20 +31,41 @@ class BlockedDomainModel extends Model
*/
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)
{
if ($this->find($domain)) {
return true;
$hashedDomain = md5($domain);
$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)
{
$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();
// set all actors from the domain as blocked
......@@ -63,6 +85,15 @@ class BlockedDomainModel extends Model
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();
// unblock all actors from the domain
model('ActorModel')
......
......@@ -11,6 +11,7 @@ namespace ActivityPub\Models;
use ActivityPub\Activities\LikeActivity;
use ActivityPub\Activities\UndoActivity;
use CodeIgniter\Events\Events;
use Michalsn\Uuid\UuidModel;
class FavouriteModel extends UuidModel
{
......@@ -49,6 +50,19 @@ class FavouriteModel extends UuidModel
)
->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);
if ($registerActivity) {
......@@ -91,6 +105,19 @@ class FavouriteModel extends UuidModel
)
->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')
->where([
'actor_id' => $actor->id,
......
......@@ -27,9 +27,8 @@ class FollowModel extends Model
protected $updatedField = null;
/**
*
* @param \ActivityPub\Entities\Actor $actor
* @param \ActivityPub\Entities\Actor $targetActor
* @param \ActivityPub\Entities\Actor $actor Actor that is following
* @param \ActivityPub\Entities\Actor $targetActor Actor that is being followed
* @param bool $registerActivity
* @return void
* @throws DatabaseException
......@@ -49,6 +48,14 @@ class FollowModel extends Model
->where('id', $targetActor->id)
->increment('followers_count');
cache()->delete(
config('ActivityPub')->cachePrefix . "actor#{$targetActor->id}",
);
cache()->delete(
config('ActivityPub')->cachePrefix .
"actor#{$targetActor->id}_followers",
);
if ($registerActivity) {
$followActivity = new FollowActivity();
......@@ -85,8 +92,8 @@ class FollowModel extends Model
}
/**
* @param \ActivityPub\Entities\Actor $actor
* @param \ActivityPub\Entities\Actor $targetActor
* @param \ActivityPub\Entities\Actor $actor Actor that is unfollowing
* @param \ActivityPub\Entities\Actor $targetActor Actor that is being unfollowed
* @return void
* @throws InvalidArgumentException
* @throws DatabaseException
......@@ -108,6 +115,14 @@ class FollowModel extends Model
->where('id', $targetActor->id)
->decrement('followers_count');
cache()->delete(
config('ActivityPub')->cachePrefix . "actor#{$targetActor->id}",
);
cache()->delete(
config('ActivityPub')->cachePrefix .
"actor#{$targetActor->id}_followers",
);
if ($registerActivity) {
$undoActivity = new UndoActivity();
// get follow activity from database
......
......@@ -17,6 +17,7 @@ use ActivityPub\Objects\TombstoneObject;
use CodeIgniter\Events\Events;
use CodeIgniter\HTTP\URI;
use CodeIgniter\I18n\Time;
use Michalsn\Uuid\UuidModel;
class NoteModel extends UuidModel
{
......@@ -54,12 +55,28 @@ class NoteModel extends UuidModel
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)
{
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[]
*/
public function getActorNotes($actorId)
public function getActorPublishedNotes($actorId)
{
return $this->where([
'actor_id' => $actorId,
'in_reply_to_id' => null,
])
->where('`published_at` <= NOW()', null, false)
->orderBy('published_at', 'DESC')
->findAll();
$cacheName =
config('ActivityPub')->cachePrefix .
"actor#{$actorId}_published_notes";
if (!($found = cache($cacheName))) {
$found = $this->where([
'actor_id' => $actorId,
'in_reply_to_id' => null,
])
->where('`published_at` <= NOW()', null, false)
->orderBy('published_at', 'DESC')
->findAll();
cache()->save($cacheName, $found, DECADE);
}
return $found;
}
/**
......@@ -88,26 +114,34 @@ class NoteModel extends UuidModel
*/
public function getNoteReplies($noteId, $withBlocked = false)
{
if (!$withBlocked) {
$this->select('activitypub_notes.*')
->join(
'activitypub_actors',
'activitypub_actors.id = activitypub_notes.actor_id',
'inner',
)
->where('activitypub_actors.is_blocked', 0);
}
$cacheName =
config('ActivityPub')->cachePrefix .
"note#{$noteId}_replies" .
($withBlocked ? '_withBlocked' : '');
if (!($found = cache($cacheName))) {
if (!$withBlocked) {
$this->select('activitypub_notes.*')
->join(
'activitypub_actors',
'activitypub_actors.id = activitypub_notes.actor_id',
'inner',
)
->where('activitypub_actors.is_blocked', 0);
}
$this->where(
'in_reply_to_id',
service('uuid')
->fromString($noteId)
->getBytes(),
)
->where('`published_at` <= NOW()', null, false)
->orderBy('published_at', 'ASC');
$this->where(
'in_reply_to_id',
$this->uuid->fromString($noteId)->getBytes(),
)
->where('`published_at` <= NOW()', null, false)
->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)
{
return $this->where('reblog_of_id', $noteId)
->where('`published_at` <= NOW()', null, false)
->orderBy('published_at', 'ASC')
->findAll();
$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)
->orderBy('published_at', 'ASC')
->findAll();
cache()->save($cacheName, $found, DECADE);
}
return $found;
}
public function addPreviewCard($noteId, $previewCardId)
{
return $this->db->table('activitypub_notes_preview_cards')->insert([
'note_id' => $noteId,
'note_id' => $this->uuid->fromString($noteId)->getBytes(),
'preview_card_id' => $previewCardId,
]);
}
......@@ -169,10 +215,6 @@ class NoteModel extends UuidModel
// problem when linking note to preview card
return false;
}
$this->db->transComplete();
return $newNoteId;
}
}
......@@ -180,17 +222,19 @@ class NoteModel extends UuidModel
->where('id', $note->actor_id)
->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);
if ($registerActivity) {
$noteUuid = service('uuid')
->fromBytes($newNoteId)
->toString();
// set note id and uri to construct NoteObject
$note->id = $noteUuid;
$note->id = $newNoteId;
$note->uri = base_url(
route_to('note', $note->actor->username, $noteUuid),
route_to('note', $note->actor->username, $newNoteId),
);
$createActivity = new CreateActivity();
......@@ -203,7 +247,7 @@ class NoteModel extends UuidModel
'Create',
$note->actor_id,
null,
$noteUuid,
$newNoteId,
$createActivity->toJSON(),
$note->published_at,
'queued',
......@@ -234,7 +278,7 @@ class NoteModel extends UuidModel
$scheduledActivity = model('ActivityModel')
->where([
'type' => 'Create',
'note_id' => service('uuid')
'note_id' => $this->uuid
->fromString($updatedNote->id)
->getBytes(),
])
......@@ -253,6 +297,12 @@ class NoteModel extends UuidModel
// update note
$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();
return $updateResult;
......@@ -268,32 +318,61 @@ class NoteModel extends UuidModel
{
$this->db->transStart();
$cachePrefix = config('ActivityPub')->cachePrefix;
model('ActorModel')
->where('id', $note->actor_id)
->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) {
// Note to remove is a reply
model('NoteModel')
->where(
'id',
service('uuid')
->fromString($note->in_reply_to_id)
->getBytes(),
$this->uuid->fromString($note->in_reply_to_id)->getBytes(),
)
->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) {
$this->removeNote($reblog);
}
// remove all replies
// remove all note replies
foreach ($note->replies as $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);
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);
$this->db->transComplete();
......@@ -349,12 +437,19 @@ class NoteModel extends UuidModel
model('NoteModel')
->where(
'id',
service('uuid')
->fromString($reply->in_reply_to_id)
->getBytes(),
$this->uuid->fromString($reply->in_reply_to_id)->getBytes(),
)
->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);
$this->db->transComplete();
......@@ -385,15 +480,19 @@ class NoteModel extends UuidModel
->where('id', $actor->id)
->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')
->where(
'id',
service('uuid')
->fromString($note->id)
->getBytes(),
)
->where('id', $this->uuid->fromString($note->id)->getBytes())
->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);
if ($registerActivity) {
......@@ -438,15 +537,26 @@ class NoteModel extends UuidModel
->where('id', $reblogNote->actor_id)
->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')
->where(
'id',
service('uuid')
->fromString($reblogNote->reblog_of_id)
->getBytes(),
$this->uuid->fromString($reblogNote->reblog_of_id)->getBytes(),
)
->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);
if ($registerActivity) {
......@@ -456,7 +566,7 @@ class NoteModel extends UuidModel
->where([
'type' => 'Announce',
'actor_id' => $reblogNote->actor_id,
'note_id' => service('uuid')
'note_id' => $this->uuid
->fromString($reblogNote->reblog_of_id)
->getBytes(),
])
......@@ -516,7 +626,7 @@ class NoteModel extends UuidModel
if (
!($reblogNote = $this->where([
'actor_id' => $actor->id,
'reblog_of_id' => service('uuid')
'reblog_of_id' => $this->uuid
->fromString($note->id)
->getBytes(),
])->first())
......@@ -529,9 +639,8 @@ class NoteModel extends UuidModel
protected function setNoteId($data)
{
$uuid4 = service('uuid')->uuid4();
$data['id'] = $uuid4->toString();
$data['data']['id'] = $uuid4->getBytes();
$uuid4 = $this->uuid->{$this->uuidVersion}();
$data['data']['id'] = $uuid4->toString();
if (!isset($data['data']['uri'])) {
$actor = model('ActorModel')->getActorById(
......
......@@ -35,22 +35,50 @@ class PreviewCardModel extends Model
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)
{
return $this->join(
'activitypub_notes_preview_cards',
'activitypub_notes_preview_cards.preview_card_id = id',
'inner',
)
->where(
'note_id',
service('uuid')
->fromString($noteId)
->getBytes(),
$cacheName =
config('ActivityPub')->cachePrefix . "note#{$noteId}_preview_card";
if (!($found = cache($cacheName))) {
$found = $this->join(
'activitypub_notes_preview_cards',
'activitypub_notes_preview_cards.preview_card_id = id',
'inner',
)
->first();
->where(
'note_id',
service('uuid')
->fromString($noteId)
->getBytes(),
)
->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);
}
}
<?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;
}
}
<?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')) {
/**
* Counting podcast episode downloads for analytic purposes
......
<?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,28 +27,31 @@ class CategoryModel extends Model
protected $useTimestamps = false;
public function findParent($parentId)
public function getCategoryById($id)
{
return $this->find($parentId);
return $this->find($id);
}
public function getCategoryOptions()
{
if (!($options = cache('category_options'))) {
$locale = service('request')->getLocale();
$cacheName = "category_options_{$locale}";
if (!($options = cache($cacheName))) {
$categories = $this->findAll();
$options = array_reduce(
$categories,
function ($result, $category) {
$result[$category->id] = lang(
'Podcast.category_options.' . $category->code
'Podcast.category_options.' . $category->code,
);
return $result;
},
[]
[],
);
cache()->save('category_options', $options, DECADE);
cache()->save($cacheName, $options, DECADE);
}
return $options;
......@@ -64,7 +67,7 @@ class CategoryModel extends Model
*/
public function setPodcastCategories($podcastId, $categories)
{
cache()->delete("podcasts{$podcastId}_categories");
cache()->delete("podcast#{$podcastId}_categories");
// Remove already previously set categories to overwrite them
$this->db
......@@ -83,7 +86,7 @@ class CategoryModel extends Model
return $result;
},
[]
[],
);
// Set podcast categories
......@@ -103,20 +106,17 @@ class CategoryModel extends Model
*/
public function getPodcastCategories($podcastId)
{
if (!($categories = cache("podcasts{$podcastId}_categories"))) {
$cacheName = "podcast#{$podcastId}_categories";
if (!($categories = cache($cacheName))) {
$categories = $this->select('categories.*')
->join(
'podcasts_categories',
'podcasts_categories.category_id = categories.id'
'podcasts_categories.category_id = categories.id',
)
->where('podcasts_categories.podcast_id', $podcastId)
->findAll();
cache()->save(
"podcasts{$podcastId}_categories",
$categories,
DECADE
);
cache()->save($cacheName, $categories, DECADE);
}
return $categories;
......
......@@ -113,7 +113,8 @@ class EpisodeModel extends Model
*/
public function getEpisodeBySlug($podcastId, $episodeSlug)
{
if (!($found = cache("podcast@{$podcastId}_episode@{$episodeSlug}"))) {
$cacheName = "podcast#{$podcastId}_episode@{$episodeSlug}";
if (!($found = cache($cacheName))) {
$builder = $this->select('episodes.*')
->where('slug', $episodeSlug)
->where('`published_at` <= NOW()', null, false);
......@@ -130,11 +131,7 @@ class EpisodeModel extends Model
$found = $builder->first();
cache()->save(
"podcast{$podcastId}_episode@{$episodeSlug}",
$found,
DECADE,
);
cache()->save($cacheName, $found, DECADE);
}
return $found;
......@@ -142,37 +139,33 @@ class EpisodeModel extends Model
public function getEpisodeById($episodeId)
{
if (!($found = cache("podcast_episode{$episodeId}"))) {
// TODO: episode id should be a composite key. The cache should include podcast_id.
$cacheName = "podcast_episode#{$episodeId}";
if (!($found = cache($cacheName))) {
$builder = $this->where([
'id' => $episodeId,
]);
$found = $builder->first();
cache()->save("podcast_episode{$episodeId}", $found, DECADE);
cache()->save($cacheName, $found, DECADE);
}
return $found;
}
public function getPublishedEpisodeById($episodeId, $podcastId = null)
public function getPublishedEpisodeById($podcastId, $episodeId)
{
if (!($found = cache("podcast{$podcastId}_episode{$episodeId}"))) {
$builder = $this->where([
$cacheName = "podcast#{$podcastId}_episode#{$episodeId}_published";
if (!($found = cache($cacheName))) {
$found = $this->where([
'id' => $episodeId,
])->where('`published_at` <= NOW()', null, false);
if ($podcastId) {
$builder->where('podcast_id', $podcastId);
}
$found = $builder->first();
])
->where('podcast_id', $podcastId)
->where('`published_at` <= NOW()', null, false)
->first();
cache()->save(
"podcast{$podcastId}_episode{$episodeId}",
$found,
DECADE,
);
cache()->save($cacheName, $found, DECADE);
}
return $found;
......@@ -195,7 +188,7 @@ class EpisodeModel extends Model
$cacheName = implode(
'_',
array_filter([
"podcast{$podcastId}",
"podcast#{$podcastId}",
$year,
$season ? 'season' . $season : null,
'episodes',
......@@ -243,106 +236,6 @@ class EpisodeModel extends Model
return $found;
}
public function getYears(int $podcastId): array
{
if (!($found = cache("podcast{$podcastId}_years"))) {
$found = $this->select(
'YEAR(published_at) as year, count(*) as number_of_episodes',
)
->where([
'podcast_id' => $podcastId,
'season_number' => null,
$this->deletedField => null,
])
->where('`published_at` <= NOW()', null, false)
->groupBy('year')
->orderBy('year', 'DESC')
->get()
->getResultArray();
$secondsToNextUnpublishedEpisode = $this->getSecondsToNextUnpublishedEpisode(
$podcastId,
);
cache()->save(
"podcast{$podcastId}_years",
$found,
$secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode
: DECADE,
);
}
return $found;
}
public function getSeasons(int $podcastId): array
{
if (!($found = cache("podcast{$podcastId}_seasons"))) {
$found = $this->select(
'season_number, count(*) as number_of_episodes',
)
->where([
'podcast_id' => $podcastId,
'season_number is not' => null,
$this->deletedField => null,
])
->where('`published_at` <= NOW()', null, false)
->groupBy('season_number')
->orderBy('season_number', 'ASC')
->get()
->getResultArray();
$secondsToNextUnpublishedEpisode = $this->getSecondsToNextUnpublishedEpisode(
$podcastId,
);
cache()->save(
"podcast{$podcastId}_seasons",
$found,
$secondsToNextUnpublishedEpisode
? $secondsToNextUnpublishedEpisode
: DECADE,
);
}
return $found;
}
/**
* Returns the default query for displaying the episode list on the podcast page
*
* @param int $podcastId
*
* @return array|null
*/
public function getDefaultQuery(int $podcastId)
{
if (!($defaultQuery = cache("podcast{$podcastId}_defaultQuery"))) {
$seasons = $this->getSeasons($podcastId);
if (!empty($seasons)) {
// get latest season
$defaultQuery = ['type' => 'season', 'data' => end($seasons)];
} else {
$years = $this->getYears($podcastId);
if (!empty($years)) {
// get most recent year
$defaultQuery = ['type' => 'year', 'data' => $years[0]];
} else {
$defaultQuery = null;
}
}
cache()->save(
"podcast{$podcastId}_defaultQuery",
$defaultQuery,
DECADE,
);
}
return $defaultQuery;
}
/**
* Returns the timestamp difference in seconds between the next episode to publish and the current timestamp
* Returns false if there's no episode to publish
......@@ -387,70 +280,43 @@ class EpisodeModel extends Model
);
// delete cache for rss feed
cache()->delete("podcast{$episode->podcast_id}_feed");
foreach (\Opawg\UserAgentsPhp\UserAgentsRSS::$db as $service) {
cache()->delete(
"podcast{$episode->podcast_id}_feed_{$service['slug']}",
);
}
cache()->deleteMatching("podcast#{$episode->podcast_id}_feed*");
// delete model requests cache
cache()->delete("podcast{$episode->podcast_id}_episodes");
cache()->delete("podcast#{$episode->podcast_id}_episodes");
cache()->delete("podcast_episode#{$episode->id}");
cache()->deleteMatching(
"podcast#{$episode->podcast_id}_episode#{$episode->id}*",
);
cache()->delete(
"podcast{$episode->podcast_id}_episode@{$episode->slug}",
"podcast#{$episode->podcast_id}_episode@{$episode->slug}",
);
cache()->delete("podcast_episode{$episode->id}");
// delete episode lists cache per year / season for a podcast
// and localized pages
$episodeModel = new EpisodeModel();
$years = $episodeModel->getYears($episode->podcast_id);
$seasons = $episodeModel->getSeasons($episode->podcast_id);
$supportedLocales = config('App')->supportedLocales;
foreach ($supportedLocales as $locale) {
cache()->delete(
"page_podcast{$episode->podcast->id}_episode{$episode->id}_{$locale}",
);
cache()->delete("credits_{$locale}");
}
cache()->deleteMatching(
"page_podcast#{$episode->podcast_id}_activity*",
);
cache()->deleteMatching(
"page_podcast#{$episode->podcast_id}_episode#{$episode->id}_*",
);
cache()->deleteMatching('page_credits_*');
foreach ($years as $year) {
cache()->delete(
"podcast{$episode->podcast_id}_year{$year['year']}_episodes",
if ($episode->season_number) {
cache()->deleteMatching("podcast#{$episode->podcast_id}_season*");
cache()->deleteMatching(
"page_podcast#{$episode->podcast_id}_episodes_season*",
);
foreach ($supportedLocales as $locale) {
cache()->delete(
"page_podcast{$episode->podcast_id}_year{$year['year']}_{$locale}",
);
}
}
foreach ($seasons as $season) {
cache()->delete(
"podcast{$episode->podcast_id}_season{$season['season_number']}_episodes",
} else {
cache()->deleteMatching("podcast#{$episode->podcast_id}_year*");
cache()->deleteMatching(
"page_podcast#{$episode->podcast_id}_episodes_year*",
);
foreach ($supportedLocales as $locale) {
cache()->delete(
"page_podcast{$episode->podcast_id}_season{$season['season_number']}_{$locale}",
);
}
}
foreach (array_keys(self::$themes) as $themeKey) {
foreach ($supportedLocales as $locale) {
cache()->delete(
"page_podcast{$episode->podcast_id}_episode{$episode->id}_embeddable_player_{$themeKey}_{$locale}",
);
}
}
// delete query cache
cache()->delete("podcast{$episode->podcast_id}_defaultQuery");
cache()->delete("podcast{$episode->podcast_id}_years");
cache()->delete("podcast{$episode->podcast_id}_seasons");
cache()->delete("podcast#{$episode->podcast_id}_defaultQuery");
cache()->delete("podcast#{$episode->podcast_id}_years");
cache()->delete("podcast#{$episode->podcast_id}_seasons");
return $data;
}
......
......@@ -38,27 +38,17 @@ class EpisodePersonModel extends Model
protected $afterInsert = ['clearCache'];
protected $beforeDelete = ['clearCache'];
public function getPersonsByEpisodeId($podcastId, $episodeId)
public function getEpisodePersons($podcastId, $episodeId)
{
if (
!($found = cache(
"podcast{$podcastId}_episodes{$episodeId}_persons"
))
) {
$cacheName = "podcast#{$podcastId}_episode#{$episodeId}_persons";
if (!($found = cache($cacheName))) {
$found = $this->select('episodes_persons.*')
->where('episode_id', $episodeId)
->join(
'persons',
'person_id=persons.id'
)
->join('persons', 'person_id=persons.id')
->orderby('full_name')
->findAll();
cache()->save(
"podcast{$podcastId}_episodes{$episodeId}_persons",
$found,
DECADE
);
cache()->save($cacheName, $found, DECADE);
}
return $found;
}
......@@ -81,11 +71,9 @@ class EpisodePersonModel extends Model
) {
if (!empty($persons)) {
$this->clearCache([
'id' => [
'podcast_id' => $podcastId,
'episode_id' => $episodeId,
],
'episode_id' => $episodeId,
]);
$data = [];
foreach ($persons as $person) {
if ($groups_roles) {
......@@ -126,23 +114,16 @@ class EpisodePersonModel extends Model
protected function clearCache(array $data)
{
$podcastId = null;
$episodeId = null;
if (
isset($data['id']['podcast_id']) &&
isset($data['id']['episode_id'])
) {
$podcastId = $data['id']['podcast_id'];
$episodeId = $data['id']['episode_id'];
if (isset($data['episode_id'])) {
$episodeId = $data['episode_id'];
} else {
$episodePerson = (new EpisodePersonModel())->find(
is_array($data['id']) ? $data['id']['id'] : $data['id']
$person = (new EpisodePersonModel())->find(
is_array($data['id']) ? $data['id']['id'] : $data['id'],
);
$podcastId = $episodePerson->podcast_id;
$episodeId = $episodePerson->episode_id;
$episodeId = $person->episode_id;
}
cache()->delete("podcast{$podcastId}_episodes{$episodeId}_persons");
(new EpisodeModel())->clearCache(['id' => $episodeId]);
return $data;
......
<?php
/**
* @copyright 2020 Podlibre
* @copyright 2021 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
......
......@@ -25,7 +25,7 @@ class PageModel extends Model
protected $validationRules = [
'title' => 'required',
'slug' =>
'required|regex_match[/^[a-zA-Z0-9\-]{1,191}$/]|is_unique[pages.slug,id,{id}]|not_in_protected_slugs',
'required|regex_match[/^[a-zA-Z0-9\-]{1,191}$/]|is_unique[pages.slug,id,{id}]',
'content' => 'required',
];
protected $validationMessages = [];
......@@ -37,46 +37,8 @@ class PageModel extends Model
protected function clearCache(array $data)
{
$page = (new PageModel())->find(
is_array($data['id']) ? $data['id'][0] : $data['id'],
);
// delete page cache
cache()->delete(md5($page->link));
// Clear the cache of all podcast and episode pages
$allPodcasts = (new PodcastModel())->findAll();
foreach ($allPodcasts as $podcast) {
// delete localized podcast and episode page cache
$episodeModel = new EpisodeModel();
$years = $episodeModel->getYears($podcast->id);
$seasons = $episodeModel->getSeasons($podcast->id);
$supportedLocales = config('App')->supportedLocales;
foreach ($years as $year) {
foreach ($supportedLocales as $locale) {
cache()->delete(
"page_podcast{$podcast->id}_year{$year['year']}_{$locale}",
);
}
}
foreach ($seasons as $season) {
foreach ($supportedLocales as $locale) {
cache()->delete(
"page_podcast{$podcast->id}_season{$season['season_number']}_{$locale}",
);
}
}
foreach ($podcast->episodes as $episode) {
foreach ($supportedLocales as $locale) {
cache()->delete(
"page_podcast{$podcast->id}_episode{$episode->id}_{$locale}",
);
}
}
}
// Clear the cache of all pages
cache()->deleteMatching('page*');
return $data;
}
......
......@@ -48,9 +48,11 @@ class PersonModel extends Model
public function getPersonById($personId)
{
if (!($found = cache("person{$personId}"))) {
$cacheName = "person#{$personId}";
if (!($found = cache($cacheName))) {
$found = $this->find($personId);
cache()->save("person{$personId}", $found, DECADE);
cache()->save($cacheName, $found, DECADE);
}
return $found;
......@@ -99,7 +101,8 @@ class PersonModel extends Model
{
$options = [];
$locale = service('request')->getLocale();
if (!($options = cache("taxonomy_options_{$locale}"))) {
$cacheName = "taxonomy_options_{$locale}";
if (!($options = cache($cacheName))) {
foreach (lang('PersonsTaxonomy.persons') as $group_key => $group) {
foreach ($group['roles'] as $role_key => $role) {
$options[
......@@ -108,7 +111,7 @@ class PersonModel extends Model
}
}
cache()->save("taxonomy_options_{$locale}", $options, DECADE);
cache()->save($cacheName, $options, DECADE);
}
return $options;
......@@ -116,19 +119,15 @@ class PersonModel extends Model
protected function clearCache(array $data)
{
$person = (new PersonModel())->getPersonById(
$person = (new PersonModel())->find(
is_array($data['id']) ? $data['id'][0] : $data['id'],
);
cache()->delete('person_options');
cache()->delete("person{$person->id}");
cache()->delete("user{$person->created_by}_persons");
cache()->delete("person#{$person->id}");
$supportedLocales = config('App')->supportedLocales;
// clear cache for every credit page
foreach ($supportedLocales as $locale) {
cache()->delete("credit_{$locale}");
}
// clear cache for every credits page
cache()->deleteMatching('page_credits_*');
return $data;
}
......