如何将表单的__referrer设置为另一个操作和控制器

时间:2015-03-11 21:58:47

标签: typo3 extbase typo3-6.2.x

我有两个域模型,名为FooBar。对于它们两者,我有一个具有默认CRUD操作的控制器。在displayAction Foo中,有一个表单可用于创建新的Bar(使用部分呈现)。感谢Helmut Hummels Extension" typoscript_rendering"我可以使用AJAX轻松地将该表单提交给BarController

但是,如果创建的Bar - 对象的验证失败,则应调用newAction的{​​{1}},以便用户可以解决问题。

但相反,BarController的{​​{1}}被调用。发生这种情况,因为Fluid会自动将当前执行的操作作为引用者插入到表单中:

displayAction

是否有任何无黑客方式将__referrer更改为FooController的{​​{1}}?

修改:根据要求,表单处于流畅状态。删除了不必要的标记,变量名称是匿名的,并且它是<input name="tx_myextension_display[__referrer][@extension]" value="MyExtension" type="hidden"> <input name="tx_myextension_display[__referrer][@vendor]" value="MyVendor" type="hidden"> <input name="tx_myextension_display[__referrer][@controller]" value="FooController" type="hidden"> <input name="tx_myextension_display[__referrer][@action]" value="display" type="hidden"> 的{​​{1}}的一部分:

newAction

这只是一个08/15表格。

如果我使用无效数据提交,我会转发BarController - displayAction的行动,而不是FooController - {namespace helhum=Helhum\TyposcriptRendering\ViewHelpers} <f:form name="bar" object="{MyBar}" action="create" controller="Bar" enctype="multipart/form-data" additionalAttributes="{data-ajax-uri: '{helhum:uri.ajaxAction(action: \\'create\\', controller: \\'Bar\\')}'}" > <f:form.textarea property="b"/> <f:form.submit value="Submit"/> </f:form> 的行动。我想这是有道理的,只要表格不是由AJAX提交的。

2 个答案:

答案 0 :(得分:2)

我找到了一个&#34;解决方案&#34;这仍然是hacky,因为它容易受到开放基类问题的影响。

它通过覆盖方法errorAction来工作,因为如果发生验证错误则会调用它。它还会重定向到引用控制器。

我的覆盖如下:

/**
 * If an error occurred while creating a new Bar with an
 * AJAX request, redirect to the new action of this controller.
 */
public function errorAction() {
    if ('createByAjaxAction' === $this->actionMethodName) {
        $this->clearCacheOnError();
        $this->addErrorFlashMessage();
        $this->forward('new');
    }
    else {
        parent::errorAction();
    }
}

/*
 * If an error occurred during this request, this adds
 * a flash message describing the error to the flash
 * message container.
 * @TODO Remove after upgrade to 7.x
 * @return void
 */
