From 3d042a3239e0d43366def228950c41045a1a796e Mon Sep 17 00:00:00 2001 From: designburo Date: Wed, 14 Jan 2026 15:13:21 +0100 Subject: [PATCH 1/9] v2.6.7 : Semantic Ask in tokens quick fix. Will be rewritten as a full API call in 2.7.x --- README.md | 1 + extension.json | 2 +- .../Request/Handlers/SemanticAsk.php | 36 +++++++++++++++---- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 3a1c5332..491aa3b6 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,7 @@ Visit this documentation page https://www.open-csp.org/DevOps:Doc/FlexForm/2.0/V Visit : https://www.open-csp.org/DevOps:Doc/FlexForm ### Changelog +* 2.6.7 : Semantic Ask in tokens quick fix. Will be rewritten as a full API call in 2.7.x * 2.6.6 : Tempex now supports select, checkboxes and radiobuttons. * 2.6.5 : Update HTMLPurifier and move to composer. Added default user for internal api call. * 2.6.4 : fix: A check at MW if a page title is usable was set to early. diff --git a/extension.json b/extension.json index 3f1cf8f6..68f5a412 100644 --- a/extension.json +++ b/extension.json @@ -1,6 +1,6 @@ { "name": "FlexForm", - "version": "2.6.6", + "version": "2.6.7", "author": [ "[https://www.wikibase-solutions.com/author/charlot Sen-Sai]", "[https://www.wikibase-solutions.com/author/marijn Marijn]" diff --git a/src/Processors/Request/Handlers/SemanticAsk.php b/src/Processors/Request/Handlers/SemanticAsk.php index cf9172a3..6163b59a 100644 --- a/src/Processors/Request/Handlers/SemanticAsk.php +++ b/src/Processors/Request/Handlers/SemanticAsk.php @@ -2,12 +2,21 @@ namespace FlexForm\Processors\Request\Handlers; +use Exception; use FlexForm\Core\Core; use FlexForm\Core\HandleResponse; +use FlexForm\FlexFormException; +use FlexForm\Processors\Content\Render; use FlexForm\Processors\Utilities\General; +use MediaWiki\MediaWikiServices; +use Wikimedia\Rdbms\DBError; +use Wikimedia\Rdbms\DBUnexpectedError; +use Wikimedia\Rdbms\IDatabase; class SemanticAsk { + // TODO: Rewrite this to be an proper API call for version 2.7.0 + /** * @param string $query * @@ -44,7 +53,7 @@ private function createNewQuery( string $searchPart ): string { * @param HandleResponse $responseHandler * * @return void - * @throws \MWException + * @throws Exception */ public function execute( HandleResponse $responseHandler ) { $ret = []; @@ -56,18 +65,13 @@ public function execute( HandleResponse $responseHandler ) { } else { $query = false; } - //$query = base64_decode( General::getGetString( 'query', true, false ) ); $q = General::getGetString( 'q', true, false ); $returnId = General::getGetString( 'returnid', true, false ); $returnText = General::getGetString( 'returntext', true, false ); $template = General::getGetString( 'template', true, false ); $limit = General::getGetString( 'limit', true, false ); - // if( strlen( $q ) < 3 ) return $ret; if ( $query !== false ) { - // $ret = createMsg('No query found.'); - // test query : $query = "[[Class::Organization]] [[Name::~*ik*]]|?Name |format=json |limit=99999" - // ik kan dat q worden voor select2 door !!! in te vullen in de query, deze wordt dan vervangen. $filterQuery = false; if ( strpos( $query, '(' ) !== false && strpos( $query, ')' ) !== false ) { if ( strpos( $query, '(fquery=' ) !== false ) { @@ -182,7 +186,7 @@ public function execute( HandleResponse $responseHandler ) { "format" => "json", "query" => $query ]; - $mRequest = new \FlexForm\Processors\Content\Render(); + $mRequest = new Render(); $data = $mRequest->makeRequest( $postdata ); if ( isset( $data['query']['results'] ) && !empty( $data['query']['results'] ) ) { @@ -214,6 +218,24 @@ public function execute( HandleResponse $responseHandler ) { $ret, JSON_PRETTY_PRINT ); + $database = MediaWikiServices::getInstance()->getDBLoadBalancer()->getConnection( DB_PRIMARY ); + + if ( $database->writesPending() ) { + + // If there is still a database update pending, commit it here + try { + $database->commit( + __METHOD__, + IDatabase::FLUSHING_INTERNAL + ); + } catch ( DBError | DBUnexpectedError $e ) { + throw new FlexFormException( + $e->getMessage(), + 0, + $e + ); + } + } die(); } } From 83a28d51ba3b09986c82aa7fa0dac7242a068cf7 Mon Sep 17 00:00:00 2001 From: Charlot Date: Wed, 21 Jan 2026 10:42:53 +0100 Subject: [PATCH 2/9] rewrite SMW query --- ApiFlexForm.php | 13 ++ FlexForm.hooks.php | 21 ++-- Modules/Handlers/semantic_ask.php | 119 +++++++++--------- .../Request/Handlers/SemanticAsk.php | 8 +- .../Themes/Plain/PlainTokenRenderer.php | 6 +- 5 files changed, 95 insertions(+), 72 deletions(-) diff --git a/ApiFlexForm.php b/ApiFlexForm.php index 279c8824..3a517a46 100644 --- a/ApiFlexForm.php +++ b/ApiFlexForm.php @@ -2,8 +2,10 @@ use FlexForm\Core\Config; use FlexForm\Core\Debug; +use FlexForm\Core\HandleResponse; use FlexForm\Core\Protect; use FlexForm\FlexFormException; +use FlexForm\Processors\Request\Handlers\SemanticAsk; use Wikimedia\ParamValidator\ParamValidator; class ApiFlexForm extends ApiBase { @@ -98,6 +100,17 @@ public function execute() { $result = $messaging->removeUserMessageById( $mId, true ); $output = $this->createResult( "ok", "ok" ); break; + case "ask": + $smwAsk = new SemanticAsk(); + $output = $smwAsk->execute( new HandleResponse() ); + $this->getResult()->addValue( + null, + 'results', + $output['results'] + ); + + return true; + break; case "decrypt": $output = $this->decrypt( $params['titleStartsWith'] ); if ( $output['status'] === "error" ) { diff --git a/FlexForm.hooks.php b/FlexForm.hooks.php index 88a2da74..d21f7a1f 100644 --- a/FlexForm.hooks.php +++ b/FlexForm.hooks.php @@ -257,15 +257,18 @@ public static function onParserFirstCallInit( Parser &$parser ) { if ( !\FlexForm\Core\Config::getConfigStatus() ) { \FlexForm\Core\Config::setConfigFromMW(); } - global $wgFlexFormConfig; - $wgFlexFormConfig['loaders'] = []; - $wgFlexFormConfig['loaders']['css'] = []; - $wgFlexFormConfig['loaders']['javascript'] = []; - $wgFlexFormConfig['loaders']['jsconfigvars'] = []; - $wgFlexFormConfig['loaders']['javascripttag'] = []; - $wgFlexFormConfig['loaders']['csstag'] = []; - $wgFlexFormConfig['loaders']['files'] = []; - + // Only set these config variables if no other form has been rendered. + // Extensions like WikiGuard creates a new parser and thus executes this possibly multiple times. + if ( Core::getRun() === false ) { + global $wgFlexFormConfig; + $wgFlexFormConfig['loaders'] = []; + $wgFlexFormConfig['loaders']['css'] = []; + $wgFlexFormConfig['loaders']['javascript'] = []; + $wgFlexFormConfig['loaders']['jsconfigvars'] = []; + $wgFlexFormConfig['loaders']['javascripttag'] = []; + $wgFlexFormConfig['loaders']['csstag'] = []; + $wgFlexFormConfig['loaders']['files'] = []; + } $formTags = [ 'wsform', '_form', diff --git a/Modules/Handlers/semantic_ask.php b/Modules/Handlers/semantic_ask.php index 2c6d3e5c..7d66e1f1 100644 --- a/Modules/Handlers/semantic_ask.php +++ b/Modules/Handlers/semantic_ask.php @@ -1,70 +1,73 @@ = 3) { - $query = html_entity_decode(urldecode($query)); - //var_dump($query); - if($q !== false) { - $q2 = ucwords( $q ); - $q3 = strtoupper( $q ); - $query = str_replace('!!!', '~*' . $q . '*||~*' . $q2 . '*||~*' . $q3 . '*', $query); - } else { - $query = str_replace('!!!', '', $query); - } - if( $returnId !== false ) { - $query .= '|?'.$returnId; - } - if( $returnText !== false ) { - $query .= '|?'.$returnText; - } - if( $limit !== false ) { - $query .= '|limit='.$limit; - } else $query .= '|limit=50'; +if ( $query === false ) { + //$ret = createMsg('No query found.'); + // test query : $query = "[[Class::Organization]] [[Name::~*ik*]]|?Name |format=json |limit=99999" + // ik kan dat q worden voor select2 door !!! in te vullen in de query, deze wordt dan vervangen. +} elseif ( strlen( $q ) >= 3 ) { + $query = html_entity_decode( urldecode( $query ) ); + //var_dump($query); + if ( $q !== false ) { + $q2 = ucwords( $q ); + $q3 = strtoupper( $q ); + $query = str_replace( '!!!', '~*' . $q . '*||~*' . $q2 . '*||~*' . $q3 . '*', $query ); + } else { + $query = str_replace( '!!!', '', $query ); + } + if ( $returnId !== false ) { + $query .= '|?' . $returnId; + } + if ( $returnText !== false ) { + $query .= '|?' . $returnText; + } + if ( $limit !== false ) { + $query .= '|limit=' . $limit; + } else { + $query .= '|limit=50'; + } - //echo $query."
"; + //echo $query."
"; + //[[Class::Organization]][[Name::~*ik*]]|?Name|?Contact|limit=99999 + //Process~*hallo*|?Name|?Name|limit=50 + //echo $query."
";
 
