<?php

namespace PhpOffice\PhpSpreadsheet;

use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;

class ReferenceHelper
{
    /**    Constants                */
    /**    Regular Expressions      */
    const REFHELPER_REGEXP_CELLREF = '((\w*|\'[^!]*\')!)?(?<![:a-z\$])(\$?[a-z]{1,3}\$?\d+)(?=[^:!\d\'])';
    const REFHELPER_REGEXP_CELLRANGE = '((\w*|\'[^!]*\')!)?(\$?[a-z]{1,3}\$?\d+):(\$?[a-z]{1,3}\$?\d+)';
    const REFHELPER_REGEXP_ROWRANGE = '((\w*|\'[^!]*\')!)?(\$?\d+):(\$?\d+)';
    const REFHELPER_REGEXP_COLRANGE = '((\w*|\'[^!]*\')!)?(\$?[a-z]{1,3}):(\$?[a-z]{1,3})';

    /**
     * Instance of this class.
     *
     * @var ReferenceHelper
     */
    private static $instance;

    /**
     * Get an instance of this class.
     *
     * @return ReferenceHelper
     */
    public static function getInstance()
    {
        if (!isset(self::$instance) || (self::$instance === null)) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * Create a new ReferenceHelper.
     */
    protected function __construct()
    {
    }

    /**
     * Compare two column addresses
     * Intended for use as a Callback function for sorting column addresses by column.
     *
     * @param string $a First column to test (e.g. 'AA')
     * @param string $b Second column to test (e.g. 'Z')
     *
     * @return int
     */
    public static function columnSort($a, $b)
    {
        return strcasecmp(strlen($a) . $a, strlen($b) . $b);
    }

    /**
     * Compare two column addresses
     * Intended for use as a Callback function for reverse sorting column addresses by column.
     *
     * @param string $a First column to test (e.g. 'AA')
     * @param string $b Second column to test (e.g. 'Z')
     *
     * @return int
     */
    public static function columnReverseSort($a, $b)
    {
        return 1 - strcasecmp(strlen($a) . $a, strlen($b) . $b);
    }

    /**
     * Compare two cell addresses
     * Intended for use as a Callback function for sorting cell addresses by column and row.
     *
     * @param string $a First cell to test (e.g. 'AA1')
     * @param string $b Second cell to test (e.g. 'Z1')
     *
     * @return int
     */
    public static function cellSort($a, $b)
    {
        sscanf($a, '%[A-Z]%d', $ac, $ar);
        sscanf($b, '%[A-Z]%d', $bc, $br);

        if ($ar == $br) {
            return strcasecmp(strlen($ac) . $ac, strlen($bc) . $bc);
        }

        return ($ar < $br) ? -1 : 1;
    }

    /**
     * Compare two cell addresses
     * Intended for use as a Callback function for sorting cell addresses by column and row.
     *
     * @param string $a First cell to test (e.g. 'AA1')
     * @param string $b Second cell to test (e.g. 'Z1')
     *
     * @return int
     */
    public static function cellReverseSort($a, $b)
    {
        sscanf($a, '%[A-Z]%d', $ac, $ar);
        sscanf($b, '%[A-Z]%d', $bc, $br);

        if ($ar == $br) {
            return 1 - strcasecmp(strlen($ac) . $ac, strlen($bc) . $bc);
        }

        return ($ar < $br) ? 1 : -1;
    }

    /**
     * Test whether a cell address falls within a defined range of cells.
     *
     * @param string $cellAddress Address of the cell we're testing
     * @param int $beforeRow Number of the row we're inserting/deleting before
     * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion)
     * @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
     * @param int $pNumCols Number of columns to insert/delete (negative values indicate deletion)
     *
     * @return bool
     */
    private static function cellAddressInDeleteRange($cellAddress, $beforeRow, $pNumRows, $beforeColumnIndex, $pNumCols)
    {
        list($cellColumn, $cellRow) = Coordinate::coordinateFromString($cellAddress);
        $cellColumnIndex = Coordinate::columnIndexFromString($cellColumn);
        //    Is cell within the range of rows/columns if we're deleting
        if ($pNumRows < 0 &&
            ($cellRow >= ($beforeRow + $pNumRows)) &&
            ($cellRow < $beforeRow)) {
            return true;
        } elseif ($pNumCols < 0 &&
            ($cellColumnIndex >= ($beforeColumnIndex + $pNumCols)) &&
            ($cellColumnIndex < $beforeColumnIndex)) {
            return true;
        }

        return false;
    }

    /**
     * Update page breaks when inserting/deleting rows/columns.
     *
     * @param Worksheet $pSheet The worksheet that we're editing
     * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
     * @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
     * @param int $pNumCols Number of columns to insert/delete (negative values indicate deletion)
     * @param int $beforeRow Number of the row we're inserting/deleting before
     * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion)
     */
    protected function adjustPageBreaks(Worksheet $pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
    {
        $aBreaks = $pSheet->getBreaks();
        ($pNumCols > 0 || $pNumRows > 0) ?
            uksort($aBreaks, ['self', 'cellReverseSort']) : uksort($aBreaks, ['self', 'cellSort']);

        foreach ($aBreaks as $key => $value) {
            if (self::cellAddressInDeleteRange($key, $beforeRow, $pNumRows, $beforeColumnIndex, $pNumCols)) {
                //    If we're deleting, then clear any defined breaks that are within the range
                //        of rows/columns that we're deleting
                $pSheet->setBreak($key, Worksheet::BREAK_NONE);
            } else {
                //    Otherwise update any affected breaks by inserting a new break at the appropriate point
                //        and removing the old affected break
                $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
                if ($key != $newReference) {
                    $pSheet->setBreak($newReference, $value)
                        ->setBreak($key, Worksheet::BREAK_NONE);
                }
            }
        }
    }

    /**
     * Update cell comments when inserting/deleting rows/columns.
     *
     * @param Worksheet $pSheet The worksheet that we're editing
     * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
     * @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
     * @param int $pNumCols Number of columns to insert/delete (negative values indicate deletion)
     * @param int $beforeRow Number of the row we're inserting/deleting before
     * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion)
     */
    protected function adjustComments($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
    {
        $aComments = $pSheet->getComments();
        $aNewComments = []; // the new array of all comments

        foreach ($aComments as $key => &$value) {
            // Any comments inside a deleted range will be ignored
            if (!self::cellAddressInDeleteRange($key, $beforeRow, $pNumRows, $beforeColumnIndex, $pNumCols)) {
                // Otherwise build a new array of comments indexed by the adjusted cell reference
                $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
                $aNewComments[$newReference] = $value;
            }
        }
        //    Replace the comments array with the new set of comments
        $pSheet->setComments($aNewComments);
    }

    /**
     * Update hyperlinks when inserting/deleting rows/columns.
     *
     * @param Worksheet $pSheet The worksheet that we're editing
     * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
     * @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
     * @param int $pNumCols Number of columns to insert/delete (negative values indicate deletion)
     * @param int $beforeRow Number of the row we're inserting/deleting before
     * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion)
     */
    protected function adjustHyperlinks($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
    {
        $aHyperlinkCollection = $pSheet->getHyperlinkCollection();
        ($pNumCols > 0 || $pNumRows > 0) ?
            uksort($aHyperlinkCollection, ['self', 'cellReverseSort']) : uksort($aHyperlinkCollection, ['self', 'cellSort']);

        foreach ($aHyperlinkCollection as $key => $value) {
            $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
            if ($key != $newReference) {
                $pSheet->setHyperlink($newReference, $value);
                $pSheet->setHyperlink($key, null);
            }
        }
    }

    /**
     * Update data validations when inserting/deleting rows/columns.
     *
     * @param Worksheet $pSheet The worksheet that we're editing
     * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
     * @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
     * @param int $pNumCols Number of columns to insert/delete (negative values indicate deletion)
     * @param int $beforeRow Number of the row we're inserting/deleting before
     * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion)
     */
    protected function adjustDataValidations($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
    {
        $aDataValidationCollection = $pSheet->getDataValidationCollection();
        ($pNumCols > 0 || $pNumRows > 0) ?
            uksort($aDataValidationCollection, ['self', 'cellReverseSort']) : uksort($aDataValidationCollection, ['self', 'cellSort']);

        foreach ($aDataValidationCollection as $key => $value) {
            $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
            if ($key != $newReference) {
                $pSheet->setDataValidation($newReference, $value);
                $pSheet->setDataValidation($key, null);
            }
        }
    }

    /**
     * Update merged cells when inserting/deleting rows/columns.
     *
     * @param Worksheet $pSheet The worksheet that we're editing
     * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
     * @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
     * @param int $pNumCols Number of columns to insert/delete (negative values indicate deletion)
     * @param int $beforeRow Number of the row we're inserting/deleting before
     * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion)
     */
    protected function adjustMergeCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
    {
        $aMergeCells = $pSheet->getMergeCells();
        $aNewMergeCells = []; // the new array of all merge cells
        foreach ($aMergeCells as $key => &$value) {
            $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
            $aNewMergeCells[$newReference] = $newReference;
        }
        $pSheet->setMergeCells($aNewMergeCells); // replace the merge cells array
    }

    /**
     * Update protected cells when inserting/deleting rows/columns.
     *
     * @param Worksheet $pSheet The worksheet that we're editing
     * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
     * @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
     * @param int $pNumCols Number of columns to insert/delete (negative values indicate deletion)
     * @param int $beforeRow Number of the row we're inserting/deleting before
     * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion)
     */
    protected function adjustProtectedCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
    {
        $aProtectedCells = $pSheet->getProtectedCells();
        ($pNumCols > 0 || $pNumRows > 0) ?
            uksort($aProtectedCells, ['self', 'cellReverseSort']) : uksort($aProtectedCells, ['self', 'cellSort']);
        foreach ($aProtectedCells as $key => $value) {
            $newReference = $this->updateCellReference($key, $pBefore, $pNumCols, $pNumRows);
            if ($key != $newReference) {
                $pSheet->protectCells($newReference, $value, true);
                $pSheet->unprotectCells($key);
            }
        }
    }

    /**
     * Update column dimensions when inserting/deleting rows/columns.
     *
     * @param Worksheet $pSheet The worksheet that we're editing
     * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
     * @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
     * @param int $pNumCols Number of columns to insert/delete (negative values indicate deletion)
     * @param int $beforeRow Number of the row we're inserting/deleting before
     * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion)
     */
    protected function adjustColumnDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
    {
        $aColumnDimensions = array_reverse($pSheet->getColumnDimensions(), true);
        if (!empty($aColumnDimensions)) {
            foreach ($aColumnDimensions as $objColumnDimension) {
                $newReference = $this->updateCellReference($objColumnDimension->getColumnIndex() . '1', $pBefore, $pNumCols, $pNumRows);
                list($newReference) = Coordinate::coordinateFromString($newReference);
                if ($objColumnDimension->getColumnIndex() != $newReference) {
                    $objColumnDimension->setColumnIndex($newReference);
                }
            }
            $pSheet->refreshColumnDimensions();
        }
    }

    /**
     * Update row dimensions when inserting/deleting rows/columns.
     *
     * @param Worksheet $pSheet The worksheet that we're editing
     * @param string $pBefore Insert/Delete before this cell address (e.g. 'A1')
     * @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
     * @param int $pNumCols Number of columns to insert/delete (negative values indicate deletion)
     * @param int $beforeRow Number of the row we're inserting/deleting before
     * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion)
     */
    protected function adjustRowDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows)
    {
        $aRowDimensions = array_reverse($pSheet->getRowDimensions(), true);
        if (!empty($aRowDimensions)) {
            foreach ($aRowDimensions as $objRowDimension) {
                $newReference = $this->updateCellReference('A' . $objRowDimension->getRowIndex(), $pBefore, $pNumCols, $pNumRows);
                list(, $newReference) = Coordinate::coordinateFromString($newReference);
                if ($objRowDimension->getRowIndex() != $newReference) {
                    $objRowDimension->setRowIndex($newReference);
                }
            }
            $pSheet->refreshRowDimensions();

            $copyDimension = $pSheet->getRowDimension($beforeRow - 1);
            for ($i = $beforeRow; $i <= $beforeRow - 1 + $pNumRows; ++$i) {
                $newDimension = $pSheet->getRowDimension($i);
                $newDimension->setRowHeight($copyDimension->getRowHeight());
                $newDimension->setVisible($copyDimension->getVisible());
                $newDimension->setOutlineLevel($copyDimension->getOutlineLevel());
                $newDimension->setCollapsed($copyDimension->getCollapsed());
            }
        }
    }

    /**
     * Insert a new column or row, updating all possible related data.
     *
     * @param string $pBefore Insert before this cell address (e.g. 'A1')
     * @param int $pNumCols Number of columns to insert/delete (negative values indicate deletion)
     * @param int $pNumRows Number of rows to insert/delete (negative values indicate deletion)
     * @param Worksheet $pSheet The worksheet that we're editing
     *
     * @throws Exception
     */
    public function insertNewBefore($pBefore, $pNumCols, $pNumRows, Worksheet $pSheet)
    {
        $remove = ($pNumCols < 0 || $pNumRows < 0);
        $allCoordinates = $pSheet->getCoordinates();

        // Get coordinate of $pBefore
        list($beforeColumn, $beforeRow) = Coordinate::coordinateFromString($pBefore);
        $beforeColumnIndex = Coordinate::columnIndexFromString($beforeColumn);

        // Clear cells if we are removing columns or rows
        $highestColumn = $pSheet->getHighestColumn();
        $highestRow = $pSheet->getHighestRow();

        // 1. Clear column strips if we are removing columns
        if ($pNumCols < 0 && $beforeColumnIndex - 2 + $pNumCols > 0) {
            for ($i = 1; $i <= $highestRow - 1; ++$i) {
                for ($j = $beforeColumnIndex - 1 + $pNumCols; $j <= $beforeColumnIndex - 2; ++$j) {
                    $coordinate = Coordinate::stringFromColumnIndex($j + 1) . $i;
                    $pSheet->removeConditionalStyles($coordinate);
                    if ($pSheet->cellExists($coordinate)) {
                        $pSheet->getCell($coordinate)->setValueExplicit('', DataType::TYPE_NULL);
                        $pSheet->getCell($coordinate)->setXfIndex(0);
                    }
                }
            }
        }

        // 2. Clear row strips if we are removing rows
        if ($pNumRows < 0 && $beforeRow - 1 + $pNumRows > 0) {
            for ($i = $beforeColumnIndex - 1; $i <= Coordinate::columnIndexFromString($highestColumn) - 1; ++$i) {
                for ($j = $beforeRow + $pNumRows; $j <= $beforeRow - 1; ++$j) {
                    $coordinate = Coordinate::stringFromColumnIndex($i + 1) . $j;
                    $pSheet->removeConditionalStyles($coordinate);
                    if ($pSheet->cellExists($coordinate)) {
                        $pSheet->getCell($coordinate)->setValueExplicit('', DataType::TYPE_NULL);
                        $pSheet->getCell($coordinate)->setXfIndex(0);
                    }
                }
            }
        }

        // Loop through cells, bottom-up, and change cell coordinate
        if ($remove) {
            // It's faster to reverse and pop than to use unshift, especially with large cell collections
            $allCoordinates = array_reverse($allCoordinates);
        }
        while ($coordinate = array_pop($allCoordinates)) {
            $cell = $pSheet->getCell($coordinate);
            $cellIndex = Coordinate::columnIndexFromString($cell->getColumn());

            if ($cellIndex - 1 + $pNumCols < 0) {
                continue;
            }

            // New coordinate
            $newCoordinate = Coordinate::stringFromColumnIndex($cellIndex + $pNumCols) . ($cell->getRow() + $pNumRows);

            // Should the cell be updated? Move value and cellXf index from one cell to another.
            if (($cellIndex >= $beforeColumnIndex) && ($cell->getRow() >= $beforeRow)) {
                // Update cell styles
                $pSheet->getCell($newCoordinate)->setXfIndex($cell->getXfIndex());

                // Insert this cell at its new location
                if ($cell->getDataType() == DataType::TYPE_FORMULA) {
                    // Formula should be adjusted
                    $pSheet->getCell($newCoordinate)
                        ->setValue($this->updateFormulaReferences($cell->getValue(), $pBefore, $pNumCols, $pNumRows, $pSheet->getTitle()));
                } else {
                    // Formula should not be adjusted
                    $pSheet->getCell($newCoordinate)->setValue($cell->getValue());
                }

                // Clear the original cell
                $pSheet->getCellCollection()->delete($coordinate);
            } else {
                /*    We don't need to update styles for rows/columns before our insertion position,
                        but we do still need to adjust any formulae    in those cells                    */
                if ($cell->getDataType() == DataType::TYPE_FORMULA) {
                    // Formula should be adjusted
                    $cell->setValue($this->updateFormulaReferences($cell->getValue(), $pBefore, $pNumCols, $pNumRows, $pSheet->getTitle()));
                }
            }
        }

        // Duplicate styles for the newly inserted cells
        $highestColumn = $pSheet->getHighestColumn();
        $highestRow = $pSheet->getHighestRow();

        if ($pNumCols > 0 && $beforeColumnIndex - 2 > 0) {
            for ($i = $beforeRow; $i <= $highestRow - 1; ++$i) {
                // Style
                $coordinate = Coordinate::stringFromColumnIndex($beforeColumnIndex - 1) . $i;
                if ($pSheet->cellExists($coordinate)) {
                    $xfIndex = $pSheet->getCell($coordinate)->getXfIndex();
                    $conditionalStyles = $pSheet->conditionalStylesExists($coordinate) ?
                        $pSheet->getConditionalStyles($coordinate) : false;
                    for ($j = $beforeColumnIndex; $j <= $beforeColumnIndex - 1 + $pNumCols; ++$j) {
                        $pSheet->getCellByColumnAndRow($j, $i)->setXfIndex($xfIndex);
                        if ($conditionalStyles) {
                            $cloned = [];
                            foreach ($conditionalStyles as $conditionalStyle) {
                                $cloned[] = clone $conditionalStyle;
                            }
                            $pSheet->setConditionalStyles(Coordinate::stringFromColumnIndex($j) . $i, $cloned);
                        }
                    }
                }
            }
        }

        if ($pNumRows > 0 && $beforeRow - 1 > 0) {
            for ($i = $beforeColumnIndex; $i <= Coordinate::columnIndexFromString($highestColumn); ++$i) {
                // Style
                $coordinate = Coordinate::stringFromColumnIndex($i) . ($beforeRow - 1);
                if ($pSheet->cellExists($coordinate)) {
                    $xfIndex = $pSheet->getCell($coordinate)->getXfIndex();
                    $conditionalStyles = $pSheet->conditionalStylesExists($coordinate) ?
                        $pSheet->getConditionalStyles($coordinate) : false;
                    for ($j = $beforeRow; $j <= $beforeRow - 1 + $pNumRows; ++$j) {
                        $pSheet->getCell(Coordinate::stringFromColumnIndex($i) . $j)->setXfIndex($xfIndex);
                        if ($conditionalStyles) {
                            $cloned = [];
                            foreach ($conditionalStyles as $conditionalStyle) {
                                $cloned[] = clone $conditionalStyle;
                            }
                            $pSheet->setConditionalStyles(Coordinate::stringFromColumnIndex($i) . $j, $cloned);
                        }
                    }
                }
            }
        }

        // Update worksheet: column dimensions
        $this->adjustColumnDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);

        // Update worksheet: row dimensions
        $this->adjustRowDimensions($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);

        //    Update worksheet: page breaks
        $this->adjustPageBreaks($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);

        //    Update worksheet: comments
        $this->adjustComments($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);

        // Update worksheet: hyperlinks
        $this->adjustHyperlinks($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);

        // Update worksheet: data validations
        $this->adjustDataValidations($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);

        // Update worksheet: merge cells
        $this->adjustMergeCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);

        // Update worksheet: protected cells
        $this->adjustProtectedCells($pSheet, $pBefore, $beforeColumnIndex, $pNumCols, $beforeRow, $pNumRows);

        // Update worksheet: autofilter
        $autoFilter = $pSheet->getAutoFilter();
        $autoFilterRange = $autoFilter->getRange();
        if (!empty($autoFilterRange)) {
            if ($pNumCols != 0) {
                $autoFilterColumns = $autoFilter->getColumns();
                if (count($autoFilterColumns) > 0) {
                    sscanf($pBefore, '%[A-Z]%d', $column, $row);
                    $columnIndex = Coordinate::columnIndexFromString($column);
                    list($rangeStart, $rangeEnd) = Coordinate::rangeBoundaries($autoFilterRange);
                    if ($columnIndex <= $rangeEnd[0]) {
                        if ($pNumCols < 0) {
                            //    If we're actually deleting any columns that fall within the autofilter range,
                            //        then we delete any rules for those columns
                            $deleteColumn = $columnIndex + $pNumCols - 1;
                            $deleteCount = abs($pNumCols);
                            for ($i = 1; $i <= $deleteCount; ++$i) {
                                if (isset($autoFilterColumns[Coordinate::stringFromColumnIndex($deleteColumn + 1)])) {
                                    $autoFilter->clearColumn(Coordinate::stringFromColumnIndex($deleteColumn + 1));
                                }
                                ++$deleteColumn;
                            }
                        }
                        $startCol = ($columnIndex > $rangeStart[0]) ? $columnIndex : $rangeStart[0];

                        //    Shuffle columns in autofilter range
                        if ($pNumCols > 0) {
                            $startColRef = $startCol;
                            $endColRef = $rangeEnd[0];
                            $toColRef = $rangeEnd[0] + $pNumCols;

                            do {
                                $autoFilter->shiftColumn(Coordinate::stringFromColumnIndex($endColRef), Coordinate::stringFromColumnIndex($toColRef));
                                --$endColRef;
                                --$toColRef;
                            } while ($startColRef <= $endColRef);
                        } else {
                            //    For delete, we shuffle from beginning to end to avoid overwriting
                            $startColID = Coordinate::stringFromColumnIndex($startCol);
                            $toColID = Coordinate::stringFromColumnIndex($startCol + $pNumCols);
                            $endColID = Coordinate::stringFromColumnIndex($rangeEnd[0] + 1);
                            do {
                                $autoFilter->shiftColumn($startColID, $toColID);
                                ++$startColID;
                                ++$toColID;
                            } while ($startColID != $endColID);
                        }
                    }
                }
            }
            $pSheet->setAutoFilter($this->updateCellReference($autoFilterRange, $pBefore, $pNumCols, $pNumRows));
        }

        // Update worksheet: freeze pane
        if ($pSheet->getFreezePane()) {
            $splitCell = $pSheet->getFreezePane();
            $topLeftCell = $pSheet->getTopLeftCell();

            $splitCell = $this->updateCellReference($splitCell, $pBefore, $pNumCols, $pNumRows);
            $topLeftCell = $this->updateCellReference($topLeftCell, $pBefore, $pNumCols, $pNumRows);

            $pSheet->freezePane($splitCell, $topLeftCell);
        }

        // Page setup
        if ($pSheet->getPageSetup()->isPrintAreaSet()) {
            $pSheet->getPageSetup()->setPrintArea($this->updateCellReference($pSheet->getPageSetup()->getPrintArea(), $pBefore, $pNumCols, $pNumRows));
        }

        // Update worksheet: drawings
        $aDrawings = $pSheet->getDrawingCollection();
        foreach ($aDrawings as $objDrawing) {
            $newReference = $this->updateCellReference($objDrawing->getCoordinates(), $pBefore, $pNumCols, $pNumRows);
            if ($objDrawing->getCoordinates() != $newReference) {
                $objDrawing->setCoordinates($newReference);
            }
        }

        // Update workbook: named ranges
        if (count($pSheet->getParent()->getNamedRanges()) > 0) {
            foreach ($pSheet->getParent()->getNamedRanges() as $namedRange) {
                if ($namedRange->getWorksheet()->getHashCode() == $pSheet->getHashCode()) {
                    $namedRange->setRange($this->updateCellReference($namedRange->getRange(), $pBefore, $pNumCols, $pNumRows));
                }
            }
        }

        // Garbage collect
        $pSheet->garbageCollect();
    }

    /**
     * Update references within formulas.
     *
     * @param string $pFormula Formula to update
     * @param int $pBefore Insert before this one
     * @param int $pNumCols Number of columns to insert
     * @param int $pNumRows Number of rows to insert
     * @param string $sheetName Worksheet name/title
     *
     * @throws Exception
     *
     * @return string Updated formula
     */
    public function updateFormulaReferences($pFormula = '', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0, $sheetName = '')
    {
        //    Update cell references in the formula
        $formulaBlocks = explode('"', $pFormula);
        $i = false;
        foreach ($formulaBlocks as &$formulaBlock) {
            //    Ignore blocks that were enclosed in quotes (alternating entries in the $formulaBlocks array after the explode)
            if ($i = !$i) {
                $adjustCount = 0;
                $newCellTokens = $cellTokens = [];
                //    Search for row ranges (e.g. 'Sheet1'!3:5 or 3:5) with or without $ absolutes (e.g. $3:5)
                $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_ROWRANGE . '/i', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
                if ($matchCount > 0) {
                    foreach ($matches as $match) {
                        $fromString = ($match[2] > '') ? $match[2] . '!' : '';
                        $fromString .= $match[3] . ':' . $match[4];
                        $modified3 = substr($this->updateCellReference('$A' . $match[3], $pBefore, $pNumCols, $pNumRows), 2);
                        $modified4 = substr($this->updateCellReference('$A' . $match[4], $pBefore, $pNumCols, $pNumRows), 2);

                        if ($match[3] . ':' . $match[4] !== $modified3 . ':' . $modified4) {
                            if (($match[2] == '') || (trim($match[2], "'") == $sheetName)) {
                                $toString = ($match[2] > '') ? $match[2] . '!' : '';
                                $toString .= $modified3 . ':' . $modified4;
                                //    Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
                                $column = 100000;
                                $row = 10000000 + trim($match[3], '$');
                                $cellIndex = $column . $row;

                                $newCellTokens[$cellIndex] = preg_quote($toString, '/');
                                $cellTokens[$cellIndex] = '/(?<!\d\$\!)' . preg_quote($fromString, '/') . '(?!\d)/i';
                                ++$adjustCount;
                            }
                        }
                    }
                }
                //    Search for column ranges (e.g. 'Sheet1'!C:E or C:E) with or without $ absolutes (e.g. $C:E)
                $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_COLRANGE . '/i', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
                if ($matchCount > 0) {
                    foreach ($matches as $match) {
                        $fromString = ($match[2] > '') ? $match[2] . '!' : '';
                        $fromString .= $match[3] . ':' . $match[4];
                        $modified3 = substr($this->updateCellReference($match[3] . '$1', $pBefore, $pNumCols, $pNumRows), 0, -2);
                        $modified4 = substr($this->updateCellReference($match[4] . '$1', $pBefore, $pNumCols, $pNumRows), 0, -2);

                        if ($match[3] . ':' . $match[4] !== $modified3 . ':' . $modified4) {
                            if (($match[2] == '') || (trim($match[2], "'") == $sheetName)) {
                                $toString = ($match[2] > '') ? $match[2] . '!' : '';
                                $toString .= $modified3 . ':' . $modified4;
                                //    Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
                                $column = Coordinate::columnIndexFromString(trim($match[3], '$')) + 100000;
                                $row = 10000000;
                                $cellIndex = $column . $row;

                                $newCellTokens[$cellIndex] = preg_quote($toString, '/');
                                $cellTokens[$cellIndex] = '/(?<![A-Z\$\!])' . preg_quote($fromString, '/') . '(?![A-Z])/i';
                                ++$adjustCount;
                            }
                        }
                    }
                }
                //    Search for cell ranges (e.g. 'Sheet1'!A3:C5 or A3:C5) with or without $ absolutes (e.g. $A1:C$5)
                $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_CELLRANGE . '/i', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
                if ($matchCount > 0) {
                    foreach ($matches as $match) {
                        $fromString = ($match[2] > '') ? $match[2] . '!' : '';
                        $fromString .= $match[3] . ':' . $match[4];
                        $modified3 = $this->updateCellReference($match[3], $pBefore, $pNumCols, $pNumRows);
                        $modified4 = $this->updateCellReference($match[4], $pBefore, $pNumCols, $pNumRows);

                        if ($match[3] . $match[4] !== $modified3 . $modified4) {
                            if (($match[2] == '') || (trim($match[2], "'") == $sheetName)) {
                                $toString = ($match[2] > '') ? $match[2] . '!' : '';
                                $toString .= $modified3 . ':' . $modified4;
                                list($column, $row) = Coordinate::coordinateFromString($match[3]);
                                //    Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
                                $column = Coordinate::columnIndexFromString(trim($column, '$')) + 100000;
                                $row = trim($row, '$') + 10000000;
                                $cellIndex = $column . $row;

                                $newCellTokens[$cellIndex] = preg_quote($toString, '/');
                                $cellTokens[$cellIndex] = '/(?<![A-Z]\$\!)' . preg_quote($fromString, '/') . '(?!\d)/i';
                                ++$adjustCount;
                            }
                        }
                    }
                }
                //    Search for cell references (e.g. 'Sheet1'!A3 or C5) with or without $ absolutes (e.g. $A1 or C$5)
                $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_CELLREF . '/i', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);

                if ($matchCount > 0) {
                    foreach ($matches as $match) {
                        $fromString = ($match[2] > '') ? $match[2] . '!' : '';
                        $fromString .= $match[3];

                        $modified3 = $this->updateCellReference($match[3], $pBefore, $pNumCols, $pNumRows);
                        if ($match[3] !== $modified3) {
                            if (($match[2] == '') || (trim($match[2], "'") == $sheetName)) {
                                $toString = ($match[2] > '') ? $match[2] . '!' : '';
                                $toString .= $modified3;
                                list($column, $row) = Coordinate::coordinateFromString($match[3]);
                                //    Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
                                $column = Coordinate::columnIndexFromString(trim($column, '$')) + 100000;
                                $row = trim($row, '$') + 10000000;
                                $cellIndex = $row . $column;

                                $newCellTokens[$cellIndex] = preg_quote($toString, '/');
                                $cellTokens[$cellIndex] = '/(?<![A-Z\$\!])' . preg_quote($fromString, '/') . '(?!\d)/i';
                                ++$adjustCount;
                            }
                        }
                    }
                }
                if ($adjustCount > 0) {
                    if ($pNumCols > 0 || $pNumRows > 0) {
                        krsort($cellTokens);
                        krsort($newCellTokens);
                    } else {
                        ksort($cellTokens);
                        ksort($newCellTokens);
                    }   //  Update cell references in the formula
                    $formulaBlock = str_replace('\\', '', preg_replace($cellTokens, $newCellTokens, $formulaBlock));
                }
            }
        }
        unset($formulaBlock);

        //    Then rebuild the formula string
        return implode('"', $formulaBlocks);
    }

    /**
     * Update cell reference.
     *
     * @param string $pCellRange Cell range
     * @param string $pBefore Insert before this one
     * @param int $pNumCols Number of columns to increment
     * @param int $pNumRows Number of rows to increment
     *
     * @throws Exception
     *
     * @return string Updated cell range
     */
    public function updateCellReference($pCellRange = 'A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0)
    {
        // Is it in another worksheet? Will not have to update anything.
        if (strpos($pCellRange, '!') !== false) {
            return $pCellRange;
        // Is it a range or a single cell?
        } elseif (!Coordinate::coordinateIsRange($pCellRange)) {
            // Single cell
            return $this->updateSingleCellReference($pCellRange, $pBefore, $pNumCols, $pNumRows);
        } elseif (Coordinate::coordinateIsRange($pCellRange)) {
            // Range
            return $this->updateCellRange($pCellRange, $pBefore, $pNumCols, $pNumRows);
        }

        // Return original
        return $pCellRange;
    }

    /**
     * Update named formulas (i.e. containing worksheet references / named ranges).
     *
     * @param Spreadsheet $spreadsheet Object to update
     * @param string $oldName Old name (name to replace)
     * @param string $newName New name
     */
    public function updateNamedFormulas(Spreadsheet $spreadsheet, $oldName = '', $newName = '')
    {
        if ($oldName == '') {
            return;
        }

        foreach ($spreadsheet->getWorksheetIterator() as $sheet) {
            foreach ($sheet->getCoordinates(false) as $coordinate) {
                $cell = $sheet->getCell($coordinate);
                if (($cell !== null) && ($cell->getDataType() == DataType::TYPE_FORMULA)) {
                    $formula = $cell->getValue();
                    if (strpos($formula, $oldName) !== false) {
                        $formula = str_replace("'" . $oldName . "'!", "'" . $newName . "'!", $formula);
                        $formula = str_replace($oldName . '!', $newName . '!', $formula);
                        $cell->setValueExplicit($formula, DataType::TYPE_FORMULA);
                    }
                }
            }
        }
    }

    /**
     * Update cell range.
     *
     * @param string $pCellRange Cell range    (e.g. 'B2:D4', 'B:C' or '2:3')
     * @param string $pBefore Insert before this one
     * @param int $pNumCols Number of columns to increment
     * @param int $pNumRows Number of rows to increment
     *
     * @throws Exception
     *
     * @return string Updated cell range
     */
    private function updateCellRange($pCellRange = 'A1:A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0)
    {
        if (!Coordinate::coordinateIsRange($pCellRange)) {
            throw new Exception('Only cell ranges may be passed to this method.');
        }

        // Update range
        $range = Coordinate::splitRange($pCellRange);
        $ic = count($range);
        for ($i = 0; $i < $ic; ++$i) {
            $jc = count($range[$i]);
            for ($j = 0; $j < $jc; ++$j) {
                if (ctype_alpha($range[$i][$j])) {
                    $r = Coordinate::coordinateFromString($this->updateSingleCellReference($range[$i][$j] . '1', $pBefore, $pNumCols, $pNumRows));
                    $range[$i][$j] = $r[0];
                } elseif (ctype_digit($range[$i][$j])) {
                    $r = Coordinate::coordinateFromString($this->updateSingleCellReference('A' . $range[$i][$j], $pBefore, $pNumCols, $pNumRows));
                    $range[$i][$j] = $r[1];
                } else {
                    $range[$i][$j] = $this->updateSingleCellReference($range[$i][$j], $pBefore, $pNumCols, $pNumRows);
                }
            }
        }

        // Recreate range string
        return Coordinate::buildRange($range);
    }

    /**
     * Update single cell reference.
     *
     * @param string $pCellReference Single cell reference
     * @param string $pBefore Insert before this one
     * @param int $pNumCols Number of columns to increment
     * @param int $pNumRows Number of rows to increment
     *
     * @throws Exception
     *
     * @return string Updated cell reference
     */
    private function updateSingleCellReference($pCellReference = 'A1', $pBefore = 'A1', $pNumCols = 0, $pNumRows = 0)
    {
        if (Coordinate::coordinateIsRange($pCellReference)) {
            throw new Exception('Only single cell references may be passed to this method.');
        }

        // Get coordinate of $pBefore
        list($beforeColumn, $beforeRow) = Coordinate::coordinateFromString($pBefore);

        // Get coordinate of $pCellReference
        list($newColumn, $newRow) = Coordinate::coordinateFromString($pCellReference);

        // Verify which parts should be updated
        $updateColumn = (($newColumn[0] != '$') && ($beforeColumn[0] != '$') && (Coordinate::columnIndexFromString($newColumn) >= Coordinate::columnIndexFromString($beforeColumn)));
        $updateRow = (($newRow[0] != '$') && ($beforeRow[0] != '$') && $newRow >= $beforeRow);

        // Create new column reference
        if ($updateColumn) {
            $newColumn = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($newColumn) + $pNumCols);
        }

        // Create new row reference
        if ($updateRow) {
            $newRow = $newRow + $pNumRows;
        }

        // Return new reference
        return $newColumn . $newRow;
    }

    /**
     * __clone implementation. Cloning should not be allowed in a Singleton!
     *
     * @throws Exception
     */
    final public function __clone()
    {
        throw new Exception('Cloning a Singleton is not allowed!');
    }
}


<?php

namespace App\Http\Requests;

use App\Role;
use App\UserManager;
use App\Http\Requests\Request;

class RolesRequest extends Request
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        switch($this->method())
        {
            case 'GET':
            case 'DELETE':
            {
                return [];
            }
            case 'POST':
            {
                return [
                    'name' => 'required|min:3|max:255|unique:roles,name',
                ];
            }
            case 'PUT':
            case 'PATCH':
            {
                $id = $this->route()->roles;
                $role = Role::findOrFail($id);
                return [
                    'name' => 'required|min:3|max:255|unique:roles,name,'.$role->id,
                ];
            }
            default:break;
        }
    }
}


