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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
15 changes: 15 additions & 0 deletions config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
10 changes: 9 additions & 1 deletion controller/main.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand All @@ -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;
}
Expand Down Expand Up @@ -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);

Expand Down
20 changes: 0 additions & 20 deletions docs/TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 4 additions & 0 deletions event/listener.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
19 changes: 19 additions & 0 deletions language/en/common.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,25 @@
2 => 'This article has been viewed <strong>%d</strong> 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',
Expand Down
1 change: 1 addition & 0 deletions language/en/permissions_wiki.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
32 changes: 32 additions & 0 deletions migrations/add_article_move.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php
/**
*
* Wiki extension for the phpBB Forum Software package
*
* @copyright (c) 2026, phpBB Modders, https://www.phpbbmodders.com/
* @license GNU General Public License, version 2 (GPL-2.0)
*
*/

namespace phpbbmodders\wiki\migrations;

/**
* The permission to move an article to a new name. Like the other wiki moderation
* permissions, it is given to whoever has moderator permissions (m_).
*/
class add_article_move extends \phpbb\db\migration\migration
{
public static function depends_on()
{
return array(
'\phpbbmodders\wiki\migrations\add_wiki_board_search',
);
}

public function update_data()
{
return array(
array('permission.add', array('u_wiki_move', true, 'm_')),
);
}
}
45 changes: 45 additions & 0 deletions styles/prosilver/template/article_move.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<!-- INCLUDE overall_header.html -->
<h2>{L_WIKI_MOVE_ARTICLE}{L_COLON} {ARTICLE_NAME}</h2>

<form id="wiki_move" method="post" action="{U_ACTION}">
<div class="panel">
<div class="inner">
<p>{L_WIKI_MOVE_EXPLAIN}</p>
<!-- IF ERROR --><p class="error">{ERROR}</p><!-- ENDIF -->
<fieldset class="fields1">
<dl>
<dt><label for="new_name">{L_WIKI_MOVE_NEW_NAME}{L_COLON}</label><br><span>{L_WIKI_MOVE_NEW_NAME_EXPLAIN}</span></dt>
<dd><input type="text" name="new_name" id="new_name" size="45" maxlength="255" value="{NEW_NAME}" class="inputbox autowidth" required></dd>
</dl>
<dl>
<dt><label for="new_title">{L_WIKI_MOVE_NEW_TITLE}{L_COLON}</label></dt>
<dd><input type="text" name="new_title" id="new_title" size="45" maxlength="255" value="{NEW_TITLE}" class="inputbox autowidth" required></dd>
</dl>
<dl>
<dt>&nbsp;</dt>
<dd><label for="leave_redirect"><input type="checkbox" name="leave_redirect" id="leave_redirect" value="1"<!-- IF S_LEAVE_REDIRECT --> checked="checked"<!-- ENDIF -->> {L_WIKI_MOVE_LEAVE_REDIRECT}</label></dd>
</dl>
<!-- IF TEMPLATE_USED_BY -->
<dl>
<dt>&nbsp;</dt>
<dd>
<p class="error">{TEMPLATE_USED_BY}</p>
<label for="confirm_template"><input type="checkbox" name="confirm_template" id="confirm_template" value="1"> {L_WIKI_MOVE_TEMPLATE_CONFIRM}</label>
</dd>
</dl>
<!-- ENDIF -->
</fieldset>
</div>
</div>

