Skip to content

Commit 7a343d7

Browse files
Merge pull request #36 from SemanticMediaWiki/issue-34
Improve KnowledgeGraph Initialization, Namespace Handling, and Multi-Instance Support
2 parents e444f34 + 67b6624 commit 7a343d7

File tree

12 files changed

+1037
-969
lines changed

12 files changed

+1037
-969
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ endif
1111
EXTENSION=KnowledgeGraph
1212

1313
# docker images
14-
MW_VERSION?=1.39
15-
PHP_VERSION?=8.1
14+
MW_VERSION?=1.43
15+
PHP_VERSION?=8.3
1616
DB_TYPE?=mysql
1717
DB_IMAGE?="mariadb:10"
1818

extension.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
"KnowledgeGraphDesigner": "SpecialKnowledgeGraphDesigner"
3737
},
3838
"Hooks":{
39-
"LoadExtensionSchemaUpdates": "KnowledgeGraph::onLoadExtensionSchemaUpdates",
4039
"BeforePageDisplay":"KnowledgeGraph::onBeforePageDisplay",
4140
"ParserFirstCallInit": "KnowledgeGraph::onParserFirstCallInit",
4241
"OutputPageParserOutput": "KnowledgeGraph::onOutputPageParserOutput",
@@ -91,6 +90,8 @@
9190
"knowledgegraph-dialog-cancel",
9291
"knowledgegraph-dialog-delete",
9392
"knowledgegraph-dialog-select-article",
93+
"knowledgegraph-dialog-select-namespace",
94+
"knowledgegraph-dialog-main-namespace",
9495
"knowledgegraph-dialog-edit-depth",
9596
"knowledgegraph-dialog-edit-limit",
9697
"knowledgegraph-dialog-edit-offset",

i18n/de.json

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"knowledgegraph-knowledgegraphdesigner-label": "KnowledgeGraph Designer",
1414
"knowledgegraph-toolbar-info": "Info",
1515
"knowledgegraph-toolbar-help": "Hilfe",
16-
"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.",
16+
"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.",
1717
"knowledgegraph-menu-open-article": "Seite öffnen",
1818
"knowledgegraph-menu-delete-node": "Knoten entfernen",
1919
"knowledgegraph-delete-node-confirm": "Diesen Knoten sicher entfernen?",
@@ -22,7 +22,9 @@
2222
"knowledgegraph-dialog-done": "Fertig",
2323
"knowledgegraph-dialog-cancel": "Abbrechen",
2424
"knowledgegraph-dialog-delete": "Löschen",
25-
"knowledgegraph-dialog-select-article": "Wählen Sie einen Artikel mit semantischen Attributen",
25+
"knowledgegraph-dialog-select-article": "{{#FORMAL:Wähle|Wählen Sie}} einen Artikel mit semantischen Attributen",
26+
"knowledgegraph-dialog-select-namespace": "{{#FORMAL:Wähle|Wählen Sie}} einen Namensraum",
27+
"knowledgegraph-dialog-main-namespace": "(Main)",
2628
"knowledgegraph-dialog-edit-depth": "Tiefe",
2729
"knowledgegraph-dialog-edit-limit": "Limit",
2830
"knowledgegraph-dialog-edit-offset": "Versatz",
@@ -42,10 +44,10 @@
4244
"knowledgegraph-dialog-results-importing-nodes": "importiert Knoten:",
4345
"knowledgegraph-copied-to-clipboard": "In die Zwischenablage kopiert!",
4446
"knowledgegraph-toolbar-reset-network": "zurücksetzen",
45-
"knowledgegraph-toolbar-reset-network-confirm": "Bist du sicher, dass du das Netzwerk neu initialisieren willst?",
47+
"knowledgegraph-toolbar-reset-network-confirm": "{{#FORMAL:Bist du|Sind Sie}} sicher, dass {{#FORMAL:du|Sie}} das Netzwerk neu initialisieren {{#FORMAL:willst|wollen}}?",
4648
"knowledgegraph-toolbar-add-node": "Knoten hinzufügen",
4749
"knowledgegraph-toolbar-toggle-config": "Konfiguration umschalten",
4850
"knowledgegraph-toolbar-export-graph": "exportiere Wikitext",
4951
"knowledgegraph-credits": "Info:",
50-
"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>"
52+
"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>"
5153
}