<?php

namespace PhpOffice\PhpSpreadsheet\Reader\Xls;

use PhpOffice\PhpSpreadsheet\Reader\Xls;

class Color
{
    /**
     * Read color.
     *
     * @param int $color Indexed color
     * @param array $palette Color palette
     * @param int $version
     *
     * @return array RGB color value, example: ['rgb' => 'FF0000']
     */
    public static function map($color, $palette, $version)
    {
        if ($color <= 0x07 || $color >= 0x40) {
            // special built-in color
            return Color\BuiltIn::lookup($color);
        } elseif (isset($palette, $palette[$color - 8])) {
            // palette color, color index 0x08 maps to pallete index 0
            return $palette[$color - 8];
        }

        // default color table
        if ($version == Xls::XLS_BIFF8) {
            return Color\BIFF8::lookup($color);
        }

        // BIFF5
        return Color\BIFF5::lookup($color);
    }
}


<?php

namespace RodrigoPedra\ClearSaleID\Service;

use RodrigoPedra\ClearSaleID\Environment\AbstractEnvironment;
use SoapClient;

class Connector
{
    /** @var  \SoapClient */
    private $client;

    /** @var  \RodrigoPedra\ClearSaleID\Environment\AbstractEnvironment */
    private $environment;

    /** @var  boolean */
    private $usesRegularEndpoint;

