diff --git a/README.md b/README.md index 2c53709..b19f2c5 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,23 @@ Example with parameters Overrides the value of the $wgPageNetworkLabelMaxLength configuration variable. + + AllowOnlyLinksToPages + true + false + + When set to true, during initialization nodes have only links drawn between the listed pages (from page or pages parameter). + + + + AllowLinkExpansion + true + false + + When set to true, when holding the left mouse button over graph node the links from node will be drawn. + Otherwise (when set to false) this behaviour is supressed. + + There is also an includable special page, Special:Network, that can be used to construct graphs interactively. The @@ -315,6 +332,32 @@ Wrong: `"font.color": "red"`, right: `"font": { "color": "red" }`, also right: ` Where `NetworkPages` contains `Page1 | Page2 | Page3` and `NetworkOptions` contains `{ "nodes": { "shape": "box" } } ` +#### Using with Semantic Mediawiki inline queries + +``` +{{#network: +{{#ask: [[NextPage::+]] +| format=list +| headers=show +| link=none +| order=ascending +| merge=true +|limit=50 +|searchlabel=| +|sep={{!}} +|template=ReturnTemplatesFirstArgument +|outro={{!}} +|intro={{!}} +}} +| class = col-lg-3 mt-0 +|AllowOnlyLinksToPages = true +|AllowLinkExpansion = false +|enableDisplayTitle = true +}} +``` + +where "ReturnTemplatesFirstArgument" contains only "{{{1}}}". Resulting graph will show only pages with NextPage property and how they are interlinked. + ## Performance / caching This extension bypasses the MediaWiki page cache. This means that your network graphs will always be up to date, diff --git a/extension.json b/extension.json index 8aa04aa..72a4240 100644 --- a/extension.json +++ b/extension.json @@ -89,6 +89,12 @@ }, "PageNetworkLabelMaxLength": { "value": 20 + }, + "PageNetworkAllowOnlyLinksToPages": { + "value": false + }, + "PageNetworkAllowLinkExpansion": { + "value": true } }, @@ -174,7 +180,11 @@ "pagenetwork-labelMaxLength-field-label", "pagenetwork-labelMaxLength-field-help", "pagenetwork-basic-tab-label", - "pagenetwork-advanced-tab-label" + "pagenetwork-advanced-tab-label", + "pagenetwork-AllowOnlyLinksToPages-field-label", + "pagenetwork-AllowOnlyLinksToPages-field-help", + "pagenetwork-AllowLinkExpansion-field-label", + "pagenetwork-AllowLinkExpansion-field-help" ], "targets": [ "desktop", "mobile" ] } diff --git a/i18n/de.json b/i18n/de.json index 3240cf1..b661dda 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -13,5 +13,9 @@ "network-aria": "Die Netzwerkvisualisierung zeigt eingehende und ausgehende Links der {{PLURAL:$1|Seite|Seiten}} $2", "pagenetwork-pages-field-label": "Seiten", "pagenetwork-class-field-label": "CSS-Klassen", - "pagenetwork-basic-tab-label": "Basisoptionen" -} + "pagenetwork-basic-tab-label": "Basisoptionen", + "pagenetwork-AllowOnlyLinksToPages-field-label": "Nur Links zu Seiten zulassen", + "pagenetwork-AllowOnlyLinksToPages-field-help": "Wenn wahr, werden nur die angegebenen Seiten als Knoten angezeigt", + "pagenetwork-AllowLinkExpansion-field-label": "Linkerweiterung zulassen", + "pagenetwork-AllowLinkExpansion-field-help": "Wenn wahr, ist eine Linkerweiterung beim Halten des Knotens möglich" + } diff --git a/i18n/en.json b/i18n/en.json index 39af399..0be97ea 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -24,5 +24,9 @@ "pagenetwork-labelMaxLength-field-label": "Maximum Label Length", "pagenetwork-labelMaxLength-field-help": "The maximum text length of a node's label or 0 if unlimited. If the node label must be truncated, an ellipsis (…) will appended.", "pagenetwork-basic-tab-label": "Basic Options", - "pagenetwork-advanced-tab-label": "Advanced Options" + "pagenetwork-advanced-tab-label": "Advanced Options", + "pagenetwork-AllowOnlyLinksToPages-field-label": "Allow Only Links To Pages", + "pagenetwork-AllowOnlyLinksToPages-field-help": "If true only specified pages will be displayed as nodes", + "pagenetwork-AllowLinkExpansion-field-label": "Allow Link Expansion", + "pagenetwork-AllowLinkExpansion-field-help": "If true Link Expansion when holding the node is possible" } diff --git a/i18n/pl.json b/i18n/pl.json index bc46156..bccc40a 100644 --- a/i18n/pl.json +++ b/i18n/pl.json @@ -1,9 +1,32 @@ { "@metadata": { "authors": [ - "Rail" + "Rail", + "pw" ] }, + "network": "Sieć", + "network-name": "Sieć", "network-desc": "Umożliwia dodawanie interaktywnych wizualizacji sieci do stron na wiki", - "network-aria": "Wizualizacja sieci pokazująca wychodzące i przychodzące połączenia dla {{PLURAL:$1|strony|stron}} $2" -} + "network-aria": "Wizualizacja sieci pokazująca wychodzące i przychodzące połączenia dla {{PLURAL:$1|strony|stron}} $2", + "pagenetwork-pages-field-label": "Strony", + "pagenetwork-pages-field-help": "Nazwa jednej lub wielu stron których połączenia mają zostać zwizualizowane, jedna na linię. Domyślnie to strona główna.", + "pagenetwork-exclude-field-label": "Wykluczone strony", + "pagenetwork-exclude-field-help": "Strony które mają zostać wykluczone z wizualizacji sieci połączeń, jedna na linię.", + "pagenetwork-excludedNamespaces-field-label": "Wykluczone przestrzenie nazw", + "pagenetwork-excludedNamespaces-field-help": "Przestrzenie nazw które mają zostać wykluczone z wizualizacji.", + "pagenetwork-class-field-label": "Klasy CSS", + "pagenetwork-class-field-help": "Dodatkowa klasa lub klasy które mają zostać dodane do wizualizacji, separowane znakiem spacji.", + "pagenetwork-options-field-label": "Opcje wizualizacji", + "pagenetwork-options-field-help": "Struktura JSON z opcjami wizualizacji vis.js.", + "pagenetwork-enableDisplayTitle-field-label": "Włącz wyświetlanie tytułu strony", + "pagenetwork-enableDisplayTitle-field-help": "Czy tytuł strony czy tytuł wyświetlania powinien być wyświetlany jako etykieta węzła?", + "pagenetwork-labelMaxLength-field-label": "Maksymalna długość etykiety", + "pagenetwork-labelMaxLength-field-help": "Maksymalna ilość znaków etykiety węzła lub zero dla braku ograniczenia. Jeżeli etkieta węzła zostanie obcięta to zostanie dodany wielokropek (…).", + "pagenetwork-basic-tab-label": "Opcje Podstawowe", + "pagenetwork-advanced-tab-label": "Opcje Zaawansowane", + "pagenetwork-AllowOnlyLinksToPages-field-label": "Wizualizuj tylko strony z listy", + "pagenetwork-AllowOnlyLinksToPages-field-help": "Wizualizuj tylko połączenia stron z listy", + "pagenetwork-AllowLinkExpansion-field-label": "Zezwalaj na dodawanie połączeń", + "pagenetwork-AllowLinkExpansion-field-help": "Umożliwia dodawanie połączeń ze strony poprzez przytrzymanie jej węzła" + } diff --git a/resources/js/Network.js b/resources/js/Network.js index b8ebcdd..ae1683d 100644 --- a/resources/js/Network.js +++ b/resources/js/Network.js @@ -7,20 +7,25 @@ module.Network = (function (vis, NetworkData) { * @param {module.PageExclusionManager} pageExclusionManager * @param {object} options * @param {int} labelMaxLength + * @param {string[]} pageNamesOrig + * @param {bool} startWithPageNamesOrigOnly + * @param {bool} allowNodeExpansion */ let Network = function( divId, pageConnectionRepo, pageExclusionManager, options, - labelMaxLength + labelMaxLength, + pageNamesOrig, + startWithPageNamesOrigOnly, + allowNodeExpansion ) { this._pageConnectionRepo = pageConnectionRepo; - this._data = new NetworkData(pageExclusionManager, labelMaxLength); + this._data = new NetworkData(pageExclusionManager, labelMaxLength, pageNamesOrig, startWithPageNamesOrigOnly, allowNodeExpansion); this._options = options; this._network = this._newNetwork(divId); this._lastZoomPosition = {x:0, y:0} - this._bindEvents(); }; @@ -28,12 +33,12 @@ module.Network = (function (vis, NetworkData) { * @param {string[]} pageNames * @return {Promise} */ - Network.prototype.showPages = function(pageNames) { + Network.prototype.showPages = function(pageNames, allowOnly_pageNamesOrigList=true) { let promise = this._pageConnectionRepo.addConnections(pageNames); promise.then( connections => { - this._data.addPages(connections.pages); + this._data.addPages(connections.pages, allowOnly_pageNamesOrigList); this._data.addLinks(connections.links); } ); @@ -42,7 +47,7 @@ module.Network = (function (vis, NetworkData) { }; Network.prototype._addPage = function(pageName) { - return this.showPages([pageName]); + return this.showPages([pageName], !this._data._allowNodeExpansion ); }; Network.prototype._newNetwork = function(divId) { diff --git a/resources/js/NetworkData.js b/resources/js/NetworkData.js index f1cdd91..4c3ebc2 100644 --- a/resources/js/NetworkData.js +++ b/resources/js/NetworkData.js @@ -3,19 +3,21 @@ */ module.NetworkData = ( function ( vis, mw ) { "use strict" - - let NetworkData = function(pageExclusionManager, labelMaxLength) { + let NetworkData = function(pageExclusionManager, labelMaxLength, pageNamesOrig, startWithPageNamesOrigOnly, allowNodeExpansion) { this.nodes = new vis.DataSet(); this.edges = new vis.DataSet(); this._pageExclusionManager = pageExclusionManager; this._labelMaxLength = labelMaxLength; + this._pageNamesOrig = pageNamesOrig; + this._startWithPageNamesOrigOnly = startWithPageNamesOrigOnly; + this._allowNodeExpansion = allowNodeExpansion; }; - NetworkData.prototype.addPages = function(pages) { + NetworkData.prototype.addPages = function(pages, allowOnly_pageNamesOrigList=true) { var maxlength = this._labelMaxLength; this.nodes.update( pages - .filter(page => this._pageTitleIsAllowed(page.title)) + .filter(page => { return this._pageTitleIsAllowed(page.title) && (!allowOnly_pageNamesOrigList||this._pageNamesOrig.includes(page.title)) }) .map(function(page) { if (maxlength > 0 && page.displayTitle.length > maxlength) { page.label = page.displayTitle.slice(0, maxlength) + '\u2026'; diff --git a/resources/js/SpecialForm.js b/resources/js/SpecialForm.js index 1b600a3..b5821eb 100644 --- a/resources/js/SpecialForm.js +++ b/resources/js/SpecialForm.js @@ -115,6 +115,32 @@ helpInline: true } ); + let AllowOnlyLinksToPagesInput = new OO.ui.CheckboxInputWidget({ + name: 'AllowOnlyLinksToPages', + selected: defaultValues['AllowOnlyLinksToPages'], + }); + let AllowOnlyLinksToPagesField = new OO.ui.FieldLayout( + AllowOnlyLinksToPagesInput, + { + label: mw.message('pagenetwork-AllowOnlyLinksToPages-field-label').text(), + align: 'inline', + help: mw.message('pagenetwork-AllowOnlyLinksToPages-field-help').text(), + helpInline: true + } ); + + let AllowLinkExpansionInput = new OO.ui.CheckboxInputWidget({ + name: 'AllowLinkExpansion', + selected: defaultValues['AllowLinkExpansion'], + }); + let AllowLinkExpansionField = new OO.ui.FieldLayout( + AllowLinkExpansionInput, + { + label: mw.message('pagenetwork-AllowLinkExpansion-field-label').text(), + align: 'inline', + help: mw.message('pagenetwork-AllowLinkExpansion-field-help').text(), + helpInline: true + } ); + let submitButton = new OO.ui.ButtonInputWidget( { label: mw.message('htmlform-submit').text(), type: 'submit', @@ -159,7 +185,9 @@ classField, optionsField, enableDisplayTitleField, - labelMaxLengthField + labelMaxLengthField, + AllowOnlyLinksToPagesField, + AllowLinkExpansionField ] } ), ] ) diff --git a/resources/js/index.js b/resources/js/index.js index e4fdfc7..3c166e4 100644 --- a/resources/js/index.js +++ b/resources/js/index.js @@ -15,10 +15,13 @@ mw.config.get('networkExcludeTalkPages') ), $this.data('options'), - $this.data('labelmaxlength') + $this.data('labelmaxlength'), + $this.data('pages'), + $this.data('allowonlylinkstopages'), + $this.data('allowlinkexpansion') ); - network.showPages($this.data('pages')).then(function() { + network.showPages($this.data('pages'),$this.data('allowonlylinkstopages')).then(function() { $this.find('canvas:first').attr( 'aria-label', mw.message( diff --git a/src/EntryPoints/NetworkFunction.php b/src/EntryPoints/NetworkFunction.php index db103e1..a249628 100644 --- a/src/EntryPoints/NetworkFunction.php +++ b/src/EntryPoints/NetworkFunction.php @@ -45,6 +45,8 @@ public function handleParserFunctionCall( Parser $parser, string ...$arguments ) $requestModel->excludedNamespaces = $this->config->getExcludedNamespaces(); $requestModel->enableDisplayTitle = $this->config->getEnableDisplayTitle(); $requestModel->labelMaxLength = $this->config->getLabelMaxLength(); + $requestModel->AllowOnlyLinksToPages = $this->config->getAllowOnlyLinksToPages(); + $requestModel->AllowLinkExpansion = $this->config->getAllowLinkExpansion(); /** * @psalm-suppress PossiblyNullReference diff --git a/src/EntryPoints/SpecialNetwork.php b/src/EntryPoints/SpecialNetwork.php index 04669da..cac360e 100644 --- a/src/EntryPoints/SpecialNetwork.php +++ b/src/EntryPoints/SpecialNetwork.php @@ -82,6 +82,32 @@ private function parseParams( WebRequest $request, NetworkConfig $config ) : arr $params['labelMaxLength'] = $request->getInt( 'labelMaxLength', $config->getLabelMaxLength() ); + if ( $this->including() ) { + $params['AllowOnlyLinksToPages'] = + filter_var( + $request->getText( 'AllowOnlyLinksToPages', strval( $config->getAllowOnlyLinksToPages() ) ), + FILTER_VALIDATE_BOOL, + FILTER_NULL_ON_FAILURE + ); + } elseif ( $request->getCheck( 'pages' ) ) { + $params['AllowOnlyLinksToPages'] = $request->getCheck('AllowOnlyLinksToPages'); + } else { + $params['AllowOnlyLinksToPages'] = $config->getAllowOnlyLinksToPages(); + } + + if ( $this->including() ) { + $params['AllowLinkExpansion'] = + filter_var( + $request->getText( 'AllowLinkExpansion', strval( $config->getAllowLinkExpansion() ) ), + FILTER_VALIDATE_BOOL, + FILTER_NULL_ON_FAILURE + ); + } elseif ( $request->getCheck( 'pages' ) ) { + $params['AllowLinkExpansion'] = $request->getCheck('AllowLinkExpansion'); + } else { + $params['AllowLinkExpansion'] = $config->getAllowLinkExpansion(); + } + return $params; } @@ -109,6 +135,10 @@ private function formatParams( array $params ) : array { $formattedParams['enableDisplayTitle'] = 'enableDisplayTitle=' . ( $params['enableDisplayTitle'] ? 'true' : 'false' ); $formattedParams['labelMaxLength'] = 'labelMaxLength=' . strval( $params['labelMaxLength'] ); + $formattedParams['AllowOnlyLinksToPages'] = 'AllowOnlyLinksToPages=' . + ( $params['AllowOnlyLinksToPages'] ? 'true' : 'false' ); + $formattedParams['AllowLinkExpansion'] = 'AllowLinkExpansion=' . + ( $params['AllowLinkExpansion'] ? 'true' : 'false' ); return $formattedParams; } @@ -127,6 +157,9 @@ public function showGraph( array $arguments, NetworkConfig $config) : string { $requestModel->excludedNamespaces = $config->getExcludedNamespaces(); $requestModel->enableDisplayTitle = $config->getEnableDisplayTitle(); $requestModel->labelMaxLength = $config->getLabelMaxLength(); + $requestModel->AllowOnlyLinksToPages = $config->getAllowOnlyLinksToPages(); + $requestModel->AllowLinkExpansion = $config->getAllowLinkExpansion(); + /** * @psalm-suppress PossiblyNullReference diff --git a/src/NetworkFunction/AbstractNetworkPresenter.php b/src/NetworkFunction/AbstractNetworkPresenter.php index 3778d5a..ab104f7 100644 --- a/src/NetworkFunction/AbstractNetworkPresenter.php +++ b/src/NetworkFunction/AbstractNetworkPresenter.php @@ -28,6 +28,8 @@ public function setHtml( ResponseModel $viewModel ): void { 'data-options' => json_encode( $viewModel->visJsOptions ), 'data-enabledisplaytitle' => json_encode( $viewModel->enableDisplayTitle ), 'data-labelmaxlength' => json_encode( $viewModel->labelMaxLength ), + 'data-allowonlylinkstopages' => json_encode( $viewModel->AllowOnlyLinksToPages), + 'data-allowlinkexpansion' => json_encode( $viewModel->AllowLinkExpansion), ] ); } diff --git a/src/NetworkFunction/NetworkConfig.php b/src/NetworkFunction/NetworkConfig.php index 63d28f1..4c32710 100644 --- a/src/NetworkFunction/NetworkConfig.php +++ b/src/NetworkFunction/NetworkConfig.php @@ -26,6 +26,8 @@ class NetworkConfig { * @var bool */ private $enableDisplayTitle; + private $AllowOnlyLinksToPages; + private $AllowLinkExpansion; /** * @var int @@ -39,6 +41,8 @@ public function __construct() { $this->excludedNamespaces = array_map( 'strval', $config->get( 'PageNetworkExcludedNamespaces' ) ); $this->enableDisplayTitle = (bool)$config->get( 'PageNetworkEnableDisplayTitle' ); $this->labelMaxLength = (int)$config->get( 'PageNetworkLabelMaxLength' ); + $this->AllowOnlyLinksToPages = (bool)$config->get( 'PageNetworkAllowOnlyLinksToPages'); + $this->AllowLinkExpansion = (bool)$config->get( 'PageNetworkAllowLinkExpansion'); } public function getOptions(): array { @@ -60,4 +64,12 @@ public function getEnableDisplayTitle(): bool { public function getLabelMaxLength(): int { return $this->labelMaxLength; } + + public function getAllowOnlyLinksToPages(): bool { + return $this->AllowOnlyLinksToPages; + } + + public function getAllowLinkExpansion(): bool { + return $this->AllowLinkExpansion; + } } diff --git a/src/NetworkFunction/NetworkUseCase.php b/src/NetworkFunction/NetworkUseCase.php index dd32476..61c2535 100644 --- a/src/NetworkFunction/NetworkUseCase.php +++ b/src/NetworkFunction/NetworkUseCase.php @@ -31,6 +31,8 @@ public function run( RequestModel $request ): void { $response->enableDisplayTitle = $this->getEnableDisplayTitle( $keyValuePairs, $request->enableDisplayTitle ); $response->labelMaxLength = $this->getLabelMaxLength( $keyValuePairs, $request->labelMaxLength ); $response->visJsOptions = $this->getVisJsOptions( $keyValuePairs ); + $response->AllowOnlyLinksToPages = $this->getAllowOnlyLinksToPages($keyValuePairs, $request->AllowOnlyLinksToPages); + $response->AllowLinkExpansion = $this->getAllowLinkExpansion($keyValuePairs, $request->AllowLinkExpansion); $this->presenter->buildGraph( $response ); } @@ -163,4 +165,27 @@ private function getEnableDisplayTitle(array $arguments, bool $enableDisplayTitl private function getLabelMaxLength(array $arguments, int $labelMaxLength ): int { return isset( $arguments['labelMaxLength'] ) ? (int)$arguments['labelMaxLength'] : $labelMaxLength; } + + /** + * @param string[] $arguments + * @param bool $AllowOnlyLinksToPages + * @return bool + */ + private function getAllowOnlyLinksToPages(array $arguments, bool $AllowOnlyLinksToPages ): bool { + return isset( $arguments['AllowOnlyLinksToPages'] ) + ? filter_var( $arguments['AllowOnlyLinksToPages'], FILTER_VALIDATE_BOOLEAN ) + : $AllowOnlyLinksToPages; + } + + /** + * @param string[] $arguments + * @param bool $AllowLinkExpansion + * @return bool + */ + private function getAllowLinkExpansion(array $arguments, bool $AllowLinkExpansion ): bool { + return isset( $arguments['AllowLinkExpansion'] ) + ? filter_var( $arguments['AllowLinkExpansion'], FILTER_VALIDATE_BOOLEAN ) + : $AllowLinkExpansion; + } + } diff --git a/src/NetworkFunction/RequestModel.php b/src/NetworkFunction/RequestModel.php index 2f040f1..de6a3f2 100644 --- a/src/NetworkFunction/RequestModel.php +++ b/src/NetworkFunction/RequestModel.php @@ -11,5 +11,8 @@ class RequestModel { public /* int[] */ $excludedNamespaces; public /* bool */ $enableDisplayTitle; public /* int */ $labelMaxLength; + public /* bool */ $AllowOnlyLinksToPages; + public /* bool */ $AllowLinkExpansion; + } diff --git a/src/NetworkFunction/ResponseModel.php b/src/NetworkFunction/ResponseModel.php index a6e3bef..2c842a6 100644 --- a/src/NetworkFunction/ResponseModel.php +++ b/src/NetworkFunction/ResponseModel.php @@ -13,5 +13,7 @@ class ResponseModel { public /* bool */ $enableDisplayTitle; public /* int */ $labelMaxLength; public /* array */ $visJsOptions; + public /* bool */ $AllowOnlyLinksToPages; + public /* bool */ $AllowLinkExpansion; } diff --git a/tests/php/NetworkFunction/NetworkUseCaseTest.php b/tests/php/NetworkFunction/NetworkUseCaseTest.php index 9758a2d..2a0b686 100644 --- a/tests/php/NetworkFunction/NetworkUseCaseTest.php +++ b/tests/php/NetworkFunction/NetworkUseCaseTest.php @@ -49,7 +49,8 @@ private function newBasicRequestModel(): RequestModel { $request->excludedNamespaces = []; $request->enableDisplayTitle = true; $request->labelMaxLength = 20; - + $request->AllowOnlyLinksToPages=false; + $request->AllowLinkExpansion=false; return $request; }