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
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ endif
EXTENSION=KnowledgeGraph

# docker images
MW_VERSION?=1.39
PHP_VERSION?=8.1
MW_VERSION?=1.43
PHP_VERSION?=8.3
DB_TYPE?=mysql
DB_IMAGE?="mariadb:10"

Expand Down
3 changes: 2 additions & 1 deletion extension.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
"KnowledgeGraphDesigner": "SpecialKnowledgeGraphDesigner"
},
"Hooks":{
"LoadExtensionSchemaUpdates": "KnowledgeGraph::onLoadExtensionSchemaUpdates",
"BeforePageDisplay":"KnowledgeGraph::onBeforePageDisplay",
"ParserFirstCallInit": "KnowledgeGraph::onParserFirstCallInit",
"OutputPageParserOutput": "KnowledgeGraph::onOutputPageParserOutput",
Expand Down Expand Up @@ -91,6 +90,8 @@
"knowledgegraph-dialog-cancel",
"knowledgegraph-dialog-delete",
"knowledgegraph-dialog-select-article",
"knowledgegraph-dialog-select-namespace",
"knowledgegraph-dialog-main-namespace",
"knowledgegraph-dialog-edit-depth",
"knowledgegraph-dialog-edit-limit",
"knowledgegraph-dialog-edit-offset",
Expand Down
10 changes: 6 additions & 4 deletions i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"knowledgegraph-knowledgegraphdesigner-label": "KnowledgeGraph Designer",
"knowledgegraph-toolbar-info": "Info",
"knowledgegraph-toolbar-help": "Hilfe",
"knowledgegraph-graph-options-message": "Klicken Sie auf die Schaltfläche unten und kopieren Sie den Inhalt der Tasten \"nodes\" und \"edges\" in einen Artikel <a target=\"_blank\" href=\"$1\">wie diesen</a>. Dann setzen Sie ihn als Wert der Parameter \"graph-options\" oder \"property-options?[Property label] der Parser-Funktion des KnowledgeGraphs.",
"knowledgegraph-graph-options-message": "{{#FORMAL:Klicke|Klicken Sie}} auf die Schaltfläche unten und {{#FORMAL:kopiere|kopieren Sie}} den Inhalt der Tasten \"nodes\" und \"edges\" in einen Artikel <a target=\"_blank\" href=\"$1\">wie diesen</a>. Dann {{#FORMAL:setze|setzen Sie}} ihn als Wert der Parameter \"graph-options\" oder \"property-options?[Property label] der Parser-Funktion des KnowledgeGraphs.",
"knowledgegraph-menu-open-article": "Seite öffnen",
"knowledgegraph-menu-delete-node": "Knoten entfernen",
"knowledgegraph-delete-node-confirm": "Diesen Knoten sicher entfernen?",
Expand All @@ -22,7 +22,9 @@
"knowledgegraph-dialog-done": "Fertig",
"knowledgegraph-dialog-cancel": "Abbrechen",
"knowledgegraph-dialog-delete": "Löschen",
"knowledgegraph-dialog-select-article": "Wählen Sie einen Artikel mit semantischen Attributen",
"knowledgegraph-dialog-select-article": "{{#FORMAL:Wähle|Wählen Sie}} einen Artikel mit semantischen Attributen",
"knowledgegraph-dialog-select-namespace": "{{#FORMAL:Wähle|Wählen Sie}} einen Namensraum",
"knowledgegraph-dialog-main-namespace": "(Main)",
"knowledgegraph-dialog-edit-depth": "Tiefe",
"knowledgegraph-dialog-edit-limit": "Limit",
"knowledgegraph-dialog-edit-offset": "Versatz",
Expand All @@ -42,10 +44,10 @@
"knowledgegraph-dialog-results-importing-nodes": "importiert Knoten:",
"knowledgegraph-copied-to-clipboard": "In die Zwischenablage kopiert!",
"knowledgegraph-toolbar-reset-network": "zurücksetzen",
"knowledgegraph-toolbar-reset-network-confirm": "Bist du sicher, dass du das Netzwerk neu initialisieren willst?",
"knowledgegraph-toolbar-reset-network-confirm": "{{#FORMAL:Bist du|Sind Sie}} sicher, dass {{#FORMAL:du|Sie}} das Netzwerk neu initialisieren {{#FORMAL:willst|wollen}}?",
"knowledgegraph-toolbar-add-node": "Knoten hinzufügen",
"knowledgegraph-toolbar-toggle-config": "Konfiguration umschalten",
"knowledgegraph-toolbar-export-graph": "exportiere Wikitext",
"knowledgegraph-credits": "Info:",
"knowledgegraph-credits-list": "<ul><li><a target=\"_blank\" href=\"https://km-a.net/consulting/knowledge-wiki\">KM-A</a></li><li><a target=\"_blank\" href=\"https://gesinn.it/de/Hauptseite\">gesinn.it</a></ul>"
"knowledgegraph-credits-list": "<ul><li><a target=\"_blank\" href=\"https://km-a.net/consulting/knowledge-wiki\">KM-A</a></li><li><a target=\"_blank\" href=\"https://gesinn.it\">gesinn.it</a></ul>"
}
2 changes: 2 additions & 0 deletions i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
"knowledgegraph-dialog-cancel": "Cancel",
"knowledgegraph-dialog-delete": "Delete",
"knowledgegraph-dialog-select-article": "Select an article with semantic properties",
"knowledgegraph-dialog-select-namespace": "Select a namespace",
"knowledgegraph-dialog-main-namespace": "(Main)",
"knowledgegraph-dialog-edit-depth": "Depth",
"knowledgegraph-dialog-edit-limit": "Limit",
"knowledgegraph-dialog-edit-offset": "Offset",
Expand Down
167 changes: 140 additions & 27 deletions includes/KnowledgeGraph.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,38 +134,57 @@ public static function initSMW() {
* @return void
*/
public static function onBeforePageDisplay( $out, $skin ) {
$out->addModules( 'ext.KnowledgeGraph' );
// Ensure that the KnowledgeGraphOptions page exists
self::ensureKnowledgeGraphOptionsPageExists();
return true;
}

/**
* @param Parser $parser
*/
public static function onParserFirstCallInit( Parser $parser ) {
$parser->setFunctionHook( 'knowledgegraph', [ self::class, 'parserFunctionKnowledgeGraph' ] );
}

/**
* @param DatabaseUpdater|null $updater
* Ensure that the KnowledgeGraphOptions page exists in the MediaWiki namespace.
* Creates it lazily if missing.
*
* @return void
*/
public static function onLoadExtensionSchemaUpdates( DatabaseUpdater $updater = null ) {
$text = file_get_contents( __DIR__ . '/../data/KnowledgeGraphOptions.js' );
$user = RequestContext::getMain()->getUser();
$title = TitleClass::makeTitleSafe( NS_MEDIAWIKI, 'KnowledgeGraphOptions' );
private static function ensureKnowledgeGraphOptionsPageExists() {
$title = Title::makeTitleSafe( NS_MEDIAWIKI, 'KnowledgeGraphOptions' );
if ( !$title ) {
return;
}

$wikiPage = self::getWikiPage( $title );
$pageUpdater = $wikiPage->newPageUpdater( $user );
if ( $wikiPage->exists() ) {
return;
}

// Create page content
$filePath = __DIR__ . '/../data/KnowledgeGraphOptions.js';
if ( !file_exists( $filePath ) ) {
wfDebugLog( 'KnowledgeGraph', 'Missing KnowledgeGraphOptions.js template file.' );
return;
}

$text = file_get_contents( $filePath );
$content = ContentHandler::makeContent(
$text,
$title,
CONTENT_MODEL_JAVASCRIPT
);

// @see includes/Defines.php
$modelId = CONTENT_MODEL_JAVASCRIPT;
$slotContent = ContentHandler::makeContent( $text, $title, $modelId );
$slotName = SlotRecord::MAIN;
$pageUpdater->setContent( $slotName, $slotContent );
$user = User::newSystemUser( 'MediaWiki default', [ 'steal' => true ] );

$summary = "KnowledgeGraph";
$flags = EDIT_INTERNAL;
$comment = CommentStoreComment::newUnsavedComment( $summary );
$pageUpdater->saveRevision( $comment, $flags );
$pageUpdater = $wikiPage->newPageUpdater( $user );
$pageUpdater->setContent( SlotRecord::MAIN, $content );
$pageUpdater->saveRevision(
CommentStoreComment::newUnsavedComment( 'Initialize KnowledgeGraphOptions' ),
EDIT_SUPPRESS_RC
);
}

/**
* @param Parser $parser
*/
public static function onParserFirstCallInit( Parser $parser ) {
$parser->setFunctionHook( 'knowledgegraph', [ self::class, 'parserFunctionKnowledgeGraph' ] );
}

/**
Expand Down Expand Up @@ -277,6 +296,7 @@ public static function parserFunctionKnowledgeGraph( Parser $parser, ...$argv )
$params['graphOptions'] = $graphOptions;
$params['propertyOptions'] = $propertyOptions;
self::$graphs[] = $params;
self::$data = [];

$out->setExtensionData( 'knowledgegraphs', self::$graphs );

Expand All @@ -290,8 +310,9 @@ public static function parserFunctionKnowledgeGraph( Parser $parser, ...$argv )
'wgKnowledgeGraphColorPalette' => $colors
] );

$index = count( self::$graphs ) - 1;
return [
'<div class="KnowledgeGraph" id="knowledgegraph-wrapper-' . key( self::$graphs ) . '">'
'<div class="KnowledgeGraph" id="knowledgegraph-wrapper-' . $index . '">'
. wfMessage( 'knowledge-graph-wrapper-loading' )->text() . '</div>',
'noparse' => true,
'isHTML' => true
Expand Down Expand Up @@ -358,6 +379,75 @@ public static function getSubjectsByProperty( $propertyText, $limit = 100, $offs
return $ret;
}

/**
* Get all properties for a given node.
* @param string $nodeTitleText
* @return array
*/
public static function getAllPropertiesForNode( string $nodeTitleText ): array {
$ret = [];

$title = Title::newFromText( $nodeTitleText );
if ( !$title || !$title->isKnown() ) {
wfDebugLog( 'KnowledgeGraph', "Invalid or unknown node: '$nodeTitleText'" );
return [];
}

$apiParams = [
'action' => 'smwbrowse',
'format' => 'json',
'browse' => 'subject',
'params' => json_encode( [
'subject' => $nodeTitleText,
'ns' => $title->getNamespace(),
] ),
];

$request = new \FauxRequest( $apiParams, false );
$api = new \ApiMain( $request );
$api->execute();
$data = $api->getResult()->getResultData();

if ( empty( $data[ 'query' ][ 'data' ] ) ) {
wfDebugLog( 'KnowledgeGraph', "No properties returned from smwbrowse for '$nodeTitleText'" );
return [];
}

foreach ( $data['query']['data'] as $propertyEntry ) {
$propKey = $propertyEntry['property'] ?? null;
$direction = $propertyEntry['direction'] ?? 'direct';

if ( !$propKey ) {
continue;
}

if (
( isset( self::$exclude ) && in_array( $propKey, self::$exclude ) ) ||
str_starts_with( $propKey, '_' ) ||
str_starts_with( $propKey, '___' ) ||
ctype_upper( str_replace( '_', '', $propKey ) )
) {
continue;
}

$propKey = str_replace( '_', ' ', $propKey );

if ( $direction === 'inverse' ) {
$propKey = '-' . $propKey;
}

$ret[] = $propKey;
}

wfDebugLog( 'KnowledgeGraph', sprintf(
"getAllPropertiesForNode (smwbrowse): node=%s, properties=%d",
$nodeTitleText,
count( $ret )
) );

return array_unique( $ret );
}

/**
* @param Title|MediaWiki\Title\Title $title $title
* @return string|null
Expand Down Expand Up @@ -403,6 +493,9 @@ public static function onOutputPageParserOutput( OutputPage $out, ParserOutput $
$out->addJsConfigVars( [
'knowledgegraphs' => json_encode( $data )
] );

// add the required JavaScript module if graphs are present
$out->addModules( 'ext.KnowledgeGraph' );
}
}

Expand Down Expand Up @@ -534,7 +627,16 @@ public static function setSemanticDataFromApi( Title $title, $onlyProperties, $d
return;
}

if ( $depth > $maxDepth ) {
// If maxDepth is 0, only create the root node without loading SMW data
if ( $maxDepth === 0 ) {
self::$data[$titleText] = [
'properties' => [],
'categories' => [],
];
return;
}

if ( $depth >= $maxDepth ) {
return;
}

Expand Down Expand Up @@ -640,8 +742,19 @@ public static function setSemanticDataFromApi( Title $title, $onlyProperties, $d

foreach ( $entry['dataitem'] ?? [] as $item ) {
if ( $item['type'] === 9 ) {
$linkedTitle = explode( '#', $item['item'] )[0];
$linkedTitle = $linkedTitle ? str_replace( '_', ' ', $linkedTitle ) : null;
$parts = explode( '#', $item['item'] );
$dbkey = $parts[0] ?? '';
$nsId = isset( $parts[1] ) && is_numeric( $parts[1] ) ? (int)$parts[1] : 0;

$namespaceInfo = MediaWiki\MediaWikiServices::getInstance()->getNamespaceInfo();
$nsName = $namespaceInfo->getCanonicalName( $nsId );

$linkedTitle = $dbkey;
if ( $nsName !== '' && $nsName !== false ) {
$linkedTitle = $nsName . ':' . $dbkey;
}

$linkedTitle = str_replace( '_', ' ', $linkedTitle );
if ( !$linkedTitle ) {
continue;
}
Expand Down
Loading