    /**
     * Connector constructor.
     *
     * @param  \RodrigoPedra\ClearSaleID\Environment\AbstractEnvironment $environment
     */
    public function __construct( AbstractEnvironment $environment )
    {
        $this->environment         = $environment;
        $this->usesRegularEndpoint = true;
        $this->client              = null;
    }

    /**
     * @param  string $function
     * @param         $parameters
     * @param  bool   $usesRegularEndpoint
     *
     * @return mixed
     */
    public function doRequest( $function, $parameters, $usesRegularEndpoint = true )
    {
        $client = ( $usesRegularEndpoint === true )
            ? $this->useRegularEndpoint()->getClient()
            : $this->useExtendedEndpoint()->getClient();

        $arguments = [ $function => $parameters ];
        $options   = [ 'location' => $this->getEndpoint() ];

        $this->environment->log( 'Connector@doRequest: Request', compact( 'arguments', 'options' ) );

        $response = $client->__soapCall( $function, $arguments, $options );

        $this->environment->log( 'Connector@doRequest: Response', compact( 'response' ) );

        return $response;
    }

    /**
     * @return \SoapClient
     */
    private function getClient()
    {
        if (is_null( $this->client )) {
            $this->client = new SoapClient( $this->getEndpoint() . '?WSDL' );
        }

        return $this->client;
    }

