Skip to content

Commit

Permalink
Create pages also on special pages (#3)
Browse files Browse the repository at this point in the history
Restricted to specific special pages listed in $egAutoCreatePageOnSpecialPages
  • Loading branch information
gesinn-it-wam authored Jul 4, 2023
1 parent 8f143e0 commit 1945b9d
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 36 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
vendor
coverage
.phpunit.result.cache
.vscode
5 changes: 4 additions & 1 deletion extension.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
},
"Hooks": {
"ParserFirstCallInit": "main",
"RevisionDataUpdates": "main"
"RevisionDataUpdates": "main",
"SpecialPageBeforeExecute": "main",
"SpecialPageAfterExecute": "main"
},
"HookHandlers": {
"main": {
Expand All @@ -27,6 +29,7 @@
"AutoCreatePageMaxRecursion": 1,
"AutoCreatePageIgnoreEmptyTitle": false,
"AutoCreatePageNamespaces": null,
"AutoCreatePageOnSpecialPages": [],
"_prefix": "eg"
},
"ExtensionMessagesFiles": {
Expand Down
112 changes: 77 additions & 35 deletions src/AutoCreatePage.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

use ContentHandler;
use MediaWiki\Hook\ParserFirstCallInitHook;
use MediaWiki\SpecialPage\Hook\SpecialPageAfterExecuteHook;
use MediaWiki\SpecialPage\Hook\SpecialPageBeforeExecuteHook;
use MediaWiki\Storage\Hook\RevisionDataUpdatesHook;
use Parser;
use ParserOutput;
use Title;
use WikiPage;

Expand All @@ -31,16 +32,22 @@
* @author Daniel Herzig
* @file
*/
class AutoCreatePage implements ParserFirstCallInitHook, RevisionDataUpdatesHook {
class AutoCreatePage implements
ParserFirstCallInitHook,
RevisionDataUpdatesHook,
SpecialPageBeforeExecuteHook,
SpecialPageAfterExecuteHook
{

/**
* @var SpecialPage[]
*/
private static $specialPageStack = [];

public function onParserFirstCallInit( $parser ) {
$parser->setFunctionHook( 'createPage', [ self::class, 'createPageIfNotExisting' ] );
}

public function onRevisionDataUpdates( $title, $renderedRevision, &$updates ) {
return self::doCreatePages( $title, $renderedRevision->getRevisionParserOutput() );
}

/**
* Handles the parser function for creating pages that don't exist yet,
* filling them with the given default content. It is possible to use <nowiki>
Expand Down Expand Up @@ -68,36 +75,37 @@ public static function createPageIfNotExisting( $parser, $newPageTitleText, $new
}
}

// Create pages only if the page calling the parser function is within defined namespaces
if ( !in_array( $parser->getTitle()->getNamespace(), $autoCreatePageNamespaces ) ) {
return '';
}

// Get the raw text of $newPageContent as it was before stripping <nowiki>:
$newPageContent = $parser->getStripState()->unstripNoWiki( $newPageContent );

// Store data in the parser output for later use:
$createPageData = $parser->getOutput()->getExtensionData( 'createPage' );
if ( $createPageData === null ) {
$createPageData = [];
$enabledSpecialPage = self::enabledSpecialPage();
if ( $enabledSpecialPage ) {
// Store data in static variable for later use in onSpecialPageAfterExecute:
$enabledSpecialPage->addPageToCreate( $newPageTitleText, $newPageContent );
} elseif ( in_array( $parser->getTitle()->getNamespace(), $autoCreatePageNamespaces ) ) {
// For pages with namespace in $autoCreatePageNamespaces store data in the parser output
// for later use in onRevisionDataUpdates:s
$createPageData = $parser->getOutput()->getExtensionData( 'createPage' );
if ( $createPageData === null ) {
$createPageData = [];
}
$createPageData[$newPageTitleText] = $newPageContent;
$parser->getOutput()->setExtensionData( 'createPage', $createPageData );
}
$createPageData[$newPageTitleText] = $newPageContent;
$parser->getOutput()->setExtensionData( 'createPage', $createPageData );

return "";
}

/**
* Creates pages that have been requested by the create page parser function. This is done only
* after the safe is complete to avoid any concurrent article modifications.
* Create pages that have been requested by the create page parser function for pages with
* namespace in $egAutoCreatePageNamespaces.
* This is done only after the safe is complete to avoid any concurrent article modifications.
* Note that article is, in spite of its name, a WikiPage object since MW 1.21.
* @param Title $sourceTitle
* @param ParserOutput $output
* @return bool
*/
private static function doCreatePages( $sourceTitle, $output ) {
public function onRevisionDataUpdates( $title, $renderedRevision, &$updates ) {
global $egAutoCreatePageMaxRecursion;

$output = $renderedRevision->getRevisionParserOutput();
$createPageData = $output->getExtensionData( 'createPage' );
if ( $createPageData === null ) {
return true; // no pages to create
Expand All @@ -106,19 +114,8 @@ private static function doCreatePages( $sourceTitle, $output ) {
// Prevent pages to be created by pages that are created to avoid loops:
$egAutoCreatePageMaxRecursion--;

$sourceTitleText = $sourceTitle->getPrefixedText();

foreach ( $createPageData as $pageTitleText => $pageContentText ) {
$pageTitle = Title::newFromText( $pageTitleText );
// wfDebugLog( 'createpage', "CREATE " . $pageTitle->getText() . " Text: " . $pageContent );

if ( $pageTitle !== null && !$pageTitle->isKnown() && $pageTitle->canExist() ) {
$newWikiPage = new WikiPage( $pageTitle );
$pageContent = ContentHandler::makeContent( $pageContentText, $sourceTitle );
$newWikiPage->doEditContent( $pageContent,
"Page created automatically by parser function on page [[$sourceTitleText]]" ); // TODO i18n
// wfDebugLog( 'createpage', "CREATED PAGE " . $pageTitle->getText() . " Text: " . $pageContent );
}
self::createPage( $title, $pageTitleText, $pageContentText );
}

// Reset state. Probably not needed since parsing is usually done here anyway:
Expand All @@ -128,4 +125,49 @@ private static function doCreatePages( $sourceTitle, $output ) {
return true;
}

/**
* Register special page in static $specialPageStack
*/
public function onSpecialPageBeforeExecute( $special, $subPage ) {
global $egAutoCreatePageOnSpecialPages;

$isEnabled = in_array( $special->getName(), $egAutoCreatePageOnSpecialPages );
array_push( self::$specialPageStack, new SpecialPage( $isEnabled ) );
}

/**
* Create pages requested on enabled special pages
*/
public function onSpecialPageAfterExecute( $special, $subPage ) {
$specialPage = array_pop( self::$specialPageStack );

if ( $specialPage->isEnabled() ) {
foreach ( $specialPage->pagesToCreate() as $pageTitleText => $pageContentText ) {
self::createPage( $special->getFullTitle(), $pageTitleText, $pageContentText );
}
}
}

/**
* @return SpecialPage|null the current special page if it is enabled, null otherwise
*/
private static function enabledSpecialPage() {
$count = count( self::$specialPageStack );
$currentSpecialPage = $count === 0 ? null : self::$specialPageStack[ $count - 1 ];
return $currentSpecialPage !== null && $currentSpecialPage->isEnabled() ? $currentSpecialPage : null;
}

private static function createPage( $sourceTitle, $pageTitleText, $pageContentText ) {
$sourceTitleText = $sourceTitle->getPrefixedText();
$pageTitle = Title::newFromText( $pageTitleText );
// wfDebugLog( 'createpage', "CREATE " . $pageTitle->getText() . " Text: " . $pageContent );

if ( $pageTitle !== null && !$pageTitle->isKnown() && $pageTitle->canExist() ) {
$newWikiPage = new WikiPage( $pageTitle );
$pageContent = ContentHandler::makeContent( $pageContentText, $sourceTitle );
$newWikiPage->doEditContent( $pageContent,
"Page created automatically by parser function on page [[$sourceTitleText]]" ); // TODO i18n
// wfDebugLog( 'createpage', "CREATED PAGE " . $pageTitle->getText() . " Text: " . $pageContent );
}
}
}
25 changes: 25 additions & 0 deletions src/SpecialPage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace ACP;

class SpecialPage {

private $isEnabled;
private $pagesToCreate = [];

public function __construct( $isEnabled ) {
$this->isEnabled = $isEnabled;
}

public function isEnabled() {
return $this->isEnabled;
}

public function addPageToCreate( $titleText, $content ) {
$this->pagesToCreate[$titleText] = $content;
}

public function pagesToCreate() {
return $this->pagesToCreate;
}
}
36 changes: 36 additions & 0 deletions tests/phpunit/Integration/AutoCreatePageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

namespace ACP\Tests\Integration;

use FauxRequest;
use MediaWiki\MediaWikiServices;
use MediaWikiIntegrationTestCase;
use RequestContext;
use Title;
use WikiPage;

Expand All @@ -27,6 +30,12 @@ protected function setUp(): void {
$egAutoCreatePageMaxRecursion = self::$defaultAutoCreatePageMaxRecursion;
}

protected function tearDown(): void {
parent::tearDown();
global $egAutoCreatePageOnSpecialPages;
$egAutoCreatePageOnSpecialPages = [];
}

public function testCreatesPage() {
[ $x, $y ] = $this->randomize( [ 'x', 'y' ] );
$this->insertPage( $x, "{{#createpageifnotex:$y|" . self::EXPECTED_TEXT . "}}" );
Expand Down Expand Up @@ -86,6 +95,33 @@ public function testRecursesIfToldSo() {
$this->assertEquals( self::EXPECTED_TEXT, $text3 );
}

public function testIsNotCalledFromUnenabledSpecialPage() {
$page = $page = self::createOnSpecialExpandTemplates( 'NotCreated' );

$this->assertFalse( $page->exists() );
}

public function testIsCalledFromEnabledSpecialPage() {
global $egAutoCreatePageOnSpecialPages;
$egAutoCreatePageOnSpecialPages = [ 'ExpandTemplates' ];

$page = self::createOnSpecialExpandTemplates( 'Created' );

$this->assertTrue( $page->exists() );
}

private static function createOnSpecialExpandTemplates( $title ) {
$ctx = new RequestContext();
$ctx->setRequest( new FauxRequest( [
'wpContextTitle' => 'X',
'wpInput' => "{{#createpageifnotex:$title|Some content...}}",
] ) );
$sp = Title::makeTitle( NS_SPECIAL, 'ExpandTemplates' );
MediaWikiServices::getInstance()->getSpecialPageFactory()->executePath( $sp, $ctx );

return WikiPage::factory( Title::newFromText( $title ) );
}

private function textOf( $title ) {
$page = WikiPage::factory( Title::newFromText( $title ) );

Expand Down

0 comments on commit 1945b9d

Please sign in to comment.