Seditio Source
Root |
./othercms/xenForo 2.2.8/src/XF/Template/Compiler/Tag/AbstractFormElement.php
<?php

namespace XF\Template\Compiler\Tag;

use
XF\Template\Compiler\Syntax\Tag;
use
XF\Template\Compiler;

use function
in_array;

abstract class
AbstractFormElement extends AbstractTag
{
   
/**
     * Specific attributes that will be treated as row-level.
     * Key is name of the attribute. The value should be true if variables within
     * should be escaped (because the value will be displayed raw) or false if
     * the value will be escaped in all cases before displaying.
     *
     * @var array
     */
   
protected $defaultRowOptions = [
       
'label' => true,
       
'labelid' => false,
       
'hint' => true,
       
'explain' => true,
       
'rowclass' => false,
       
'rowid' => false,
       
'rowtype' => false,
       
'initialhtml' => true,
       
'html' => true,
       
'finalhtml' => true,
       
'error' => true,
    ];

    public function
compileTextInput($functionBaseName, $isRowLevel, Tag $tag, Compiler $compiler, array $context)
    {
       
$context['escape'] = true;

        list(
$controlOptions, $rowOptions) = $this->getOptionsFromAttributes($tag, $compiler, $context);

        foreach (
$tag->children AS $child)
        {
           
$foundExpected = (
               
$this->compileRowOptionChild($child, $compiler, $context, $rowOptions)
            );
            if (!
$foundExpected)
            {
                throw
$child->exception(\XF::phrase('tag_x_contains_unexpected_child_element', ['name' => $tag->name]));
            }
        }

        return
$this->getFinalInputElementCode(
           
$functionBaseName, $isRowLevel, $compiler,
           
$controlOptions, $rowOptions
       
);
    }

    public function
compileChoiceInput($functionBaseName, $isRowLevel, Tag $tag, Compiler $compiler, array $context, $allowDependent = false)
    {
       
$context['escape'] = true;

        list(
$controlOptions, $rowOptions) = $this->getOptionsFromAttributes($tag, $compiler, $context);

       
$choices = new Compiler\ChoiceBuilder($compiler);

        foreach (
$tag->children AS $child)
        {
           
$foundExpected = (
               
$this->compileRowOptionChild($child, $compiler, $context, $rowOptions)
                ||
$this->compileExpectedChoice($choices, $tag, $child, $compiler, $context, $allowDependent)
            );
            if (!
$foundExpected)
            {
                throw
$child->exception(\XF::phrase('tag_x_contains_unexpected_child_element', ['name' => $tag->name]));
            }
        }

        return
$this->getFinalChoiceElementCode(
           
$functionBaseName, $isRowLevel, $compiler,
           
$controlOptions, $choices, $rowOptions
       
);
    }

    public function
getOptionsFromAttributes(Tag $tag, Compiler $compiler, array $htmlContext, array $htmlAttributes = [])
    {
       
$htmlContext['escape'] = true;

       
$textContext = $htmlContext;
       
$textContext['escape'] = false;

       
$controlOptions = [];
       
$rowOptions = [];
        foreach (
$tag->attributes AS $name => $value)
        {
            if (isset(
$this->defaultRowOptions[$name]))
            {
                if (
$this->defaultRowOptions[$name])
                {
                   
// allow html - escape variables now
                   
$rowOptions[$name] = $compiler->compileToArraySyntax($value, $name, $htmlContext);
                }
                else
                {
                   
// text only - escape at runtime
                   
$rowOptions[$name] = $compiler->compileToArraySyntax($value, $name, $textContext);
                }
            }
            else
            {
                if (
$htmlAttributes && in_array($name, $htmlAttributes))
                {
                   
$controlOptions[$name] = $compiler->compileToArraySyntax($value, $name, $htmlContext);
                }
                else
                {
                   
$controlOptions[$name] = $compiler->compileToArraySyntax($value, $name, $textContext);
                }
            }
        }

        return [
$controlOptions, $rowOptions];
    }

    public function
compileRowOptionChild(
       
Compiler\Syntax\AbstractSyntax $child,
       
Compiler $compiler,
        array
$context,
        array &
$rowOptions
   
)
    {
        if (
$this->isNamedTag($child, ['label', 'hint', 'explain', 'initialhtml', 'html', 'finalhtml']))
        {
           
/** @var $child Tag */
           
$rowOptions[$child->name] = $compiler->compileToArraySyntax($child->children, $child->name, $context);
            return
true;
        }

        if (
$this->isEmptyString($child))
        {
           
// ok, ignore
           
return true;
        }

        return
false;
    }

    public function
compileExpectedChoice(
       
Compiler\ChoiceBuilder $choices,
       
Tag $tag,
       
Compiler\Syntax\AbstractSyntax $child,
       
Compiler $compiler,
        array
$context,
       
$allowDependent = false
   
)
    {
       
$rawContext = $context;
       
$rawContext['escape'] = false;

        if (
$this->isNamedTag($child, 'option'))
        {
           
/** @var $child Tag */
           
$optionTag = $this->compileAttributesAsArray($child->attributes, $compiler, $context,
                [
'label', 'hint'], ['selected']
            );
           
$this->compileOptionChildren($child->children, $compiler, $context, $optionTag, $allowDependent);
           
$choices->handleOptionTag($optionTag);

            return
true;
        }

        if (
$this->isNamedTag($child, 'options'))
        {
           
/** @var $child Tag */
           
$child->assertAttribute('source');

           
$choices->handleOptionsTag(
               
$compiler->compileForcedExpression($child->attributes['source'], $rawContext)
            );

            return
true;
        }

        if (
$this->isNamedTag($child, 'optgroup'))
        {
            if (
$choices->inOptGroup())
            {
                throw
$child->exception(\XF::phrase('optgroup_tags_cannot_be_nested'));
            }

           
/** @var $child Tag */
           
$optgroupAttributes = $this->compileAttributesAsArray($child->attributes, $compiler, $context);
           
$choices->startOptGroup($optgroupAttributes);

            foreach (
$child->children AS $grandchild)
            {
               
$success = $this->compileExpectedChoice($choices, $tag, $grandchild, $compiler, $context, $allowDependent);
                if (!
$success)
                {
                    throw
$child->exception(\XF::phrase('tag_x_contains_unexpected_child_element', ['name' => $child->name]));
                }
            }

           
$choices->endOptGroup();

            return
true;
        }

        if (
$this->isNamedTag($child, 'if'))
        {
           
/** @var $child Tag */

           
$choices->forceTempVariable();

           
$callback = function($type, array $elements) use ($child, $choices, $tag, $compiler, $context, $allowDependent)
            {
                if (
$type == 'contentcheck')
                {
                    throw
$child->exception(\XF::phrase('contentcheck_based_if_tags_not_supported_with_choice_based_tags'));
                }

                foreach (
$elements AS $element)
                {
                   
$success = $this->compileExpectedChoice($choices, $tag, $element, $compiler, $context, $allowDependent);
                    if (!
$success)
                    {
                        throw
$child->exception(\XF::phrase('tag_x_contains_unexpected_child_element', ['name' => $child->name]));
                    }
                }
            };

           
/** @var IfTag $handler */
           
$handler = $child->getTag($compiler);
           
$handler->compileToCallback($child, $compiler, $context, $callback);

            return
true;
        }

        if (
$this->isNamedTag($child, 'foreach'))
        {
           
/** @var $child Tag */

           
$choices->forceTempVariable();

           
$callback = function($type, array $elements) use ($child, $choices, $tag, $compiler, $context, $allowDependent)
            {
                foreach (
$elements AS $element)
                {
                   
$success = $this->compileExpectedChoice($choices, $tag, $element, $compiler, $context, $allowDependent);
                    if (!
$success)
                    {
                        throw
$child->exception(\XF::phrase('tag_x_contains_unexpected_child_element', ['name' => $child->name]));
                    }
                }
            };

           
/** @var ForeachTag $handler */
           
$handler = $child->getTag($compiler);
           
$handler->compileToCallback($child, $compiler, $context, $callback);

            return
true;
        }

        if (
$this->isNamedTag($child, 'set'))
        {
           
/** @var $child Tag */

           
$choices->forceTempVariable();

           
/** @var Set $handler */
           
$handler = $child->getTag($compiler);
           
$handler->compile($child, $compiler, $context, false);

            return
true;
        }

        if (
$this->isEmptyString($child))
        {
           
// ok, ignore
           
return true;
        }

        return
false;
    }

    public function
compileOptionChildren(array $children, Compiler $compiler, array $context, array &$optionTag, $allowDependent = false)
    {
        if (empty(
$optionTag['label']) && $children)
        {
           
$hasTag = false;
            foreach (
$children AS $child)
            {
                if (
$child instanceof Tag)
                {
                   
$hasTag = true;
                    break;
                }
            }

            if (!
$hasTag)
            {
               
// children are automatically the label if there are no other tags within
               
$optionTag['label'] = $compiler->compileToArraySyntax($children, 'label', $context);
                return;
            }
        }

       
/** @var $children Compiler\Syntax\AbstractSyntax[] */
       
foreach ($children AS $child)
        {
            if (
$this->isNamedTag($child, ['label', 'hint', 'html', 'afterhint', 'afterhtml']))
            {
               
/** @var $child Tag */
               
$optionTag[$child->name] = $compiler->compileToArraySyntax($child->children, $child->name, $context);
                continue;
            }

            if (
$allowDependent)
            {
                if (
$this->isNamedTag($child, [
                   
'assetupload', 'checkbox', 'dateinput', 'macro', 'radio', 'select',
                   
'textarea', 'textbox', 'tokeninput', 'numberbox', 'upload'
               
]))
                {
                   
/** @var $child Tag */
                   
$optionTag['_dependent'][] = $child->compile($compiler, $context, true);
                    continue;
                }

                if (
$this->isNamedTag($child, 'dependent'))
                {
                   
/** @var $child Tag */
                   
$optionTag['_dependent'][] = $compiler->compileInlineList($child->children, $context);
                    continue;
                }
            }

            if (
$this->isEmptyString($child))
            {
               
// ok, ignore
               
continue;
            }

            throw
$child->exception(\XF::phrase('option_tag_contains_unexpected_child_element'));
        }
    }

    public function
getFinalChoiceElementCode(
       
$functionBaseName,
       
$isRowLevel,
       
Compiler $compiler,
        array
$controlOptions,
       
Compiler\ChoiceBuilder $choices,
        array
$rowOptions
   
)
    {
       
$indent = $compiler->indent();
       
$rowOptionCode = "array(" . implode('', $rowOptions)  . "\n$indent)";
       
$controlOptionCode = "array(" . implode('', $controlOptions) . "\n$indent)";
       
$optionsCode = $choices->toInline();

        if (
$isRowLevel)
        {
            return
"{$compiler->templaterVariable}->form{$functionBaseName}Row($controlOptionCode, $optionsCode, $rowOptionCode)";
        }
        else
        {
            return
"{$compiler->templaterVariable}->form{$functionBaseName}($controlOptionCode, $optionsCode)";
        }
    }

    public function
getFinalInputElementCode(
       
$functionBaseName,
       
$isRowLevel,
       
Compiler $compiler,
        array
$controlOptions,
        array
$rowOptions
   
)
    {
       
$indent = $compiler->indent();
       
$rowOptionCode = "array(" . implode('', $rowOptions)  . "\n$indent)";
       
$controlOptionCode = "array(" . implode('', $controlOptions) . "\n$indent)";

        if (
$isRowLevel)
        {
            return
"{$compiler->templaterVariable}->form{$functionBaseName}Row($controlOptionCode, $rowOptionCode)";
        }
        else
        {
            return
"{$compiler->templaterVariable}->form{$functionBaseName}($controlOptionCode)";
        }
    }
}