    /**
     * @return string
     */
    private function getEndpoint()
    {
        if ($this->usesRegularEndpoint) {
            return $this->environment->getRegularEndpoint();
        }

        return $this->environment->getExtendedEndpoint();
    }

    /**
     * @return $this
     */
    private function useRegularEndpoint()
    {
        if ($this->usesRegularEndpoint === false) {
            $this->client = null;
        }

        $this->usesRegularEndpoint = true;

        return $this;
    }

    /**
     * @return $this
     */
    private function useExtendedEndpoint()
    {
        if ($this->usesRegularEndpoint === true) {
            $this->client = null;
        }

        $this->usesRegularEndpoint = false;

        return $this;
    }

    /**
     * @return string
     */
    public function getEntityCode()
    {
        return $this->environment->getEntityCode();
    }

    /**
     * @param  string $message
     * @param  array  $context
     */
    public function log( $message, array $context = [] )
    {
        $this->environment->log( $message, $context );
    }
}


<?php

namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;

use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;

class RelsRibbon extends WriterPart
{
    /**
     * Write relationships for additional objects of custom UI (ribbon).
     *
     * @param Spreadsheet $spreadsheet
     *
     * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
     *
     * @return string XML Output
     */
    public function writeRibbonRelationships(Spreadsheet $spreadsheet)
    {
        // Create XML writer
        $objWriter = null;
        if ($this->getParentWriter()->getUseDiskCaching()) {
            $objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
        } else {
            $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
        }

        // XML header
        $objWriter->startDocument('1.0', 'UTF-8', 'yes');

        // Relationships
        $objWriter->startElement('Relationships');
        $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
        $localRels = $spreadsheet->getRibbonBinObjects('names');
        if (is_array($localRels)) {
            foreach ($localRels as $aId => $aTarget) {
                $objWriter->startElement('Relationship');
                $objWriter->writeAttribute('Id', $aId);
                $objWriter->writeAttribute('Type', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image');
                $objWriter->writeAttribute('Target', $aTarget);
                $objWriter->endElement();
            }
        }
        $objWriter->endElement();

        return $objWriter->getData();
    }
}


<?php

namespace Cityware\MemoryShared\Storage;

class Segment implements StorageInterface, Feature\CapacityStorageInterface {

