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

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
Show changes
Commits on Source (6)
Showing
with 304 additions and 219 deletions
......@@ -11,7 +11,7 @@
[
"@semantic-release/exec",
{
"prepareCmd": "./scripts/bundle.sh ${nextRelease.version} && ./scripts/package.sh && npx prettier --write CHANGELOG.md"
"prepareCmd": "./scripts/bundle.sh ${nextRelease.version} && ./scripts/package.sh ${nextRelease.version} && npx prettier --write CHANGELOG.md"
}
],
"@semantic-release/npm",
......
# [1.0.0-alpha.61](https://code.podlibre.org/podlibre/castopod-host/compare/v1.0.0-alpha.60...v1.0.0-alpha.61) (2021-06-23)
### Bug Fixes
- **release:** add missing version number to castopod-host package
([8f3e9d9](https://code.podlibre.org/podlibre/castopod-host/commit/8f3e9d90c14545d3f84d4469b26a53db4554b4dc))
- **ux:** allow for empty message upon episode publication and warn user on
submit
([33d01b8](https://code.podlibre.org/podlibre/castopod-host/commit/33d01b8d4fd6ebf24e9f011aa705c456c846956c)),
closes [#129](https://code.podlibre.org/podlibre/castopod-host/issues/129)
# [1.0.0-alpha.60](https://code.podlibre.org/podlibre/castopod-host/compare/v1.0.0-alpha.59...v1.0.0-alpha.60) (2021-06-21)
### Features
......
......@@ -11,7 +11,7 @@ declare(strict_types=1);
|
| NOTE: this constant is updated upon release with Continuous Integration.
*/
defined('CP_VERSION') || define('CP_VERSION', '1.0.0-alpha.60');
defined('CP_VERSION') || define('CP_VERSION', '1.0.0-alpha.61');
/*
| --------------------------------------------------------------------
......
......@@ -5,7 +5,7 @@ declare(strict_types=1);
namespace Config;
use App\Entities\Actor;
use App\Entities\Note;
use App\Entities\Status;
use App\Entities\User;
use CodeIgniter\Events\Events;
use CodeIgniter\Exceptions\FrameworkException;
......@@ -120,82 +120,82 @@ Events::on('on_undo_follow', function ($actor, $targetActor): void {
});
/**
* @param Note $note
* @param Status $status
*/
Events::on('on_note_add', function ($note): void {
if ($note->in_reply_to_id !== null) {
$note = $note->reply_to_note;
Events::on('on_status_add', function ($status): void {
if ($status->in_reply_to_id !== null) {
$status = $status->reply_to_status;
}
if ($note->episode_id) {
if ($status->episode_id) {
model('EpisodeModel')
->where('id', $note->episode_id)
->increment('notes_total');
->where('id', $status->episode_id)
->increment('statuses_total');
}
if ($note->actor->is_podcast) {
if ($status->actor->is_podcast) {
// Removing all of the podcast pages is a bit overkill, but works to avoid caching bugs
// same for other events below
cache()
->deleteMatching("podcast#{$note->actor->podcast->id}*");
->deleteMatching("podcast#{$status->actor->podcast->id}*");
cache()
->deleteMatching("page_podcast#{$note->actor->podcast->id}*");
->deleteMatching("page_podcast#{$status->actor->podcast->id}*");
}
});
/**
* @param Note $note
* @param Status $status
*/
Events::on('on_note_remove', function ($note): void {
if ($note->in_reply_to_id !== null) {
Events::trigger('on_note_remove', $note->reply_to_note);
Events::on('on_status_remove', function ($status): void {
if ($status->in_reply_to_id !== null) {
Events::trigger('on_status_remove', $status->reply_to_status);
}
if ($episodeId = $note->episode_id) {
if ($episodeId = $status->episode_id) {
model('EpisodeModel')
->where('id', $episodeId)
->decrement('notes_total', 1 + $note->reblogs_count);
->decrement('statuses_total', 1 + $status->reblogs_count);
model('EpisodeModel')
->where('id', $episodeId)
->decrement('reblogs_total', $note->reblogs_count);
->decrement('reblogs_total', $status->reblogs_count);
model('EpisodeModel')
->where('id', $episodeId)
->decrement('favourites_total', $note->favourites_count);
->decrement('favourites_total', $status->favourites_count);
}
if ($note->actor->is_podcast) {
if ($status->actor->is_podcast) {
cache()
->deleteMatching("podcast#{$note->actor->podcast->id}*");
->deleteMatching("podcast#{$status->actor->podcast->id}*");
cache()
->deleteMatching("page_podcast#{$note->actor->podcast->id}*");
->deleteMatching("page_podcast#{$status->actor->podcast->id}*");
}
cache()
->deleteMatching("page_note#{$note->id}*");
->deleteMatching("page_status#{$status->id}*");
});
/**
* @param Actor $actor
* @param Note $note
* @param Status $status
*/
Events::on('on_note_reblog', function ($actor, $note): void {
if ($episodeId = $note->episode_id) {
Events::on('on_status_reblog', function ($actor, $status): void {
if ($episodeId = $status->episode_id) {
model('EpisodeModel')
->where('id', $episodeId)
->increment('reblogs_total');
model('EpisodeModel')
->where('id', $episodeId)
->increment('notes_total');
->increment('statuses_total');
}
if ($note->actor->is_podcast) {
if ($status->actor->is_podcast) {
cache()
->deleteMatching("podcast#{$note->actor->podcast->id}*");
->deleteMatching("podcast#{$status->actor->podcast->id}*");
cache()
->deleteMatching("page_podcast#{$note->actor->podcast->id}*");
->deleteMatching("page_podcast#{$status->actor->podcast->id}*");
}
if ($actor->is_podcast) {
......@@ -205,111 +205,111 @@ Events::on('on_note_reblog', function ($actor, $note): void {
}
cache()
->deleteMatching("page_note#{$note->id}*");
->deleteMatching("page_status#{$status->id}*");
if ($note->in_reply_to_id !== null) {
cache()->deleteMatching("page_note#{$note->in_reply_to_id}");
if ($status->in_reply_to_id !== null) {
cache()->deleteMatching("page_status#{$status->in_reply_to_id}");
}
});
/**
* @param Note $reblogNote
* @param Status $reblogStatus
*/
Events::on('on_note_undo_reblog', function ($reblogNote): void {
$note = $reblogNote->reblog_of_note;
if ($episodeId = $note->episode_id) {
Events::on('on_status_undo_reblog', function ($reblogStatus): void {
$status = $reblogStatus->reblog_of_status;
if ($episodeId = $status->episode_id) {
model('EpisodeModel')
->where('id', $episodeId)
->decrement('reblogs_total');
model('EpisodeModel')
->where('id', $episodeId)
->decrement('notes_total');
->decrement('statuses_total');
}
if ($note->actor->is_podcast) {
if ($status->actor->is_podcast) {
cache()
->deleteMatching("podcast#{$note->actor->podcast->id}*");
->deleteMatching("podcast#{$status->actor->podcast->id}*");
cache()
->deleteMatching("page_podcast#{$note->actor->podcast->id}*");
->deleteMatching("page_podcast#{$status->actor->podcast->id}*");
}
cache()
->deleteMatching("page_note#{$note->id}*");
->deleteMatching("page_status#{$status->id}*");
cache()
->deleteMatching("page_note#{$reblogNote->id}*");
->deleteMatching("page_status#{$reblogStatus->id}*");
if ($note->in_reply_to_id !== null) {
cache()->deleteMatching("page_note#{$note->in_reply_to_id}");
if ($status->in_reply_to_id !== null) {
cache()->deleteMatching("page_status#{$status->in_reply_to_id}");
}
if ($reblogNote->actor->is_podcast) {
if ($reblogStatus->actor->is_podcast) {
cache()
->deleteMatching("podcast#{$reblogNote->actor->podcast->id}*");
->deleteMatching("podcast#{$reblogStatus->actor->podcast->id}*");
cache()
->deleteMatching("page_podcast#{$reblogNote->actor->podcast->id}*");
->deleteMatching("page_podcast#{$reblogStatus->actor->podcast->id}*");
}
});
/**
* @param Note $reply
* @param Status $reply
*/
Events::on('on_note_reply', function ($reply): void {
$note = $reply->reply_to_note;
Events::on('on_status_reply', function ($reply): void {
$status = $reply->reply_to_status;
if ($note->actor->is_podcast) {
if ($status->actor->is_podcast) {
cache()
->deleteMatching("podcast#{$note->actor->podcast->id}*");
->deleteMatching("podcast#{$status->actor->podcast->id}*");
cache()
->deleteMatching("page_podcast#{$note->actor->podcast->id}*");
->deleteMatching("page_podcast#{$status->actor->podcast->id}*");
}
cache()
->deleteMatching("page_note#{$note->id}*");
->deleteMatching("page_status#{$status->id}*");
});
/**
* @param Note $reply
* @param Status $reply
*/
Events::on('on_reply_remove', function ($reply): void {
$note = $reply->reply_to_note;
$status = $reply->reply_to_status;
if ($note->actor->is_podcast) {
if ($status->actor->is_podcast) {
cache()
->deleteMatching("page_podcast#{$note->actor->podcast->id}*");
->deleteMatching("page_podcast#{$status->actor->podcast->id}*");
cache()
->deleteMatching("podcast#{$note->actor->podcast->id}*");
->deleteMatching("podcast#{$status->actor->podcast->id}*");
}
cache()
->deleteMatching("page_note#{$note->id}*");
->deleteMatching("page_status#{$status->id}*");
cache()
->deleteMatching("page_note#{$reply->id}*");
->deleteMatching("page_status#{$reply->id}*");
});
/**
* @param Actor $actor
* @param Note $note
* @param Status $status
*/
Events::on('on_note_favourite', function ($actor, $note): void {
if ($note->episode_id) {
Events::on('on_status_favourite', function ($actor, $status): void {
if ($status->episode_id) {
model('EpisodeModel')
->where('id', $note->episode_id)
->where('id', $status->episode_id)
->increment('favourites_total');
}
if ($note->actor->is_podcast) {
if ($status->actor->is_podcast) {
cache()
->deleteMatching("podcast#{$note->actor->podcast->id}*");
->deleteMatching("podcast#{$status->actor->podcast->id}*");
cache()
->deleteMatching("page_podcast#{$note->actor->podcast->id}*");
->deleteMatching("page_podcast#{$status->actor->podcast->id}*");
}
cache()
->deleteMatching("page_note#{$note->id}*");
->deleteMatching("page_status#{$status->id}*");
if ($note->in_reply_to_id !== null) {
cache()->deleteMatching("page_note#{$note->in_reply_to_id}*");
if ($status->in_reply_to_id !== null) {
cache()->deleteMatching("page_status#{$status->in_reply_to_id}*");
}
if ($actor->is_podcast) {
......@@ -321,27 +321,27 @@ Events::on('on_note_favourite', function ($actor, $note): void {
/**
* @param Actor $actor
* @param Note $note
* @param Status $status
*/
Events::on('on_note_undo_favourite', function ($actor, $note): void {
if ($note->episode_id) {
Events::on('on_status_undo_favourite', function ($actor, $status): void {
if ($status->episode_id) {
model('EpisodeModel')
->where('id', $note->episode_id)
->where('id', $status->episode_id)
->decrement('favourites_total');
}
if ($note->actor->is_podcast) {
if ($status->actor->is_podcast) {
cache()
->deleteMatching("podcast#{$note->actor->podcast->id}*");
->deleteMatching("podcast#{$status->actor->podcast->id}*");
cache()
->deleteMatching("page_podcast#{$note->actor->podcast->id}*");
->deleteMatching("page_podcast#{$status->actor->podcast->id}*");
}
cache()
->deleteMatching("page_note#{$note->id}*");
->deleteMatching("page_status#{$status->id}*");
if ($note->in_reply_to_id !== null) {
cache()->deleteMatching("page_note#{$note->in_reply_to_id}*");
if ($status->in_reply_to_id !== null) {
cache()->deleteMatching("page_status#{$status->in_reply_to_id}*");
}
if ($actor->is_podcast) {
......@@ -356,7 +356,7 @@ Events::on('on_block_actor', function (int $actorId): void {
cache()
->deleteMatching('podcast*');
cache()
->deleteMatching('page_note*');
->deleteMatching('page_status*');
});
Events::on('on_unblock_actor', function (int $actorId): void {
......@@ -364,7 +364,7 @@ Events::on('on_unblock_actor', function (int $actorId): void {
cache()
->deleteMatching('podcast*');
cache()
->deleteMatching('page_note*');
->deleteMatching('page_status*');
});
Events::on('on_block_domain', function (string $domainName): void {
......@@ -372,7 +372,7 @@ Events::on('on_block_domain', function (string $domainName): void {
cache()
->deleteMatching('podcast*');
cache()
->deleteMatching('page_note*');
->deleteMatching('page_status*');
});
Events::on('on_unblock_domain', function (string $domainName): void {
......@@ -380,5 +380,5 @@ Events::on('on_unblock_domain', function (string $domainName): void {
cache()
->deleteMatching('podcast*');
cache()
->deleteMatching('page_note*');
->deleteMatching('page_status*');
});
......@@ -35,7 +35,7 @@ $routes->addPlaceholder('podcastName', '[a-zA-Z0-9\_]{1,32}');
$routes->addPlaceholder('slug', '[a-zA-Z0-9\-]{1,191}');
$routes->addPlaceholder('base64', '[A-Za-z0-9\.\_]+\-{0,2}');
$routes->addPlaceholder('platformType', '\bpodcasting|\bsocial|\bfunding');
$routes->addPlaceholder('noteAction', '\bfavourite|\breblog|\breply');
$routes->addPlaceholder('statusAction', '\bfavourite|\breblog|\breply');
$routes->addPlaceholder('embeddablePlayerTheme', '\blight|\bdark|\blight-transparent|\bdark-transparent');
$routes->addPlaceholder(
'uuid',
......@@ -310,6 +310,15 @@ $routes->group(
'permission:podcast-manage_publications',
],
);
$routes->get(
'publish-cancel',
'EpisodeController::publishCancel/$1/$2',
[
'as' => 'episode-publish-cancel',
'filter' =>
'permission:podcast-manage_publications',
],
);
$routes->get(
'unpublish',
'EpisodeController::unpublish/$1/$2',
......@@ -746,71 +755,71 @@ $routes->post('interact-as-actor', 'AuthController::attemptInteractAsActor', [
* Overwriting ActivityPub routes file
*/
$routes->group('@(:podcastName)', function ($routes): void {
$routes->post('notes/new', 'NoteController::attemptCreate/$1', [
'as' => 'note-attempt-create',
$routes->post('statuses/new', 'StatusController::attemptCreate/$1', [
'as' => 'status-attempt-create',
'filter' => 'permission:podcast-manage_publications',
]);
// Note
$routes->group('notes/(:uuid)', function ($routes): void {
$routes->get('/', 'NoteController::view/$1/$2', [
'as' => 'note',
// Status
$routes->group('statuses/(:uuid)', function ($routes): void {
$routes->get('/', 'StatusController::view/$1/$2', [
'as' => 'status',
'alternate-content' => [
'application/activity+json' => [
'namespace' => 'ActivityPub\Controllers',
'controller-method' => 'NoteController/$2',
'controller-method' => 'StatusController/$2',
],
'application/ld+json; profile="https://www.w3.org/ns/activitystreams' => [
'namespace' => 'ActivityPub\Controllers',
'controller-method' => 'NoteController/$2',
'controller-method' => 'StatusController/$2',
],
],
]);
$routes->get('replies', 'NoteController/$1/$2', [
'as' => 'note-replies',
$routes->get('replies', 'StatusController/$1/$2', [
'as' => 'status-replies',
'alternate-content' => [
'application/activity+json' => [
'namespace' => 'ActivityPub\Controllers',
'controller-method' => 'NoteController::replies/$2',
'controller-method' => 'StatusController::replies/$2',
],
'application/ld+json; profile="https://www.w3.org/ns/activitystreams' => [
'namespace' => 'ActivityPub\Controllers',
'controller-method' => 'NoteController::replies/$2',
'controller-method' => 'StatusController::replies/$2',
],
],
]);
// Actions
$routes->post('action', 'NoteController::attemptAction/$1/$2', [
'as' => 'note-attempt-action',
$routes->post('action', 'StatusController::attemptAction/$1/$2', [
'as' => 'status-attempt-action',
'filter' => 'permission:podcast-interact_as',
]);
$routes->post(
'block-actor',
'NoteController::attemptBlockActor/$1/$2',
'StatusController::attemptBlockActor/$1/$2',
[
'as' => 'note-attempt-block-actor',
'as' => 'status-attempt-block-actor',
'filter' => 'permission:fediverse-block_actors',
],
);
$routes->post(
'block-domain',
'NoteController::attemptBlockDomain/$1/$2',
'StatusController::attemptBlockDomain/$1/$2',
[
'as' => 'note-attempt-block-domain',
'as' => 'status-attempt-block-domain',
'filter' => 'permission:fediverse-block_domains',
],
);
$routes->post('delete', 'NoteController::attemptDelete/$1/$2', [
'as' => 'note-attempt-delete',
$routes->post('delete', 'StatusController::attemptDelete/$1/$2', [
'as' => 'status-attempt-delete',
'filter' => 'permission:podcast-manage_publications',
]);
$routes->get(
'remote/(:noteAction)',
'NoteController::remoteAction/$1/$2/$3',
'remote/(:statusAction)',
'StatusController::remoteAction/$1/$2/$3',
[
'as' => 'note-remote-action',
'as' => 'status-remote-action',
],
);
});
......
......@@ -13,12 +13,12 @@ namespace App\Controllers\Admin;
use App\Entities\Episode;
use App\Entities\Image;
use App\Entities\Location;
use App\Entities\Note;
use App\Entities\Podcast;
use App\Entities\Status;
use App\Models\EpisodeModel;
use App\Models\NoteModel;
use App\Models\PodcastModel;
use App\Models\SoundbiteModel;
use App\Models\StatusModel;
use CodeIgniter\Exceptions\PageNotFoundException;
use CodeIgniter\HTTP\RedirectResponse;
use CodeIgniter\I18n\Time;
......@@ -388,7 +388,7 @@ class EpisodeController extends BaseController
return redirect()->back();
}
public function publish(): string
public function publish(): string | RedirectResponse
{
if ($this->episode->publication_status === 'not_published') {
helper(['form']);
......@@ -405,7 +405,10 @@ class EpisodeController extends BaseController
return view('admin/episode/publish', $data);
}
throw PageNotFoundException::forPageNotFound();
return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id])->with(
'error',
lang('Episode.publish_error')
);
}
public function attemptPublish(): RedirectResponse
......@@ -426,7 +429,7 @@ class EpisodeController extends BaseController
$db = db_connect();
$db->transStart();
$newNote = new Note([
$newStatus = new Status([
'actor_id' => $this->podcast->actor_id,
'episode_id' => $this->episode->id,
'message' => $this->request->getPost('message'),
......@@ -453,15 +456,15 @@ class EpisodeController extends BaseController
$this->episode->published_at = Time::now();
}
$newNote->published_at = $this->episode->published_at;
$newStatus->published_at = $this->episode->published_at;
$noteModel = new NoteModel();
if (! $noteModel->addNote($newNote)) {
$statusModel = new StatusModel();
if (! $statusModel->addStatus($newStatus)) {
$db->transRollback();
return redirect()
->back()
->withInput()
->with('errors', $noteModel->errors());
->with('errors', $statusModel->errors());
}
$episodeModel = new EpisodeModel();
......@@ -478,7 +481,7 @@ class EpisodeController extends BaseController
return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id]);
}
public function publishEdit(): string
public function publishEdit(): string | RedirectResponse
{
if ($this->episode->publication_status === 'scheduled') {
helper(['form']);
......@@ -486,7 +489,7 @@ class EpisodeController extends BaseController
$data = [
'podcast' => $this->podcast,
'episode' => $this->episode,
'note' => (new NoteModel())
'status' => (new StatusModel())
->where([
'actor_id' => $this->podcast->actor_id,
'episode_id' => $this->episode->id,
......@@ -500,13 +503,17 @@ class EpisodeController extends BaseController
]);
return view('admin/episode/publish_edit', $data);
}
throw PageNotFoundException::forPageNotFound();
return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id])->with(
'error',
lang('Episode.publish_edit_error')
);
}
public function attemptPublishEdit(): RedirectResponse
{
$rules = [
'note_id' => 'required',
'status_id' => 'required',
'publication_method' => 'required',
'scheduled_publication_date' =>
'valid_date[Y-m-d H:i]|permit_empty',
......@@ -542,19 +549,19 @@ class EpisodeController extends BaseController
$this->episode->published_at = Time::now();
}
$note = (new NoteModel())->getNoteById($this->request->getPost('note_id'));
$status = (new StatusModel())->getStatusById($this->request->getPost('status_id'));
if ($note !== null) {
$note->message = $this->request->getPost('message');
$note->published_at = $this->episode->published_at;
if ($status !== null) {
$status->message = $this->request->getPost('message');
$status->published_at = $this->episode->published_at;
$noteModel = new NoteModel();
if (! $noteModel->editNote($note)) {
$statusModel = new StatusModel();
if (! $statusModel->editStatus($status)) {
$db->transRollback();
return redirect()
->back()
->withInput()
->with('errors', $noteModel->errors());
->with('errors', $statusModel->errors());
}
}
......@@ -572,7 +579,44 @@ class EpisodeController extends BaseController
return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id]);
}
public function unpublish(): string
public function publishCancel(): RedirectResponse
{
if ($this->episode->publication_status === 'scheduled') {
$db = db_connect();
$db->transStart();
$statusModel = new StatusModel();
$status = $statusModel
->where([
'actor_id' => $this->podcast->actor_id,
'episode_id' => $this->episode->id,
])
->first();
$statusModel->removeStatus($status);
$this->episode->published_at = null;
$episodeModel = new EpisodeModel();
if (! $episodeModel->update($this->episode->id, $this->episode)) {
$db->transRollback();
return redirect()
->back()
->withInput()
->with('errors', $episodeModel->errors());
}
$db->transComplete();
return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id]);
}
return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id])->with(
'error',
lang('Episode.publish_cancel_error')
);
}
public function unpublish(): string | RedirectResponse
{
if ($this->episode->publication_status === 'published') {
helper(['form']);
......@@ -589,7 +633,10 @@ class EpisodeController extends BaseController
return view('admin/episode/unpublish', $data);
}
throw PageNotFoundException::forPageNotFound();
return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id])->with(
'error',
lang('Episode.unpublish_error')
);
}
public function attemptUnpublish(): RedirectResponse
......@@ -609,13 +656,13 @@ class EpisodeController extends BaseController
$db->transStart();
$allNotesLinkedToEpisode = (new NoteModel())
$allStatusesLinkedToEpisode = (new StatusModel())
->where([
'episode_id' => $this->episode->id,
])
->findAll();
foreach ($allNotesLinkedToEpisode as $note) {
(new NoteModel())->removeNote($note);
foreach ($allStatusesLinkedToEpisode as $status) {
(new StatusModel())->removeStatus($status);
}
// set episode published_at to null to unpublish
......
......@@ -13,8 +13,8 @@ namespace App\Controllers;
use Analytics\AnalyticsTrait;
use App\Entities\Podcast;
use App\Models\EpisodeModel;
use App\Models\NoteModel;
use App\Models\PodcastModel;
use App\Models\StatusModel;
use CodeIgniter\Exceptions\PageNotFoundException;
class PodcastController extends BaseController
......@@ -64,7 +64,7 @@ class PodcastController extends BaseController
if (! ($cachedView = cache($cacheName))) {
$data = [
'podcast' => $this->podcast,
'notes' => (new NoteModel())->getActorPublishedNotes($this->podcast->actor_id),
'statuses' => (new StatusModel())->getActorPublishedStatuses($this->podcast->actor_id),
];
// if user is logged in then send to the authenticated activity view
......
......@@ -10,21 +10,21 @@ declare(strict_types=1);
namespace App\Controllers;
use ActivityPub\Controllers\NoteController as ActivityPubNoteController;
use ActivityPub\Entities\Note as ActivityPubNote;
use ActivityPub\Controllers\StatusController as ActivityPubStatusController;
use ActivityPub\Entities\Status as ActivityPubStatus;
use Analytics\AnalyticsTrait;
use App\Entities\Actor;
use App\Entities\Note as CastopodNote;
use App\Entities\Podcast;
use App\Entities\Status as CastopodStatus;
use App\Models\EpisodeModel;
use App\Models\NoteModel;
use App\Models\PodcastModel;
use App\Models\StatusModel;
use CodeIgniter\Exceptions\PageNotFoundException;
use CodeIgniter\HTTP\RedirectResponse;
use CodeIgniter\HTTP\URI;
use CodeIgniter\I18n\Time;
class NoteController extends ActivityPubNoteController
class StatusController extends ActivityPubStatusController
{
use AnalyticsTrait;
......@@ -50,9 +50,9 @@ class NoteController extends ActivityPubNoteController
if (
count($params) > 1 &&
($note = (new NoteModel())->getNoteById($params[1])) !== null
($status = (new StatusModel())->getStatusById($params[1])) !== null
) {
$this->note = $note;
$this->status = $status;
unset($params[0]);
unset($params[1]);
......@@ -72,7 +72,7 @@ class NoteController extends ActivityPubNoteController
'_',
array_filter([
'page',
"note#{$this->note->id}",
"status#{$this->status->id}",
service('request')
->getLocale(),
can_user_interact() ? '_authenticated' : null,
......@@ -83,15 +83,15 @@ class NoteController extends ActivityPubNoteController
$data = [
'podcast' => $this->podcast,
'actor' => $this->actor,
'note' => $this->note,
'status' => $this->status,
];
// if user is logged in then send to the authenticated activity view
if (can_user_interact()) {
helper('form');
return view('podcast/note_authenticated', $data);
return view('podcast/status_authenticated', $data);
}
return view('podcast/note', $data, [
return view('podcast/status', $data, [
'cache' => DECADE,
'cache_name' => $cacheName,
]);
......@@ -116,7 +116,7 @@ class NoteController extends ActivityPubNoteController
$message = $this->request->getPost('message');
$newNote = new CastopodNote([
$newStatus = new CastopodStatus([
'actor_id' => interact_as_actor_id(),
'published_at' => Time::now(),
'created_by' => user_id(),
......@@ -129,23 +129,23 @@ class NoteController extends ActivityPubNoteController
($params = extract_params_from_episode_uri(new URI($episodeUri))) &&
($episode = (new EpisodeModel())->getEpisodeBySlug($params['podcastName'], $params['episodeSlug']))
) {
$newNote->episode_id = $episode->id;
$newStatus->episode_id = $episode->id;
}
$newNote->message = $message;
$newStatus->message = $message;
$noteModel = new NoteModel();
$statusModel = new StatusModel();
if (
! $noteModel
->addNote($newNote, ! (bool) $newNote->episode_id, true)
! $statusModel
->addStatus($newStatus, ! (bool) $newStatus->episode_id, true)
) {
return redirect()
->back()
->withInput()
->with('errors', $noteModel->errors());
->with('errors', $statusModel->errors());
}
// Note has been successfully created
// Status has been successfully created
return redirect()->back();
}
......@@ -162,36 +162,36 @@ class NoteController extends ActivityPubNoteController
->with('errors', $this->validator->getErrors());
}
$newNote = new ActivityPubNote([
$newStatus = new ActivityPubStatus([
'actor_id' => interact_as_actor_id(),
'in_reply_to_id' => $this->note->id,
'in_reply_to_id' => $this->status->id,
'message' => $this->request->getPost('message'),
'published_at' => Time::now(),
'created_by' => user_id(),
]);
$noteModel = new NoteModel();
if (! $noteModel->addReply($newNote)) {
$statusModel = new StatusModel();
if (! $statusModel->addReply($newStatus)) {
return redirect()
->back()
->withInput()
->with('errors', $noteModel->errors());
->with('errors', $statusModel->errors());
}
// Reply note without preview card has been successfully created
// Reply status without preview card has been successfully created
return redirect()->back();
}
public function attemptFavourite(): RedirectResponse
{
model('FavouriteModel')->toggleFavourite(interact_as_actor(), $this->note);
model('FavouriteModel')->toggleFavourite(interact_as_actor(), $this->status);
return redirect()->back();
}
public function attemptReblog(): RedirectResponse
{
(new NoteModel())->toggleReblog(interact_as_actor(), $this->note);
(new StatusModel())->toggleReblog(interact_as_actor(), $this->status);
return redirect()->back();
}
......@@ -230,20 +230,20 @@ class NoteController extends ActivityPubNoteController
$cacheName = implode(
'_',
array_filter(['page', "note#{$this->note->id}", "remote_{$action}", service('request') ->getLocale()]),
array_filter(['page', "status#{$this->status->id}", "remote_{$action}", service('request') ->getLocale()]),
);
if (! ($cachedView = cache($cacheName))) {
$data = [
'podcast' => $this->podcast,
'actor' => $this->actor,
'note' => $this->note,
'status' => $this->status,
'action' => $action,
];
helper('form');
return view('podcast/note_remote_action', $data, [
return view('podcast/status_remote_action', $data, [
'cache' => DECADE,
'cache_name' => $cacheName,
]);
......
......@@ -157,7 +157,7 @@ class AddEpisodes extends Migration
'unsigned' => true,
'default' => 0,
],
'notes_total' => [
'statuses_total' => [
'type' => 'INT',
'unsigned' => true,
'default' => 0,
......
......@@ -3,7 +3,7 @@
declare(strict_types=1);
/**
* Class AddEpisodeIdToNotes Adds episode_id field to activitypub_notes table in database
* Class AddEpisodeIdToStatuses Adds episode_id field to activitypub_statuses table in database
*
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
......@@ -14,23 +14,23 @@ namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddEpisodeIdToNotes extends Migration
class AddEpisodeIdToStatuses extends Migration
{
public function up(): void
{
$prefix = $this->db->getPrefix();
$createQuery = <<<CODE_SAMPLE
ALTER TABLE {$prefix}activitypub_notes
ALTER TABLE {$prefix}activitypub_statuses
ADD COLUMN `episode_id` INT UNSIGNED NULL AFTER `replies_count`,
ADD FOREIGN KEY {$prefix}activitypub_notes_episode_id_foreign(episode_id) REFERENCES {$prefix}episodes(id) ON DELETE CASCADE;
ADD FOREIGN KEY {$prefix}activitypub_statuses_episode_id_foreign(episode_id) REFERENCES {$prefix}episodes(id) ON DELETE CASCADE;
CODE_SAMPLE;
$this->db->query($createQuery);
}
public function down(): void
{
$this->forge->dropForeignKey('activitypub_notes', 'activitypub_notes_episode_id_foreign');
$this->forge->dropColumn('activitypub_notes', 'episode_id');
$this->forge->dropForeignKey('activitypub_statuses', 'activitypub_statuses_episode_id_foreign');
$this->forge->dropColumn('activitypub_statuses', 'episode_id');
}
}
......@@ -3,7 +3,7 @@
declare(strict_types=1);
/**
* Class AddCreatedByToNotes Adds created_by field to activitypub_notes table in database
* Class AddCreatedByToStatuses Adds created_by field to activitypub_statuses table in database
*
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
......@@ -14,23 +14,23 @@ namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddCreatedByToNotes extends Migration
class AddCreatedByToStatuses extends Migration
{
public function up(): void
{
$prefix = $this->db->getPrefix();
$createQuery = <<<CODE_SAMPLE
ALTER TABLE {$prefix}activitypub_notes
ALTER TABLE {$prefix}activitypub_statuses
ADD COLUMN `created_by` INT UNSIGNED AFTER `episode_id`,
ADD FOREIGN KEY {$prefix}activitypub_notes_created_by_foreign(created_by) REFERENCES {$prefix}users(id) ON DELETE CASCADE;
ADD FOREIGN KEY {$prefix}activitypub_statuses_created_by_foreign(created_by) REFERENCES {$prefix}users(id) ON DELETE CASCADE;
CODE_SAMPLE;
$this->db->query($createQuery);
}
public function down(): void
{
$this->forge->dropForeignKey('activitypub_notes', 'activitypub_notes_created_by_foreign');
$this->forge->dropColumn('activitypub_notes', 'created_by');
$this->forge->dropForeignKey('activitypub_statuses', 'activitypub_statuses_created_by_foreign');
$this->forge->dropColumn('activitypub_statuses', 'created_by');
}
}
......@@ -162,13 +162,13 @@ class AuthSeeder extends Seeder
[
'name' => 'manage_publications',
'description' =>
'Publish / unpublish episodes & notes of a podcast',
'Publish / unpublish episodes & statuses of a podcast',
'has_permission' => ['podcast_admin'],
],
[
'name' => 'interact_as',
'description' =>
'Interact as the podcast to favourite / share or reply to notes.',
'Interact as the podcast to favourite / share or reply to statuses.',
'has_permission' => ['podcast_admin'],
],
],
......
......@@ -11,10 +11,10 @@ declare(strict_types=1);
namespace App\Entities;
use App\Libraries\SimpleRSSElement;
use App\Models\NoteModel;
use App\Models\PersonModel;
use App\Models\PodcastModel;
use App\Models\SoundbiteModel;
use App\Models\StatusModel;
use CodeIgniter\Entity\Entity;
use CodeIgniter\Files\File;
use CodeIgniter\HTTP\Files\UploadedFile;
......@@ -67,7 +67,7 @@ use RuntimeException;
* @property string $custom_rss_string
* @property int $favourites_total
* @property int $reblogs_total
* @property int $notes_total
* @property int $statuses_total
* @property int $created_by
* @property int $updated_by
* @property string $publication_status;
......@@ -117,9 +117,9 @@ class Episode extends Entity
protected ?array $soundbites = null;
/**
* @var Note[]|null
* @var Status[]|null
*/
protected ?array $notes = null;
protected ?array $statuses = null;
protected ?Location $location = null;
......@@ -165,7 +165,7 @@ class Episode extends Entity
'custom_rss' => '?json-array',
'favourites_total' => 'integer',
'reblogs_total' => 'integer',
'notes_total' => 'integer',
'statuses_total' => 'integer',
'created_by' => 'integer',
'updated_by' => 'integer',
];
......@@ -382,19 +382,19 @@ class Episode extends Entity
}
/**
* @return Note[]
* @return Status[]
*/
public function getNotes(): array
public function getStatuses(): array
{
if ($this->id === null) {
throw new RuntimeException('Episode must be created before getting soundbites.');
}
if ($this->notes === null) {
$this->notes = (new NoteModel())->getEpisodeNotes($this->id);
if ($this->statuses === null) {
$this->statuses = (new StatusModel())->getEpisodeStatuses($this->id);
}
return $this->notes;
return $this->statuses;
}
public function getLink(): string
......
......@@ -10,7 +10,7 @@ declare(strict_types=1);
namespace App\Entities;
use ActivityPub\Entities\Note as ActivityPubNote;
use ActivityPub\Entities\Status as ActivityPubStatus;
use App\Models\EpisodeModel;
use RuntimeException;
......@@ -18,7 +18,7 @@ use RuntimeException;
* @property int|null $episode_id
* @property Episode|null $episode
*/
class Note extends ActivityPubNote
class Status extends ActivityPubStatus
{
protected ?Episode $episode = null;
......@@ -41,12 +41,12 @@ class Note extends ActivityPubNote
];
/**
* Returns the note's attached episode
* Returns the status' attached episode
*/
public function getEpisode(): ?Episode
{
if ($this->episode_id === null) {
throw new RuntimeException('Note must have an episode_id before getting episode.');
throw new RuntimeException('Status must have an episode_id before getting episode.');
}
if ($this->episode === null) {
......
......@@ -86,11 +86,11 @@ if (! function_exists('button')) {
}
if ($options['iconLeft']) {
$label = icon($options['iconLeft'], 'mr-2') . $label;
$label = icon((string) $options['iconLeft'], 'mr-2') . $label;
}
if ($options['iconRight']) {
$label .= icon($options['iconRight'], 'ml-2');
$label .= icon((string) $options['iconRight'], 'ml-2');
}
if ($uri !== '') {
......
......@@ -26,9 +26,9 @@ return [
one {# total share}
other {# total shares}
}',
'total_notes' => '{numberOfTotalNotes, plural,
one {# note}
other {# total notes}
'total_statuses' => '{numberOfTotalStatuses, plural,
one {# total post}
other {# total posts}
}',
'all_podcast_episodes' => 'All podcast episodes',
'back_to_podcast' => 'Go back to podcast',
......@@ -36,6 +36,10 @@ return [
'publish' => 'Publish',
'publish_edit' => 'Edit publication',
'unpublish' => 'Unpublish',
'publish_error' => 'Episode is already published.',
'publish_edit_error' => 'Episode is already published.',
'publish_cancel_error' => 'Episode is already published.',
'unpublish_error' => 'Episode is not published.',
'delete' => 'Delete',
'go_to_page' => 'Go to page',
'create' => 'Add an episode',
......@@ -112,9 +116,10 @@ return [
'submit_edit' => 'Save episode',
],
'publish_form' => [
'note' => 'Your note',
'note_hint' =>
'The message you write will be broadcasted to all your followers in the fediverse.',
'back_to_episode_dashboard' => 'Back to episode dashboard',
'status' => 'Your announcement post',
'status_hint' =>
"Write a message to announce the publication of your episode. The message will be broadcasted to all your followers in the fediverse and be featured in your podcast's homepage.",
'publication_date' => 'Publication date',
'publication_method' => [
'now' => 'Now',
......@@ -126,6 +131,10 @@ return [
'You can schedule the episode release by setting a future publication date. This field must be formatted as YYYY-MM-DD HH:mm',
'submit' => 'Publish',
'submit_edit' => 'Edit publication',
'cancel_publication' => 'Cancel publication',
'message_warning' => 'You did not write a message for your announcement post!',
'message_warning_hint' => 'Having a message increases social engagement, resulting in a better visibility for your episode.',
'message_warning_submit' => 'Publish anyways',
],
'unpublish_form' => [
'disclaimer' =>
......
......@@ -223,9 +223,9 @@ return [
one {<span class="font-semibold">#</span> follower}
other {<span class="font-semibold">#</span> followers}
}',
'notes' => '{numberOfNotes, plural,
one {<span class="font-semibold">#</span> note}
other {<span class="font-semibold">#</span> notes}
'statuses' => '{numberOfStatuses, plural,
one {<span class="font-semibold">#</span> post}
other {<span class="font-semibold">#</span> posts}
}',
'activity' => 'Activity',
'episodes' => 'Episodes',
......
......@@ -10,7 +10,7 @@ declare(strict_types=1);
return [
'title' => "{actorDisplayName}'s Note",
'back_to_actor_notes' => 'Back to {actor} notes',
'back_to_actor_statuses' => 'Back to {actor} notes',
'actor_shared' => '{actor} shared',
'reply_to' => 'Reply to @{actorUsername}',
'form' => [
......
......@@ -26,9 +26,9 @@ return [
one {# partage en tout}
other {# partages en tout}
}',
'total_notes' => '{numberOfTotalNotes, plural,
one {# note}
other {# notes}
'total_statuses' => '{numberOfTotalStatuses, plural,
one {# message}
other {# messages}
}',
'all_podcast_episodes' => 'Tous les épisodes du podcast',
'back_to_podcast' => 'Revenir au podcast',
......@@ -36,6 +36,10 @@ return [
'publish' => 'Publier',
'publish_edit' => 'Modifier la publication',
'unpublish' => 'Dépublier',
'publish_error' => 'L’épisode est déjà publié.',
'publish_edit_error' => 'L’épisode est déjà publié.',
'publish_cancel_error' => 'L’épisode est déjà publié.',
'unpublish_error' => 'L’épisode n’est pas publié.',
'delete' => 'Supprimer',
'go_to_page' => 'Voir',
'create' => 'Ajouter un épisode',
......@@ -115,9 +119,10 @@ return [
'submit_edit' => 'Enregistrer l’épisode',
],
'publish_form' => [
'note' => 'Votre note',
'note_hint' =>
'Le message que vous écrirez sera diffusé à toutes les personnes qui vous suivent dans le fédiverse.',
'back_to_episode_dashboard' => 'Retour au tableau de bord de l’épisode',
'status' => 'Votre message de publication',
'status_hint' =>
'Écrivez un message pour annoncer la publication de votre épisode. Le message sera diffusé à toutes les personnes qui vous suivent dans le fédiverse et mis en évidence sur la page d’accueil de votre podcast.',
'publication_date' => 'Date de publication',
'publication_date_clear' => 'Effacer la date de publication',
'publication_date_hint' =>
......@@ -132,6 +137,10 @@ return [
'Vous pouvez planifier la sortie de l’épisode en saisissant une date de publication future. Ce champ doit être au format YYYY-MM-DD HH:mm',
'submit' => 'Publier',
'submit_edit' => 'Modifier la publication',
'cancel_publication' => 'Annuler la publication',
'message_warning' => 'Vous n’avez pas saisi de message pour l’annonce de votre épisode !',
'message_warning_hint' => 'Ajouter un message augmente l’engagement social, menant à une meilleure visibilité pour votre épisode.',
'message_warning_submit' => 'Publish quand même',
],
'soundbites' => 'Extraits sonores',
'soundbites_form' => [
......
......@@ -225,9 +225,9 @@ return [
one {<span class="font-semibold">#</span> abonné·e}
other {<span class="font-semibold">#</span> abonné·e·s}
}',
'notes' => '{numberOfNotes, plural,
one {<span class="font-semibold">#</span> note}
other {<span class="font-semibold">#</span> notes}
'notes' => '{numberOfStatuses, plural,
one {<span class="font-semibold">#</span> message}
other {<span class="font-semibold">#</span> messages}
}',
'activity' => 'Activité',
'episodes' => 'Épisodes',
......