From 951ae8881c6c6f808bc0bf9cb108447d195d6691 Mon Sep 17 00:00:00 2001 From: William Jacoby Date: Wed, 14 Oct 2026 12:25:07 -0500 Subject: [PATCH 1/2] Move an article to a new name Move article, under Moderator controls in the version history, renames an article's address (and optionally its title). Every version, its uploaded files and its watchers move in one transaction, and a redirect is left at the old name unless the box is cleared. An article can be moved back over its own redirects. Links are not rewritten. Template lookup now follows a redirect to another template, one step, so articles using a moved template keep working. Moving a template that articles use (directly or through a redirect) warns with the count and needs a second confirmation. New permission u_wiki_move, given like the other wiki moderation permissions to whoever has m_. Co-Authored-By: Claude Opus 5.5 --- README.md | 1 + config/services.yml | 15 + controller/main.php | 10 +- docs/TODO.md | 20 -- event/listener.php | 4 + language/en/common.php | 19 ++ language/en/permissions_wiki.php | 1 + migrations/add_article_move.php | 32 ++ styles/prosilver/template/article_move.html | 45 +++ .../prosilver/template/article_versions.html | 5 +- tests/unit/move_test.php | 44 +++ wiki/compare.php | 2 + wiki/move.php | 303 ++++++++++++++++++ wiki/templates.php | 15 +- 14 files changed, 491 insertions(+), 25 deletions(-) create mode 100644 migrations/add_article_move.php create mode 100644 styles/prosilver/template/article_move.html create mode 100644 tests/unit/move_test.php create mode 100644 wiki/move.php diff --git a/README.md b/README.md index 869a873..eaebb47 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ A wiki for phpBB with version history, side-by-side compare, and a moderation qu - Link to another article by writing `[[Article name]]`, in articles and in forum posts; links to articles that don't exist yet are shown in red and open the editor. Each article lists the articles that link to it under **What links here**. See [Linking between articles](#linking-between-articles). - Every edit is saved as a new version; compare any two versions. The compare page opens in a **Readable** view: the newer version as readers see it, with added text in green and underlined, removed text in red and struck through, and the changed words marked inside a line, list item or table row that changed. **Source** shows the BBCode diff. A `Template:` page is compared as written. - **Restore** in the version history brings back an older version by saving its text, title, description, sources and redirect as a new version, so no version is lost. Members who may edit can restore; the restored version goes live straight away for members who may set versions active, and otherwise waits for approval like any edit. +- **Move article**, under **Moderator controls** in the version history, gives an article a new name (its address) and, if wanted, a new title. Every version, its uploaded files and its watchers move with it, and a redirect is left at the old name unless that box is cleared; an article can be moved back over its own redirect. Links in other articles are not rewritten, so the redirect keeps them working, and articles that use a moved template keep showing it through the redirect. Moving a template that articles use shows how many and asks for a second confirmation. It needs the **Can move an article to a new name** permission, which moderators get when the extension is updated. - Edits can wait for approval: moderators get a **Pending articles** list with Approve and Disapprove. Approving a version older than the live one is blocked, so a stale draft can't overwrite newer content. - Moderators can take an article offline or delete it, and articles can be made sticky or set to redirect. - A **Recent changes** page at `/wiki/recent-changes` lists every version saved to the wiki that is or was live, newest first, with RSS and Atom feeds at `/wiki/recent-changes.rss` and `/wiki/recent-changes.atom` (the latest 50). The ACP setting **Recent changes feed** under **Extensions > Wiki > Settings** offers RSS, Atom or both, and the header advertises the offered feeds to feed readers. Versions waiting for approval never appear. diff --git a/config/services.yml b/config/services.yml index ae3d5c8..c692ef6 100644 --- a/config/services.yml +++ b/config/services.yml @@ -138,8 +138,23 @@ services: - '@phpbbmodders.wiki.discussion' - '@phpbbmodders.wiki.files' - '@phpbbmodders.wiki.watch' + - '@phpbbmodders.wiki.move' - '%core.root_path%' - '%core.php_ext%' + phpbbmodders.wiki.move: + class: phpbbmodders\wiki\wiki\move + arguments: + - '@auth' + - '@dbal.conn' + - '@controller.helper' + - '@request' + - '@template' + - '@user' + - '@phpbbmodders.wiki.links' + - '@phpbbmodders.wiki.search_sync' + - '%tables.wiki.article%' + - '%tables.wiki.file%' + - '%tables.wiki.watch%' phpbbmodders.wiki.listener: class: phpbbmodders\wiki\event\listener arguments: diff --git a/controller/main.php b/controller/main.php index 5788f71..9e15856 100644 --- a/controller/main.php +++ b/controller/main.php @@ -48,6 +48,9 @@ class main /** @var \phpbbmodders\wiki\wiki\watch */ protected $watch; + /** @var \phpbbmodders\wiki\wiki\move */ + protected $move; + /** @var string phpbb_root_path */ protected $phpbb_root_path; @@ -69,10 +72,11 @@ class main * @param \phpbbmodders\wiki\wiki\discussion $discussion Start discussion object * @param \phpbbmodders\wiki\wiki\files $files Uploaded files object * @param \phpbbmodders\wiki\wiki\watch $watch Watched articles object + * @param \phpbbmodders\wiki\wiki\move $move Move article object * @param string $phpbb_root_path * @param string $php_ext */ - public function __construct(\phpbb\auth\auth $auth, \phpbb\controller\helper $helper, \phpbb\request\request $request, \phpbb\template\template $template, \phpbb\user $user, \phpbbmodders\wiki\wiki\delete $delete, \phpbbmodders\wiki\wiki\edit $edit, \phpbbmodders\wiki\wiki\compare $compare, \phpbbmodders\wiki\wiki\view $view, \phpbbmodders\wiki\wiki\discussion $discussion, \phpbbmodders\wiki\wiki\files $files, \phpbbmodders\wiki\wiki\watch $watch, $phpbb_root_path, $php_ext) + public function __construct(\phpbb\auth\auth $auth, \phpbb\controller\helper $helper, \phpbb\request\request $request, \phpbb\template\template $template, \phpbb\user $user, \phpbbmodders\wiki\wiki\delete $delete, \phpbbmodders\wiki\wiki\edit $edit, \phpbbmodders\wiki\wiki\compare $compare, \phpbbmodders\wiki\wiki\view $view, \phpbbmodders\wiki\wiki\discussion $discussion, \phpbbmodders\wiki\wiki\files $files, \phpbbmodders\wiki\wiki\watch $watch, \phpbbmodders\wiki\wiki\move $move, $phpbb_root_path, $php_ext) { $this->auth = $auth; $this->helper = $helper; @@ -86,6 +90,7 @@ public function __construct(\phpbb\auth\auth $auth, \phpbb\controller\helper $he $this->discussion = $discussion; $this->files = $files; $this->watch = $watch; + $this->move = $move; $this->phpbb_root_path = $phpbb_root_path; $this->php_ext = $php_ext; } @@ -156,6 +161,9 @@ public function article($article) case 'deactivate': return $this->edit->deactivate($article); + case 'move': + return $this->move->page($article); + case 'restore': return $this->edit->restore($id); diff --git a/docs/TODO.md b/docs/TODO.md index 6203a7d..f036208 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -17,26 +17,6 @@ Not built: repository below better than code in the extension. - Starting from an existing article, or a template that asks for values first. -## Move/rename an article - -The URL slug is fixed forever once an article is created — only the title -can change. There's no way to rename a page's URL while keeping its version -history attached; today that means manually recreating the article under a -new URL and losing the connection to its history. Needs a "move" action that -renames `article_url` across all of an article's versions. - -Decided 10/13/2026: - -- It gets its own permission, named like the existing ones (`u_wiki_move`), - rather than reusing `u_wiki_set_redirect`. -- A move carries the whole article with it: every version, the uploaded files - and the watchers. -- The move form has a "leave a redirect at the old address" box, ticked by default. -- Moving a `Template:` page that articles use shows a warning with the number of articles, - and asks for a second confirmation before it goes ahead, because every one of them would - need fixing by hand if no redirect is left. -- Links in other articles are not rewritten. The redirect left at the old address covers - them, so it has to stay in place for those links to keep working. ## Categories diff --git a/event/listener.php b/event/listener.php index e691688..9ea8eb3 100644 --- a/event/listener.php +++ b/event/listener.php @@ -107,6 +107,10 @@ public function permissions($event) 'lang' => 'ACL_U_WIKI_SET_STICKY', 'cat' => 'wiki' ), + 'u_wiki_move' => array( + 'lang' => 'ACL_U_WIKI_MOVE', + 'cat' => 'wiki' + ), 'u_wiki_set_redirect' => array( 'lang' => 'ACL_U_WIKI_SET_REDIRECT', 'cat' => 'wiki' diff --git a/language/en/common.php b/language/en/common.php index 3ba97aa..d17b64c 100644 --- a/language/en/common.php +++ b/language/en/common.php @@ -79,6 +79,25 @@ 2 => 'This article has been viewed %d times.', ), 'CONFIRM_ACTIVATE_VERSION' => 'Are you sure you want to set the selected version as the active version for this article?', + 'WIKI_MOVE_ARTICLE' => 'Move article', + 'WIKI_MOVE_EXPLAIN' => 'Give the article a new name. Every version, its uploaded files and its watchers move with it. Links in other articles are not changed; a redirect left at the old name sends readers to the new one.', + 'WIKI_MOVE_NEW_NAME' => 'New name', + 'WIKI_MOVE_NEW_NAME_EXPLAIN' => 'The name in the article’s address, as written in [[links]]. It cannot hold [ ] | < > " # ? % / or \\.', + 'WIKI_MOVE_NEW_TITLE' => 'Title', + 'WIKI_MOVE_LEAVE_REDIRECT' => 'Leave a redirect at the old name', + 'WIKI_MOVE_TEMPLATE_WARNING' => array( + 1 => 'This template is used in 1 article. Templates are included by name: with a redirect left behind, the article keeps working; without one, it shows the template as missing until it is fixed by hand.', + 2 => 'This template is used in %d articles. Templates are included by name: with a redirect left behind, they keep working; without one, every one of them shows the template as missing until it is fixed by hand.', + ), + 'WIKI_MOVE_TEMPLATE_CONFIRM' => 'Yes, move this template anyway', + 'WIKI_MOVE_TEMPLATE_CONFIRM_MISSING' => 'This template is used in articles. Tick the box to confirm the move.', + 'WIKI_MOVE_INVALID_NAME' => 'The new name is empty, too long, starts or ends with a space, or holds a character a name cannot have.', + 'WIKI_MOVE_INVALID_TITLE' => 'The title is empty or longer than 255 characters.', + 'WIKI_MOVE_SAME_NAME' => 'The new name is the same as the current one.', + 'WIKI_MOVE_NAME_TAKEN' => 'An article called “%s” already exists, or has versions waiting for approval. Choose another name.', + 'WIKI_MOVE_REDIRECT_REASON' => 'Moved to %s', + 'WIKI_MOVE_SUCCESS' => 'The article has been moved.', + 'WIKI_MOVE_SUCCESS_REDIRECT' => 'The article has been moved, and the old name now redirects to it.', 'CONFIRM_RESTORE_VERSION' => 'Restore the version of %s? It is saved as a new version, so the history keeps every version as it is.', 'RESTORE_VERSION' => 'Restore', 'RESTORED_VERSION_REASON' => 'Restored the version of %s', diff --git a/language/en/permissions_wiki.php b/language/en/permissions_wiki.php index a6cccad..fe9af0c 100644 --- a/language/en/permissions_wiki.php +++ b/language/en/permissions_wiki.php @@ -44,6 +44,7 @@ 'ACL_U_WIKI_SET_ACTIVE' => 'Can change the active version of an article', 'ACL_U_WIKI_SET_STICKY' => 'Can mark an article as important', 'ACL_U_WIKI_SET_REDIRECT' => 'Can set up a redirect on an article', + 'ACL_U_WIKI_MOVE' => 'Can move an article to a new name', 'ACL_M_WIKI_DELETE' => 'Can delete versions of an article', 'ACL_M_WIKI_DELETE_ARTICLE' => 'Can delete an entire article', 'ACL_M_WIKI_VIEW_INACTIVE' => 'Can view inactive articles', diff --git a/migrations/add_article_move.php b/migrations/add_article_move.php new file mode 100644 index 0000000..09cdbd3 --- /dev/null +++ b/migrations/add_article_move.php @@ -0,0 +1,32 @@ + +

{L_WIKI_MOVE_ARTICLE}{L_COLON} {ARTICLE_NAME}

+ +
+
+
+

{L_WIKI_MOVE_EXPLAIN}

+

{ERROR}

+
+
+

{L_WIKI_MOVE_NEW_NAME_EXPLAIN}
+
+
+
+
+
+
+
+
 
+
+
+ +
+
 
+
+

{TEMPLATE_USED_BY}

+ +
+
+ +
+
+
+ +
+
+
+ {L_CANCEL} + + {S_FORM_TOKEN} +
+
+
+
+ diff --git a/styles/prosilver/template/article_versions.html b/styles/prosilver/template/article_versions.html index f4e418f..3daef93 100644 --- a/styles/prosilver/template/article_versions.html +++ b/styles/prosilver/template/article_versions.html @@ -55,7 +55,7 @@

{L_VERSIONS_OF_ARTICLE}{L_COLON} {ARTICLE_TITLE}

- +