    /**
     * identifier
     * @var string
     */
    protected $identifier;

    /**
     *
     * @var mixed
     */
    protected $memory;

    /**
     * Bloc size
     * @var int
     */
    protected $segmentSize = 256;

    /**
     * Bloc size
     * @var int
     */
    protected $blocSize = 8;

    /**
     * Construct segment memory
     * @param type $identifier
     */
    public function __construct($identifier = 'Z') {
        if (is_array($identifier)) {
            if (!isset($identifier['identifier'])) {
                throw new Exception\RuntimeException(
                'Segment storage options must be an identifier '
                . 'name or array with a "identifier" key'
                );
            }
            $identifier = $identifier['identifier'];
        }
        $this->identifier = $identifier;
    }

    public function realloc($segmentSize, $blocSize = null) {
        $this->close();
        $this->setSegmentSize($segmentSize);
        if ($blocSize) {
            $this->setBlocSize($blocSize);
        }
    }

    /**
     * Memory alloc
     */
    protected function alloc() {
        if (null !== $this->memory) {
            return;
        }
        $this->memory = shmop_open(ftok(__FILE__, $this->identifier), "c", 0644, $this->segmentSize);
    }

    /**
     * Test if has datas with $uid key
     * @param mixed $uid
     * @return boolean
     */
    public function has($uid) {
        if (null === $this->memory) {
            return false;
        }
        $data = $this->read($uid);
        return false !== $data;
    }

    /**
     * Read datas with $uid key
     * @param mixed $uid
     * @return mixed
     */
    public function read($uid) {
        if (!is_int($uid) && !is_numeric($uid)) {
            throw new Exception\RuntimeException('Segment type key must integer or numeric.');
        }
        if ($uid * $this->blocSize >= $this->segmentSize) {
            throw new Exception\RuntimeException('Invalid access bloc. Only ' . floor($this->segmentSize / $this->blocSize) . ' blocs are allowed.');
        }
        $this->alloc();
        $str = shmop_read($this->memory, $uid * $this->blocSize, $this->blocSize);
        $str = trim($str);
        if (!$str) {
            return false;
        }
        return $str;
    }

    /**
     * Write datas on $uid key
     * @param mixed $uid
     * @param mixed $mixed
     */
    public function write($uid, $mixed) {
        if (!is_int($uid) && !is_numeric($uid)) {
            throw new Exception\RuntimeException('Segment type key must integer or numeric.');
        }
        if (is_object($mixed) && method_exists($mixed, '__toString')) {
            $mixed = $mixed->__toString();
        }
        if (is_int($mixed) || is_float($mixed) || is_bool($mixed)) {
            $mixed = (string) $mixed;
        }
        if (!is_string($mixed)) {
            $mixed = '';
        }
        if ($uid * $this->blocSize >= $this->segmentSize) {
            throw new Exception\RuntimeException('Invalid access bloc. Only ' . floor($this->segmentSize / $this->blocSize) . ' blocs are allowed.');
        }
        $this->alloc();
        $limit = $this->getBlocSize();
        $str = mb_substr($mixed, 0, $limit);
        $str = str_pad($str, $this->blocSize);
        return shmop_write($this->memory, $str, $uid * $this->blocSize);
    }

    /**
     * Clear datas with $uid key
     * @param mixed $uid
     * @return void
     */
    public function clear($uid = null) {
        if (null === $uid) {
            $this->alloc();
            return shmop_delete($this->memory);
        }
        if (!is_int($uid) && !is_numeric($uid)) {
            throw new Exception\RuntimeException('Segment type key must integer or numeric.');
        }
        $this->alloc();
        return shmop_write($this->memory, str_repeat(' ', $this->blocSize), $uid * $this->blocSize);
    }

    /**
     * Close segment
     * @param int
     */
    public function close() {
        if (null === $this->memory) {
            return;
        }
        shmop_close($this->memory);
        $this->memory = null;
    }

    /**
     * Get segment memory
     * @return type
     */
    public function getSegment() {
        return $this->memory;
    }

    /**
     * Get bloc size
     * @return int
     */
    public function getBlocSize() {
        return $this->blocSize;
    }

    /**
     * Set bloc size
     * @param int
     */
    public function setBlocSize($size) {
        if (null !== $this->memory) {
            throw new Exception\RuntimeException(
            'You can not change the segment size because memory is already allocated.'
            . ' Use realloc() function to create new memory segment.'
            );
        }
        $this->blocSize = (integer) $size;
        return $this;
    }

    /**
     * Get segment size
     * @return int
     */
    public function getSegmentSize() {
        return $this->segmentSize;
    }

    /**
     * Set segment size
     * @param int
     */
    public function setSegmentSize($size) {
        if (null !== $this->memory) {
            throw new Exception\RuntimeException(
            'You can not change the segment size because memory is already allocated.'
            . ' Use realloc() function to create new memory segment.'
            );
        }
        $this->segmentSize = (integer) $size;
        return $this;
    }

    /**
     * Get max bloc allow
     */
    public function canAllowBlocsMemory($numBloc) {
        return floor($this->segmentSize / $this->blocSize) >= $numBloc;
    }

}


<?php

/*
 * This file is part of Symplify
 * Copyright (c) 2016 Tomas Votruba (http://tomasvotruba.cz).
 */

namespace Symplify\PHP7_CodeSniffer\EventDispatcher;

use PHP_CodeSniffer\Sniffs\Sniff;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symplify\PHP7_CodeSniffer\EventDispatcher\Event\CheckFileTokenEvent;

final class SniffDispatcher extends EventDispatcher
{
    /**
     * @var CurrentListenerSniffCodeProvider
     */
    private $currentListenerSniffCodeProvider;

