Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions app/Actions/Post/UpdatePost.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,20 @@ public static function execute(Workspace $workspace, Post $post, array $data): a

foreach (data_get($data, 'platforms', []) as $platformData) {
$updateData = ['enabled' => true];
$needsExistingRow = data_get($platformData, 'meta') !== null || Arr::has($platformData, 'media_ids');
$postPlatform = $needsExistingRow
? $post->postPlatforms()->where('id', data_get($platformData, 'id'))->first()
: null;

if (data_get($platformData, 'content_type') !== null) {
$updateData['content_type'] = data_get($platformData, 'content_type');
}

if (data_get($platformData, 'meta') !== null) {
$postPlatform = $post->postPlatforms()->where('id', data_get($platformData, 'id'))->first();

if ($postPlatform) {
$updateData['meta'] = array_filter(
array_merge($postPlatform->meta ?? [], data_get($platformData, 'meta') ?? []),
fn (mixed $value): bool => $value !== null,
);
}
if (data_get($platformData, 'meta') !== null && $postPlatform) {
$updateData['meta'] = array_filter(
array_merge($postPlatform->meta ?? [], data_get($platformData, 'meta') ?? []),
fn (mixed $value): bool => $value !== null,
);
}

$post->postPlatforms()
Expand All @@ -74,11 +74,11 @@ public static function execute(Workspace $workspace, Post $post, array $data): a
// client that never sends it must not accidentally clear every
// platform's scoping), while an explicit empty array clears it
// back to "publish every media item on the post" (see
// PostPlatform::scopedMediaItems()).
if (Arr::has($platformData, 'media_ids')) {
$postPlatform = $post->postPlatforms()->where('id', data_get($platformData, 'id'))->first();

$postPlatform?->media()->sync(data_get($platformData, 'media_ids', []));
// PostPlatform::scopedMediaItems()). $postPlatform is already
// resolved above via $needsExistingRow when media_ids is present,
// no need to re-fetch it here.
if (Arr::has($platformData, 'media_ids') && $postPlatform) {
$postPlatform->media()->sync(data_get($platformData, 'media_ids', []));
}
}
}
Expand Down
8 changes: 7 additions & 1 deletion app/Http/Controllers/App/PostController.php
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,13 @@ public function edit(Request $request, Post $post): Response|RedirectResponse
SyncPostPlatforms::execute($post);
}

$post->load(['postPlatforms.socialAccount', 'labels']);
$post->load(['postPlatforms.socialAccount', 'postPlatforms.media', 'labels']);

// `media_ids` is only appended here, not globally on the model, so the
// Index/Calendar listings (which don't eager-load `media`) don't take
// an unwanted lazy-load hit on every post_platform they render.
$post->postPlatforms->each(fn (PostPlatform $postPlatform) => $postPlatform->append('media_ids'));

$socialAccounts = $workspace->socialAccounts()->active()->get();
$labels = $workspace->labels;
$signatures = $workspace->signatures;
Expand Down
16 changes: 16 additions & 0 deletions app/Http/Requests/App/Post/UpdatePostRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ public function rules(): array
'string',
Rule::in(array_column(ContentType::cases(), 'value')),
],
// Per-platform media assignment (Edit.vue's media toggle grid). An
// id must be one of the media items this same request is saving on
// the post, never an arbitrary/unrelated media id (IDOR guard).
'platforms.*.media_ids' => ['sometimes', 'array'],
'platforms.*.media_ids.*' => ['string', Rule::in($this->postMediaIds())],
...PostPlatformMetaRules::rules(),
'label_ids' => ['sometimes', 'array'],
'label_ids.*' => ['uuid', Rule::exists('workspace_labels', 'id')->where('workspace_id', $this->user()->currentWorkspace->id)],
Expand Down Expand Up @@ -150,6 +155,17 @@ private function addMediaCompatibilityErrors(Validator $validator): void
}
}

/**
* The media ids this request is saving on the post itself, the only ids
* a `platforms.*.media_ids` entry is allowed to reference.
*
* @return array<int, string>
*/
private function postMediaIds(): array
{
return collect($this->input('media', []))->pluck('id')->filter()->all();
}