-    //[[Class::Organization]][[Name::~*ik*]]|?Name|?Contact|limit=99999
-    //Process~*hallo*|?Name|?Name|limit=50
-    //echo $query."
";
+	$postdata = http_build_query( [
+		"action" => "ask",
+		"format" => "json",
+		"query" => $query
+	] );
+	$data = $api->apiPost( $postdata, true );
 
-    $postdata = http_build_query([
-        "action" => "ask",
-        "format" => "json",
-        "query" => $query
-    ]);
-    $data = $api->apiPost($postdata, true);
+	if ( isset( $data['received']['query']['results'] ) && !empty( $data['received']['query']['results'] ) ) {
+		$data = $data['received']['query']['results'];
 
+		$t = 0;
+		foreach ( $data as $k => $val ) {
+			if ( $returnText === false ) {
+				$ret['results'][$t]['text'] = htmlentities( $val['displaytitle'] );
+			} elseif ( isset( $val['printouts'][$returnText][0] ) ) {
+				$ret['results'][$t]['text'] = htmlentities( $val['printouts'][$returnText][0] );
+			} else {
+				$ret['results'][$t]['text'] = 'Not found';
+			}
 
-    if (isset($data['received']['query']['results']) && !empty( $data['received']['query']['results'] )) {
-        $data = $data['received']['query']['results'];
-
-        $t=0;
-        foreach($data as $k => $val) {
-            if( $returnText === false ) {
-                $ret['results'][$t]['text'] = htmlentities($val['displaytitle'] );
-            } elseif( isset( $val['printouts'][$returnText][0] ) ) {
-                $ret['results'][$t]['text'] = htmlentities( $val['printouts'][$returnText][0] );
-            } else $ret['results'][$t]['text'] = 'Not found';
-
-            if( $returnId === false ) {
-                $ret['results'][$t]['id'] = htmlentities( $k );
-            } elseif( isset( $val['printouts'][$returnId][0] ) ) {
-                $ret['results'][$t]['id'] = htmlentities( $val['printouts'][$returnId][0] );
-            } else $ret['results'][$t]['id'] = 'Not found';
-            $t++;
-        }
-    }
-
+			if ( $returnId === false ) {
+				$ret['results'][$t]['id'] = htmlentities( $k );
+			} elseif ( isset( $val['printouts'][$returnId][0] ) ) {
+				$ret['results'][$t]['id'] = htmlentities( $val['printouts'][$returnId][0] );
+			} else {
+				$ret['results'][$t]['id'] = 'Not found';
+			}
+			$t++;
+		}
+	}
 }
 
