qcubed/framework

View on GitHub
includes/base_controls/QCheckBoxLegacyColumn.class.php

Summary

Maintainability
A
2 hrs
Test Coverage
<?php
/*****
 * A QDataGridLegacyColumn that contains checkboxes
 * Inspired by Hunter Jensen's work at <http://www.qcodo.com/forums/topic.php/3267>
 * 
 * @author Ryan Peters
 * @copyright ICOM Productions 2010
 * @license MIT
 * @name QCheckBoxColumn
 */

class QCheckBoxLegacyColumn extends QDataGridLegacyColumn
{
    protected $objDataGrid;
    protected $blnHtmlEntities = false;
    protected $chkSelectAll;
    protected $colIndex = -1;
    protected $objCheckboxCallback = null;
    protected $strCheckboxCallbackFunc = null;
    protected $strPrimaryKey = 'Id';

    /**
     * Creates a QDataGridLegacyColumn of checkboxes
     *
     * @param string    $strName               The header to give the column, shown as the label for the Select All checkbox
     * @param QDataGridLegacy $dataGrid              The parent DataGrid. This does not add the column to that datagrid
     * @param mixed     $objOverrideParameters Same as QDataGridLegacy
     */
    public function __construct($strName = '', QDataGridLegacy $dataGrid, $objOverrideParameters = null)
    {
        $this->objDataGrid = $dataGrid;
        
        $arrParentArgs = func_get_args();
        
        //change the QDataGridLegacy argument we get, and pass the parent constructor the HTML parameter it expects
        $arrParentArgs[1] = '<?=$_COLUMN->chkSelected_Render($_ITEM) ?>';
        if (version_compare(PHP_VERSION, '5.1.6', '>=')) 
            return call_user_func_array(array($this, 'QDataGridLegacyColumn::__construct'), $arrParentArgs);
        else
        {
            $parent_class=get_parent_class($this);
            return call_user_func_array(array($parent_class, '__construct'), $arrParentArgs);
        }
    }
    
    /**
     * Returns the index of this column in the parent datagrid
     *
     * @return int The index of the column, or -1 if not found
     *
     */
    protected function GetColIndex()
    {
        //cached, to improve performance
        if($this->colIndex == -1)
        {
            $columns = $this->objDataGrid->GetAllColumns();
            foreach($columns as $index=>$col)
                //=== so that we don't spent CPU time comparing properties, and nesting too deep
                if($col === $this)
                    $this->colIndex = $index;    
        }
        return $this->colIndex;
    }
    
    
    /**
     * Sets the callback method for when a checkbox is created. Used for initializing the checkbox state
     *
     * @param Object $objParent The object the callback function belongs to
     * @param string $strFuncName The name of the function to call back
     * @return void
     *
     */
    public function SetCheckboxCallback($objParent, $strFuncName)
    {
        $this->objCheckboxCallback = $objParent;
        $this->strCheckboxCallbackFunc = $strFuncName;
    }
    
    // Render the Select All checkbox to be displayed in the datagrid header row
    public function chkSelectAll_Render($blnWithLabel = false) {
        $colIndex = $this->GetColIndex();
        
        $controlId = 'chkSelectAll' . $colIndex . $this->objDataGrid->ControlId ;
        
        $this->chkSelectAll = $this->objDataGrid->GetChildControl($controlId);
        
        if(null === $this->chkSelectAll) {
            
            $this->chkSelectAll = new QCheckBox($this->objDataGrid, $controlId);
            $this->chkSelectAll->Name = QApplication::Translate('Select All');
            
            $colIndex = $this->GetColIndex();
            $strControlIdStart = 'chkSelect' . $colIndex.$this->objDataGrid->ControlId.'n';
            $strControlIdStartLen = strlen($strControlIdStart);
            
            //Since a QDataGridLegacyColumn isn't a control, we can't include external js files, or have EndScripts
            //so we'll just have to include all the code in the onclick itself
            //hopefully this won't result in much duplication, since there shouldn't be too many
            //of these on a single form
            $strJavascript = "var datagrid = document.getElementById('{$this->objDataGrid->ControlId}');var selectAll = document.getElementById('{$this->chkSelectAll->ControlId}');var childInputs = datagrid.getElementsByTagName('input');for(var i = 0; i < childInputs.length; i++){var subid = childInputs[i].id.substring($strControlIdStartLen, 0);if(subid == '$strControlIdStart')childInputs[i].checked = selectAll.checked;}";
            
            $this->chkSelectAll->AddAction(new QClickEvent(), new QJavaScriptAction($strJavascript));
        }
        
        $strOutput = $this->chkSelectAll->Render(false);
        
        if ($blnWithLabel) {
            $strOutput = QHtml::RenderTag('label', null,  $this->strName . ' ' . $strOutput);
        }
        
        return $strOutput;
    }
    
    public function chkSelected_Render($_ITEM) {
        $intId = $_ITEM->{$this->strPrimaryKey};
        $colIndex = $this->GetColIndex();
        $strControlId = 'chkSelect' . $colIndex . $this->objDataGrid->ControlId . 'n' . $intId;
        
        //Don't re-render an existing checkbox
        $chkSelected = $this->objDataGrid->GetChildControl($strControlId);
        if (!$chkSelected) {
            $chkSelected = new QCheckBox($this->objDataGrid, $strControlId);
            //callback so the creator can set up the checkbox checked state
            if(null !== $this->objCheckboxCallback && null !== $this->strCheckboxCallbackFunc)
            {
                $funcName = $this->strCheckboxCallbackFunc;
                $this->objCheckboxCallback->$funcName($_ITEM, $chkSelected);
            }
            
            //remember the Item ID this checkbox is for and it's original state
            $chkSelected->ActionParameter = $intId.','.($chkSelected->Checked?1:0);
        }
        return $chkSelected->Render(false);
    }

    /**
     * Convert a checkbox id to its corresponding item id
     *
     * @param $strCheckId
     *
     * @return string
     */
    public function chkIdToItemId ($strCheckId) {
        $colIndex = $this->GetColIndex();
        $strControlIdPrefix = 'chkSelect' . $colIndex . $this->objDataGrid->ControlId . 'n';
        
        $len = strlen ($strControlIdPrefix);
        return substr ($strCheckId, $len);
    }

    /**
     * Returns an array of items for the selected rows, loaded using the specified class's Load($id) method
     *
     * @param string $strClass   The class name of the object type to return
     * @param bool   $blnIndex   Whether to spend extra time indexing the array by Id
     * @param null   $objClauses QQClause expression
     *
     * @return array An array of selected Items
     */
    public function GetSelectedItems($strClass, $blnIndex = true, $objClauses = null)
    {
        $itemIds = $this->GetSelectedIds();
        
        //load these items, using QQ::In so that it's a single DB hit
        $idQQNode = QQN::$strClass()->{$this->strPrimaryKey};
        $conditions = QQ::In($idQQNode, $itemIds);
        $items = call_user_func(array($strClass, 'QueryArray'), $conditions, $objClauses);
        
        //Use the item's Id as the index, if desired.
        if($blnIndex)
        {
            $newitems = array();
            foreach($items as $item)
                $newitems[$item->{$this->strPrimaryKey}] = $item;
            return $newitems;
        }
        
        return $items;
    }
    
    /**
     * Returns an array of the Ids of the items selected. Note this only includes rendered controls.
     *
     * @return array An array of selected Ids
     *
     */
    public function GetSelectedIds()
    {
        //because of formstate, this will even include ones not currently displayed.
        $childControls = $this->objDataGrid->GetChildControls();
        
        $colIndex = $this->GetColIndex();
        $strSubId = 'chkSelect' . $colIndex.$this->objDataGrid->ControlId .'n';
        
        $itemIds = array();
        foreach ($childControls as $objControl) {
            //if it's a checkbox for this column
            if ($objControl instanceof QCheckBox && substr($objControl->ControlId, 0, strlen($strSubId)) == $strSubId) {
                if ($objControl->Checked) {
                    $arrParams = explode(',', $objControl->ActionParameter);
                    $id = $arrParams[0];
                    $itemIds[$id] = $id;
                }
            }
        }
        return $itemIds;
    }


    /**
     * Returns an array of changed ids.
     *
     * @param bool $blnRemember
     *
     * @return bool[] Key is the object Id, value is the new check state (bln)
     */
    public function GetChangedIds($blnRemember = false)
    {
        //because of formstate, this will even include ones not currently displayed.
        $childControls = $this->objDataGrid->GetChildControls();
        
        $colIndex = $this->GetColIndex();
        $strSubId = 'chkSelect' . $colIndex.$this->objDataGrid->ControlId .'n';
        
        $itemIds = array();
        foreach ($childControls as $objControl) 
        {
            //if it's a checkbox for this column
            if($objControl instanceof QCheckBox && substr($objControl->ControlId, 0, strlen($strSubId)) == $strSubId)
            {
                $arrParams = explode(',',$objControl->ActionParameter);
                $id = $arrParams[0];
                $wasChecked = $arrParams[1] == 1;
                if($wasChecked != $objControl->Checked)
                    $itemIds[$id] = $objControl->Checked;
                if($blnRemember)
                    $objControl->ActionParameter = $id.','.($objControl->Checked?1:0);
            }
        }
        
        return $itemIds;
    }
    
    /**
     * Considers the current state to be the new baseline
     *
     * @return void
     *
     */
    public function AcceptChanges()
    {
        $this->GetChangedIds(true);
    }
    
    public function SetSelectAllCheckbox($value)
    {
        $colIndex = $this->GetColIndex();
        
        $controlId = 'chkSelectAll' . $colIndex.$this->objDataGrid->ControlId ;
        $checkbox = $this->objDataGrid->GetChildControl($controlId);
        if(null === $checkbox)
            throw new exception('Select All Checkbox not found');
        $checkbox->Checked = $value;
    }
    
    public function SetCheckbox($itemId, $value)
    {
        $colIndex = $this->GetColIndex();
        $controlId = 'chkSelect' . $colIndex.$this->objDataGrid->ControlId .'n'.$itemId;
        $checkbox = $this->objDataGrid->GetChildControl($controlId);
        if(null === $checkbox)
            return;
        $checkbox->Checked = $value;
    }
    
    public function __get($strName) {
        switch ($strName) {
            case "PrimaryKey": 
                return $this->strPrimaryKey;
            default:
            try {
                return parent::__get($strName);
            } catch (QCallerException $objExc) {
                $objExc->IncrementOffset();
                throw $objExc;
            }
        }
    }

    /**
     * Override method to perform a property "Set"
     * This will set the property $strName to be $mixValue
     *
     * @param string $strName  Name of the property to set
     * @param string $mixValue New value of the property
     *
     * @return mixed
     * @throws Exception|QCallerException
     */
    public function __set($strName, $mixValue) {
        switch ($strName) {
            ///////////////////
            // Member Variables
            ///////////////////
            case 'PrimaryKey':
                /**
                 * Sets the value for strPrimaryKey 
                 * @param integer $mixValue
                 * @return string
                 */
                try {
                    return ($this->strPrimaryKey = QType::Cast($mixValue, QType::String));
                } catch (QCallerException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }
            default:
                try {
                    return parent::__set($strName, $mixValue);
                } catch (QCallerException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
            }
        }
    }

}