Add scheduled social posts, Discord channels config, and LinkedIn personal posting fix
- Scheduled posts: new Schedule tab with multi-platform post scheduling (Discord, Mastodon, LinkedIn); SQLite queue with per-target delivery tracking and retry; cron script in bin/process-scheduled-posts.php processes due targets inline - Discord channels: new config section for webhook-based channels used as scheduled post targets; webhook URLs stored in .env as DISCORD_WEBHOOK_<KEY> - LinkedIn: fix botcompose_post to use member_id (urn:li:person) for personal profiles instead of hardcoded org author; filter org pages out of compose UI and RSS modal picker since org posting is unsupported; label org pages clearly Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
09e3406847
commit
30b21b6214
9 changed files with 848 additions and 14 deletions
|
|
@ -31,8 +31,21 @@
|
|||
|
||||
<hr>
|
||||
|
||||
<!-- LinkedIn Pages -->
|
||||
<h6 class="mb-3 mt-3"><i class="bi bi-linkedin me-1 text-primary"></i>LinkedIn Pages</h6>
|
||||
<!-- Discord Channels (for scheduled posts) -->
|
||||
<h6 class="mb-1 mt-3"><i class="bi bi-discord me-1 text-primary"></i>Discord Channels</h6>
|
||||
<p class="text-muted small mb-3">
|
||||
Add the webhook URL to <code>.env</code> as <code>DISCORD_WEBHOOK_<KEY></code> (uppercase).
|
||||
These channels are available as targets for scheduled posts.
|
||||
</p>
|
||||
<div id="discordChannelList" class="mb-2"><div class="text-muted small">Loading…</div></div>
|
||||
<button class="btn btn-sm btn-outline-primary mb-4" id="addDiscordChannelBtn">
|
||||
<i class="bi bi-plus-lg me-1"></i>Add Channel
|
||||
</button>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- LinkedIn -->
|
||||
<h6 class="mb-3 mt-3"><i class="bi bi-linkedin me-1 text-primary"></i>LinkedIn</h6>
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card-body py-2 d-flex align-items-center gap-3">
|
||||
|
|
@ -202,6 +215,36 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Discord channel modal -->
|
||||
<div class="modal fade" id="discordChannelModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header py-2">
|
||||
<h6 class="modal-title" id="discordChannelModalTitle">Add Discord Channel</h6>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="discordChannelModalMode">
|
||||
<div class="mb-2">
|
||||
<label class="form-label small">Channel key</label>
|
||||
<input type="text" id="discordChannelName" class="form-control form-control-sm font-monospace"
|
||||
placeholder="general" autocomplete="off">
|
||||
<div class="form-text small">Used as <code>DISCORD_WEBHOOK_<KEY></code> in .env (uppercase).</div>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label small">Label</label>
|
||||
<input type="text" id="discordChannelLabel" class="form-control form-control-sm" placeholder="General">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer py-2">
|
||||
<div class="text-danger small d-none flex-grow-1" id="discordChannelModalError"></div>
|
||||
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-primary btn-sm" id="discordChannelModalSaveBtn">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- LinkedIn page modal -->
|
||||
<div class="modal fade" id="linkedinPageModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-sm">
|
||||
|
|
|
|||
80
views/project/_tab_botschedule.php
Normal file
80
views/project/_tab_botschedule.php
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
<div id="botSchedulePanel" data-project-id="<?= $pid ?>">
|
||||
|
||||
<!-- Create form -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header py-2 small fw-semibold"><i class="bi bi-clock me-1"></i>Schedule a Post</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-2">
|
||||
<label class="form-label small">Content</label>
|
||||
<textarea id="schedContent" rows="4" class="form-control form-control-sm font-monospace"
|
||||
placeholder="What's on your mind?"></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small">URL <span class="text-muted">(optional — appended to content)</span></label>
|
||||
<input type="url" id="schedUrl" class="form-control form-control-sm font-monospace"
|
||||
placeholder="https://example.com/post">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small">Send at</label>
|
||||
<input type="datetime-local" id="schedAt" class="form-control form-control-sm" style="max-width:240px">
|
||||
</div>
|
||||
|
||||
<label class="form-label small">Platforms</label>
|
||||
|
||||
<div class="mb-2">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="schedUseDiscord">
|
||||
<label class="form-check-label small fw-semibold" for="schedUseDiscord">
|
||||
<i class="bi bi-discord me-1 text-primary"></i>Discord
|
||||
</label>
|
||||
</div>
|
||||
<div id="schedDiscordPicker" class="d-none ms-3 mt-1">
|
||||
<div id="schedDiscordChannels" class="d-flex flex-wrap gap-2"></div>
|
||||
<div class="text-muted small d-none" id="schedDiscordEmpty">
|
||||
No Discord channels configured — add them in the Config tab.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="schedUseMastodon">
|
||||
<label class="form-check-label small fw-semibold" for="schedUseMastodon">
|
||||
<i class="bi bi-mastodon me-1 text-primary"></i>Mastodon
|
||||
</label>
|
||||
</div>
|
||||
<div id="schedMastodonPicker" class="d-none ms-3 mt-1">
|
||||
<div id="schedMastodonAccounts" class="d-flex flex-wrap gap-2"></div>
|
||||
<div class="text-muted small d-none" id="schedMastodonEmpty">
|
||||
No Mastodon accounts configured — add them in the Config tab.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="schedUseLinkedin">
|
||||
<label class="form-check-label small fw-semibold" for="schedUseLinkedin">
|
||||
<i class="bi bi-linkedin me-1 text-primary"></i>LinkedIn
|
||||
</label>
|
||||
</div>
|
||||
<div id="schedLinkedinPicker" class="d-none ms-3 mt-1">
|
||||
<div id="schedLinkedinProfiles" class="d-flex flex-wrap gap-2"></div>
|
||||
<div class="text-muted small d-none" id="schedLinkedinEmpty">
|
||||
No LinkedIn profiles connected — connect your account in the Config tab.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-danger small d-none mb-2" id="schedError"></div>
|
||||
<button class="btn btn-primary btn-sm" id="schedSubmitBtn">
|
||||
<i class="bi bi-calendar-check me-1"></i>Schedule Post
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Post list -->
|
||||
<h6 class="mb-3">Scheduled & Recent Posts</h6>
|
||||
<div id="schedPostsList"><div class="text-muted small">Loading…</div></div>
|
||||
|
||||
</div>
|
||||
|
|
@ -40,7 +40,7 @@ $tabLabels = [
|
|||
'plugins' => 'Plugins', 'git' => 'Git',
|
||||
'notes' => 'Notes', 'settings' => 'Settings',
|
||||
'bot' => 'Bot', 'botconfig' => 'Config',
|
||||
'logs' => 'Logs',
|
||||
'botschedule' => 'Schedule', 'logs' => 'Logs',
|
||||
];
|
||||
$tabIcons = [
|
||||
'dashboard' => 'bi-grid-1x2', 'analytics' => 'bi-graph-up',
|
||||
|
|
@ -50,11 +50,11 @@ $tabIcons = [
|
|||
'plugins' => 'bi-puzzle', 'git' => 'bi-git',
|
||||
'notes' => 'bi-sticky', 'settings' => 'bi-gear',
|
||||
'bot' => 'bi-robot', 'botconfig' => 'bi-sliders2',
|
||||
'logs' => 'bi-journal-text',
|
||||
'botschedule' => 'bi-calendar-event', 'logs' => 'bi-journal-text',
|
||||
];
|
||||
$tabGroups = [
|
||||
['dashboard', 'analytics'],
|
||||
['bot', 'botconfig', 'logs'],
|
||||
['bot', 'botconfig', 'botschedule', 'logs'],
|
||||
['posts', 'config', 'files', 'media'],
|
||||
['run', 'themes', 'plugins', 'git'],
|
||||
['notes', 'settings'],
|
||||
|
|
@ -267,6 +267,9 @@ $tabGroups = [
|
|||
<?php elseif ($tab === 'botconfig'): ?>
|
||||
<?php include ROOT . '/views/project/_tab_botconfig.php'; ?>
|
||||
|
||||
<?php elseif ($tab === 'botschedule'): ?>
|
||||
<?php include ROOT . '/views/project/_tab_botschedule.php'; ?>
|
||||
|
||||
<?php elseif ($tab === 'logs'): ?>
|
||||
<?php include ROOT . '/views/project/_tab_logs.php'; ?>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue