<commit_before><?php

/**
 * This class provides a number of reuseable static methods which Admin Controllers can make use of
 *
 * @package     Nails
 * @subpackage  module-admin
 * @category    Helper
 * @author      Nails Dev Team
 * @link
 */

namespace Nails\Admin;

use Nails\Common\Exception\ViewNotFoundException;
use Nails\Factory;

class Helper
{
    protected static $aHeaderButtons = [];

    // --------------------------------------------------------------------------

    /**
     * Loads a view in admin taking into account the module being accessed. Passes controller
     * data and optionally loads the header and footer views.
     *
     * @param  string  $sViewFile      The view to load
     * @param  boolean $bLoadStructure Whether or not to include the header and footers in the output
     * @param  boolean $bReturnView    Whether to return the view or send it to the Output class
     *
     * @return mixed                  String when $bReturnView is true, void otherwise
     * @throws \Nails\Common\Exception\FactoryException
     */
    public static function loadView($sViewFile, $bLoadStructure = true, $bReturnView = false)
    {
        $aData  =& getControllerData();
        $oInput = Factory::service('Input');
        $oView  = Factory::service('View');

        //  Are we in a modal?
        if ($oInput->get('isModal')) {

            if (!isset($aData['headerOverride']) && !isset($aData['isModal'])) {
                $aData['isModal'] = true;
            }

            if (empty($aData['headerOverride'])) {
                $aData['headerOverride'] = '_components/structure/headerBlank';
            }

            if (empty($aData['footerOverride'])) {
                $aData['footerOverride'] = '_components/structure/footerBlank';
            }
        }

        $aHeaderViews = array_filter([
            $bLoadStructure && !empty($aData['headerOverride']) ? $aData['headerOverride'] : '',
            $bLoadStructure && empty($aData['headerOverride']) ? '_components/structure/header' : '',
        ]);
        $sHeaderView  = reset($aHeaderViews);

        $aFooterViews = array_filter([
            $bLoadStructure && !empty($aData['footerOverride']) ? $aData['footerOverride'] : '',
            $bLoadStructure && empty($aData['footerOverride']) ? '_components/structure/footer' : '',
        ]);
        $sFooterView  = reset($aFooterViews);

        if ($bReturnView) {
            $sOut = $oView->load($sHeaderView, $aData, true);
            $sOut .= static::loadInlineView($sViewFile, $aData, true);
            $sOut .= $oView->load($sFooterView, $aData, true);
            return $sOut;
        } else {
            $oView->load($sHeaderView, $aData);
            static::loadInlineView($sViewFile, $aData);
            $oView->load($sFooterView, $aData);
        }
    }

    // --------------------------------------------------------------------------

    /**
     * Generates a CSV and sends to the browser, if a filename is given then it's
     * sent as a download
     *
     * @param  mixed   $mData      The data to render, either an array or a DB query object
     * @param  string  $sFilename  The filename to give the file if downloading
     * @param  boolean $bHeaderRow The first element in the $mData resultset is a header row
     *
     * @return void
     * @throws \Nails\Common\Exception\FactoryException
     */
    public static function loadCsv($mData, $sFilename = '', $bHeaderRow = true)
    {
        //  Determine what type of data has been supplied
        if (is_array($mData) || get_class($mData) == 'CI_DB_mysqli_result') {

            //  If filename has been specified then set some additional headers
            if (!empty($sFilename)) {

                $oInput  = Factory::service('Input');
                $oOutput = Factory::service('Output');

                //  Common headers
                $oOutput->set_content_type('text/csv');
                $oOutput->set_header('Content-Disposition: attachment; filename="' . $sFilename . '"');
                $oOutput->set_header('Expires: 0');
                $oOutput->set_header("Content-Transfer-Encoding: binary");

                //  Handle IE, classic.
                $userAgent = $oInput->server('HTTP_USER_AGENT');

                if (strpos($userAgent, "MSIE") !== false) {

                    $oOutput->set_header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
                    $oOutput->set_header('Pragma: public');

                } else {

                    $oOutput->set_header('Pragma: no-cache');
                }
            }

            //  Not using self::loadInlineView() as this may be called from many contexts
            $oView = Factory::service('View');
            if (is_array($mData)) {
                $oView->load('admin/_components/csv/array', ['data' => $mData, 'header' => $bHeaderRow]);
            } elseif (get_class($mData) == 'CI_DB_mysqli_result') {
                $oView->load('admin/_components/csv/dbResult', ['data' => $mData, 'header' => $bHeaderRow]);
            }

        } else {

            $subject = 'Unsupported object type passed to ' . get_class() . '::loadCSV';
            $message = 'An unsupported object was passed to ' . get_class() . '::loadCSV. A CSV ';
            $message .= 'file could not be generated. Details are shown below:<br /><br />' . print_r($mData, true);

            showFatalError($subject, $message);
        }
    }

