Enhance article import functionality: Add author selection and preserve attribution during imports; update metadata version to 1.1.2.

This commit is contained in:
interlope committed 2026-11-01 18:48:10 +01:00
1 parent 41e623d5d1
commit c56ae0ced2
5 files changed
+73 -15

No files matched your search

+31 -7
View File
@@ -90,7 +90,7 @@ class pluginBluditGithub extends Plugin
try {
if (!empty($_POST['previewImport'])) {
$this->previewImport($_POST['importSlug'] ?? '', !empty($_POST['importAsDraft']));
$this->previewImport($_POST['importSlug'] ?? '', !empty($_POST['importAsDraft']), $_POST['importAuthor'] ?? '');
} elseif (!empty($_POST['confirmImport'])) {
$this->confirmImport($_POST['importConfirmation'] ?? '');
} elseif (!empty($_POST['cancelImport'])) {
@@ -188,6 +188,7 @@ class pluginBluditGithub extends Plugin
// Import previews are bound to the current admin session and expire after 30 minutes.
private function importForm()
{
global $users;
$plan = Session::get('bluditGithubImport');
$e = function ($v) { return htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8'); };
$draftChecked = !is_array($plan) || !empty($plan['asDraft']) ? ' checked' : '';
@@ -195,16 +196,23 @@ class pluginBluditGithub extends Plugin
$html .= '<label>Import one article from GitHub</label>';
$html .= '<input name="importSlug" type="text" value="' . $e($plan['slug'] ?? '') . '" placeholder="load-balancing-my-homelab">';
$html .= '<label><input type="checkbox" name="importAsDraft" value="1"' . $draftChecked . '> Import as draft (including existing articles)</label>';
$html .= '<label>Article author</label><select name="importAuthor">';
$html .= '<option value="">Use repository author, otherwise preserve existing author (new pages: logged-in user)</option>';
foreach ($users->keys() as $username) {
$selected = ($plan['authorOverride'] ?? '') === $username ? ' selected' : '';
$html .= '<option value="' . $e($username) . '"' . $selected . '>' . $e($username) . '</option>';
}
$html .= '</select><p class="tip">Choose an existing Bludit account to change the author. No user is created by importing.</p>';
$html .= '<p class="tip">Preview reads the configured branch without changing articles. Confirmation replaces the selected page only. No automatic pulling or deletion.</p>';
$html .= '<button name="previewImport" value="1" type="submit" class="btn btn-secondary">Preview import</button>';
if (is_array($plan)) {
$html .= '<hr><h4>Import preview: ' . $e($plan['slug']) . '</h4>';
$html .= '<p>' . ($plan['local'] === null ? 'Create a new page' : 'Replace the existing page') . ' from commit <code>' . $e($plan['commit']) . '</code>.</p>';
$html .= '<dl>';
foreach (['title', 'description', 'date', 'category', 'tags', 'type'] as $field) {
foreach (['title', 'username', 'description', 'date', 'category', 'tags', 'type'] as $field) {
$value = $plan['args'][$field] ?? '(preserve existing value)';
if ($field === 'type') { $value = $plan['effectiveType']; }
$html .= '<dt>' . $e($field === 'type' ? 'Resulting status' : $field) . '</dt><dd>' . $e($value) . '</dd>';
$html .= '<dt>' . $e($field === 'type' ? 'Resulting status' : ($field === 'username' ? 'Author' : $field)) . '</dt><dd>' . $e($value) . '</dd>';
}
$html .= '</dl><p>Assets to download:</p><ul>';
foreach ($plan['assets'] as $asset) {
@@ -240,9 +248,9 @@ class pluginBluditGithub extends Plugin
return ['row' => $current->getPageDB($slug), 'content' => $content];
}
private function previewImport($slug, $asDraft)
private function previewImport($slug, $asDraft, $authorOverride = '')
{
global $pages, $categories;
global $pages, $categories, $users;
Session::remove('bluditGithubImport');
if (!$this->isConfigured()) { throw new RuntimeException('Configure the GitHub repository first.'); }
BluditGithubImport::slug($slug);
@@ -273,6 +281,12 @@ class pluginBluditGithub extends Plugin
throw new RuntimeException('Invalid Bludit page upload identifier.');
}
$args = ['slug' => $slug, 'title' => Sanitize::htmlDecode($meta['title'])];
if (!is_string($authorOverride)) { throw new RuntimeException('Invalid author selection.'); }
$author = $authorOverride !== '' ? $authorOverride : (($meta['author'] ?? '') ?: ($local['row']['username'] ?? Session::get('username')));
if (!$users->exists($author)) {
throw new RuntimeException('The author does not exist in Bludit: ' . $author . '. Choose an existing author in the import form.');
}
$args['username'] = $author;
foreach (['description', 'category'] as $field) {
if (array_key_exists($field, $meta)) { $args[$field] = Sanitize::htmlDecode($meta[$field]); }
}
@@ -320,7 +334,7 @@ class pluginBluditGithub extends Plugin
}
$plan = ['slug' => $slug, 'commit' => $commit['sha'], 'config' => $this->importConfigHash(), 'created' => time(),
'confirmation' => bin2hex(random_bytes(24)), 'local' => $local, 'uuid' => $uuid, 'args' => $args,
'effectiveType' => $effectiveType, 'asDraft' => $asDraft, 'content' => $parsed['content'], 'cover' => $meta['cover'] ?? null, 'assets' => $assets];
'effectiveType' => $effectiveType, 'asDraft' => $asDraft, 'authorOverride' => $authorOverride, 'content' => $parsed['content'], 'cover' => $meta['cover'] ?? null, 'assets' => $assets];
Session::set('bluditGithubImport', $plan);
$this->setStatus('Preview ready. Review the incoming content and resulting status, then confirm to import.');
}
@@ -357,12 +371,15 @@ class pluginBluditGithub extends Plugin
private function confirmImport($confirmation)
{
global $pages;
global $pages, $users;
$plan = Session::get('bluditGithubImport');
if (!is_array($plan) || !is_string($confirmation) || !hash_equals($plan['confirmation'], $confirmation) || time() - $plan['created'] > 1800 || !hash_equals($plan['config'], $this->importConfigHash())) {
Session::remove('bluditGithubImport');
throw new RuntimeException('Import preview expired or settings changed. Preview again.');
}
if (!$users->exists($plan['args']['username'])) {
throw new RuntimeException('The selected author no longer exists. Preview again.');
}
$lock = fopen($this->workspace() . 'import.lock', 'c');
if (!$lock || !flock($lock, LOCK_EX | LOCK_NB)) {
if ($lock) { fclose($lock); }
@@ -426,6 +443,12 @@ class pluginBluditGithub extends Plugin
$this->importing = true;
$attempted = true;
$key = $plan['local'] === null ? createPage($args) : editPage($args);
// createPage() deliberately assigns the logged-in account, ignoring username.
// Apply an explicitly selected/repository author through a normal core edit,
// without impersonating another user or changing the administrator's session.
if ($key === $plan['slug'] && $plan['local'] === null && (new Page($key))->getValue('username') !== $args['username']) {
$key = editPage(['key' => $key, 'username' => $args['username']]);
}
if ($key !== $plan['slug'] || !$pages->save()) { throw new RuntimeException('Bludit could not persist the imported page.'); }
$map = $this->assetMap($plan['slug']);
foreach ($plan['assets'] as $asset) { $map[$asset['filename']] = $asset['relative']; }
@@ -655,6 +678,7 @@ class pluginBluditGithub extends Plugin
$fm .= 'title: ' . json_encode(Sanitize::htmlDecode($page->title()), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n";
$fm .= 'date: ' . date('Y-m-d', strtotime($page->getValue('dateRaw'))) . "\n";
$fm .= 'slug: ' . $key . "\n";
$fm .= 'author: ' . json_encode($page->getValue('username'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n";
$fm .= 'status: ' . $page->getValue('type') . "\n";
$fm .= 'tags:' . ($tagLines ?: ' []') . "\n";