diff --git a/src/Processors/Request/Handlers/SemanticAsk.php b/src/Processors/Request/Handlers/SemanticAsk.php
index 6163b59a..04260085 100644
--- a/src/Processors/Request/Handlers/SemanticAsk.php
+++ b/src/Processors/Request/Handlers/SemanticAsk.php
@@ -56,6 +56,9 @@ private function createNewQuery( string $searchPart ): string {
 	 * @throws Exception
 	 */
 	public function execute( HandleResponse $responseHandler ) {
+
+		// Todo: rewrite to allow API variables.
+
 		$ret            = [];
 		$ret['results'] = [];
 		$queryEncoded = General::getGetString( 'query', true, false );
@@ -213,8 +216,9 @@ public function execute( HandleResponse $responseHandler ) {
 				}
 			}
 		}
-		header( 'Content-Type: application/json' );
-		echo json_encode(
+		// header( 'Content-Type: application/json' );
+		return $ret;
+		return json_encode(
 			$ret,
 			JSON_PRETTY_PRINT
 		);
diff --git a/src/Render/Themes/Plain/PlainTokenRenderer.php b/src/Render/Themes/Plain/PlainTokenRenderer.php
index b9fd4d58..59e8c304 100644
--- a/src/Render/Themes/Plain/PlainTokenRenderer.php
+++ b/src/Render/Themes/Plain/PlainTokenRenderer.php
@@ -197,9 +197,9 @@ private function renderSelectJavascript(
 
 		global $wgScriptPath, $wgServer;
 		if ( $wgScriptPath !== "" ) {
-			$smwQueryUrl = "/" . ltrim( $wgScriptPath, '/' ) . '/index.php/Special:FlexForm';
+			$smwQueryUrl = "/" . ltrim( $wgScriptPath, '/' ) . '/api.php';
 		} else {
-			$smwQueryUrl = '/index.php/Special:FlexForm';
+			$smwQueryUrl = '/api.php';
 		}
 		if ( $smwQuery !== null ) {
 			$filterQuery = $this->checkFilterQuery( $smwQuery );
@@ -209,7 +209,7 @@ private function renderSelectJavascript(
 			} else {
 				$ffFormField = '';
 			}
-			$smwQueryUrl .= '?action=handleExternalRequest';
+			$smwQueryUrl .= '?action=flexform&format=json';
 			$smwQueryUrl .= '&script=SemanticAsk&query=';
 			$smwQueryUrlQ = base64_encode( $smwQuery );
 		} else {

From 08500f534fc285eb2369befa0905786bdead8fb0 Mon Sep 17 00:00:00 2001
From: Charlot 
Date: Wed, 21 Jan 2026 11:48:50 +0100
Subject: [PATCH 3/9] rewrite SMW query

---
 src/Processors/Request/Handlers/SemanticAsk.php | 1 +
 src/Render/Themes/Plain/PlainTokenRenderer.php  | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/Processors/Request/Handlers/SemanticAsk.php b/src/Processors/Request/Handlers/SemanticAsk.php
index 04260085..8e83c0c1 100644
--- a/src/Processors/Request/Handlers/SemanticAsk.php
+++ b/src/Processors/Request/Handlers/SemanticAsk.php
@@ -73,6 +73,7 @@ public function execute( HandleResponse $responseHandler ) {
 		$returnText     = General::getGetString( 'returntext', true, false );
 		$template       = General::getGetString( 'template', true, false );
 		$limit          = General::getGetString( 'limit', true, false );
+		//return [ 'results' => $_GET ];
 
 		if ( $query !== false ) {
 			$filterQuery = false;
diff --git a/src/Render/Themes/Plain/PlainTokenRenderer.php b/src/Render/Themes/Plain/PlainTokenRenderer.php
index 59e8c304..23cecf00 100644
--- a/src/Render/Themes/Plain/PlainTokenRenderer.php
+++ b/src/Render/Themes/Plain/PlainTokenRenderer.php
@@ -210,7 +210,7 @@ private function renderSelectJavascript(
 				$ffFormField = '';
 			}
 			$smwQueryUrl .= '?action=flexform&format=json';
-			$smwQueryUrl .= '&script=SemanticAsk&query=';
+			$smwQueryUrl .= '&what=ask&titleStartsWith=ask&query=';
 			$smwQueryUrlQ = base64_encode( $smwQuery );
 		} else {
 			$smwQueryUrl = null;

From a57555360c55d2b6299bcb4f997f9b60f1fb5310 Mon Sep 17 00:00:00 2001
From: designburo 
Date: Wed, 21 Jan 2026 15:35:06 +0100
Subject: [PATCH 4/9] SMW ask as API

---
 ApiAskFlexForm.php                            | 318 ++++++++++++++++++
 extension.json                                |   4 +-
 .../Themes/Plain/PlainTokenRenderer.php       |   4 +-
 3 files changed, 323 insertions(+), 3 deletions(-)
 create mode 100644 ApiAskFlexForm.php

diff --git a/ApiAskFlexForm.php b/ApiAskFlexForm.php
new file mode 100644
index 00000000..be1be483
--- /dev/null
+++ b/ApiAskFlexForm.php
@@ -0,0 +1,318 @@
+checkUserRightsAny( [ 'read' ] );
+		$params = $this->extractRequestParams();
+		$ret            = [];
+		$ret['results'] = [];
+		$queryEncoded = $params['query'];
+		$this->query = base64_decode( str_replace( ' ', '+', $queryEncoded ) );
+		$this->q              = $params['q'];
+		$this->returnId       = $params['returnid'];
+		$this->returnText     = $params['returntext'];
+		$this->template       = $params['template'];
+		$this->limit          = $params['limit'];
+		$this->ffform         = $params['ffform'];
+
+		$this->setupQuery();
+		$this->handleQType();
+		$postdata = [
+			"action" => "ask",
+			"format" => "json",
+			"query"  => $this->query
+		];
+		$mRequest = new Render();
+		echo "
";
+		$dataRet = $mRequest->makeRequest( $postdata );
+		var_dump( $dataRet );
+		var_dump( $this->handleResults( $dataRet, $ret ) );
+		die();
+		$results = $this->handleResults( $mRequest->makeRequest( $postdata ), $ret );
+		$this->getResult()->addValue(
+			null,
+			'results',
+			$results['results']
+		);
+		return true;
+	}
+
+	/**
+	 * @return void
+	 */
+	private function setupQuery(): void {
+		$filterQuery = false;
+		if ( str_contains( $this->query, '(' ) && str_contains( $this->query, ')' ) ) {
+			if ( str_contains( $this->query, '(fquery=' ) ) {
+				$fQuery = Core::get_string_between( $this->query, '(fquery=', ')' );
+				$fQueryOld = $fQuery;
+				if ( strpos( $fQuery, '__^^__' ) !== false ) {
+					if ( empty( $this->ffform ) ) {
+						$fQuery = '';
+					} else {
+						$fQuery = str_replace( '__^^__', base64_decode( $this->ffform ), $fQuery );
+						$filterQuery = true;
+					}
+				}
+				$this->query = str_replace(
+					'(fquery=' . $fQueryOld . ')',
+					'',
+					$this->query
+				);
+			}
+			if ( str_contains( $this->query, '(returntext=' ) ) {
+				$returnText = Core::get_string_between( $this->query, '(returntext=', ')' );
+				$this->query = str_replace(
+					'(returntext=' . $returnText . ')',
+					'',
+					$this->query
+				);
+			}
+			if ( str_contains( $this->query, '(template=' ) ) {
+				$template = Core::get_string_between( $this->query, '(template=', ')' );
+				$this->query = str_replace(
+					'(template=' . $template . ')',
+					'',
+					$this->query
+				);
+			}
+			if ( str_contains( $this->query, '(returnid=' ) ) {
+				$returnId = Core::get_string_between( $this->query, '(returnid=', ')' );
+				$this->query = str_replace(
+					'(returnid=' . $returnId . ')',
+					'',
+					$this->query
+				);
+			}
+			if ( str_contains( $this->query, '(limit=' ) ) {
+				$limit = Core::get_string_between( $this->query, '(limit=', ')' );
+				$this->query = str_replace(
+					'(limit=' . $limit . ')',
+					'',
+					$this->query
+				);
+			}
+		}
+		if ( $filterQuery ) {
+			$this->query .= $fQuery;
+		}
+	}
+
+	/**
+	 * @return void
+	 */
+	private function handleQType(): void {
+		if ( $this->q !== null || !empty( $this->q ) ) {
+			// Are there spaces in the query?
+			if ( str_contains( $this->q, ' ' ) ) {
+				$mainQuery = $this->getMainQuery( $this->query );
+				$explodedQuery = explode( ' ', $this->q );
+				$newQuery = '';
+
+				foreach ( $explodedQuery as $seperated ) {
+					if ( !empty( $seperated ) ) {
+						$newQuery .= '[[' . $mainQuery . '::' . $this->createNewQuery( $seperated ) . ']]';
+					}
+				}
+				$this->query = str_replace(
+					'[[' . $mainQuery . '::!!!]]',
+					$newQuery,
+					$this->query
+				);
+			} else {
+				$this->query = str_replace(
+					'!!!',
+					$this->createNewQuery( $this->q ),
+					$this->query
+				);
+			}
+		} else {
+			$this->query = str_replace(
+				'!!!',
+				'',
+				$this->query
+			);
+		}
+		if ( $this->returnId !== null ) {
+			$this->query .= '|?' . $this->returnId;
+		}
+		if ( $this->returnText !== null ) {
+			$this->query .= '|?' . $this->returnText;
+		}
+		if ( $this->limit !== null ) {
+			$this->query .= '|limit=' . $this->limit;
+		} else {
+			$this->query .= '|limit=50';
+		}
+		if ( $this->template !== null ) {
+			$this->query .= '|template=' . $this->template;
+		}
+	}
+
+	/**
+	 * @param string $query
+	 *
+	 * @return string
+	 */
+	private function getMainQuery( string $query ): string {
+		$matches = [];
+		$mainQuery = '';
+		preg_match_all( '/\[\[(.*?)\]\]/', $query, $matches );
+		foreach ( $matches[1] as $key => $match ) {
+			// Looking for the actual query
+			if ( strpos( $match, '!!!' ) ) {
+				$matchExploded = explode( '::', $match );
+				$mainQuery = $matchExploded[0];
+				break;
+			}
+		}
+		return $mainQuery;
+	}
+
+	/**
+	 * Take a search string or part and add Uppercase first letter and all uppercase to them
+	 * @param string $searchPart
+	 *
+	 * @return string
+	 */
+	private function createNewQuery( string $searchPart ): string {
+		$q2    = ucwords( $searchPart );
+		$q3    = strtoupper( $searchPart );
+		return '~*' . $searchPart . '*||~*' . $q2 . '*||~*' . $q3 . '*';
+	}
+
+	/**
+	 * @param array $data
+	 * @param array $ret
+	 *
+	 * @return array
+	 */
+	private function handleResults( array $data, array $ret ): array {
+		if ( !empty( $data['query']['results'] ) ) {
+			$data = $data['query']['results'];
+
+			$t = 0;
+			foreach ( $data as $k => $val ) {
+				if ( $this->returnText === false ) {
+					$ret['results'][$t]['text'] = $val['displaytitle'];
+				} elseif ( isset( $val['printouts'][$this->returnText][0] ) ) {
+					$ret['results'][$t]['text'] = $val['printouts'][$this->returnText][0];
+				} else {
+					$ret['results'][$t]['text'] = 'Not found';
+				}
+
+				if ( $this->returnId === false ) {
+					$ret['results'][$t]['id'] = $k;
+				} elseif ( isset( $val['printouts'][$this->returnId][0] ) ) {
+					$ret['results'][$t]['id'] = $val['printouts'][$this->returnId][0];
+				} else {
+					$ret['results'][$t]['id'] = 'Not found';
+				}
+				$t++;
+			}
+		}
+		return $ret;
+	}
+
+	public function needsToken() {
+		return false;
+		//return "csrf";
+	}
+
+	public function isWriteMode() {
+		return false;
+	}
+
+	/**
+	 * @return array
+	 */
+	public function getAllowedParams() {
+		return [
+			'query'            => [
+				ParamValidator::PARAM_TYPE     => 'string',
+				ParamValidator::PARAM_REQUIRED => true
+			],
+			'q' => [
+				ParamValidator::PARAM_TYPE     => 'string',
+			],
+			'returnid' => [
+				ParamValidator::PARAM_TYPE     => 'string',
+			],
+			'returntext' => [
+				ParamValidator::PARAM_TYPE     => 'string',
+			],
+			'template' => [
+				ParamValidator::PARAM_TYPE     => 'string',
+			],
+			'ffform' => [
+				ParamValidator::PARAM_TYPE     => 'string',
+			],
+			'limit' => [
+				ParamValidator::PARAM_TYPE     => 'string',
+			]
+		];
+	}
+
+	/**
+	 * @return array
+	 */
+	protected function getExamplesMessages() : array {
+		return array(
+			'action=FlexFormAsk&query=base64query&q=search' => 'apihelp-flexform-bot-example-1'
+		);
+	}
+
+}
\ No newline at end of file
diff --git a/extension.json b/extension.json
index 68f5a412..6c3559b2 100644
--- a/extension.json
+++ b/extension.json
@@ -12,6 +12,7 @@
   "AutoloadClasses": {
     "ApiFlexForm": "ApiFlexForm.php",
     "ApiBotFlexForm": "ApiBotFlexForm.php",
+    "ApiAskFlexForm": "ApiAskFlexForm.php",
     "FlexFormHooks": "FlexForm.hooks.php"
   },
   "AutoloadNamespaces": {
@@ -22,7 +23,8 @@
   },
   "APIModules": {
     "flexform": "ApiFlexForm",
-    "FlexFormBot": "ApiBotFlexForm"
+    "FlexFormBot": "ApiBotFlexForm",
+    "FlexFormAsk": "ApiAskFlexForm"
   },
   "Hooks": {
     "LoadExtensionSchemaUpdates": "FlexForm\\Core\\Sql::addTables",
diff --git a/src/Render/Themes/Plain/PlainTokenRenderer.php b/src/Render/Themes/Plain/PlainTokenRenderer.php
index 23cecf00..737301a4 100644
--- a/src/Render/Themes/Plain/PlainTokenRenderer.php
+++ b/src/Render/Themes/Plain/PlainTokenRenderer.php
@@ -209,8 +209,8 @@ private function renderSelectJavascript(
 			} else {
 				$ffFormField = '';
 			}
-			$smwQueryUrl .= '?action=flexform&format=json';
-			$smwQueryUrl .= '&what=ask&titleStartsWith=ask&query=';
+			$smwQueryUrl .= '?action=FlexFormAsk&format=json';
+			$smwQueryUrl .= '&query=';
 			$smwQueryUrlQ = base64_encode( $smwQuery );
 		} else {
 			$smwQueryUrl = null;

From 756f932aa1d92e396b57f28a7cd3085124b07059 Mon Sep 17 00:00:00 2001
From: designburo 
Date: Wed, 21 Jan 2026 15:38:09 +0100
Subject: [PATCH 5/9] SMW ask as API, removed debug

---
 ApiAskFlexForm.php | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/ApiAskFlexForm.php b/ApiAskFlexForm.php
index be1be483..50719994 100644
--- a/ApiAskFlexForm.php
+++ b/ApiAskFlexForm.php
@@ -76,11 +76,6 @@ public function execute() {
 			"query"  => $this->query
 		];
 		$mRequest = new Render();
-		echo "
";
-		$dataRet = $mRequest->makeRequest( $postdata );
-		var_dump( $dataRet );
-		var_dump( $this->handleResults( $dataRet, $ret ) );
-		die();
 		$results = $this->handleResults( $mRequest->makeRequest( $postdata ), $ret );
 		$this->getResult()->addValue(
 			null,
@@ -246,7 +241,7 @@ private function handleResults( array $data, array $ret ): array {
 
 			$t = 0;
 			foreach ( $data as $k => $val ) {
-				if ( $this->returnText === false ) {
+				if ( $this->returnText === null ) {
 					$ret['results'][$t]['text'] = $val['displaytitle'];
 				} elseif ( isset( $val['printouts'][$this->returnText][0] ) ) {
 					$ret['results'][$t]['text'] = $val['printouts'][$this->returnText][0];
@@ -254,7 +249,7 @@ private function handleResults( array $data, array $ret ): array {
 					$ret['results'][$t]['text'] = 'Not found';
 				}
 
-				if ( $this->returnId === false ) {
+				if ( $this->returnId === null ) {
 					$ret['results'][$t]['id'] = $k;
 				} elseif ( isset( $val['printouts'][$this->returnId][0] ) ) {
 					$ret['results'][$t]['id'] = $val['printouts'][$this->returnId][0];

From 5ff5de743d464978a79dde6a00db97f01f997540 Mon Sep 17 00:00:00 2001
From: designburo 
Date: Wed, 21 Jan 2026 15:39:50 +0100
Subject: [PATCH 6/9] small astatic changes

---
 ApiAskFlexForm.php | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/ApiAskFlexForm.php b/ApiAskFlexForm.php
index 50719994..f31c13b1 100644
--- a/ApiAskFlexForm.php
+++ b/ApiAskFlexForm.php
@@ -264,7 +264,6 @@ private function handleResults( array $data, array $ret ): array {
 
 	public function needsToken() {
 		return false;
-		//return "csrf";
 	}
 
 	public function isWriteMode() {
@@ -274,7 +273,7 @@ public function isWriteMode() {
 	/**
 	 * @return array
 	 */
-	public function getAllowedParams() {
+	public function getAllowedParams(): array {
 		return [
 			'query'            => [
 				ParamValidator::PARAM_TYPE     => 'string',
@@ -305,9 +304,9 @@ public function getAllowedParams() {
 	 * @return array
 	 */
 	protected function getExamplesMessages() : array {
-		return array(
+		return [
 			'action=FlexFormAsk&query=base64query&q=search' => 'apihelp-flexform-bot-example-1'
-		);
+		];
 	}
 
 }
\ No newline at end of file

From c3a46180a48c5dd35c789dd78a1273c27ab9b421 Mon Sep 17 00:00:00 2001
From: designburo 
Date: Wed, 21 Jan 2026 15:44:17 +0100
Subject: [PATCH 7/9] remove redundant smw classes

---
 Modules/Handlers/semantic_ask.php             |  73 ------
 .../Request/Handlers/SemanticAsk.php          | 248 ------------------
 2 files changed, 321 deletions(-)
 delete mode 100644 Modules/Handlers/semantic_ask.php
 delete mode 100644 src/Processors/Request/Handlers/SemanticAsk.php

diff --git a/Modules/Handlers/semantic_ask.php b/Modules/Handlers/semantic_ask.php
deleted file mode 100644
index 7d66e1f1..00000000
--- a/Modules/Handlers/semantic_ask.php
+++ /dev/null
@@ -1,73 +0,0 @@
-= 3 ) {
-	$query = html_entity_decode( urldecode( $query ) );
-	//var_dump($query);
-	if ( $q !== false ) {
-		$q2 = ucwords( $q );
-		$q3 = strtoupper( $q );
-		$query = str_replace( '!!!', '~*' . $q . '*||~*' . $q2 . '*||~*' . $q3 . '*', $query );
-	} else {
-		$query = str_replace( '!!!', '', $query );
-	}
-	if ( $returnId !== false ) {
-		$query .= '|?' . $returnId;
-	}
-	if ( $returnText !== false ) {
-		$query .= '|?' . $returnText;
-	}
-	if ( $limit !== false ) {
-		$query .= '|limit=' . $limit;
-	} else {
-		$query .= '|limit=50';
-	}
-
-	//echo $query."
"; - - //[[Class::Organization]][[Name::~*ik*]]|?Name|?Contact|limit=99999 - //Process~*hallo*|?Name|?Name|limit=50 - //echo $query."
";
-
-	$postdata = http_build_query( [
-		"action" => "ask",
-		"format" => "json",
-		"query" => $query
-	] );
-	$data = $api->apiPost( $postdata, true );
-
-	if ( isset( $data['received']['query']['results'] ) && !empty( $data['received']['query']['results'] ) ) {
-		$data = $data['received']['query']['results'];
-
-		$t = 0;
-		foreach ( $data as $k => $val ) {
-			if ( $returnText === false ) {
-				$ret['results'][$t]['text'] = htmlentities( $val['displaytitle'] );
-			} elseif ( isset( $val['printouts'][$returnText][0] ) ) {
-				$ret['results'][$t]['text'] = htmlentities( $val['printouts'][$returnText][0] );
-			} else {
-				$ret['results'][$t]['text'] = 'Not found';
-			}
-
-			if ( $returnId === false ) {
-				$ret['results'][$t]['id'] = htmlentities( $k );
-			} elseif ( isset( $val['printouts'][$returnId][0] ) ) {
-				$ret['results'][$t]['id'] = htmlentities( $val['printouts'][$returnId][0] );
-			} else {
-				$ret['results'][$t]['id'] = 'Not found';
-			}
-			$t++;
-		}
-	}
-}
-
diff --git a/src/Processors/Request/Handlers/SemanticAsk.php b/src/Processors/Request/Handlers/SemanticAsk.php
deleted file mode 100644
index 8e83c0c1..00000000
--- a/src/Processors/Request/Handlers/SemanticAsk.php
+++ /dev/null
@@ -1,248 +0,0 @@
- $match ) {
-			// Looking for the actual query
-			if ( strpos( $match, '!!!' ) ) {
-				$matchExploded = explode( '::', $match );
-				$mainQuery = $matchExploded[0];
-				break;
-			}
-		}
-		return $mainQuery;
-	}
-
-	/**
-	 * Take a search string or part and add Uppercase first letter and all uppercase to them
-	 * @param string $searchPart
-	 *
-	 * @return string
-	 */
-	private function createNewQuery( string $searchPart ): string {
-		$q2    = ucwords( $searchPart );
-		$q3    = strtoupper( $searchPart );
-		return '~*' . $searchPart . '*||~*' . $q2 . '*||~*' . $q3 . '*';
-	}
-
-	/**
-	 * @param HandleResponse $responseHandler
-	 *
-	 * @return void
-	 * @throws Exception
-	 */
-	public function execute( HandleResponse $responseHandler ) {
-
-		// Todo: rewrite to allow API variables.
-
-		$ret            = [];
-		$ret['results'] = [];
-		$queryEncoded = General::getGetString( 'query', true, false );
-		if ( $queryEncoded !== false ) {
-			// $_GET will urldecode automatically. Make sure any spaces return to a +
-			$query = base64_decode( str_replace( ' ', '+', $queryEncoded ) );
-		} else {
-			$query = false;
-		}
-		$q              = General::getGetString( 'q', true, false );
-		$returnId       = General::getGetString( 'returnid', true, false );
-		$returnText     = General::getGetString( 'returntext', true, false );
-		$template       = General::getGetString( 'template', true, false );
-		$limit          = General::getGetString( 'limit', true, false );
-		//return [ 'results' => $_GET ];
-
-		if ( $query !== false ) {
-			$filterQuery = false;
-			if ( strpos( $query, '(' ) !== false && strpos( $query, ')' ) !== false ) {
-				if ( strpos( $query, '(fquery=' ) !== false ) {
-					$fQuery = Core::get_string_between( $query, '(fquery=', ')' );
-					$fQueryOld = $fQuery;
-					if ( strpos( $fQuery, '__^^__' ) !== false ) {
-						$fform = General::getGetString( 'ffform', true, false );
-						if ( $fform === false ) {
-							$fQuery = '';
-						} else {
-							$fQuery = str_replace( '__^^__', base64_decode( $fform ), $fQuery );
-							$filterQuery = true;
-						}
-					}
-					$query = str_replace(
-						'(fquery=' . $fQueryOld . ')',
-						'',
-						$query
-					);
-				}
-				if ( strpos( $query, '(returntext=' ) !== false ) {
-					$returnText = Core::get_string_between( $query, '(returntext=', ')' );
-					$query = str_replace(
-						'(returntext=' . $returnText . ')',
-						'',
-						$query
-					);
-				}
-				if ( strpos( $query, '(template=' ) !== false ) {
-					$template = Core::get_string_between( $query, '(template=', ')' );
-					$query = str_replace(
-						'(template=' . $template . ')',
-						'',
-						$query
-					);
-				}
-				if ( strpos( $query, '(returnid=' ) !== false ) {
-					$returnId = Core::get_string_between( $query, '(returnid=', ')' );
-					$query = str_replace(
-						'(returnid=' . $returnId . ')',
-						'',
-						$query
-					);
-				}
-				if ( strpos( $query, '(limit=' ) !== false ) {
-					$limit = Core::get_string_between( $query, '(limit=', ')' );
-					$query = str_replace(
-						'(limit=' . $limit . ')',
-						'',
-						$query
-					);
-				}
-			}
-			if ( $filterQuery ) {
-				$query .= $fQuery;
-			}
-
-			if ( $q !== false ) {
-				// Are there spaces in the query?
-				if ( strpos( $q, ' ' ) !== false ) {
-					$mainQuery = $this->getMainQuery( $query );
-					$explodedQuery = explode( ' ', $q );
-					$newQuery = '';
-//
-					foreach ( $explodedQuery as $seperated ) {
-						if ( !empty( $seperated ) ) {
-							$newQuery .= '[[' . $mainQuery . '::' . $this->createNewQuery( $seperated ) . ']]';
-						}
-					}
-					$query = str_replace(
-						'[[' . $mainQuery . '::!!!]]',
-						$newQuery,
-						$query
-					);
-				} else {
-					$query = str_replace(
-						'!!!',
-						$this->createNewQuery( $q ),
-						$query
-					);
-				}
-			} else {
-				$query = str_replace(
-					'!!!',
-					'',
-					$query
-				);
-			}
-			if ( $returnId !== false ) {
-				$query .= '|?' . $returnId;
-			}
-			if ( $returnText !== false ) {
-				$query .= '|?' . $returnText;
-			}
-			if ( $limit !== false ) {
-				$query .= '|limit=' . $limit;
-			} else {
-				$query .= '|limit=50';
-			}
-			if ( $template !== false ) {
-				$query .= '|template=' . $template;
-			}
-
-			// echo $query."
"; - - //[[Class::Organization]][[Name::~*ik*]]|?Name|?Contact|limit=99999 - //Process~*hallo*|?Name|?Name|limit=50 - //echo $query; - - $postdata = [ - "action" => "ask", - "format" => "json", - "query" => $query - ]; - $mRequest = new Render(); - $data = $mRequest->makeRequest( $postdata ); - - if ( isset( $data['query']['results'] ) && !empty( $data['query']['results'] ) ) { - $data = $data['query']['results']; - - $t = 0; - foreach ( $data as $k => $val ) { - if ( $returnText === false ) { - $ret['results'][$t]['text'] = $val['displaytitle']; - } elseif ( isset( $val['printouts'][$returnText][0] ) ) { - $ret['results'][$t]['text'] = $val['printouts'][$returnText][0]; - } else { - $ret['results'][$t]['text'] = 'Not found'; - } - - if ( $returnId === false ) { - $ret['results'][$t]['id'] = $k; - } elseif ( isset( $val['printouts'][$returnId][0] ) ) { - $ret['results'][$t]['id'] = $val['printouts'][$returnId][0]; - } else { - $ret['results'][$t]['id'] = 'Not found'; - } - $t++; - } - } - } - // header( 'Content-Type: application/json' ); - return $ret; - return json_encode( - $ret, - JSON_PRETTY_PRINT - ); - $database = MediaWikiServices::getInstance()->getDBLoadBalancer()->getConnection( DB_PRIMARY ); - - if ( $database->writesPending() ) { - - // If there is still a database update pending, commit it here - try { - $database->commit( - __METHOD__, - IDatabase::FLUSHING_INTERNAL - ); - } catch ( DBError | DBUnexpectedError $e ) { - throw new FlexFormException( - $e->getMessage(), - 0, - $e - ); - } - } - die(); - } -} - - From a1f302ba9016aa420d0e2effa1d1b2446b829de5 Mon Sep 17 00:00:00 2001 From: designburo Date: Wed, 21 Jan 2026 18:06:50 +0100 Subject: [PATCH 8/9] Pandoc different import namespace fix --- src/Processors/Convert/PandocConverter.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Processors/Convert/PandocConverter.php b/src/Processors/Convert/PandocConverter.php index 83259b2f..e3c56037 100644 --- a/src/Processors/Convert/PandocConverter.php +++ b/src/Processors/Convert/PandocConverter.php @@ -14,8 +14,8 @@ use FlexForm\Core\Debug; use FlexForm\FlexFormException; use FlexForm\Processors\Files\Convert; -use import\classes\Pandoc\Pandoc; -use import\classes\Pandoc\PandocException; +use Pandoc\Pandoc; +use Pandoc\PandocException; class PandocConverter extends Convert { /** @@ -36,7 +36,7 @@ private function getPandocMediaPath(): string { } /** - * @return import\classes\Pandoc\Pandoc + * @return Pandoc * @throws FlexFormException */ private function giveMePandoc(): Pandoc { From a9ae50506ed99bd039108769ad70e94a56ac19a7 Mon Sep 17 00:00:00 2001 From: designburo Date: Wed, 21 Jan 2026 18:16:24 +0100 Subject: [PATCH 9/9] Pandoc different import namespace fix --- src/Processors/Convert/PandocConverter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Processors/Convert/PandocConverter.php b/src/Processors/Convert/PandocConverter.php index e3c56037..6aa521ee 100644 --- a/src/Processors/Convert/PandocConverter.php +++ b/src/Processors/Convert/PandocConverter.php @@ -91,7 +91,7 @@ public function convertFile(): string { ]; try { $wiki = $pandoc->runWith( $this->getFile(), $options ); - } catch ( import\classes\Pandoc\PandocException $e ) { + } catch ( PandocException $e ) { $params = [ 'file' => $e->getFile(), 'line' => $e->getLine(),