    // --------------------------------------------------------------------------

    /**
     * Load a single view taking into account the module being accessed.
     *
     * @param  string  $sViewFile   The view to load
     * @param  array   $aViewData   The data to pass to the view
     * @param  boolean $bReturnView Whether to return the view or send it to the Output class
     *
     * @throws \Exception
     *
     * @return mixed               String when $bReturnView is true, void otherwise
     */
    public static function loadInlineView($sViewFile, $aViewData = [], $bReturnView = false)
    {
        $aCtrlData   =& getControllerData();
        $sCtrlPath   = !empty($aCtrlData['currentRequest']['path']) ? $aCtrlData['currentRequest']['path'] : '';
        $sCtrlName   = basename($sCtrlPath, '.php');
        $aCtrlPath   = explode(DIRECTORY_SEPARATOR, $sCtrlPath);
        $aCtrlPath   = array_splice($aCtrlPath, 0, count($aCtrlPath) - 2);
        $aCtrlPath[] = 'views';
        $aCtrlPath[] = $sCtrlName;
        $aCtrlPath[] = $sViewFile;
        $sViewPath   = implode(DIRECTORY_SEPARATOR, $aCtrlPath) . '.php';

        $oView = Factory::service('View');

        //  Load the view
        try {
            return $oView->load($sViewPath, $aViewData, $bReturnView);
        } catch (ViewNotFoundException $e) {
            //  If it fails, and the controller is a default admin controller then
            //  load up that view
            $aParentClasses = class_parents($aCtrlData['currentRequest']['className']);
            if (!in_array('Nails\\Admin\\Controller\\DefaultController', $aParentClasses)) {
                throw new ViewNotFoundException(
                    $e->getMessage(),
                    $e->getCode()
                );
            }

            return $oView->load('admin/DefaultController/' . $sViewFile, $aViewData, $bReturnView);
        }
    }

    // --------------------------------------------------------------------------

    /**
     * Loads the admin "search" component
     *
     * @param  \stdClass $oSearchObj  An object as created by self::searchObject();
     * @param  boolean   $bReturnView Whether to return the view to the caller, or output to the browser
     *
     * @return mixed                  String when $bReturnView is true, void otherwise
     * @throws \Nails\Common\Exception\FactoryException
     */
    public static function loadSearch($oSearchObj, $bReturnView = true)
    {
        $aData = [
            'searchable'     => isset($oSearchObj->searchable) ? $oSearchObj->searchable : true,
            'sortColumns'    => isset($oSearchObj->sortColumns) ? $oSearchObj->sortColumns : [],
            'sortOn'         => isset($oSearchObj->sortOn) ? $oSearchObj->sortOn : null,<commit_msg>Improved handling of extended default controller views<commit_after><?php

/**
 * This class provides a number of reuseable static methods which Admin Controllers can make use of
 *
 * @package     Nails
 * @subpackage  module-admin
 * @category    Helper
 * @author      Nails Dev Team
 * @link
 */

namespace Nails\Admin;

use Nails\Common\Exception\ViewNotFoundException;
use Nails\Factory;

class Helper
{
    protected static $aHeaderButtons = [];

    // --------------------------------------------------------------------------

    /**
     * Loads a view in admin taking into account the module being accessed. Passes controller
     * data and optionally loads the header and footer views.
     *
     * @param  string  $sViewFile      The view to load
     * @param  boolean $bLoadStructure Whether or not to include the header and footers in the output
     * @param  boolean $bReturnView    Whether to return the view or send it to the Output class
     *
     * @return mixed                  String when $bReturnView is true, void otherwise
     * @throws \Nails\Common\Exception\FactoryException
     */
    public static function loadView($sViewFile, $bLoadStructure = true, $bReturnView = false)
    {
        $aData  =& getControllerData();
        $oInput = Factory::service('Input');
        $oView  = Factory::service('View');

        //  Are we in a modal?
        if ($oInput->get('isModal')) {

            if (!isset($aData['headerOverride']) && !isset($aData['isModal'])) {
                $aData['isModal'] = true;
            }

            if (empty($aData['headerOverride'])) {
                $aData['headerOverride'] = '_components/structure/headerBlank';
            }

            if (empty($aData['footerOverride'])) {
                $aData['footerOverride'] = '_components/structure/footerBlank';
            }
        }

        $aHeaderViews = array_filter([
            $bLoadStructure && !empty($aData['headerOverride']) ? $aData['headerOverride'] : '',
            $bLoadStructure && empty($aData['headerOverride']) ? '_components/structure/header' : '',
        ]);
        $sHeaderView  = reset($aHeaderViews);

        $aFooterViews = array_filter([
            $bLoadStructure && !empty($aData['footerOverride']) ? $aData['footerOverride'] : '',
            $bLoadStructure && empty($aData['footerOverride']) ? '_components/structure/footer' : '',
        ]);
        $sFooterView  = reset($aFooterViews);

        if ($bReturnView) {
            $sOut = $oView->load($sHeaderView, $aData, true);
            $sOut .= static::loadInlineView($sViewFile, $aData, true);
            $sOut .= $oView->load($sFooterView, $aData, true);
            return $sOut;
        } else {
            $oView->load($sHeaderView, $aData);
            static::loadInlineView($sViewFile, $aData);
            $oView->load($sFooterView, $aData);
        }
    }

    // --------------------------------------------------------------------------

    /**
     * Generates a CSV and sends to the browser, if a filename is given then it's
     * sent as a download
     *
     * @param  mixed   $mData      The data to render, either an array or a DB query object
     * @param  string  $sFilename  The filename to give the file if downloading
     * @param  boolean $bHeaderRow The first element in the $mData resultset is a header row
     *
     * @return void
     * @throws \Nails\Common\Exception\FactoryException
     */
    public static function loadCsv($mData, $sFilename = '', $bHeaderRow = true)
    {
        //  Determine what type of data has been supplied
        if (is_array($mData) || get_class($mData) == 'CI_DB_mysqli_result') {

            //  If filename has been specified then set some additional headers
            if (!empty($sFilename)) {

                $oInput  = Factory::service('Input');
                $oOutput = Factory::service('Output');

                //  Common headers
                $oOutput->set_content_type('text/csv');
                $oOutput->set_header('Content-Disposition: attachment; filename="' . $sFilename . '"');
                $oOutput->set_header('Expires: 0');
                $oOutput->set_header("Content-Transfer-Encoding: binary");

                //  Handle IE, classic.
                $userAgent = $oInput->server('HTTP_USER_AGENT');

                if (strpos($userAgent, "MSIE") !== false) {

                    $oOutput->set_header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
                    $oOutput->set_header('Pragma: public');

                } else {

                    $oOutput->set_header('Pragma: no-cache');
                }
            }

            //  Not using self::loadInlineView() as this may be called from many contexts
            $oView = Factory::service('View');
            if (is_array($mData)) {
                $oView->load('admin/_components/csv/array', ['data' => $mData, 'header' => $bHeaderRow]);
            } elseif (get_class($mData) == 'CI_DB_mysqli_result') {
                $oView->load('admin/_components/csv/dbResult', ['data' => $mData, 'header' => $bHeaderRow]);
            }

        } else {

            $subject = 'Unsupported object type passed to ' . get_class() . '::loadCSV';
            $message = 'An unsupported object was passed to ' . get_class() . '::loadCSV. A CSV ';
            $message .= 'file could not be generated. Details are shown below:<br /><br />' . print_r($mData, true);

            showFatalError($subject, $message);
        }
    }

    // --------------------------------------------------------------------------

    /**
     * Load a single view taking into account the module being accessed.
     *
     * @param  string  $sViewFile   The view to load
     * @param  array   $aViewData   The data to pass to the view
     * @param  boolean $bReturnView Whether to return the view or send it to the Output class
     *
     * @throws \Exception
     *
     * @return mixed               String when $bReturnView is true, void otherwise
     */
    public static function loadInlineView($sViewFile, $aViewData = [], $bReturnView = false)
    {
        $aCtrlData   =& getControllerData();
        $sCtrlPath   = !empty($aCtrlData['currentRequest']['path']) ? $aCtrlData['currentRequest']['path'] : '';
        $sCtrlName   = basename($sCtrlPath, '.php');
        $aCtrlPath   = explode(DIRECTORY_SEPARATOR, $sCtrlPath);
        $aCtrlPath   = array_splice($aCtrlPath, 0, count($aCtrlPath) - 2);
        $aCtrlPath[] = 'views';
        $aCtrlPath[] = $sCtrlName;
        $aCtrlPath[] = $sViewFile;
        $sViewPath   = implode(DIRECTORY_SEPARATOR, $aCtrlPath) . '.php';

        $oView = Factory::service('View');

        //  Load the view
        try {
            return $oView->load($sViewPath, $aViewData, $bReturnView);
        } catch (ViewNotFoundException $e) {
            //  If it fails, and the controller is a default admin controller then load up that view
            $sClassName = $aCtrlData['currentRequest']['className'];
            if (!classExtends($sClassName, 'Nails\\Admin\\Controller\\DefaultController')) {
                throw new ViewNotFoundException(
                    $e->getMessage(),
                    $e->getCode()
                );
            }

            //  Step through the class hierarchy and look there
            $aParents = class_parents($sClassName);
            foreach ($aParents as $sParent) {
                try {

                    if ($sParent !== 'Nails\\Admin\\Controller\\DefaultController') {

                        $oReflection = new \ReflectionClass('\\' . $sParent);
                        $sViewPath   = realpath(dirname($oReflection->getFileName()) . '/../views') . '/';
                        $aClassBits  = explode('\\', $oReflection->getName());
                        $sViewPath   .= end($aClassBits) . '/';

                    } else {
                        $sViewPath     = 'admin/DefaultController/';
                        $bTriedDefault = true;
                    };

                    return $oView->load(
                        $sViewPath . $sViewFile,
                        $aViewData,
                        $bReturnView
                    );
                } catch (ViewNotFoundException $e) {
                    //  Allow the loop to continue, unless we've already tried the default views
                    if (!empty($bTriedDefault)) {
                        throw $e;
                    }
                }
            }
        }
    }

    // --------------------------------------------------------------------------

    /**
     * Loads the admin "search" component
     *
     * @param  \stdClass $oSearchObj  An object as created by self::searchObject();
     * @param  boolean   $bReturnView Whether to return the view to the caller, or output to the browser
     *
     * @return mixed                  String when $bReturnView is true, void otherwise
     * @throws \Nails\Common\Exception\FactoryException
     */
    public static function loadSearch($oSearchObj, $bReturnView = true)
    {
        $aData = [
            'searchable'     => isset($oSearchObj->searchable) ? $oSearchObj->searchable : true,
            'sortColumns'    => isset($oSearchObj->sortColumns) ? $oSearchObj->sortColumns : [],
            'sortOn'         => isset($oSearchObj->sortOn) ? $oSearchObj->sortOn : null,
{'fraction_non_alphanumeric': 0.15678301140856207, 'fraction_numerical': 0.0005647803004631198, 'mean_word_length': 1.853206574282952, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 73, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1723237', 'n_tokens_mistral': 4837, 'n_tokens_neox': 4683, 'n_words': 1194}