i18n/en.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
"knowledgegraph-dialog-cancel": "Cancel",
2727
"knowledgegraph-dialog-delete": "Delete",
2828
"knowledgegraph-dialog-select-article": "Select an article with semantic properties",
29+
"knowledgegraph-dialog-select-namespace": "Select a namespace",
30+
"knowledgegraph-dialog-main-namespace": "(Main)",
2931
"knowledgegraph-dialog-edit-depth": "Depth",
3032
"knowledgegraph-dialog-edit-limit": "Limit",
3133
"knowledgegraph-dialog-edit-offset": "Offset",

includes/KnowledgeGraph.php

Lines changed: 140 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -134,38 +134,57 @@ public static function initSMW() {
134134
* @return void
135135
*/
136136
public static function onBeforePageDisplay( $out, $skin ) {
137-
$out->addModules( 'ext.KnowledgeGraph' );
137+
// Ensure that the KnowledgeGraphOptions page exists
138+
self::ensureKnowledgeGraphOptionsPageExists();
138139
return true;
139140
}
140141

141142
/**
142-
* @param Parser $parser
143-
*/
144-
public static function onParserFirstCallInit( Parser $parser ) {
145-
$parser->setFunctionHook( 'knowledgegraph', [ self::class, 'parserFunctionKnowledgeGraph' ] );
146-
}
147-
148-
/**
149-
* @param DatabaseUpdater|null $updater
143+
* Ensure that the KnowledgeGraphOptions page exists in the MediaWiki namespace.
144+
* Creates it lazily if missing.
145+
*
146+
* @return void
150147
*/
151-
public static function onLoadExtensionSchemaUpdates( DatabaseUpdater $updater = null ) {
152-
$text = file_get_contents( __DIR__ . '/../data/KnowledgeGraphOptions.js' );
153-
$user = RequestContext::getMain()->getUser();
154-
$title = TitleClass::makeTitleSafe( NS_MEDIAWIKI, 'KnowledgeGraphOptions' );
148+
private static function ensureKnowledgeGraphOptionsPageExists() {
149+
$title = Title::makeTitleSafe( NS_MEDIAWIKI, 'KnowledgeGraphOptions' );
150+
if ( !$title ) {
151+
return;
152+
}
155153

156154
$wikiPage = self::getWikiPage( $title );
157-
$pageUpdater = $wikiPage->newPageUpdater( $user );
155+
if ( $wikiPage->exists() ) {
156+
return;
157+
}
158+
159+
// Create page content
160+
$filePath = __DIR__ . '/../data/KnowledgeGraphOptions.js';
161+
if ( !file_exists( $filePath ) ) {
162+
wfDebugLog( 'KnowledgeGraph', 'Missing KnowledgeGraphOptions.js template file.' );
163+
return;
164+
}
165+
166+
$text = file_get_contents( $filePath );
167+
$content = ContentHandler::makeContent(
168+
$text,
169+
$title,
170+
CONTENT_MODEL_JAVASCRIPT
171+
);
158172

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

165-
$summary = "KnowledgeGraph";
166-
$flags = EDIT_INTERNAL;
167-
$comment = CommentStoreComment::newUnsavedComment( $summary );
168-
$pageUpdater->saveRevision( $comment, $flags );
175+
$pageUpdater = $wikiPage->newPageUpdater( $user );
176+
$pageUpdater->setContent( SlotRecord::MAIN, $content );
177+
$pageUpdater->saveRevision(
178+
CommentStoreComment::newUnsavedComment( 'Initialize KnowledgeGraphOptions' ),
179+
EDIT_SUPPRESS_RC
180+
);
181+
}
182+
183+
/**
184+
* @param Parser $parser
185+
*/
186+
public static function onParserFirstCallInit( Parser $parser ) {
187+
$parser->setFunctionHook( 'knowledgegraph', [ self::class, 'parserFunctionKnowledgeGraph' ] );
169188
}
170189

171190
/**
@@ -277,6 +296,7 @@ public static function parserFunctionKnowledgeGraph( Parser $parser, ...$argv )
277296
$params['graphOptions'] = $graphOptions;
278297
$params['propertyOptions'] = $propertyOptions;
279298
self::$graphs[] = $params;
299+
self::$data = [];
280300

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

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

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

382+
/**
383+
* Get all properties for a given node.
384+
* @param string $nodeTitleText
385+
* @return array
386+
*/
387+
public static function getAllPropertiesForNode( string $nodeTitleText ): array {
388+
$ret = [];
389+
390+
$title = Title::newFromText( $nodeTitleText );
391+
if ( !$title || !$title->isKnown() ) {
392+
wfDebugLog( 'KnowledgeGraph', "Invalid or unknown node: '$nodeTitleText'" );
393+
return [];
394+
}
395+
396+
$apiParams = [
397+
'action' => 'smwbrowse',
398+
'format' => 'json',
399+
'browse' => 'subject',
400+
'params' => json_encode( [
401+
'subject' => $nodeTitleText,
402+
'ns' => $title->getNamespace(),
403+
] ),
404+
];
405+
406+
$request = new \FauxRequest( $apiParams, false );
407+
$api = new \ApiMain( $request );
408+
$api->execute();
409+
$data = $api->getResult()->getResultData();
410+
411+
if ( empty( $data[ 'query' ][ 'data' ] ) ) {
412+
wfDebugLog( 'KnowledgeGraph', "No properties returned from smwbrowse for '$nodeTitleText'" );
413+
return [];
414+
}
415+
416+
foreach ( $data['query']['data'] as $propertyEntry ) {
417+
$propKey = $propertyEntry['property'] ?? null;
418+
$direction = $propertyEntry['direction'] ?? 'direct';
419+
420+
if ( !$propKey ) {
421+
continue;
422+
}
423+
424+
if (
425+
( isset( self::$exclude ) && in_array( $propKey, self::$exclude ) ) ||
426+
str_starts_with( $propKey, '_' ) ||
427+
str_starts_with( $propKey, '___' ) ||
428+
ctype_upper( str_replace( '_', '', $propKey ) )
429+
) {
430+
continue;
431+
}
432+
433+
$propKey = str_replace( '_', ' ', $propKey );
434+
435+
if ( $direction === 'inverse' ) {
436+
$propKey = '-' . $propKey;
437+
}
438+
439+
$ret[] = $propKey;
440+
}
441+
442+
wfDebugLog( 'KnowledgeGraph', sprintf(
443+
"getAllPropertiesForNode (smwbrowse): node=%s, properties=%d",
444+
$nodeTitleText,
445+
count( $ret )
446+
) );
447+
448+
return array_unique( $ret );
449+
}
450+
361451
/**
362452
* @param Title|MediaWiki\Title\Title $title $title
363453
* @return string|null
@@ -403,6 +493,9 @@ public static function onOutputPageParserOutput( OutputPage $out, ParserOutput $
403493
$out->addJsConfigVars( [
404494
'knowledgegraphs' => json_encode( $data )
405495
] );
496+
497+
// add the required JavaScript module if graphs are present
498+
$out->addModules( 'ext.KnowledgeGraph' );
406499
}
407500
}
408501

@@ -534,7 +627,16 @@ public static function setSemanticDataFromApi( Title $title, $onlyProperties, $d
534627
return;
535628
}
536629

537-
if ( $depth > $maxDepth ) {
630+
// If maxDepth is 0, only create the root node without loading SMW data
631+
if ( $maxDepth === 0 ) {
632+
self::$data[$titleText] = [
633+
'properties' => [],
634+
'categories' => [],
635+
];
636+
return;
637+
}
638+
639+
if ( $depth >= $maxDepth ) {
538640
return;
539641
}
540642

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

641743
foreach ( $entry['dataitem'] ?? [] as $item ) {
642744
if ( $item['type'] === 9 ) {
643-
$linkedTitle = explode( '#', $item['item'] )[0];
644-
$linkedTitle = $linkedTitle ? str_replace( '_', ' ', $linkedTitle ) : null;
745+
$parts = explode( '#', $item['item'] );
746+
$dbkey = $parts[0] ?? '';
747+
$nsId = isset( $parts[1] ) && is_numeric( $parts[1] ) ? (int)$parts[1] : 0;
748+
749+
$namespaceInfo = MediaWiki\MediaWikiServices::getInstance()->getNamespaceInfo();
750+
$nsName = $namespaceInfo->getCanonicalName( $nsId );
751+
752+
$linkedTitle = $dbkey;
753+
if ( $nsName !== '' && $nsName !== false ) {
754+
$linkedTitle = $nsName . ':' . $dbkey;
755+
}
756+
757+
$linkedTitle = str_replace( '_', ' ', $linkedTitle );
645758
if ( !$linkedTitle ) {
646759
continue;
647760
}

0 commit comments

Comments
 (0)