    public function __construct(CurrentListenerSniffCodeProvider $currentListenerSniffCodeProvider)
    {
        $this->currentListenerSniffCodeProvider = $currentListenerSniffCodeProvider;
    }

    /**
     * @param Sniff[] $sniffs
     */
    public function addSniffListeners(array $sniffs)
    {
        foreach ($sniffs as $sniffCode => $sniffObject) {
            $tokens = $sniffs[$sniffCode]->register();
            foreach ($tokens as $token) {
                $this->addTokenSniffListener($token, $sniffObject);
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function doDispatch($listeners, $eventName, Event $event)
    {
        foreach ($listeners as $listener) {
            if ($event->isPropagationStopped()) {
                break;
            }

            $this->currentListenerSniffCodeProvider->setCurrentListener($listener);
            call_user_func($listener, $event, $eventName, $this);
        }
    }

    private function addTokenSniffListener(string $token, Sniff $sniffObject)
    {
        $this->addListener(
            $token,
            function (CheckFileTokenEvent $checkFileToken) use ($sniffObject) {
                $sniffObject->process(
                    $checkFileToken->getFile(),
                    $checkFileToken->getStackPointer()
                );
            }
        );
    }
}


<?php

namespace PhpOffice\PhpSpreadsheet\Chart;

class Legend
{
    /** Legend positions */
    const XL_LEGEND_POSITION_BOTTOM = -4107; //    Below the chart.
    const XL_LEGEND_POSITION_CORNER = 2; //    In the upper right-hand corner of the chart border.
    const XL_LEGEND_POSITION_CUSTOM = -4161; //    A custom position.
    const XL_LEGEND_POSITION_LEFT = -4131; //    Left of the chart.
    const XL_LEGEND_POSITION_RIGHT = -4152; //    Right of the chart.
    const XL_LEGEND_POSITION_TOP = -4160; //    Above the chart.

    const POSITION_RIGHT = 'r';
    const POSITION_LEFT = 'l';
    const POSITION_BOTTOM = 'b';
    const POSITION_TOP = 't';
    const POSITION_TOPRIGHT = 'tr';

    private static $positionXLref = [
        self::XL_LEGEND_POSITION_BOTTOM => self::POSITION_BOTTOM,
        self::XL_LEGEND_POSITION_CORNER => self::POSITION_TOPRIGHT,
        self::XL_LEGEND_POSITION_CUSTOM => '??',
        self::XL_LEGEND_POSITION_LEFT => self::POSITION_LEFT,
        self::XL_LEGEND_POSITION_RIGHT => self::POSITION_RIGHT,
        self::XL_LEGEND_POSITION_TOP => self::POSITION_TOP,
    ];

    /**
     * Legend position.
     *
     * @var string
     */
    private $position = self::POSITION_RIGHT;

    /**
     * Allow overlay of other elements?
     *
     * @var bool
     */
    private $overlay = true;

    /**
     * Legend Layout.
     *
     * @var Layout
     */
    private $layout;

    /**
     * Create a new Legend.
     *
     * @param string $position
     * @param null|Layout $layout
     * @param bool $overlay
     */
    public function __construct($position = self::POSITION_RIGHT, Layout $layout = null, $overlay = false)
    {
        $this->setPosition($position);
        $this->layout = $layout;
        $this->setOverlay($overlay);
    }

    /**
     * Get legend position as an excel string value.
     *
     * @return string
     */
    public function getPosition()
    {
        return $this->position;
    }

    /**
     * Get legend position using an excel string value.
     *
     * @param string $position see self::POSITION_*
     *
     * @return bool
     */
    public function setPosition($position)
    {
        if (!in_array($position, self::$positionXLref)) {
            return false;
        }

        $this->position = $position;

        return true;
    }

    /**
     * Get legend position as an Excel internal numeric value.
     *
     * @return int
     */
    public function getPositionXL()
    {
        return array_search($this->position, self::$positionXLref);
    }

    /**
     * Set legend position using an Excel internal numeric value.
     *
     * @param int $positionXL see self::XL_LEGEND_POSITION_*
     *
     * @return bool
     */
    public function setPositionXL($positionXL)
    {
        if (!isset(self::$positionXLref[$positionXL])) {
            return false;
        }

        $this->position = self::$positionXLref[$positionXL];

        return true;
    }

    /**
     * Get allow overlay of other elements?
     *
     * @return bool
     */
    public function getOverlay()
    {
        return $this->overlay;
    }

    /**
     * Set allow overlay of other elements?
     *
     * @param bool $overlay
     *
     * @return bool
     */
    public function setOverlay($overlay)
    {
        if (!is_bool($overlay)) {
            return false;
        }

        $this->overlay = $overlay;

        return true;
    }

    /**
     * Get Layout.
     *
     * @return Layout
     */
    public function getLayout()
    {
        return $this->layout;
    }
}


<?php

namespace XQueue\Maileon\API\JSON;

/**
 * Abstract base class for all JSON serializable elements.
 *
 * All classes derived from this must initialize their member classes in the
 * constructor.
 *
 * @author Balogh Viktor <balogh.viktor@maileon.hu> | Maileon - Wanadis Kft.
 */
abstract class AbstractJSONWrapper {
    /**
     * Used to serialize this object to a JSON string. Override this to modify
     * JSON parameters.
     *
     * @return array
     *  This class in array form
     */
    function toArray() {
        $result = array();
        $object_vars = get_object_vars($this);

        // copy each of this objects properties to an associative array
        // indexed by the property names
        foreach($object_vars as $key => $value) {
            if($value == null) { continue; }

            // if $value is an object, we can call toArray on it, and it
            // should be serialized (isEmpty returns true) than we call
            // toArray and insert the result in our array;
            // otherwise we insert the value as-is
            if(gettype($value) == "object" &&
             is_subclass_of($value, 'AbstractJSONWrapper')) {
                if(!$value->isEmpty()) {
                    $result[$key] = $value->toArray();
                }
            } else {
                // TODO: maybe deal with com_maileon_api_json_AbstractJSONWrapper
                // derived classes that have 'non-serializable' properties
                $result[$key] = $value;
            }
        }

        // return the resulting array
        return $result;
    }

    /**
     * Used to initialize this object from JSON. Override this to modify JSON
     * parameters.
     *
     * @param array $object_vars
     *  The array from json_decode
     */
    function fromArray($object_vars) {
        // copy each key to the property named the same way; if the property
        // is a serializable Maileon class, call fromArray on it
        foreach($object_vars as $key => $value) {
            if(class_exists('AbstractJSONWrapper') && is_subclass_of( $this->{$key},'AbstractJSONWrapper' )) {
                $this->{$key}->fromArray($value);
            } else {
                $this->{$key} = $value;
            }
        }
    }

    /**
     * Creates a string representation from this object in the following format:
     * ObjectName [ property1=value1, property2=value2, ... , propertyN=valueN ]
     *
     * @return string
     */
    public function __toString() {
        $object_vars = get_object_vars($this);
        $elements = "";

        // add each property of this class to a string
        foreach($object_vars as $key => $value) {
            $elements .= $key . "=" . $value . ", ";
        }

        return get_class($this) . " [ " . mb_substr($elements, 0, mb_strlen($elements) - 2) . " ]";
    }

    /**
     * Can be overridden in derived classes to signal that this object is empty
     *
     * @return boolean
     */
    function isEmpty() {
        return false;
    }
}


<?php

namespace XQueue\Maileon\API\Mailings;

use XQueue\Maileon\API\XML\AbstractXMLWrapper;
use XQueue\Maileon\API\XML\XMLUtils;

/**
 * The wrapper class for a Maileon mailing. This class wraps the XML structure.
 *
 * @author Marcus Ständer | Trusted Technologies GmbH | marcus.staender@trusted-technologies.de
 */
class Mailing extends AbstractXMLWrapper
{
    public $id;
    public $fields;

    /**
     * Constructor initializing default values.
     *
     * @param number $id
     *  The Maileon mailing id.
     * @param array $fields
     *  An array of fields.
     */
    function __construct(
        $id = null,
        $fields = array())
    {
        $this->id = $id;
        $this->fields = $fields;
    }

    /**
     * Initialization of the mailing from a simple xml element.
     *
     * @param SimpleXMLElement $xmlElement
     *  The xml element that is used to parse the mailing from.
     */
    function fromXML($xmlElement)
    {
        if (isset($xmlElement->id)) $this->id = $xmlElement->id;

        if (isset($xmlElement->fields)) {
            $this->fields = array();
            foreach ($xmlElement->fields->children() as $field) {
                $this->fields[trim($field->name)] = (string)$field->value; // The trim is required to make a safer string from the object
            }
        }
    }

    /**
     * Returns the value of the field with the given name
     *
     * @param string fieldName
     *  The field name of the element to return the value of
     *
     * @return \em string
     *  The value or undefined, if not found
     */
    function getFieldValue($fieldName)
    {
        $name = trim($fieldName);
        if (isset($this->fields)) {
            return ($this->fields[$name]);
        }
        return;
    }

    /**
     * Serialization to a simple XML element.
     *
     * @param bool $addXMLDeclaration
     *
     * @return \em SimpleXMLElement
     *  Generate a XML element from the contact object.
     */
    function toXML($addXMLDeclaration = true)
    {
        $xmlString = $addXMLDeclaration ? "<?xml version=\"1.0\"?><mailing></mailing>" : "<mailing></mailing>";
        $xml = new SimpleXMLElement($xmlString);

        if (isset($this->id)) $xml->addChild("id", $this->id);

        if (isset($this->fields)) {
            $standard_fields = $xml->addChild("fields");
            foreach ($this->fields as $index => $value) {
                $field = $standard_fields->addChild("field");
                $field->addChild("name", $index);

                XMLUtils::addChildAsCDATA($field, "value", $value);
            }
        }

        return $xml;
    }

    /**
     * Serialization to a simple XML element as string
     *
     * @return \em string
     *  The string representation of the XML document for this mailing.
     */
    function toXMLString()
    {
        $xml = $this->toXML();
        return $xml->asXML();
    }

    /**
     * Human readable representation of this wrapper.
     *
     * @return \em string
     *  A human readable version of the mailing.
     */
    function toString()
    {

        // Generate standard field string
        $fields = "";
        if (isset($this->fields)) {
            foreach ($this->fields as $index => $value) {
                $fields .= $index . "=" . $value . ",";
            }
            $fields = rtrim($fields, ',');
        }

        return "Mailing [id=" . $this->id . ", fields={" . $fields . "}]";
    }
}


<?php
/**
 * Shipping Method Bag
 */
namespace Omniship\Common;

use Omniship\Helper\Collection;

/**
 * Shipping Item Bag
 *
 * This class defines a bag (multi element set or array) of single shipping items
 * in the Omniship system.
 *
 * @see Item
 */
class ShippingQuoteBag extends Collection
{

    /**
     * Create a new collection.
     *
     * @param  mixed  $items
     */
    public function __construct($items = [])
    {
        $items = array_map(function($item) {
            return is_array($item) ? new ShippingQuote($item) : $item;
        }, $items);
        parent::__construct($items);
    }

    /**
     * Set the item at a given offset.
     *
     * @param  mixed  $key
     * @param  mixed  $value
     * @return void
     */
    public function offsetSet($key, $value)
    {
        if(is_array($value)) {
            $value = new ShippingQuote($value);
        }
        parent::offsetSet($key, $value);
    }

    /**
     * Get all of the items in the collection.
     *
     * @return ShippingQuote[]
     */
    public function all()
    {
        return parent::all();
    }
}


<?php
/*   _            _          _ ____   ___  _____
 *  | |          | |        | |___ \ / _ \| ____|
 *  | |      __ _| |__   ___| | __) | | | | |__
 *  | |     / _` | '_ \ / _ \ ||__ <|  -  |___ \
 *  | |____| (_| | |_) |  __/ |___) |     |___) |
 *  |______|\__,_|_.__/ \___|_|____/ \___/|____/
 *
 *  Copyright Label305 B.V. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace Label305\AujaLaravel\Factory;

use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\URL;
use Label305\Auja\Icons;
use Label305\Auja\Menu\LinkMenuItem;
use Label305\Auja\Menu\Menu;
use Label305\Auja\Menu\Property\Searchable;
use Label305\Auja\Menu\ResourceMenuItem;
use Label305\Auja\Menu\SpacerMenuItem;
use Label305\AujaLaravel\Config\AujaConfigurator;
use Label305\AujaLaravel\Config\ModelConfig;
use Label305\AujaLaravel\Routing\AujaRouter;

class NoAssociationsIndexMenuFactory {

    /**
     * @var AujaConfigurator
     */
    private $aujaConfigurator;

    /**
     * @var AujaRouter
     */
    private $aujaRouter;

    public function __construct(AujaConfigurator $aujaConfigurator, AujaRouter $aujaRouter) {
        $this->aujaConfigurator = $aujaConfigurator;
        $this->aujaRouter = $aujaRouter;
    }

    /**
     * Builds a simple menu for given model, where typically this model should not have any relations to other models.
     *
     * The menu will include:
     *  - An Add LinkMenuItem;
     *  - A SpacerMenuItem with the model's name;
     *  - A ResourceMenuItem to hold entries of the model.
     *
     * @param String      $modelName The name of the model.
     * @param ModelConfig $config    (optional) The `ModelConfig` to use.
     *
     * @return Menu the Menu, which can be configured further.
     */
    public function create($modelName, ModelConfig $config = null) {
        $menu = new Menu();

        $addMenuItem = new LinkMenuItem();
        $addMenuItem->setText(Lang::trans('Add'));
        $addMenuItem->setIcon(Icons::ion_plus);
        $addMenuItem->setTarget(URL::route($this->aujaRouter->getCreateName($modelName)));
        $menu->addMenuItem($addMenuItem);

        $spacerMenuItem = new SpacerMenuItem();
        $spacerMenuItem->setText(Lang::trans($modelName));
        $menu->addMenuItem($spacerMenuItem);

        $resourceMenuItem = new ResourceMenuItem();
        $resourceMenuItem->setTarget(URL::route($this->aujaRouter->getIndexName($modelName)));

        $model = $this->aujaConfigurator->getModel($modelName);
        if($this->aujaConfigurator->isSearchable($model, $config)){
            $target = urldecode(URL::route($this->aujaRouter->getIndexName($modelName), ['q' => '%s'])); /* urldecode because the '%' gets escaped. */
            $property = new Searchable($target);
            $resourceMenuItem->addProperty($property);
        }

        $menu->addMenuItem($resourceMenuItem);

        return $menu;
    }
}


<?php
/**
 * This file is the view file of the application for creating PDF responses.
 *
 * CakeSpreadsheet: Additional elements of the appearance of the application
 * @copyright Copyright 2017-2018, Andrey Klimov.
 * @license https://opensource.org/licenses/mit-license.php MIT License
 * @package plugin.View
 */

App::uses('View', 'View');
App::import('Vendor', 'CakeSpreadsheet.Spreadsheet', ['file' => 'phpspreadsheet' . DS . 'autoload.php']);
App::uses('Language', 'CakeBasicFunctions.Utility');
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Fill;

/**
 * A view class that is used for creating PDF responses.
 *
 * @package plugin.View
 */
class SpreadsheetView extends View {

/**
 * Stores the Spreadsheet() instance
 *
 * @var object
 */
	public $Spreadsheet = null;

/**
 * Default file name
 *
 * @var string
 */
	protected $_fileName = 'Report';

/**
 * Style of table header
 *
 * @var array
 */
	protected $_styleTableHeader = [
		'borders' => [
			'outline' => [
				'borderStyle' => Border::BORDER_DOUBLE,
				'color' => ['argb' => '000000'],
			],
			'inside' => [
				'borderStyle' => Border::BORDER_THIN,
				'color' => ['argb' => '000000'],
			],
		],
		'font' => [
			'bold' => true,
		],
		'alignment' => [
			'horizontal' => Alignment::HORIZONTAL_CENTER,
			'vertical' => Alignment::VERTICAL_CENTER,
			'wrap' => false,
		],
		'fill' => [
			'fillType' => Fill::FILL_SOLID,
			'color' => [
				'argb' => 'C2FABD'
			]
		],
	];

/**
 * Style of table subheader
 *
 * @var array
 */
	protected $_styleTableSubhader = [
		'borders' => [
			'outline' => [
				'borderStyle' => Border::BORDER_DOUBLE,
				'color' => ['argb' => '000000'],
			],
			'inside' => [
				'borderStyle' => Border::BORDER_THIN,
				'color' => ['argb' => '000000'],
			],
		],

		'font' => [
			'bold' => true,
			'italic' => true,
		],
		'alignment' => [
			'horizontal' => Alignment::HORIZONTAL_CENTER,
			'vertical' => Alignment::VERTICAL_CENTER,
			'wrap' => false,
		],
		'fill' => [
			'fillType' => Fill::FILL_SOLID,
			'color' => [
				'argb' => 'FFFFCC'
			]
		],
	];

/**
 * Style of table row
 *
 * @var array
 */
	protected $_styleTableRow = [
		'borders' => [
			'allBorders' => [
				'borderStyle' => Border::BORDER_THIN,
				'color' => ['argb' => '000000'],
			],
		],
		'alignment' => [
			//'horizontal' => Alignment::HORIZONTAL_LEFT,
			'vertical' => Alignment::VERTICAL_CENTER,
			'wrap' => true,
		],
	];

/**
 * Set file name
 *
 * @param string $fileName File name
 * @return bool Success
 */
	public function setFileName($fileName = null) {
		if (empty($fileName)) {
			return false;
		}

		$this->_fileName = (string)$fileName;

		return true;
	}

/**
 * Return file name
 *
 * @return string Return file name.
 */
	public function getFileName() {
		return $this->_fileName;
	}

/**
 * Constructor
 *
 * @param Controller $controller A controller object to pull View::_passedVars from.
 * @return void
 */
	public function __construct(Controller $controller = null) {
		parent::__construct($controller);

		$this->response->type(CAKE_SPREADSHEET_FILE_EXTENSION);
		\PhpOffice\PhpSpreadsheet\Cell\Cell::setValueBinder(new \PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder());
		$this->Spreadsheet = new Spreadsheet();
	}

/**
 * Renders view for given view file and layout.
 *
 * @param string $action Name of view file to use
 * @param string $layout Layout to use.
 * @return string|null Rendered content or null if content already rendered and returned earlier.
 * @triggers View.beforeRender $this, array($viewFileName)
 * @triggers View.afterRender $this, array($viewFileName)
 * @throws CakeException If there is an error in the view.
 */
	public function render($action = null, $layout = null) {
		$this->viewPath .= DS . 'Spreadsheet';

		$content = parent::render($action, false);
		if ($this->response->type() == 'text/html') {
			return $content;
		}

		ob_start();
		$writer = IOFactory::createWriter($this->Spreadsheet, CAKE_SPREADSHEET_PHPSPREADSHEET_WRITER);
		$writer->setPreCalculateFormulas(false);
		$writer->save('php://output');
		$content = ob_get_clean();

		$this->Blocks->set('content', $content);
		$fileName = $this->getFileName();
		$fileName .= '.' . CAKE_SPREADSHEET_FILE_EXTENSION;
		$this->response->download($fileName);

		return $this->Blocks->get('content');
	}

/**
 * Return style of table header
 *
 * @return array Return array style of table header.
 */
	protected function _getStyleTableHeader() {
		return (array)$this->_styleTableHeader;
	}

/**
 * Return style of table subheader
 *
 * @return array Return array style of table subheader.
 */
	protected function _getStyleTableSubheader() {
		return (array)$this->_styleTableSubhader;
	}

/**
 * Return style of table row
 *
 * @return array Return array style of table row.
 */
	protected function _getStyleTableRow() {
		return (array)$this->_styleTableRow;
	}

/**
 * Write a table
 *
 * @param array $data Array of rows for table.
 * @param array $width Array of width for table cells.
 * @param array $align Array of align position for table cells.
 * @param array $header Array of header for table cells.
 * @return void
 */
	public function table($data = [], $width = [], $align = [], $header = []) {
		$language = new Language();
		$locale = $language->getCurrentUIlang(true);
		if (empty($locale)) {
			$locale = 'en';
		}
		\PhpOffice\PhpSpreadsheet\Settings::setLocale($locale);

		$styleTableHeader = $this->_getStyleTableHeader();
		$styleTableSubheader = $this->_getStyleTableSubheader();
		$styleTableRow = $this->_getStyleTableRow();
		$sheetIndex = 0;
		if (empty($data)) {
			$sheet = $this->Spreadsheet->getSheet($sheetIndex);
			$sheet->setTitle(__d('view_extension', 'No data to display'));
			$sheet->setCellValue('B2', __d('view_extension', 'No data to display'));
			$sheet->getColumnDimension('B')->setAutoSize(true);
			$sheet->getStyle('B2')->applyFromArray($styleTableHeader);

			return;
		}

		// Render data
		foreach ($data as $group => $info) {
			if (empty($info)) {
				continue;
			}

			// Sheet - Group
			if ($sheetIndex > 0) {
				$this->Spreadsheet->createSheet();
			}
			$sheet = $this->Spreadsheet->getSheet($sheetIndex);
			$sheetTitle = mb_strimwidth(strip_tags($group), 0, 31, '...', 'UTF-8');
			if (empty($sheetTitle)) {
				$sheetTitle = 'Sheet_' . ($sheetIndex + 1);
			}
			$sheet->setTitle($sheetTitle);

			// Header
			$iRow = 2;
			$azLiters = range('A', 'Z');
			$azLitersCount = count($azLiters);
			$columnCache = [];
			foreach ($header as $i => $headerItem) {
				if ($i > ($azLitersCount - 1)) {
					$part = floor($i / $azLitersCount);
					$liter = $azLiters[$part - 1] . $azLiters[$i - ($part * $azLitersCount)];
				} else {
					$liter = $azLiters[$i + 1];
				}
				$sheet->setCellValue($liter . $iRow, $headerItem);
				$sheet->getColumnDimension($liter)->setWidth($width[$i]);
				$columnCache[] = $liter;
			}
			$sheet->getStyle('B' . $iRow . ':' . $liter . $iRow)->applyFromArray($styleTableHeader);
			$iRow++;
			foreach ($info as $subGroup => $row) {
				if (is_int($subGroup)) {
					foreach ($row as $i => $field) {
						$sheet->setCellValue($columnCache[$i] . $iRow, strip_tags(preg_replace('#<br\s*/?>#i', "\n", $field)));
						switch ($align[$i]) {
							case 'R':
								$alignStyle = Alignment::HORIZONTAL_RIGHT;
								break;
							case 'C':
								$alignStyle = Alignment::HORIZONTAL_CENTER;
								break;
							case 'L':
							default:
								$alignStyle = Alignment::HORIZONTAL_LEFT;
						}
						$sheet->getStyle($columnCache[$i] . $iRow)->getAlignment()->setHorizontal($alignStyle);
						$sheet->getStyle('B' . $iRow . ':' . $liter . $iRow)->applyFromArray($styleTableRow);
					}
				} else {
					if (!empty($subGroup)) {
						$sheet->setCellValue('B' . $iRow, $subGroup);
						$sheet->mergeCells('B' . $iRow . ':' . $liter . $iRow);
						$sheet->getStyle('B' . $iRow . ':' . $liter . $iRow)->applyFromArray($styleTableSubheader);
						$iRow++;
					}
					foreach ($row as $rowItem) {
						foreach ($rowItem as $i => $field) {
							$sheet->setCellValue($columnCache[$i] . $iRow, strip_tags(preg_replace('#<br\s*/?>#i', "\r\n", $field)));
							switch ($align[$i]) {
								case 'R':
									$alignStyle = Alignment::HORIZONTAL_RIGHT;
									break;
								case 'C':
									$alignStyle = Alignment::HORIZONTAL_CENTER;
									break;
								case 'L':
								default:
									$alignStyle = Alignment::HORIZONTAL_LEFT;
							}
							$sheet->getStyle($columnCache[$i] . $iRow)->getAlignment()->setHorizontal($alignStyle);
							$sheet->getStyle('B' . $iRow . ':' . $liter . $iRow)->applyFromArray($styleTableRow);
						}
						$iRow++;
					}
				}
				$iRow++;
			}

			// Set AutoFilter
			$this->Spreadsheet->getActiveSheet()->setAutoFilter('B2:' . $liter . --$iRow);
			$sheetIndex++;
		}

		$this->Spreadsheet->setActiveSheetIndex(0);
	}
}


<?php
/**
 * Custom Images Grifus · Extensions For Grifus
 *
 * @author    Josantonius <hello@josantonius.com>
 * @package   eliasis-framework/custom-images-grifus
 * @copyright 2017 - 2018 (c) Josantonius - Custom Images Grifus
 * @license   GPL-2.0+
 * @link      https://github.com/eliasis-framework/custom-images-grifus.git
 * @since     1.0.0
 */

namespace EFG\Modules\CustomImagesGrifus\Controller\Admin\Page;

use Josantonius\WP_Register\WP_Register;
use Josantonius\WP_Menu\WP_Menu;
use Eliasis\Framework\App;
use Eliasis\Framework\Controller;
use Eliasis\Complement\Type\Module;

/**
 * Handler Custom Images Grifus page.
 */
class Custom_Images extends Controller {

	/**
	 * Slug for this administration page.
	 *
	 * @var string $slug
	 */
	public $slug = 'custom-images-grifus';

	/**
	 * Class initializer method.
	 */
	public function init() {

		$this->run_ajax();
	}

	/**
	 * Add submenu for this page.
	 */
	public function set_submenu() {

		WP_Menu::add(
			'submenu',
			Module::CustomImagesGrifus()->getOption(
				'submenu',
				'custom-images-grifus'
			),
			[ $this, 'render' ],
			[ $this, 'add_styles' ],
			[ $this, 'add_scripts' ]
		);
	}

	/**
	 * Load scripts.
	 */
	public function add_scripts() {

		$scripts = [
			'eliasisMaterial',
			'extensionsForGrifusAdmin',
		];

		$js = App::EFG()->getOption( 'assets', 'js' );

		foreach ( $scripts as $script ) {
			WP_Register::add( 'script', $js[ $script ] );
		}

		$js = Module::CustomImagesGrifus()->getOption( 'assets', 'js' );

		$js = $js['customImagesGrifusAdmin'];

		$params = [
			'revised_text' => __(
				'All post have already been reviewed',
				'extensions-for-grifus-images'
			),
			'added_text' => __(
				'Posts were modified',
				'extensions-for-grifus-images'
			),
		];

		$js['params'] = array_merge( $js['params'], $params );

		WP_Register::add( 'script', $js );
	}

	/**
	 * Load styles.
	 */
	public function add_styles() {

		$css = App::EFG()->getOption( 'assets', 'css' );

		WP_Register::add( 'style', $css['extensionsForGrifusAdmin'] );

		$css = Module::CustomImagesGrifus()->getOption( 'assets', 'css' );

		WP_Register::add( 'style', $css['customImagesGrifusAdmin'] );
	}

	/**
	 * Renderizate admin page.
	 */
	public function render() {

		$layout = App::EFG()->getOption( 'path', 'layout' );

		$page = Module::CustomImagesGrifus()->getOption( 'path', 'page' );

		$replace = Module::CustomImagesGrifus()->getOption( 'replace-when-add' );

		$data = [ 'replace-when-add' => $replace ];

		$this->view->renderizate( $layout, 'header' );
		$this->view->renderizate( $page, 'custom-images', $data );
		$this->view->renderizate( $layout, 'footer' );
	}

	/**
	 * Run ajax in panel admin.
	 *
	 * @since 1.0.0
	 */
	public function run_ajax() {

		$image = Module::CustomImagesGrifus()->getControllerInstance( 'Image' );

		add_action( 'wp_ajax_replace_old_images', [ $image, 'replace_old_images' ] );
		add_action( 'wp_ajax_replace_when_add', [ $image, 'replace_when_add' ] );
	}
}