Seditio Source
Root |
./othercms/ips_4.3.4/system/Helpers/Form/Matrix.php
<?php
/**
 * @brief        Matrix Builder
 * @author        <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
 * @copyright    (c) Invision Power Services, Inc.
 * @license        https://www.invisioncommunity.com/legal/standards/
 * @package        Invision Community
 * @since        26 Feb 2013
 */

namespace IPS\Helpers\Form;

/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
   
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
    exit;
}

/**
 * Matrix Builder
 * @code
$matrix = new \IPS\Helpers\Form\Matrix;

$matrix->columns = array(
    'foo'    => function( $key, $value, $data )
    {
        return new \IPS\Helpers\Form\Text( $key, $value );
    },
    //...
);

$matrix->rows = array(
    0    => array(
        'foo'    => TRUE,
        // ...
    ),
    // ...
)

if ( $values = $matrix->values() )
{

}

\IPS\Output::i()->output = $matrix;
 * @endcode
 */
class _Matrix extends \IPS\Helpers\Form
{
   
/**
     * @brief    Input Elements array
     */
   
public $elements = NULL;
   
   
/**
     * @brief    Columns array
     */
   
public $columns = array();
   
   
/**
     * @brief    Widths
     */
   
public $widths = array();
   
   
/**
     * @brief    Columns to have "check all" checkboxes
     */
   
public $checkAlls = array();
   
   
/**
     * @brief    Should rows have all/none toggles?
     */
   
public $checkAllRows = FALSE;
   
   
/**
     * @brief    Rows array
     */
   
public $rows = array();
   
   
/**
     * @brief    Manageable? (Rows can be added and deleted)
     */
   
public $manageable = TRUE;

   
/**
     * @brief    Squash fields? (values in the matrix are json_encode'd as a single value to get around max_post_vars limits)
     */
   
public $squashFields = TRUE;
   
   
/**
     * @brief    Added rows
     * @see        \IPS\Helpers\Form\Matrix::values
     */
   
public $addedRows = array();
   
   
/**
     * @brief    Changed rows
     * @see        \IPS\Helpers\Form\Matrix::values
     */
   
public $changedRows = array();
   
   
/**
     * @brief    Removed rows
     * @see        \IPS\Helpers\Form\Matrix::elements
     */
   
public $removedRows = array();
   
   
/**
     * @brief    Prefix to add to the language keys used for column headers
     */
   
public $langPrefix = '';

   
/**
     * @brief    Classnames to add to the table within the matrix
     */
   
public $classes = array();

   
/**
     * @brief    Show tooltips in each cell?
     */
   
public $showTooltips = FALSE;

   
/**
     * @brief    Form Id, Set it Matrix is part of a form
     */
   
public $formId = NULL;
   
   
/**
     * Get HTML
     *
     * @return    string
     */
   
public function __toString()
    {
        try
        {            
            return \
IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->matrix( $this->id, array_keys( $this->columns ), $this->elements(), $this->action, $this->hiddenValues, $this->actionButtons, $this->langPrefix, $this->calculateWidths(), $this->manageable, $this->checkAlls, $this->checkAllRows, $this->classes, $this->showTooltips, $this->squashFields );
        }
        catch ( \
Exception $e )
        {
            \
IPS\IPS::exceptionHandler( $e );
        }
        catch ( \
Throwable $e )
        {
            \
IPS\IPS::exceptionHandler( $e );
        }
    }
   
   
/**
     * Get Nested HTML
     *
     * @return    string
     */
   
public function nested()
    {        
        return \
IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->matrixNested( $this->id, array_keys( $this->columns ), $this->elements(), $this->action, $this->hiddenValues, $this->actionButtons, $this->langPrefix, $this->calculateWidths(), $this->manageable, $this->checkAlls, $this->checkAllRows, $this->classes, $this->showTooltips, $this->squashFields );
    }
   
   
/**
     * Calculate widths
     *
     * @return    array
     */
   
protected function calculateWidths()
    {
       
$widths = $this->widths;
       
$width = ( ( 100 - array_sum( $this->widths ) ) / count( $this->columns ) );
        foreach (
array_keys( $this->columns ) as $c )
        {
            if ( !isset(
$widths[ $c ] ) )
            {
               
$widths[ $c ] = $width;
            }
        }
               
        return
$widths;
    }
   
   
/**
     * Get elements
     *
     * @param    $bool    Get values?
     * @return    array
     */
   
public function elements( $getValues=TRUE )
    {
        if (
$this->elements === NULL )
        {
           
/* Stuff about our form */
           
$name = "{$this->id}_submitted";
           
$formName = $this->formId ? "{$this->formId}_submitted" : NULL;
           
$matrixName = "{$this->id}_matrixRows";
           
$matrixValues = \IPS\Request::i()->$matrixName;

           
/* Loop our defined rows */
           
$this->elements = array();
            foreach (
$this->rows as $rowId => $data )
            {
               
/* Have we deleted this row? */
               
$deleteKey = "{$rowId}_delete";
                if (
$this->manageable and ( isset( \IPS\Request::i()->$name ) or ( $formName and isset( \IPS\Request::i()->$formName ) ) ) and ( isset( \IPS\Request::i()->$deleteKey ) or !isset( $matrixValues[ $rowId ] ) or !$matrixValues[ $rowId ] ) )
                {
                   
$this->removedRows[] = $rowId;
                    continue;
                }
               
               
/* Build the row */
               
$this->elements[ $rowId ] = $this->buildRow( $rowId, $data );
            }
                       
           
/* Create blank row */
           
if ( $this->manageable )
            {
               
$blankRow = $this->buildRow( "_new_[x]", NULL );
            }
                       
           
/* Look for added ones */
           
if ( isset( \IPS\Request::i()->_new_ ) )
            {
               
$i = 1;
                foreach ( \
IPS\Request::i()->_new_ as $newId => $data )
                {
                   
$added = TRUE;
                    if (
$newId === 'x_unlimited' )
                    {
                        continue;
                    }
                    elseif (
$newId === 'x' )
                    {
                       
$added = FALSE;
                        foreach (
$data as $k => $v )
                        {
                            if ( isset(
$blankRow[$k] ) and $v and $v !== $blankRow[$k]->value )
                            {
                               
$added = TRUE;
                               
$blankRow[$k] = $blankRow[$k]->getValue();
                            }
                        }
                    }
                   
                    if (
$added )
                    {
                       
/* Select lists can have user-supplied input, so look for that too */
                       
foreach( \IPS\Request::i() as $inputKey => $inputValue )
                        {
                            if(
$pos = mb_strpos( $inputKey, '_new_' ) AND $inputKey !== '_new_' )
                            {
                                if(
is_array( $inputValue ) AND isset( $inputValue[ $newId ] ) )
                                {
                                   
/* @note This previously used an array_merge() however this was causing elements to be overwritten with a blank value in some cases */
                                   
foreach( $inputValue[ $newId ] AS $key => $value )
                                    {
                                        if (
$value )
                                        {
                                           
$data[ $key ] = $value;
                                        }
                                    }
                                }
                            }
                        }

                       
$this->elements["_new_[{$i}]"] = $this->buildRow( "_new_[{$i}]", $data );
                       
$this->addedRows[] = "_new_[{$i}]";
                       
$i++;
                    }
                }
            }
                                   
           
/* Add blank one */
           
if ( $this->manageable and $getValues )
            {
               
$this->elements["_new_[x]"] = $blankRow;
            }
        }

        return
$this->elements;
    }
   
   
/**
     * Build Row
     *
     * @param    mixed    $rowId    Row identifier
     * @param    array    $data    Values
     * @return    array
     */
   
protected function buildRow( $rowId, $data )
    {
       
$row = array();
        if (
is_string( $data ) )
        {
            return
$data;
        }
        foreach (
$this->columns as $columnName => $columnData )
        {
           
$inputName = "{$rowId}[{$columnName}]";
               
           
/* Create using array */
           
if ( is_array( $columnData ) )
            {
               
$classname = '\IPS\Helpers\Form\\' . $columnData[0];
               
$row[ $columnName ] = new $classname(
                   
$inputName,
                    isset(
$data[ $columnName ] ) ? $data[ $columnName ] : ( isset( $columnData[1] ) ? $columnData[1] : NULL ),
                    isset(
$columnData[2] ) ? $columnData[2] : FALSE,
                    isset(
$columnData[3] ) ? $columnData[3] : array()
                    );
            }
           
/* Create using callback function */
           
else
            {
               
$row[ $columnName ] = call_user_func( $columnData, $inputName, isset( $data[ $columnName ] ) ? $data[ $columnName ] : NULL, $data );
            }
        }
        if ( isset(
$data['_level'] ) )
        {
           
$row['_level'] = $data['_level'];
        }

        return
$row;
    }
   
   
/**
     * Get submitted values
     *
     * @param    bool            $force    If true, wil not check if form was submitted (used for nested matrixes)
     * @return    array|FALSE        Array of field values or FALSE if the form has not been submitted or if there were validation errors
     */
   
public function values( $force=FALSE )
    {
       
$values = array();
               
       
$name = "{$this->id}_submitted";        
        if(
$force or ( isset( \IPS\Request::i()->$name ) and \IPS\Request::i()->csrfKey === \IPS\Session::i()->csrfKey ) )
        {
           
// Do we need to unsquash any values?
            // Squashed values are json_encoded by javascript to prevent us exceeding max_post_vars
           
$squashedField = $this->id . '_squashed';
           
           
// If 'squashedField' isn't in the request it might indicate the user didn't have JS enabled
           
if ( $this->squashFields && isset( \IPS\Request::i()->$squashedField ) )
            {
                if ( isset( \
IPS\Request::i()->$squashedField ) )
                {
                   
$unsquashed = json_decode( \IPS\Request::i()->$squashedField, TRUE );
                   
                    foreach(
$unsquashed as $key => $value )
                    {
                        \
IPS\Request::i()->$key = $value;
                    }
                }
            }

            foreach (
$this->elements( FALSE ) as $rowId => $columns )
            {
                if (
is_array( $columns ) )
                {
                    foreach (
$columns as $columnName => $element )
                    {
                       
/* If this was a ehader or something, skip it */
                       
if ( !is_object( $element ) )
                        {
                            continue;
                        }
                       
                       
/* Return FALSE on error */
                       
if( $element->error !== NULL )
                        {
                            return
FALSE;
                        }
                       
                       
/* If the "check all" box was checked, set it to TRUE */
                       
if ( $element instanceof Checkbox and isset( \IPS\Request::i()->__all[ $columnName ] ) )
                        {
                           
$element->value = TRUE;
                        }
                       
                       
/* If our element has an unlimited option, and that's set, set that. */
                       
if ( isset( $element->options['unlimited'] ) )
                        {
                           
$unlimitedKey = "{$rowId}[{$columnName}_unlimited]";
                            if (
$value = \IPS\Request::i()->valueFromArray( $unlimitedKey ) )
                            {
                               
$element->value = $value;
                            }
                        }
                                                                               
                       
/* Set value */
                       
$values[ $rowId ][ $columnName ] = $element->value;
                       
                       
/* Not if it's changed */
                       
if ( $element->value !== $element->defaultValue and mb_substr( $rowId, 0, 5 ) !== '_new_' )
                        {                    
                           
$this->changedRows[ $rowId ] = $rowId;
                        }
                    }
                }
            }
               
            return
$values;
        }
       
        return
FALSE;
    }
}