protected function addErrorFlashMessage() {
    $errorFlashMessage = $this->getErrorFlashMessage();
    if ($errorFlashMessage !== FALSE) {
        $this->addFlashMessage($errorFlashMessage, '', \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
    }
}

/**
 * Create action that is supposed to be used by AJAX.
 * @param \MyVendor\MyExt\Domain\Model\Bar
 */
public function createByAjaxAction($bar) {
    $this->forward('create');
}

/**
 * Normal create action.
 * @param \MyVendor\MyExt\Domain\Model\Bar
 */
public function createAction($bar) {
    // Normal entity creation stuff
}

我对此方法的问题是:如果来自extbase的errorAction的原始ActionController将来发生变化(与7.x中的变化一样,请参阅this change) ,这段代码很乐意破解。如果成员变量actionMethodName发生变化,则相同。

因此,如果有人知道如何以更加面向未来的方式做到这一点,请添加答案。

答案 1 :(得分:2)

你的问题是在viewhelper f:form

中打开@todo
         * @todo filter out referrer information that is equal to the target (e.g. same packageKey)
     */
    protected function renderHiddenReferrerFields()
...

我的副本和粘贴 - 解决方法是扩展viewhelper f:form。我在我的扩展中定义了一个子类。

<?php
namespace Porth\Positioner\ViewHelpers;

class FormViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper {


/**
 * Renders hidden form fields for referrer information about
 * the current controller and action.
 *
 * Renders hidden form field for secured referrer information about the current controller and action.
 *
 * This method is called twice, to deal with subclasses of this class in a most compatible way
 *
 * @param string|null $action
 * @param string|null $controller
 * @param string|null $vendor
 * @param string|null $extension
 *
 * @return string Hidden field with secured referrer information
 * @todo filter out referrer information that is equal to the target (e.g. same packageKey)
 */
protected function renderHiddenReferrerFields($action =null,  $controller = null, $vendor = null, $extension = null )
{
    $request = $this->controllerContext->getRequest();
    $extensionName = ((is_null($extension))? $request->getControllerExtensionName() : $extension);
    $vendorName = ((is_null($vendor))? $request->getControllerVendorName() : $vendor);
    $controllerName = ((is_null($controller)) ? $request->getControllerName() : $controller);
    $actionName = ((is_null($action))? $request->getControllerActionName() : $action);
    $result = LF;
    $result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[@extension]') . '" value="' . $extensionName . '" />' . LF;
    if ($vendorName !== null) {
        $result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[@vendor]') . '" value="' . $vendorName . '" />' . LF;
    }
    $result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[@controller]') . '" value="' . $controllerName . '" />' . LF;
    $result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[@action]') . '" value="' . $actionName . '" />' . LF;
    $result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[arguments]') . '" value="' . htmlspecialchars($this->hashService->appendHmac(base64_encode(serialize($request->getArguments())))) . '" />' . LF;
    $result .= $this->renderHiddenSecuredReferrerField($actionName,  $controllerName, $vendorName, $extensionName);

    return $result;
}

/**
 * Renders hidden form field for secured referrer information about the current controller and action.
 *
 * This method is called twice, to deal with subclasses of this class in a most compatible way
 *
 * @param string|null $action
 * @param string|null $controller
 * @param string|null $vendor
 * @param string|null $extension
 *
 * @return string Hidden field with secured referrer information
 */
protected function renderHiddenSecuredReferrerField($action = null,  $controller = null, $vendor = null, $extension = null )
{
    if ($this->securedReferrerFieldRendered) {
        return '';
    }
    $request = $this->renderingContext->getControllerContext()->getRequest();
    $extensionName = ((is_null($extension))? $request->getControllerExtensionName() : $extension);
    $vendorName = ((is_null($vendor))? $request->getControllerVendorName() : $vendor);
    $controllerName = ((is_null($controller)) ? $request->getControllerName() : $controller);
    $actionName = ((is_null($action))? $request->getControllerActionName() : $action);
    $actionRequest = [
        '@extension' => $extensionName,
        '@controller' => $controllerName,
        '@action' => $actionName,
    ];
        if ($vendorName !== null) {
        $actionRequest['@vendor'] = $vendorName;
    }
    $result = '<input type="hidden" name="' . $this->prefixFieldName('__referrer[@request]') . '" value="' . htmlspecialchars($this->hashService->appendHmac(serialize($actionRequest))) . '" />' . LF;
    $this->securedReferrerFieldRendered = true;
    return $result;
}


/**
 * Render the form.
 *
 * @param string $action Target action
 * @param array $arguments Arguments
 * @param string $controller Target controller
 * @param string $vendor Target vendor
 * @param string $extensionName Target Extension Name (without "tx_" prefix and no underscores). If NULL the current extension name is used
 * @param string $pluginName Target plugin. If empty, the current plugin name is used
 * @param int $pageUid Target page uid
 * @param mixed $object Object to use for the form. Use in conjunction with the "property" attribute on the sub tags
 * @param int $pageType Target page type
 * @param bool $noCache set this to disable caching for the target page. You should not need this.
 * @param bool $noCacheHash set this to suppress the cHash query parameter created by TypoLink. You should not need this.
 * @param string $section The anchor to be added to the action URI (only active if $actionUri is not set)
 * @param string $format The requested format (e.g. ".html") of the target page (only active if $actionUri is not set)
 * @param array $additionalParams additional action URI query parameters that won't be prefixed like $arguments (overrule $arguments) (only active if $actionUri is not set)
 * @param bool $absolute If set, an absolute action URI is rendered (only active if $actionUri is not set)
 * @param bool $addQueryString If set, the current query parameters will be kept in the action URI (only active if $actionUri is not set)
 * @param array $argumentsToBeExcludedFromQueryString arguments to be removed from the action URI. Only active if $addQueryString = TRUE and $actionUri is not set
 * @param string $fieldNamePrefix Prefix that will be added to all field names within this form. If not set the prefix will be tx_yourExtension_plugin
 * @param string $actionUri can be used to overwrite the "action" attribute of the form tag
 * @param string $objectName name of the object that is bound to this form. If this argument is not specified, the name attribute of this form is used to determine the FormObjectName
 * @param string $hiddenFieldClassName
 * @return string rendered form
 */
public function render($action = null, array $arguments = array(), $controller = null, $vendor = null, $extensionName = null, $pluginName = null, $pageUid = null, $object = null, $pageType = 0, $noCache = false, $noCacheHash = false, $section = '', $format = '', array $additionalParams = array(), $absolute = false, $addQueryString = false, array $argumentsToBeExcludedFromQueryString = array(), $fieldNamePrefix = null, $actionUri = null, $objectName = null, $hiddenFieldClassName = null)
{
    $this->setFormActionUri();
    if (strtolower($this->arguments['method']) === 'get') {
        $this->tag->addAttribute('method', 'get');
    } else {
        $this->tag->addAttribute('method', 'post');
    }
    $this->addFormObjectNameToViewHelperVariableContainer();
    $this->addFormObjectToViewHelperVariableContainer();
    $this->addFieldNamePrefixToViewHelperVariableContainer();
    $this->addFormFieldNamesToViewHelperVariableContainer();
    $formContent = $this->renderChildren();

    if ($this->arguments['hiddenFieldClassName'] !== null) {
        $content = LF . '<div class="' . htmlspecialchars($this->arguments['hiddenFieldClassName']) . '">';
    } else {
        $content = LF . '<div>';
    }

    $content .= $this->renderHiddenIdentityField($this->arguments['object'], $this->getFormObjectName());
    $content .= $this->renderAdditionalIdentityFields();
    $content .= $this->renderHiddenReferrerFields($action ,  $controller , $vendor , $extensionName );
//        $content .= $this->renderHiddenSecuredReferrerField($action ,  $controller , $vendor , $extensionName );

    // Render the trusted list of all properties after everything else has been rendered
    $content .= $this->renderTrustedPropertiesField();

    $content .= LF . '</div>' . LF;
    $content .= $formContent;
    $this->tag->setContent($content);
    $this->removeFieldNamePrefixFromViewHelperVariableContainer();
    $this->removeFormObjectFromViewHelperVariableContainer();
    $this->removeFormObjectNameFromViewHelperVariableContainer();
    $this->removeFormFieldNamesFromViewHelperVariableContainer();
    $this->removeCheckboxFieldNamesFromViewHelperVariableContainer();
    return $this->tag->render();
}

}