/**
* @return Collection<int|string, Platform>
*/
Expand Down
19 changes: 17 additions & 2 deletions app/Models/PostPlatform.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,15 +104,30 @@ public function scopedMediaItems(): Collection
}

/**
* Only platforms still enabled for publishing disabled ones are
* Only platforms still enabled for publishing (disabled ones are
* excluded from PublishPost, so anything else that mirrors publish
* eligibility (previews, validation, proactive checks) must too.
* eligibility (previews, validation, proactive checks) must too).
*/
public function scopeEnabled(Builder $query): Builder
{
return $query->where('post_platforms.enabled', true);
}

/**
* The ids of media items currently scoped to this platform. Empty means
* "no scoping, applies to every post media item", see scopedMediaItems().
* Only used for the editor's per-platform media assignment UI, so this
* relies on `media` already being eager-loaded (or explicitly appended
* via `append('media_ids')`, see PostController::edit()) to avoid a lazy
* load nobody asked for on every other page that serializes this model.
*
* @return array<int, string>
*/
public function getMediaIdsAttribute(): array
{
return $this->media->pluck('id')->all();
}

/**
* Get display name, falling back to snapshot if account was deleted.
*/
Expand Down
5 changes: 5 additions & 0 deletions lang/ar/posts.php
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,11 @@
'drop_zone_subtitle' => 'اسحب الملفات وأفلتها أو انقر للتصفح',
'add' => 'إضافة',
'publish_to' => 'النشر إلى',
'media_assignment' => [
'label' => 'الوسائط لهذه القناة',
'included' => 'مضمّن لهذه القناة',
'excluded' => 'مستبعد من هذه القناة',
],
'organize' => 'تنظيم',
'signatures' => 'التوقيعات',
'view_on_platform' => 'العرض على المنصة',
Expand Down
5 changes: 5 additions & 0 deletions lang/de/posts.php
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,11 @@
'drop_zone_subtitle' => 'Dateien ziehen & ablegen oder zum Durchsuchen klicken',
'add' => 'Hinzufügen',
'publish_to' => 'Veröffentlichen auf',
'media_assignment' => [
'label' => 'Medien für diesen Kanal',
'included' => 'Enthalten für diesen Kanal',
'excluded' => 'Ausgeschlossen von diesem Kanal',
],
'organize' => 'Organisieren',
'signatures' => 'Signaturen',
'view_on_platform' => 'Auf der Plattform ansehen',
Expand Down
5 changes: 5 additions & 0 deletions lang/el/posts.php
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,11 @@
'drop_zone_subtitle' => 'Σύρετε και αποθέστε αρχεία ή κάντε κλικ για αναζήτηση',
'add' => 'Προσθήκη',
'publish_to' => 'Δημοσίευση σε',
'media_assignment' => [
'label' => 'Πολυμέσα για αυτό το κανάλι',
'included' => 'Περιλαμβάνεται σε αυτό το κανάλι',
'excluded' => 'Εξαιρείται από αυτό το κανάλι',
],
'organize' => 'Οργάνωση',
'signatures' => 'Υπογραφές',
'view_on_platform' => 'Προβολή στην πλατφόρμα',
Expand Down
5 changes: 5 additions & 0 deletions lang/en/posts.php
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,11 @@
'drop_zone_subtitle' => 'Drag & drop files or click to browse',
'add' => 'Add',
'publish_to' => 'Publish to',
'media_assignment' => [
'label' => 'Media for this channel',
'included' => 'Included for this channel',
'excluded' => 'Excluded from this channel',
],
'organize' => 'Organize',
'signatures' => 'Signatures',
'view_on_platform' => 'View on platform',
Expand Down
5 changes: 5 additions & 0 deletions lang/es/posts.php
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,11 @@
'drop_zone_subtitle' => 'Arrastra archivos o haz clic para seleccionar',
'add' => 'Añadir',
'publish_to' => 'Publicar en',
'media_assignment' => [
'label' => 'Contenido para este canal',
'included' => 'Incluido en este canal',
'excluded' => 'Excluido de este canal',
],
'organize' => 'Organizar',
'no_labels' => 'Todavía no hay etiquetas creadas',
'pick_time' => 'Elegir hora',
Expand Down
5 changes: 5 additions & 0 deletions lang/fr/posts.php
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,11 @@
'drop_zone_subtitle' => 'Glissez-déposez des fichiers ou cliquez pour parcourir',
'add' => 'Ajouter',
'publish_to' => 'Publier sur',
'media_assignment' => [
'label' => 'Contenu pour ce canal',
'included' => 'Inclus pour ce canal',
'excluded' => 'Exclu de ce canal',
],
'organize' => 'Organiser',
'signatures' => 'Signatures',
'view_on_platform' => 'Voir sur la plateforme',
Expand Down
5 changes: 5 additions & 0 deletions lang/it/posts.php
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,11 @@
'drop_zone_subtitle' => 'Trascina e rilascia i file oppure clicca per sfogliare',
'add' => 'Aggiungi',
'publish_to' => 'Pubblica su',
'media_assignment' => [
'label' => 'Contenuti per questo canale',
'included' => 'Incluso per questo canale',
'excluded' => 'Escluso da questo canale',
],
'organize' => 'Organizza',
'signatures' => 'Firme',
'view_on_platform' => 'Visualizza sulla piattaforma',
Expand Down
5 changes: 5 additions & 0 deletions lang/ja/posts.php
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,11 @@
'drop_zone_subtitle' => 'ファイルをドラッグ&ドロップするか、クリックして参照',
'add' => '追加',
'publish_to' => '公開先',
'media_assignment' => [
'label' => 'このチャンネルのメディア',
'included' => 'このチャンネルに含まれています',
'excluded' => 'このチャンネルから除外されています',
],
'organize' => '整理',
'signatures' => '署名',
'view_on_platform' => 'プラットフォームで表示',
Expand Down
5 changes: 5 additions & 0 deletions lang/ko/posts.php
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,11 @@
'drop_zone_subtitle' => '파일을 끌어다 놓거나 클릭하여 찾아보기',
'add' => '추가',
'publish_to' => '게시 대상',
'media_assignment' => [
'label' => '이 채널의 미디어',
'included' => '이 채널에 포함됨',
'excluded' => '이 채널에서 제외됨',
],
'organize' => '정리',
'signatures' => '서명',
'view_on_platform' => '플랫폼에서 보기',
Expand Down
5 changes: 5 additions & 0 deletions lang/nl/posts.php
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,11 @@
'drop_zone_subtitle' => 'Sleep bestanden hierheen of klik om te bladeren',
'add' => 'Toevoegen',
'publish_to' => 'Publiceren naar',
'media_assignment' => [
'label' => 'Media voor dit kanaal',
'included' => 'Inbegrepen voor dit kanaal',
'excluded' => 'Uitgesloten van dit kanaal',
],
'organize' => 'Organiseren',
'signatures' => 'Handtekeningen',
'view_on_platform' => 'Bekijken op platform',
Expand Down
5 changes: 5 additions & 0 deletions lang/pl/posts.php
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,11 @@
'drop_zone_subtitle' => 'Przeciągnij i upuść pliki lub kliknij, aby przeglądać',
'add' => 'Dodaj',
'publish_to' => 'Publikuj na',
'media_assignment' => [
'label' => 'Media dla tego kanału',
'included' => 'Uwzględnione dla tego kanału',
'excluded' => 'Wykluczone z tego kanału',
],
'organize' => 'Organizuj',
'signatures' => 'Sygnatury',
'view_on_platform' => 'Zobacz na platformie',
Expand Down
5 changes: 5 additions & 0 deletions lang/pt-BR/posts.php
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,11 @@
'drop_zone_subtitle' => 'Arraste arquivos ou clique para selecionar',
'add' => 'Adicionar',
'publish_to' => 'Publicar em',
'media_assignment' => [
'label' => 'Mídia para este canal',
'included' => 'Incluído neste canal',
'excluded' => 'Excluído deste canal',
],
'organize' => 'Organizar',
'no_labels' => 'Nenhuma etiqueta criada ainda',
'pick_time' => 'Escolher horário',
Expand Down
5 changes: 5 additions & 0 deletions lang/ru/posts.php
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,11 @@
'drop_zone_subtitle' => 'Перетащите файлы или нажмите для выбора',
'add' => 'Добавить',
'publish_to' => 'Опубликовать в',
'media_assignment' => [
'label' => 'Медиа для этого канала',
'included' => 'Включено для этого канала',
'excluded' => 'Исключено из этого канала',
],
'organize' => 'Организовать',
'signatures' => 'Подписи',
'view_on_platform' => 'Открыть на платформе',
Expand Down
5 changes: 5 additions & 0 deletions lang/tr/posts.php
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,11 @@
'drop_zone_subtitle' => 'Dosyaları sürükleyip bırakın veya göz atmak için tıklayın',
'add' => 'Ekle',
'publish_to' => 'Şuraya yayınla',
'media_assignment' => [
'label' => 'Bu kanal için medya',
'included' => 'Bu kanala dahil',
'excluded' => 'Bu kanaldan hariç',
],
'organize' => 'Düzenle',
'signatures' => 'İmzalar',
'view_on_platform' => 'Platformda görüntüle',
Expand Down
5 changes: 5 additions & 0 deletions lang/uk/posts.php
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,11 @@
'drop_zone_subtitle' => 'Перетягніть файли або натисніть, щоб обрати',
'add' => 'Додати',
'publish_to' => 'Опублікувати в',
'media_assignment' => [
'label' => 'Медіа для цього каналу',
'included' => 'Включено для цього каналу',
'excluded' => 'Виключено з цього каналу',
],
'organize' => 'Організувати',
'signatures' => 'Підписи',
'view_on_platform' => 'Переглянути на платформі',
Expand Down
5 changes: 5 additions & 0 deletions lang/zh/posts.php
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,11 @@
'drop_zone_subtitle' => '拖放文件或点击浏览',
'add' => '添加',
'publish_to' => '发布到',
'media_assignment' => [
'label' => '此频道的媒体',
'included' => '已包含在此频道',
'excluded' => '已从此频道排除',
],
'organize' => '整理',
'signatures' => '签名',
'view_on_platform' => '在平台上查看',
Expand Down
9 changes: 9 additions & 0 deletions resources/js/components/ChannelConfigurator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import DiscordSettings from '@/components/posts/editor/DiscordSettings.vue';
import FacebookSettings from '@/components/posts/editor/FacebookSettings.vue';
import InstagramSettings from '@/components/posts/editor/InstagramSettings.vue';
import LinkedInSettings from '@/components/posts/editor/LinkedInSettings.vue';
import MediaAssignmentGrid from '@/components/posts/editor/MediaAssignmentGrid.vue';
import PinterestSettings from '@/components/posts/editor/PinterestSettings.vue';
import TikTokSettings from '@/components/posts/editor/TikTokSettings.vue';
import { Avatar } from '@/components/ui/avatar';
Expand Down Expand Up @@ -33,6 +34,7 @@ const emit = defineEmits<{
toggle: [id: string];
'update:contentType': [id: string, value: string];
'update:meta': [id: string, value: Record<string, any>];
'update:mediaIds': [id: string, mediaIds: string[]];
}>();

const isSelected = (id: string): boolean => props.selectedIds.includes(id);
Expand Down Expand Up @@ -121,6 +123,13 @@ const settingsProps = (channel: Channel) => ({
<slot />

<template v-for="channel in selectedChannels" :key="channel.id">
<MediaAssignmentGrid
:platform="channel.platform"
:media="media"
:selected-media-ids="channel.mediaIds"
:disabled="disabled"
@update:selected-media-ids="(ids) => emit('update:mediaIds', channel.id, ids)"
/>
<InstagramSettings
v-if="channel.platform === Platform.Instagram || channel.platform === Platform.InstagramFacebook"
v-bind="settingsProps(channel)"
Expand Down
Loading