Skip to content
Open
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
11 changes: 10 additions & 1 deletion docs/reference/extending.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,16 @@ class StaticPagePropertyProvider implements PagePropertyProvider {
```

Register with `NeoWikiRegistrar::addPagePropertyProvider()`. The context exposes the page id, title,
creation and modification times, categories, and last editor. Example:
creation and modification times, categories, and last editor, so providers can derive Page Properties
from the page content without re-fetching or re-parsing it.

To derive Page Properties from the content, prefer the parse products: `categories`, and
`parserProperties` — the MediaWiki page properties recorded during parsing (e.g. those a parser hook
sets via `ParserOutput::setPageProperty`). These are template-expansion-safe and robust. (Note that
`parserProperties` are an input from MediaWiki's parse; they are not the NeoWiki Page Properties this
provider returns.) The raw main slot `content` and its `contentModel` are also exposed, but scraping
raw wikitext is fragile — reach for them mainly when handling a custom, non-wikitext content model that
the parse products do not cover. Example:
[`src/StaticPagePropertyProvider.php`](https://github.com/ProfessionalWiki/NeoWiki/blob/master/tests/RedHerb/src/StaticPagePropertyProvider.php).

### Reading NeoWiki data and authorization
Expand Down
11 changes: 11 additions & 0 deletions src/Domain/Page/PagePropertyProviderContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@
* @param string $modificationTime In the standard MediaWiki format, ie 20230726163439
* @param string[] $categories
* @param string $lastEditor Plain username of the last editor, e.g. "JohnDoe". Empty string if unknown.
* @param string $content Serialized main slot content of the revision, e.g. the wikitext.
* Empty string if the content is unavailable.
* @param string $contentModel Content model of the main slot, e.g. "wikitext".
* Empty string if the content is unavailable.
* @param array<string, int|float|string|bool|null> $parserProperties Properties recorded during parsing of the
* content (MediaWiki page properties, e.g. "defaultsort" or values set by parser hooks via
* ParserOutput::setPageProperty). These are inputs from the parse, not to be confused with the NeoWiki
* Page Properties that providers return.
*/
public function __construct(
public PageId $pageId,
Expand All @@ -22,6 +30,9 @@ public function __construct(
public string $modificationTime,
public array $categories,
public string $lastEditor,
public string $content,
public string $contentModel,
public array $parserProperties,

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@osnard unsure if this is sufficient for your use cases and which of those you actually need? Perhaps just parserProperties?

) {
}

Expand Down
29 changes: 13 additions & 16 deletions src/PagePropertiesBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

namespace ProfessionalWiki\NeoWiki;

use MediaWiki\Content\Content;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\Content\Renderer\ContentParseParams;
use MediaWiki\Parser\ParserOutput;
use MediaWiki\Revision\RevisionRecord;
use MediaWiki\Revision\RevisionStore;
use MediaWiki\Revision\SlotRecord;
Expand Down Expand Up @@ -40,18 +42,28 @@ public function getPagePropertiesFor( RevisionRecord $revision, ?UserIdentity $u

private function buildContext( RevisionRecord $revision, ?UserIdentity $user ): PagePropertyProviderContext {
$linkTarget = $revision->getPageAsLinkTarget();
$content = $revision->getContent( SlotRecord::MAIN );
$parserOutput = $content === null ? null : $this->parse( $content, $revision );

return new PagePropertyProviderContext(
pageId: new PageId( $revision->getPageId() ),
pageTitle: $this->titleFormatter->getPrefixedText( $linkTarget ),
namespaceId: $linkTarget->getNamespace(),
creationTime: $this->getCreationTime( $revision ),
modificationTime: $this->getModificationTime( $revision ),
categories: $this->getCategories( $revision ),
categories: $parserOutput === null ? [] : $parserOutput->getCategoryNames(),
lastEditor: $user?->getName() ?? '',
content: $content === null ? '' : $content->serialize(),
contentModel: $content === null ? '' : $content->getModel(),
parserProperties: $parserOutput === null ? [] : $parserOutput->getPageProperties(),
);
}

private function parse( Content $content, RevisionRecord $revision ): ParserOutput {
return $this->contentHandlerFactory->getContentHandler( $content->getModel() )
->getParserOutput( $content, new ContentParseParams( $revision->getPage() ) );
}

private function getCreationTime( RevisionRecord $revision ): string {
$time = $this->revisionStore->getFirstRevision( $revision->getPage() )?->getTimestamp();

Expand All @@ -72,19 +84,4 @@ private function getModificationTime( RevisionRecord $revision ): string {
return $time;
}

/**
* @return string[]
*/
private function getCategories( RevisionRecord $revision ): array {
$content = $revision->getContent( SlotRecord::MAIN );

if ( $content === null ) {
return [];
}

return $this->contentHandlerFactory->getContentHandler( $content->getModel() )
->getParserOutput( $content, new ContentParseParams( $revision->getPage() ) )
->getCategoryNames();
}

}
66 changes: 66 additions & 0 deletions tests/phpunit/PagePropertiesBuilderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

declare( strict_types = 1 );

namespace ProfessionalWiki\NeoWiki\Tests;

use ProfessionalWiki\NeoWiki\Domain\Page\PagePropertyProviderContext;
use ProfessionalWiki\NeoWiki\Domain\Page\PagePropertyProviderRegistry;
use ProfessionalWiki\NeoWiki\PagePropertiesBuilder;
use ProfessionalWiki\NeoWiki\Tests\TestDoubles\SpyPagePropertyProvider;

/**
* @covers \ProfessionalWiki\NeoWiki\PagePropertiesBuilder
* @group Database
*/
class PagePropertiesBuilderTest extends NeoWikiIntegrationTestCase {

public function testProviderReceivesContent(): void {
$context = $this->getContextForNewPageWithContent( 'Some text [[Category:Cats]]' );

$this->assertSame( 'Some text [[Category:Cats]]', $context->content );
}

public function testProviderReceivesContentModel(): void {
$context = $this->getContextForNewPageWithContent( 'Whatever wikitext' );

$this->assertSame( CONTENT_MODEL_WIKITEXT, $context->contentModel );
}

public function testProviderReceivesParserRecordedProperties(): void {
$context = $this->getContextForNewPageWithContent( 'Sorted {{DEFAULTSORT:Zebra}}' );

$this->assertSame( 'Zebra', $context->parserProperties['defaultsort'] );
}

public function testProviderReceivesCategoriesFromParsedContent(): void {
$context = $this->getContextForNewPageWithContent( 'Some text [[Category:Cats]]' );

$this->assertSame( [ 'Cats' ], $context->categories );
}

private function getContextForNewPageWithContent( string $wikitext ): PagePropertyProviderContext {
$revision = $this->editPage( 'PagePropertiesBuilderTestPage', $wikitext )->getNewRevision();

$spy = new SpyPagePropertyProvider();

$this->newPagePropertiesBuilder( $spy )->getPagePropertiesFor( $revision, null );

return $spy->getReceivedContext();
}

private function newPagePropertiesBuilder( SpyPagePropertyProvider $provider ): PagePropertiesBuilder {
$registry = new PagePropertyProviderRegistry();
$registry->addProvider( $provider );

$services = $this->getServiceContainer();

return new PagePropertiesBuilder(
revisionStore: $services->getRevisionStore(),
contentHandlerFactory: $services->getContentHandlerFactory(),
titleFormatter: $services->getTitleFormatter(),
providerRegistry: $registry,
);
}

}
3 changes: 3 additions & 0 deletions tests/phpunit/Persistence/CorePagePropertyProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ private function getPropertiesForContext(
modificationTime: $modificationTime,
categories: $categories,
lastEditor: $lastEditor,
content: 'Whatever content',
contentModel: 'wikitext',
parserProperties: [],
)
);
}
Expand Down
27 changes: 27 additions & 0 deletions tests/phpunit/TestDoubles/SpyPagePropertyProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare( strict_types = 1 );

namespace ProfessionalWiki\NeoWiki\Tests\TestDoubles;

use ProfessionalWiki\NeoWiki\Domain\Page\PagePropertyProvider;
use ProfessionalWiki\NeoWiki\Domain\Page\PagePropertyProviderContext;

class SpyPagePropertyProvider implements PagePropertyProvider {

private ?PagePropertyProviderContext $receivedContext = null;

public function getProperties( PagePropertyProviderContext $context ): array {
$this->receivedContext = $context;
return [];
}

public function getReceivedContext(): PagePropertyProviderContext {
if ( $this->receivedContext === null ) {
throw new \LogicException( 'getProperties was not called' );
}

return $this->receivedContext;
}

}
Loading