Enhance article import functionality: Add author selection and preserve attribution during imports; update metadata version to 1.1.2.
This commit is contained in:
1 parent
41e623d5d1
commit
c56ae0ced2
5 files changed
+73
-15
No files matched your search
@@ -65,12 +65,13 @@ For example, to import the article stored as `load-balancing-my-homelab/content/
|
|||||||
|
|
||||||
1. Push the article and its referenced assets to the configured GitHub branch.
|
1. Push the article and its referenced assets to the configured GitHub branch.
|
||||||
2. Open the plugin settings and enter `load-balancing-my-homelab` under **Import one article from GitHub**. With a path prefix, still enter only the article folder name.
|
2. Open the plugin settings and enter `load-balancing-my-homelab` under **Import one article from GitHub**. With a path prefix, still enter only the article folder name.
|
||||||
3. Choose the status behavior **before previewing**:
|
3. Choose **Article author** before previewing. Select an existing Bludit account to override the author, or leave the default to use repository `author`, otherwise preserve the existing page author (new pages: logged-in account). To repair an earlier import attributed to `admin`, select the intended account and reimport the same article folder.
|
||||||
|
4. Choose the status behavior **before previewing**:
|
||||||
- **Import as draft** is checked by default, including for an existing published article. Confirming with this option changes the page to a draft.
|
- **Import as draft** is checked by default, including for an existing published article. Confirming with this option changes the page to a draft.
|
||||||
- Uncheck it to use a nonempty repository `status`. A blank or omitted status preserves an existing page's type; a new page remains a draft. Future published dates are scheduled by Bludit.
|
- Uncheck it to use a nonempty repository `status`. A blank or omitted status preserves an existing page's type; a new page remains a draft. Future published dates are scheduled by Bludit.
|
||||||
4. Click **Preview import**. Review create versus replace, commit, metadata, resulting status, incoming source text and the assets to download. Existing content can be expanded for comparison. The incoming HTML is escaped in the preview, not executed.
|
5. Click **Preview import**. Review create versus replace, commit, metadata, resulting status, incoming source text and the assets to download. Existing content can be expanded for comparison. The incoming HTML is escaped in the preview, not executed.
|
||||||
5. Click **Confirm this import**. The plugin downloads and verifies the assets, saves a protected backup when replacing a page, then uses Bludit's `createPage()` or `editPage()` functions to save the article and rebuild category/tag indexes.
|
6. Click **Confirm this import**. The plugin downloads and verifies the assets, saves a protected backup when replacing a page, then uses Bludit's `createPage()` or `editPage()` functions to save the article and rebuild category/tag indexes.
|
||||||
6. Check the imported draft or live page in Bludit, including its images and attachment links.
|
7. Check the imported draft or live page in Bludit, including its images and attachment links.
|
||||||
|
|
||||||
The preview is pinned to the Git commit it displayed; newer GitHub commits require another preview. It belongs to the current administrator session, expires after 30 minutes and can only be confirmed once. **Cancel preview** discards it without editing an article.
|
The preview is pinned to the Git commit it displayed; newer GitHub commits require another preview. It belongs to the current administrator session, expires after 30 minutes and can only be confirmed once. **Cancel preview** discards it without editing an article.
|
||||||
|
|
||||||
@@ -78,13 +79,14 @@ Confirmation checks the persisted local page metadata and body again, including
|
|||||||
|
|
||||||
### Metadata and content
|
### Metadata and content
|
||||||
|
|
||||||
- Supported fields: `title`, `date`, `slug`, `status`, `tags`, `description`, `category`, `cover`.
|
- Supported fields: `title`, `date`, `slug`, `author`, `status`, `tags`, `description`, `category`, `cover`.
|
||||||
- A title and nonempty body are required. If present, `slug` must match the selected folder. Only a single top-level page key is supported, not nested pages or renames; Bludit slug normalization/collisions are refused.
|
- A title and nonempty body are required. If present, `slug` must match the selected folder. Only a single top-level page key is supported, not nested pages or renames; Bludit slug normalization/collisions are refused.
|
||||||
- Dates use `YYYY-MM-DD`. An unchanged date preserves the existing time; a changed date uses midnight in the site's timezone.
|
- Dates use `YYYY-MM-DD`. An unchanged date preserves the existing time; a changed date uses midnight in the site's timezone.
|
||||||
- Supported statuses: `draft`, `published`, `static`, `sticky`, `scheduled`, or blank for legacy exports. Normal Bludit scheduling applies.
|
- Supported statuses: `draft`, `published`, `static`, `sticky`, `scheduled`, or blank for legacy exports. Normal Bludit scheduling applies.
|
||||||
- Tags use `tags: []` or an indented ` - tag` list. Omitted metadata is preserved on update, apart from the explicit draft option and required title. Blank fields clear their corresponding values.
|
- Tags use `tags: []` or an indented ` - tag` list. Omitted metadata is preserved on update, apart from the explicit draft option and required title. Blank fields clear their corresponding values.
|
||||||
- A nonempty category key must already exist in Bludit; create it in the administration interface first. Tags are rebuilt through Bludit after save.
|
- A nonempty category key must already exist in Bludit; create it in the administration interface first. Tags are rebuilt through Bludit after save.
|
||||||
- Existing author, UUID and unrelated metadata are retained. New articles belong to the logged-in administrator.
|
- `author` is a Bludit username, not a display nickname. It must refer to an existing account. Explicit selection in the import form takes priority over repository metadata. Without either, an existing author is preserved and a new page belongs to the logged-in administrator. The preview shows the resulting author; no account is created or impersonated. New pages needing a different author are created through `createPage()` and then adjusted through `editPage()`, with this plugin's export hooks suppressed for both operations.
|
||||||
|
- Existing UUID and unrelated metadata are retained.
|
||||||
- Front matter supports the small scalar/list format emitted by this plugin, not general YAML. Nested objects, aliases, tags, multiline scalars, unknown fields and duplicate fields are rejected. Quote strings containing YAML punctuation with JSON-style double quotes.
|
- Front matter supports the small scalar/list format emitted by this plugin, not general YAML. Nested objects, aliases, tags, multiline scalars, unknown fields and duplicate fields are rejected. Quote strings containing YAML punctuation with JSON-style double quotes.
|
||||||
- A `cover` may be a repository `../img/…` reference or an HTTP(S) URL. A repository cover image is imported with the other assets.
|
- A `cover` may be a repository `../img/…` reference or an HTTP(S) URL. A repository cover image is imported with the other assets.
|
||||||
- The body is stored as supplied, with asset destinations rewritten to Bludit uploads. The site's usual Markdown configuration controls Markdown rendering. Confirmed HTML is trusted author content, just like content saved in Bludit's editor; previewing it does not sanitize the eventual article.
|
- The body is stored as supplied, with asset destinations rewritten to Bludit uploads. The site's usual Markdown configuration controls Markdown rendering. Confirmed HTML is trusted author content, just like content saved in Bludit's editor; previewing it does not sanitize the eventual article.
|
||||||
@@ -112,6 +114,7 @@ Backups and asset mapping files remain in the workspace across plugin upgrades.
|
|||||||
- Saving a page exports it when its auto-export setting is enabled.
|
- Saving a page exports it when its auto-export setting is enabled.
|
||||||
- **Export everything** exports drafts and published articles; **Export published only** exports the published selection.
|
- **Export everything** exports drafts and published articles; **Export published only** exports the published selection.
|
||||||
- Bludit upload URLs become relative `../img/` or `../files/` references. Imported asset mappings retain their original paths.
|
- Bludit upload URLs become relative `../img/` or `../files/` references. Imported asset mappings retain their original paths.
|
||||||
|
- Exports include the page's actual `author` username so later imports can preserve attribution.
|
||||||
- Exported `status` comes from Bludit's actual page `type`. Earlier plugin versions exported an empty `status` field; imports accept that legacy format.
|
- Exported `status` comes from Bludit's actual page `type`. Earlier plugin versions exported an empty `status` field; imports accept that legacy format.
|
||||||
- Contents API reads use the configured branch, so export SHA lookups do not accidentally use the repository's default branch.
|
- Contents API reads use the configured branch, so export SHA lookups do not accidentally use the repository's default branch.
|
||||||
- Deleting a Bludit page retains the existing automatic GitHub deletion behavior: its document and direct files in `img/` and `files/` are removed. This is unrelated to importing: missing repository pages never trigger local deletion. Nested repository asset directories are not recursively deleted.
|
- Deleting a Bludit page retains the existing automatic GitHub deletion behavior: its document and direct files in `img/` and `files/` are removed. This is unrelated to importing: missing repository pages never trigger local deletion. Nested repository asset directories are not recursively deleted.
|
||||||
|
|||||||
+1
-1
@@ -73,7 +73,7 @@ class BluditGithubImport
|
|||||||
throw new RuntimeException('Unsupported front matter line: ' . $line);
|
throw new RuntimeException('Unsupported front matter line: ' . $line);
|
||||||
}
|
}
|
||||||
$name = $field[1];
|
$name = $field[1];
|
||||||
if (!in_array($name, ['title', 'date', 'slug', 'status', 'tags', 'description', 'category', 'cover'], true) || array_key_exists($name, $meta)) {
|
if (!in_array($name, ['title', 'date', 'slug', 'author', 'status', 'tags', 'description', 'category', 'cover'], true) || array_key_exists($name, $meta)) {
|
||||||
throw new RuntimeException('Unknown or duplicate front matter field: ' . $name);
|
throw new RuntimeException('Unknown or duplicate front matter field: ' . $name);
|
||||||
}
|
}
|
||||||
if ($name === 'tags') {
|
if ($name === 'tags') {
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
"author": "spleenftw",
|
"author": "spleenftw",
|
||||||
"email": "",
|
"email": "",
|
||||||
"website": "https://github.com/Spleenftw/bludit-github-sync",
|
"website": "https://github.com/Spleenftw/bludit-github-sync",
|
||||||
"version": "1.1.1",
|
"version": "1.1.2",
|
||||||
"releaseDate": "2026-11-01",
|
"releaseDate": "2026-11-01",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"compatible": "3.22.0",
|
"compatible": "3.22.0",
|
||||||
|
|||||||
+31
-7
@@ -90,7 +90,7 @@ class pluginBluditGithub extends Plugin
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (!empty($_POST['previewImport'])) {
|
if (!empty($_POST['previewImport'])) {
|
||||||
$this->previewImport($_POST['importSlug'] ?? '', !empty($_POST['importAsDraft']));
|
$this->previewImport($_POST['importSlug'] ?? '', !empty($_POST['importAsDraft']), $_POST['importAuthor'] ?? '');
|
||||||
} elseif (!empty($_POST['confirmImport'])) {
|
} elseif (!empty($_POST['confirmImport'])) {
|
||||||
$this->confirmImport($_POST['importConfirmation'] ?? '');
|
$this->confirmImport($_POST['importConfirmation'] ?? '');
|
||||||
} elseif (!empty($_POST['cancelImport'])) {
|
} 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.
|
// Import previews are bound to the current admin session and expire after 30 minutes.
|
||||||
private function importForm()
|
private function importForm()
|
||||||
{
|
{
|
||||||
|
global $users;
|
||||||
$plan = Session::get('bluditGithubImport');
|
$plan = Session::get('bluditGithubImport');
|
||||||
$e = function ($v) { return htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8'); };
|
$e = function ($v) { return htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8'); };
|
||||||
$draftChecked = !is_array($plan) || !empty($plan['asDraft']) ? ' checked' : '';
|
$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 .= '<label>Import one article from GitHub</label>';
|
||||||
$html .= '<input name="importSlug" type="text" value="' . $e($plan['slug'] ?? '') . '" placeholder="load-balancing-my-homelab">';
|
$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><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 .= '<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>';
|
$html .= '<button name="previewImport" value="1" type="submit" class="btn btn-secondary">Preview import</button>';
|
||||||
if (is_array($plan)) {
|
if (is_array($plan)) {
|
||||||
$html .= '<hr><h4>Import preview: ' . $e($plan['slug']) . '</h4>';
|
$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 .= '<p>' . ($plan['local'] === null ? 'Create a new page' : 'Replace the existing page') . ' from commit <code>' . $e($plan['commit']) . '</code>.</p>';
|
||||||
$html .= '<dl>';
|
$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)';
|
$value = $plan['args'][$field] ?? '(preserve existing value)';
|
||||||
if ($field === 'type') { $value = $plan['effectiveType']; }
|
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>';
|
$html .= '</dl><p>Assets to download:</p><ul>';
|
||||||
foreach ($plan['assets'] as $asset) {
|
foreach ($plan['assets'] as $asset) {
|
||||||
@@ -240,9 +248,9 @@ class pluginBluditGithub extends Plugin
|
|||||||
return ['row' => $current->getPageDB($slug), 'content' => $content];
|
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');
|
Session::remove('bluditGithubImport');
|
||||||
if (!$this->isConfigured()) { throw new RuntimeException('Configure the GitHub repository first.'); }
|
if (!$this->isConfigured()) { throw new RuntimeException('Configure the GitHub repository first.'); }
|
||||||
BluditGithubImport::slug($slug);
|
BluditGithubImport::slug($slug);
|
||||||
@@ -273,6 +281,12 @@ class pluginBluditGithub extends Plugin
|
|||||||
throw new RuntimeException('Invalid Bludit page upload identifier.');
|
throw new RuntimeException('Invalid Bludit page upload identifier.');
|
||||||
}
|
}
|
||||||
$args = ['slug' => $slug, 'title' => Sanitize::htmlDecode($meta['title'])];
|
$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) {
|
foreach (['description', 'category'] as $field) {
|
||||||
if (array_key_exists($field, $meta)) { $args[$field] = Sanitize::htmlDecode($meta[$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(),
|
$plan = ['slug' => $slug, 'commit' => $commit['sha'], 'config' => $this->importConfigHash(), 'created' => time(),
|
||||||
'confirmation' => bin2hex(random_bytes(24)), 'local' => $local, 'uuid' => $uuid, 'args' => $args,
|
'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);
|
Session::set('bluditGithubImport', $plan);
|
||||||
$this->setStatus('Preview ready. Review the incoming content and resulting status, then confirm to import.');
|
$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)
|
private function confirmImport($confirmation)
|
||||||
{
|
{
|
||||||
global $pages;
|
global $pages, $users;
|
||||||
$plan = Session::get('bluditGithubImport');
|
$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())) {
|
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');
|
Session::remove('bluditGithubImport');
|
||||||
throw new RuntimeException('Import preview expired or settings changed. Preview again.');
|
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');
|
$lock = fopen($this->workspace() . 'import.lock', 'c');
|
||||||
if (!$lock || !flock($lock, LOCK_EX | LOCK_NB)) {
|
if (!$lock || !flock($lock, LOCK_EX | LOCK_NB)) {
|
||||||
if ($lock) { fclose($lock); }
|
if ($lock) { fclose($lock); }
|
||||||
@@ -426,6 +443,12 @@ class pluginBluditGithub extends Plugin
|
|||||||
$this->importing = true;
|
$this->importing = true;
|
||||||
$attempted = true;
|
$attempted = true;
|
||||||
$key = $plan['local'] === null ? createPage($args) : editPage($args);
|
$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.'); }
|
if ($key !== $plan['slug'] || !$pages->save()) { throw new RuntimeException('Bludit could not persist the imported page.'); }
|
||||||
$map = $this->assetMap($plan['slug']);
|
$map = $this->assetMap($plan['slug']);
|
||||||
foreach ($plan['assets'] as $asset) { $map[$asset['filename']] = $asset['relative']; }
|
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 .= '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 .= 'date: ' . date('Y-m-d', strtotime($page->getValue('dateRaw'))) . "\n";
|
||||||
$fm .= 'slug: ' . $key . "\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 .= 'status: ' . $page->getValue('type') . "\n";
|
||||||
$fm .= 'tags:' . ($tagLines ?: ' []') . "\n";
|
$fm .= 'tags:' . ($tagLines ?: ' []') . "\n";
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ $pages = new Pages(); $categories = new TestIndex(); $tags = new TestIndex(); $s
|
|||||||
$site = new class { public function sortBy() { return 'date'; } public function uriFilters($unused = null) { return ''; } };
|
$site = new class { public function sortBy() { return 'date'; } public function uriFilters($unused = null) { return ''; } };
|
||||||
$L = new class { public function g($s) { return $s; } public function getunicodeChars() { return []; } public function currentLanguage() { return 'en'; } };
|
$L = new class { public function g($s) { return $s; } public function getunicodeChars() { return []; } public function currentLanguage() { return 'en'; } };
|
||||||
Session::set('username', 'admin');
|
Session::set('username', 'admin');
|
||||||
|
$users = new class { public function keys() { return ['admin', 'spleenftw']; } public function exists($name) { return in_array($name, $this->keys(), true); } };
|
||||||
$plugin = new TestPlugin();
|
$plugin = new TestPlugin();
|
||||||
$plugin->init();
|
$plugin->init();
|
||||||
try {
|
try {
|
||||||
@@ -213,6 +214,36 @@ try {
|
|||||||
$plan['created'] = time() - 1801; Session::set('bluditGithubImport', $plan);
|
$plan['created'] = time() - 1801; Session::set('bluditGithubImport', $plan);
|
||||||
rejects(function () use ($plugin, $plan) { $plugin->invoke('confirmImport', $plan['confirmation']); }, 'expired');
|
rejects(function () use ($plugin, $plan) { $plugin->invoke('confirmImport', $plan['confirmation']); }, 'expired');
|
||||||
|
|
||||||
|
// New pages must use the chosen author despite createPage() forcing the session user.
|
||||||
|
$plugin->fixture('author-new', document('author-new', 'body'));
|
||||||
|
$plugin->requests = [];
|
||||||
|
$plugin->invoke('previewImport', 'author-new', true, 'spleenftw'); $plan = Session::get('bluditGithubImport');
|
||||||
|
check($plan['args']['username'] === 'spleenftw' && strpos($plugin->form(), '>Author</dt><dd>spleenftw') !== false, 'Preview selected author');
|
||||||
|
$plugin->invoke('confirmImport', $plan['confirmation']);
|
||||||
|
check((new Page('author-new'))->username() === 'spleenftw', 'New import uses selected author');
|
||||||
|
check(Session::get('username') === 'admin', 'Do not impersonate the selected author');
|
||||||
|
check(count(array_filter($plugin->requests, function ($r) { return $r[0] !== 'GET'; })) === 0, 'Author adjustment must not trigger exports');
|
||||||
|
$exported = $plugin->invoke('buildMarkdown', new Page('author-new'), 'author-new', 'body');
|
||||||
|
check(BluditGithubImport::document($exported, 'author-new')['meta']['author'] === 'spleenftw', 'Export the actual author');
|
||||||
|
|
||||||
|
// Repair an existing admin-authored page without changing its UUID or body intent.
|
||||||
|
$plugin->fixture('article', document('article', 'corrected article'));
|
||||||
|
$oldUuid = (new Page('article'))->uuid();
|
||||||
|
$plugin->invoke('previewImport', 'article', true, 'spleenftw'); $plan = Session::get('bluditGithubImport');
|
||||||
|
$plugin->invoke('confirmImport', $plan['confirmation']);
|
||||||
|
check((new Page('article'))->username() === 'spleenftw' && (new Page('article'))->uuid() === $oldUuid, 'Update existing author in place');
|
||||||
|
$plugin->invoke('previewImport', 'article', true); $plan = Session::get('bluditGithubImport');
|
||||||
|
check($plan['args']['username'] === 'spleenftw', 'Legacy documents without author preserve existing author');
|
||||||
|
$plugin->fixture('author-meta', document('author-meta', 'body', "author: spleenftw\n"));
|
||||||
|
$plugin->invoke('previewImport', 'author-meta', true); $plan = Session::get('bluditGithubImport');
|
||||||
|
$plugin->invoke('confirmImport', $plan['confirmation']);
|
||||||
|
check((new Page('author-meta'))->username() === 'spleenftw', 'Repository author survives creation');
|
||||||
|
$plugin->fixture('author-bad', document('author-bad', 'body', "author: unknown\n"));
|
||||||
|
rejects(function () use ($plugin) { $plugin->invoke('previewImport', 'author-bad', true); }, 'author does not exist');
|
||||||
|
$plugin->invoke('previewImport', 'author-bad', true, 'spleenftw');
|
||||||
|
check(Session::get('bluditGithubImport')['args']['username'] === 'spleenftw', 'A valid explicit selection overrides an unavailable repository author');
|
||||||
|
echo "PASS: selected/repository authors on create and update, author export, no impersonation or export loop\n";
|
||||||
|
|
||||||
// Optional end-to-end fixture using an actual exported article directory.
|
// Optional end-to-end fixture using an actual exported article directory.
|
||||||
if (($articleDir = getenv('BLOG_ARTICLE_DIR'))) {
|
if (($articleDir = getenv('BLOG_ARTICLE_DIR'))) {
|
||||||
$slug = basename(rtrim($articleDir, '/'));
|
$slug = basename(rtrim($articleDir, '/'));
|
||||||
|
|||||||
Reference in new issue
Block a user