I’m listing the possible solutions (in Neos 1.2) here:
1. Use the \TYPO3\TypoScript\View\TypoScriptView
in the Plugin controller
So the default view needs to be changed:
class MyPluginController extends \TYPO3\Flow\Mvc\Controller\ActionController {
protected $defaultViewObjectName = 'TYPO3\TypoScript\View\TypoScriptView';
protected function initializeView(\TYPO3\Flow\Mvc\View\ViewInterface $view) {
$view->setTypoScriptPath('typoScriptPathPatterns', array('resource://@package/Private/TypoScript/MyPlugin'));
}
...
}
This also needs a view option for the TypoScript include since the TYPO3.TypoScript
view does the inclusion of TypoScript completely different (recursively) than the Neos TypoScript view.
Downside: This doesn’t include the Neos TypoScript, especially the auto-generated TypoScript for Node types. So even by including everything by hand (I tried that) this doesn’t solve the problem of rendering content in a convenient way.
2. Use the TYPO3\Neos\View\TypoScriptView
in the Plugin controller
So again the default view needs to be changed:
class MyPluginController extends \TYPO3\Flow\Mvc\Controller\ActionController {
protected $defaultViewObjectName = 'TYPO3\Neos\View\TypoScriptView';
protected function initializeView(\TYPO3\Flow\Mvc\View\ViewInterface $view) {
$view->setTypoScriptPath('plugins/myPlugin/' . $this->request->getControllerActionName());
}
...
}
This approach sets the TypoScript path that should be rendered by the view dependent on the action name. This is needed so every action can get it’s own template.
Positive: It’s easy to render content with this approach.
Downside: There’s currently no support to pass custom variables from the controller to the view (into the TypoScript context).This makes plugins basically useless because no data can be passed from the controller to the view.
3. Roll your own view
So let’s combine the flexibility of passing variables to the context and re-using the Neos TypoScript in a custom TypoScript view:
class PluginTypoScriptView extends \TYPO3\Flow\Mvc\View\AbstractView {
/**
* @Flow\Inject
* @var \TYPO3\Neos\Domain\Service\TypoScriptService
*/
protected $typoScriptService;
/**
* @var \TYPO3\TypoScript\Core\Runtime
*/
protected $typoScriptRuntime;
/**
* @Flow\Inject
* @var \TYPO3\Flow\Security\Context
*/
protected $securityContext;
/**
* Renders the view
*
* @return string The rendered view
* @throws \Exception if no node is given
* @api
*/
public function render() {
$currentNode = $this->getCurrentNode();
$currentSiteNode = $currentNode->getContext()->getCurrentSiteNode();
$typoScriptRuntime = $this->getTypoScriptRuntime($currentSiteNode);
$contextVariables = array(
'node' => $currentNode,
'documentNode' => $this->getClosestDocumentNode($currentNode),
'site' => $currentSiteNode,
'account' => $this->securityContext->canBeInitialized() ? $this->securityContext->getAccount() : NULL,
'editPreviewMode' => isset($this->variables['editPreviewMode']) ? $this->variables['editPreviewMode'] : NULL
);
$contextVariables = array_merge($contextVariables, $this->variables);
$typoScriptRuntime->pushContextArray($contextVariables);
try {
$output = $typoScriptRuntime->render('plugins/' . $this->controllerContext->getRequest()->getControllerName() . '/' . $this->controllerContext->getRequest()->getControllerActionName());
} catch (\TYPO3\TypoScript\Exception\RuntimeException $exception) {
throw $exception->getPrevious();
}
$typoScriptRuntime->popContext();
return $output;
}
/**
* @return NodeInterface
* @throws \TYPO3\Neos\Exception
*/
protected function getCurrentNode() {
$currentNode = isset($this->variables['node']) ? $this->variables['node'] : NULL;
if (!$currentNode instanceof NodeInterface) {
throw new \TYPO3\Neos\Exception('TypoScriptView needs a variable \'node\' set with a Node object.', 1432647766);
}
return $currentNode;
}
/**
* @param NodeInterface $currentSiteNode
* @return \TYPO3\TypoScript\Core\Runtime
*/
protected function getTypoScriptRuntime(NodeInterface $currentSiteNode) {
if ($this->typoScriptRuntime === NULL) {
$this->typoScriptRuntime = $this->typoScriptService->createRuntime($currentSiteNode, $this->controllerContext);
}
return $this->typoScriptRuntime;
}
/**
* @param NodeInterface $node
* @return NodeInterface
*/
protected function getClosestDocumentNode(NodeInterface $node) {
while ($node !== NULL && !$node->getNodeType()->isOfType('TYPO3.Neos:Document')) {
$node = $node->getParent();
}
return $node;
}
}
This is basically a copy of the Neos TS view without some unneeded code. To use it inside the plugin just specify the view object name in the controller:
class MyPluginController extends \TYPO3\Flow\Mvc\Controller\ActionController {
protected $defaultViewObjectName = 'MyVendor\MyPackage\View\PluginTypoScriptView';
...
public function indexAction() {
// Access the current node (document or plugin content node) from internal arguments
$node = $this->request->getInternalArgument('__node');
// Do your plugin logic here
$mySpecialValue = ...;
$this->assign('node', $node);
$this->assign('mySpecialValue', $mySpecialValue);
}
}
Each action needs its own TypoScript definition, make sure to include that in your site Root.ts2
:
plugins.MyPlugin.index = TYPO3.TypoScript:Template {
templatePath = 'resource://MyVendor.MyPackage/Private/Templates/MyPlugin/Index.html'
mySpecialValue = ${mySpecialValue}
someContent = TYPO3.Neos:ContentCollection {
// This expects a ContentCollection child node with path "myContent" for the current node
nodePath = 'myContent'
}
}
So this works as expected and allows to render both data from the plugin and content as already defined for the Neos content rendering. Let’s see if we want to have this in the Neos core.