<div class="panel bg2">
<div class="inner">
<fieldset class="submit-buttons">
<a href="{U_CANCEL}" class="button2">{L_CANCEL}</a>
<input type="submit" name="submit" value="{L_WIKI_MOVE_ARTICLE}" class="button1">
{S_FORM_TOKEN}
</fieldset>
</div>
</div>
</form>
<!-- INCLUDE overall_footer.html -->
5 changes: 4 additions & 1 deletion styles/prosilver/template/article_versions.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ <h2>{L_VERSIONS_OF_ARTICLE}{L_COLON} {ARTICLE_TITLE}</h2>
</div>
<p><input type="submit" value="{L_COMPARE}" class="button1" name="submit"></p>
</form>
<!-- IF S_SET_ACTIVE or S_DELETE_ARTICLE -->
<!-- IF S_SET_ACTIVE or S_DELETE_ARTICLE or S_MOVE -->
<div class="dropdown-container dropdown-container-left dropdown-up dropdown-right dropdown-button-control" id="quickmod">
<span title="{L_MODERATOR_CONTROLS}" class="dropdown-trigger button icon-button modtools-icon dropdown-select">{L_MODERATOR_CONTROLS}</span>
<div class="dropdown hidden">
Expand All @@ -64,6 +64,9 @@ <h2>{L_VERSIONS_OF_ARTICLE}{L_COLON} {ARTICLE_TITLE}</h2>
<!-- IF S_SET_ACTIVE -->
<li><a href="{U_SET_INACTIV}">{L_SET_INACTIV}</a></li>
<!-- ENDIF -->
<!-- IF S_MOVE -->
<li><a href="{U_MOVE}">{L_WIKI_MOVE_ARTICLE}</a></li>
<!-- ENDIF -->
<!-- IF S_DELETE_ARTICLE -->
<li><a href="{U_DELETE_ARTICLE}">{L_DELETE_ARTICLE}</a></li>
<!-- ENDIF -->
Expand Down
44 changes: 44 additions & 0 deletions tests/unit/move_test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php
/**
*
* Wiki extension for the phpBB Forum Software package
*
* @copyright (c) 2026, phpBB Modders, https://www.phpbbmodders.com/
* @license GNU General Public License, version 2 (GPL-2.0)
*
*/

namespace phpbbmodders\wiki\tests\unit;

use phpbbmodders\wiki\wiki\move;

/**
* Which names an article can be moved to: the same names a [[link]] can reach.
*/
class move_test extends \PHPUnit\Framework\TestCase
{
public function test_ordinary_names_are_valid()
{
foreach (array('Main Page', 'Template:Out of date', 'Category:Extensions', 'Grüße aus Köln', 'C++ & friends', "It's 1.0") as $name)
{
$this->assertTrue(move::valid_name($name), $name);
}
}

public function test_names_with_characters_links_cannot_hold_are_refused()
{
foreach (array('a/b', 'a\\b', 'a#b', 'a?b', 'a%b', 'a|b', 'a[b', 'a]b', 'a<b', 'a>b', 'a"b', "a\nb") as $name)
{
$this->assertFalse(move::valid_name($name), $name);
}
}

public function test_empty_padded_and_too_long_names_are_refused()
{
$this->assertFalse(move::valid_name(''));
$this->assertFalse(move::valid_name(' Main Page'));
$this->assertFalse(move::valid_name('Main Page '));
$this->assertTrue(move::valid_name(str_repeat('ä', 255)));
$this->assertFalse(move::valid_name(str_repeat('ä', 256)));
}
}
2 changes: 1 addition & 1 deletion tests/unit/parser_functions_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ protected function setUp(): void
/** @var string|null */
public $article = 'Template:Hello';

protected function source($article)
protected function source($article, $follow_redirect = true)
{
return $this->pages[$article] ?? null;
}
Expand Down
2 changes: 2 additions & 0 deletions wiki/compare.php
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ public function view_versions($article)
'S_DELETE' => $this->auth->acl_get('m_wiki_delete'),
'S_RESTORE' => $this->auth->acl_get('u_wiki_edit'),
'S_DELETE_ARTICLE' => $this->auth->acl_get('m_wiki_delete_article'),
'S_MOVE' => $this->auth->acl_get('u_wiki_move'),
'U_MOVE' => $this->helper->route('phpbbmodders_wiki_article', array('article' => $article, 'action' => 'move')),
'U_ACTION' => $this->helper->route('phpbbmodders_wiki_article', array('article' => $article, 'action' => 'compare')),
'U_DELETE_ARTICLE' => $this->helper->route('phpbbmodders_wiki_article', array('article' => $article, 'action' => 'detele_article')),
'U_SET_INACTIV' => $this->helper->route('phpbbmodders_wiki_article', array('article' => $article, 'action' => 'deactivate')),
Expand Down
Loading
Loading