Developer
Architecture
Ai3 FAQ follows the Ai3 Suite capability pattern provided by wegewerk/ai3_core :
- A Capability class registers the feature under a unique key.
- An Endpoint class implements the ZAK-AI API call.
- A FormEngine element renders the backend widget.
- A backend AJAX controller handles the generation request.
- Bootstrap Package integration provides accordion functionality.
PHP API
ArticlefaqCapability
- class ArticlefaqCapability
-
- Fully qualified name
-
\Wegewerk\
Ai3Faq\ Domain\ Capabilities\ Articlefaq Capability
Extends
Wegewerk\Ai3Core\Domain\Capabilities\Capability. Registered viaConfiguration/Services.yamlwith the following wiring:$key:articlefaq$title:Article FAQ$endpoint:ZakAiFaq- Tagged as
ai3.capability
ZakAiFaq
- class ZakAiFaq
-
- Fully qualified name
-
\Wegewerk\
Ai3Faq\ Api\ Zak Ai Faq
Implements
Wegewerk\Ai3Core\Api\ZakAiEndpointInterface. WrapsZakAiClientto call the ZAK-AIfaqREST endpoint.- generate ( $imagePath, $description, $language)
-
Sends page content to the ZAK-AI API and returns the generated FAQ data as a JSON string containing question-answer pairs.
- param string $imagePath
-
Unused; reserved for future use.
- param string $description
-
The page content text to analyze for FAQ generation.
- param string $language
-
The target language for the FAQ (default: 'de').
- returntype
-
string
The response contains an array of objects with
questionandanswerproperties.
ArticlefaqController
- class ArticlefaqController
-
- Fully qualified name
-
\Wegewerk\
Ai3Faq\ Controller\ Ajax\ Articlefaq Controller
Backend AJAX controller (
#[AsController]), extendsWegewerk\Ai3Core\Controller\Ajax\AbstractAjaxController.- getFaq ( ServerRequestInterface $request)
-
Handles the AJAX FAQ generation request.
- Reads
page_idandlanguagefrom the POST body. - Fetches all page content via
PagesRepository::getPageContent(). - Calls
ArticlefaqCapabilityendpoint to generate the FAQ. - Parses the JSON response to extract question-answer pairs.
- Returns a JSON response with
faqData,source, andtype: 'faq'on success, or an error JSON on failure.
- returntype
-
ResponseInterface
- Reads
FormEngine integration
The custom FormEngine node ai3FaqGeneratorElement is registered in
TCA configuration:
$GLOBALS['TCA']['tt_content']['columns']['tx_ai3_faq_generator'] = [
'exclude' => true,
'label' => 'LLL:EXT:ai3_faq/Resources/Private/Language/locallang.xlf:tx_ai3.faq.generator',
'config' => [
'type' => 'user',
'renderType' => 'ai3FaqGeneratorElement',
],
];
- class Ai3FaqGeneratorElement
-
- Fully qualified name
-
\Wegewerk\
Ai3Faq\ Form Engine\ Ai3Faq Generator Element
Extends
TYPO3\CMS\Backend\Form\Element\AbstractFormElement. Renders a<div data-ai3="ai3-faq-container">withdata-page-idanddata-record-uidattributes. The Lit-HTMLFaqAppmounts into the inner<div data-ai3="ai3-faq-app">.
TCA Configuration
Content element registration
The ai3_faq content type is registered in Configuration/TCA/Overrides/tt_content_faq.php:
ExtensionManagementUtility::addTcaSelectItem('tt_content',
'CType',
[
'label' => 'LLL:EXT:ai3_faq/Resources/Private/Language/locallang.xlf:tx_ai3.faq.label_ctype',
'value' => 'ai3_faq',
'icon' => 'ai3-faq-icon',
'description' => 'LLL:EXT:ai3_faq/Resources/Private/Language/locallang.xlf:tx_ai3.faq.label_description',
'group' => 'AI3',
],
'textmedia',
'after',
);
Bootstrap Package integration
The FAQ element inherits accordion functionality from Bootstrap Package:
$GLOBALS['TCA']['tt_content']['types']['ai3_faq'] = $GLOBALS['TCA']['tt_content']['types']['accordion'];
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPiFlexFormValue(
'*',
'FILE:EXT:bootstrap_package/Configuration/FlexForms/Accordion.xml',
'ai3_faq'
);
AJAX route
The backend AJAX route is registered in
Configuration/Backend/AjaxRoutes.php:
| Route name | Path | Handler |
|---|---|---|
ai3_faq_generate | /ai3/faq/generate | ArticlefaqController::getFaq |
Request (POST, JSON body):
{
"page_id": 42,
"language": "de"
}
Response (success):
{
"success": true,
"faqData": [
{
"question": "What is this page about?",
"answer": "This page describes..."
},
{
"question": "How do I use this feature?",
"answer": "To use this feature, you need to..."
}
],
"source": "Page content extracted from...",
"type": "faq"
}
Response (error):
{
"success": false,
"error": "Error message describing what went wrong"
}
JavaScript modules
JavaScript modules are registered via Configuration/JavaScriptModules.php
under the @wegewerk/Ai3Faq/ import map prefix.
- ``@wegewerk/Ai3Faq/ai3api.js`` --
Ai3Apiclass withgenerateFaq(pageId, recordUid, language)that POSTs to the AJAX route via TYPO3'sAjaxRequest. - ``@wegewerk/Ai3Faq/faq.js`` -- Lit-HTML
FaqAppthat renders the backend widget, handles button clicks, callsAi3Api, displays FAQ preview, and manages the accordion data.
FaqApp Class
.. js:class:: FaqAppThe main JavaScript class that handles the FAQ generation interface.
.. js:method:: generateFaq()Initiates FAQ generation by calling the backend AJAX endpoint. Shows loading state and handles success/error responses.
.. js:method:: previewFaq(generatedFAQ)Displays the generated FAQ in a preview table with questions and answers.
.. js:method:: useFaq()Saves the generated FAQ data to the content element fields and triggers
form submission via FormEngine.saveDocument().
Updates the hidden AI3 core fields with the generated FAQ data:
tx_ai3_type- Set to 'faq'tx_ai3_source- Contains the original page contenttx_ai3_raw- Contains the JSON-encoded FAQ data
Services Configuration
The extension's services are configured in Configuration/Services.yaml:
services:
_defaults:
autowire: true
autoconfigure: true
public: false
Wegewerk\Ai3Faq\:
resource: '../Classes/*'
Wegewerk\Ai3Faq\Domain\Capabilities\ArticlefaqCapability:
arguments:
$key: 'articlefaq'
$title: 'Article FAQ'
$endpoint: '@Wegewerk\Ai3Faq\Api\ZakAiFaq'
tags:
- { name: 'ai3.capability' }
Event listener
- class AfterFormEnginePageInitializedEventListener
-
- Fully qualified name
-
\Wegewerk\
Ai3Faq\ Event Listener\ After Form Engine Page Initialized Event Listener
Listens to
AfterFormEnginePageInitializedEvent(#[AsEventListener]). Adds the extension'slocallang.xlfas an inline language label file to thePageRenderer, making all translation keys available to JavaScript.
Schema.org Integration
The extension automatically generates schema.org markup for FAQ pages when using the brotkrueml/schema extension. This provides structured data for search engines and enables rich snippets.
The schema markup uses the FAQPage type and includes:
- Main entity as
FAQPage - Individual
Questionentities for each FAQ item - Proper
acceptedAnswermarkup for each answer
Customization and Extension
Adding Language Support
To add support for additional languages, modify the getLanguageOptions()
method in faq.js:
getLanguageOptions() {
return [
{
value: 'de',
label: 'Deutsch'
},
{
value: 'en',
label: 'English'
},
{
value: 'fr',
label: 'Français'
}
];
}
Custom FAQ Processing
To customize FAQ processing, extend the ZakAiFaq class:
class CustomZakAiFaq extends ZakAiFaq
{
public function generate(string $imagePath, string $description, string $language): string
{
$result = parent::generate($imagePath, $description, $language);
// Custom processing of FAQ data
$faqData = json_decode($result, true);
$processedData = $this->customProcessing($faqData);
return json_encode($processedData);
}
private function customProcessing(array $faqData): array
{
// Your custom logic here
return $faqData;
}
}
Template Customization
Override the Bootstrap Package accordion template to customize FAQ output:
<!-- EXT:your_sitepackage/Resources/Private/Templates/ContentElements/Accordion.html -->
<div class="accordion faq-accordion" id="accordion_{data.uid}">
<f:for each="{accordionItems}" as="item" iteration="iteration">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button faq-question" type="button"
data-bs-toggle="collapse"
data-bs-target="#collapse_{data.uid}_{iteration.cycle}">
{item.question}
</button>
</h2>
<div id="collapse_{data.uid}_{iteration.cycle}"
class="accordion-collapse collapse">
<div class="accordion-body faq-answer">
<f:format.html>{item.answer}</f:format.html>
</div>
</div>
</div>
</f:for>
</div>