mambax7/extgallery

View on GitHub
class/CategoryHandler.php

Summary

Maintainability
D
2 days
Test Coverage
<?php

namespace XoopsModules\Extgallery;

/**
 * ExtGallery Class Manager
 *
 * You may not change or alter any portion of this comment or credits
 * of supporting developers from this source code or any supporting source code
 * which is considered copyrighted (c) material of the original comment or credit authors.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * @copyright   {@link https://xoops.org/ XOOPS Project}
 * @license     GNU GPL 2 (https://www.gnu.org/licenses/old-licenses/gpl-2.0.html)
 * @author      Zoullou (http://www.zoullou.net)
 * @package     ExtGallery
 */

use XoopsModules\Extgallery;

/**
 * Class Extgallery\CategoryHandler
 */
class CategoryHandler extends Extgallery\PersistableObjectHandler
{
    //var $_nestedTree;
    public $_photoHandler;

    /**
     * @param \XoopsDatabase|null $db
     * @param                     $type
     */
    public function __construct(\XoopsDatabase $db, $type)
    {
        parent::__construct($db, 'extgallery_' . $type . 'cat', \ucfirst($type) . 'Category', 'cat_id');
        //$this->_nestedTree = new NestedTree($db, 'extgallery_'.$type.'cat', 'cat_id', 'cat_pid', 'cat_id');
        $this->_photoHandler = Extgallery\Helper::getInstance()->getHandler(\ucfirst($type) . 'Photo');
    }

    /**
     * @param $data
     *
     * @return bool
     */
    public function createCat($data)
    {
        $cat = $this->create();
        $cat->setVars($data);

        if (!$this->hasValidParent($cat)) {
            return false;
        }

        $this->insert($cat, true);
        $this->rebuild();

        return true;
    }

    /**
     * @param $data
     *
     * @return bool
     */
    public function modifyCat($data)
    {
        $cat = $this->get($data['cat_id']);
        $cat->setVars($data);

        if (!$this->hasValidParent($cat)) {
            return false;
        }
        $this->insert($cat, true);

        // Rebluid the tree only if the structure is modified
        if (isset($data['cat_pid']) || isset($data['nlevel']) || isset($data['nright']) || isset($data['nleft'])) {
            $this->rebuild();
        }

        return '';
    }

    /**
     * @param int $catId
     */
    public function deleteCat($catId)
    {
        $children = $this->getDescendants($catId, false, true);
        foreach ($children as $child) {
            $this->_photoHandler->deletePhotoByCat($child->getVar('cat_id'));
            $this->deleteCat($child->getVar('cat_id'));
        }
        $this->_photoHandler->deletePhotoByCat($catId);
        $this->deleteById($catId);
    }

    /**
     * @param int    $id
     * @param bool   $includeSelf
     * @param bool   $childrenOnly
     * @param bool   $withRestrict
     * @param string $permType
     *
     * @return array
     */
    public function getDescendants(
        $id = 0,
        $includeSelf = false,
        $childrenOnly = false,
        $withRestrict = true,
        $permType = 'public_access'
    ) {
        $cat = $this->get($id);

        $nleft     = $cat->getVar('nleft');
        $nright    = $cat->getVar('nright');
        $parent_id = $cat->getVar('cat_id');

        $criteria = new \CriteriaCompo();

        if ($childrenOnly) {
            $criteria->add(new \Criteria('cat_pid', $parent_id), 'OR');
            if ($includeSelf) {
                $criteria->add(new \Criteria('cat_id', $parent_id));
                //$query = sprintf('select * from %s where %s = %d or %s = %d order by nleft', $this->table, $this->fields['id'], $parent_id, $this->fields['parent'], $parent_id);
            }/* else {
                //$query = sprintf('select * from %s where %s = %d order by nleft', $this->table, $this->fields['parent'], $parent_id);
            }*/
        } else {
            if ($nleft > 0 && $includeSelf) {
                $criteria->add(new \Criteria('nleft', $nleft, '>='));
                $criteria->add(new \Criteria('nright', $nright, '<='));
                //$query = sprintf('select * from %s where nleft >= %d and nright <= %d order by nleft', $this->table, $nleft, $nright);
            } else {
                if ($nleft > 0) {
                    $criteria->add(new \Criteria('nleft', $nleft, '>'));
                    $criteria->add(new \Criteria('nright', $nright, '<'));
                    //$query = sprintf('select * from %s where nleft > %d and nright < %d order by nleft', $this->table, $nleft, $nright);
                }/* else {
                $query = sprintf('select * from %s order by nleft', $this->table);
                }*/
            }
        }
        if ($withRestrict) {
            $temp = $this->getCatRestrictCriteria($permType);
            if (false !== $temp) {
                $criteria->add($temp);
            }
            $temp = $this->getCatRestrictCriteria('public_displayed');
            if (false !== $temp) {
                $criteria->add($temp);
            }
        }
        $criteria->setSort('nleft');

        return $this->getObjects($criteria);
    }

    /**
     * @param int $id
     * @return mixed|null
     */
    public function getCat($id = 0)
    {
        $criteria = new \CriteriaCompo();
        $temp     = $this->getCatRestrictCriteria('public_displayed');
        if (false !== $temp) {
            $criteria->add($temp);
        }

        $criteria->add(new \Criteria('cat_id', $id));
        $ret = $this->getObjects($criteria);

        if (\count($ret) > 0) {
            return $ret[0];
        }

        return null;
    }

    public function hasValidParent()
    {
        exit('hasValidParent() method must be defined on sub classes');
    }

    /**
     * @param $cat
     *
     * @return bool
     */
    public function _isAlbum($cat)
    {
        $nbPhoto = $this->nbPhoto($cat);

        return 0 != $nbPhoto;
    }

    /**
     * @param Extgallery\Category $cat
     *
     * @return mixed
     */
    public function nbPhoto(&$cat)
    {
        /** @var Extgallery\CategoryHandler $this ->_photoHandler */
        return $this->_photoHandler->nbPhoto($cat);
    }

    /**
     * @param int  $id
     * @param bool $includeSelf
     *
     * @return array
     */
    public function getPath($id = 0, $includeSelf = false)
    {
        $cat = $this->get($id);
        if (null === $cat) {
            return [];
        }

        $criteria = new \CriteriaCompo();
        if ($includeSelf) {
            $criteria->add(new \Criteria('nleft', $cat->getVar('nleft'), '<='));
            $criteria->add(new \Criteria('nright', $cat->getVar('nright'), '>='));
            //$query = sprintf('select * from %s where nleft <= %d and nright >= %d order by nlevel', $this->table, $node['nleft'], $node['nright']);
        } else {
            $criteria->add(new \Criteria('nleft', $cat->getVar('nleft'), '<'));
            $criteria->add(new \Criteria('nright', $cat->getVar('nright'), '>'));
            //$query = sprintf('select * from %s where nleft < %d and nright > %d order by nlevel', $this->table, $node['nleft'], $node['nright']);
        }
        $temp = $this->getCatRestrictCriteria();
        if (\is_object($temp)) {
            $criteria->add($temp);
        }
        $temp2 = $this->getCatRestrictCriteria('public_displayed');
        if (\is_object($temp2)) {
            $criteria->add($temp2);
        }
        $criteria->setSort('nlevel');

        return $this->getObjects($criteria);
    }

    /**
     * @return array
     */
    public function getTree()
    {
        return $this->getDescendants(0, false, false, false);
    }

    /**
     * @param int  $id
     * @param bool $includeSelf
     *
     * @return array
     */
    public function getChildren($id = 0, $includeSelf = false)
    {
        return $this->getDescendants($id, $includeSelf, true);
    }

    /**
     * @param int $id
     *
     * @return int
     */
    public function nbAlbum($id = 0)
    {
        $criteria = new \CriteriaCompo(new \Criteria('nright - nleft', 1));
        //$query = sprintf('select count(*) as num_leef from %s where nright - nleft = 1', $this->table);
        if (0 != $id) {
            $cat = $this->get($id);
            $criteria->add(new \Criteria('nleft', $cat->getVar('nleft'), '>'));
            $criteria->add(new \Criteria('nright', $cat->getVar('nright'), '<'));
            //$query .= sprintf(' AND nleft > %d AND nright < %d', $node['nleft'], $node['nright']);
        }

        return $this->getCount($criteria);
    }

    /**
     * @param        $name
     * @param        $selectMode
     * @param bool   $addEmpty
     * @param int    $selected
     * @param string $extra
     * @param bool   $displayWeight
     * @param string $permType
     *
     * @return string
     */
    public function getSelect(
        $name,
        $selectMode,
        $addEmpty = false,
        $selected = 0,
        $extra = '',
        $displayWeight = false,
        $permType = 'public_access'
    ) {
        $cats = $this->getDescendants(0, false, false, true, $permType);

        return $this->makeSelect($cats, $name, $selectMode, $addEmpty, $selected, $extra, $displayWeight);
    }

    /**
     * @param        $name
     * @param bool   $addEmpty
     * @param int    $selected
     * @param string $extra
     * @param string $permType
     *
     * @return string
     */
    public function getLeafSelect($name, $addEmpty = false, $selected = 0, $extra = '', $permType = 'public_access')
    {
        return $this->getSelect($name, 'node', $addEmpty, $selected, $extra, false, $permType);
    }

    /**
     * @param        $name
     * @param bool   $addEmpty
     * @param int    $selected
     * @param string $extra
     *
     * @return string
     */
    public function getNodeSelect($name, $addEmpty = false, $selected = 0, $extra = '')
    {
        return $this->getSelect($name, 'leaf', $addEmpty, $selected, $extra);
    }

    /**
     * @param array  $cats
     * @param string $name
     * @param string $selectMode
     * @param        $addEmpty
     * @param        $selected
     * @param        $extra
     * @param        $displayWeight
     *
     * @return string
     */
    public function makeSelect($cats, $name, $selectMode, $addEmpty, $selected, $extra, $displayWeight)
    {
        $ret = '<select name="' . $name . '" id="' . $name . '"' . $extra . '>';
        if ($addEmpty) {
            $ret .= '<option value="0">-----</option>';
        }
        /** @var Extgallery\Category $cat */
        foreach ($cats as $cat) {
            $disableOption = '';
            if ('node' === $selectMode && (1 != $cat->getVar('nright') - $cat->getVar('nleft'))) {
                // If the brownser is IE the parent cat isn't displayed
                //                if (preg_match('`MSIE`', $_SERVER['HTTP_USER_AGENT'])) {
                if (false !== mb_strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')) {
                    continue;
                }
                $disableOption = ' disabled="disabled"';
            } elseif ('leaf' === $selectMode && (1 == $cat->getVar('cat_isalbum'))) {
                continue;
            }

            $selectedOption = '';
            if ($cat->getVar('cat_id') == $selected) {
                $selectedOption = ' selected';
            }

            $prefix = '';
            for ($i = 0; $i < $cat->getVar('nlevel') - 1; ++$i) {
                $prefix .= '--';
            }
            $catName = $prefix . ' ' . $cat->getVar('cat_name');
            if ($displayWeight) {
                $catName .= ' [' . $cat->getVar('cat_weight') . ']';
            }

            $ret .= '<option value="' . $cat->getVar('cat_id') . '"' . $selectedOption . '' . $disableOption . '>' . $catName . '</option>';
        }
        $ret .= '</select>';

        return $ret;
    }

    /**
     * @param array $selected
     *
     * @return string
     */
    public function getBlockSelect($selected = [])
    {
        $cats           = $this->getDescendants();
        $ret            = '<select name="options[]" multiple="multiple">';
        $selectedOption = '';
        $allCat         = \in_array(0, $selected);
        if ($allCat) {
            $selectedOption = ' selected';
        }
        $ret .= '<option value="0"' . $selectedOption . '>' . \_MB_EXTGALLERY_ALL_CATEGORIES . '</option>';
        foreach ($cats as $cat) {
            $prefix = '';
            for ($i = 0; $i < $cat->getVar('nlevel') - 1; ++$i) {
                $prefix .= '-';
            }
            $selectedOption = '';
            $disableOption  = '';

            if (!$allCat && \in_array($cat->getVar('cat_id'), $selected)) {
                $selectedOption = ' selected';
            }

            if (1 != $cat->getVar('nright') - $cat->getVar('nleft')) {
                $disableOption = ' disabled="disabled"';
            }

            $ret .= '<option value="' . $cat->getVar('cat_id') . '"' . $selectedOption . '' . $disableOption . '>' . $prefix . ' ' . $cat->getVar('cat_name') . '</option>';
        }
        $ret .= '</select>';

        return $ret;
    }

    /**
     * @return array
     */
    public function getTreeWithChildren()
    {
        $criteria = new \CriteriaCompo();
        $criteria->setSort('cat_weight, cat_name');
        //$query = sprintf('select * from %s order by %s', $this->table, $this->fields['sort']);

        //$result = $this->db->query($query);
        $categories = $this->getObjects($criteria, false, false);

        // create a root node to hold child data about first level items
        $root             = [];
        $root['cat_id']   = 0;
        $root['children'] = [];

        $arr = [
            $root,
        ];

        // populate the array and create an empty children array
        /*while (false !== ($row = $this->db->fetchArray($result))) {
            $arr[$row[$this->fields['id']]] = $row;
            $arr[$row[$this->fields['id']]]['children'] = array ();
        }*/
        foreach ($categories as $row) {
            $arr[$row['cat_id']]             = $row;
            $arr[$row['cat_id']]['children'] = [];
        }

        // now process the array and build the child data
        foreach ($arr as $id => $row) {
            if (isset($row['cat_pid'])) {
                $arr[$row['cat_pid']]['children'][$id] = $id;
            }
        }

        return $arr;
    }

    /**
     * Rebuilds the tree data and saves it to the database
     */
    public function rebuild()
    {
        $data = $this->getTreeWithChildren();

        $n     = 0; // need a variable to hold the running n tally
        $level = 0; // need a variable to hold the running level tally

        // invoke the recursive function. Start it processing
        // on the fake "root node" generated in getTreeWithChildren().
        // because this node doesn't really exist in the database, we
        // give it an initial nleft value of 0 and an nlevel of 0.
        $this->_generateTreeData($data, 0, 0, $n);
        //echo "<pre>";print_r($data);echo "</pre>";
        // at this point the root node will have nleft of 0, nlevel of 0
        // and nright of (tree size * 2 + 1)

        // Errase category and photo counter
        $query = \sprintf('UPDATE `%s` SET cat_nb_album = 0, cat_nb_photo = 0;', $this->table);
        $this->db->queryF($query);

        foreach ($data as $id => $row) {
            // skip the root node
            if (0 == $id) {
                continue;
            }

            // Update the photo number
            if (1 == $row['nright'] - $row['nleft']) {
                // Get the number of photo in this album
                $criteria = new \CriteriaCompo();
                $criteria->add(new \Criteria('cat_id', $id));
                $criteria->add(new \Criteria('photo_approved', 1));
                /** @var Extgallery\CategoryHandler $this ->_photoHandler */
                $nbPhoto = $this->_photoHandler->getCount($criteria);

                // Update all parent of this album
                $upNbAlbum = '';
                if (0 != $nbPhoto) {
                    $upNbAlbum = 'cat_nb_album = cat_nb_album + 1, ';
                }
                $sql   = 'UPDATE `%s` SET ' . $upNbAlbum . 'cat_nb_photo = cat_nb_photo + %d WHERE nleft < %d AND nright > %d;';
                $query = \sprintf($sql, $this->table, $nbPhoto, $row['nleft'], $row['nright']);
                $this->db->queryF($query);

                // Update this album if needed
                if (0 != $nbPhoto) {
                    $sql   = 'UPDATE `%s`SET cat_nb_photo = %d WHERE `%s` = %d';
                    $query = \sprintf($sql, $this->table, $nbPhoto, $this->keyName, $id);
                    $this->db->queryF($query);
                }
            }

            $query = \sprintf('UPDATE `%s`SET nlevel = %d, nleft = %d, nright = %d WHERE `%s` = %d;', $this->table, $row['nlevel'], $row['nleft'], $row['nright'], $this->keyName, $id);
            $this->db->queryF($query);
        }
    }

    /**
     * Generate the tree data. A single call to this generates the n-values for
     * 1 node in the tree. This function assigns the passed in n value as the
     * node's nleft value. It then processes all the node's children (which
     * in turn recursively processes that node's children and so on), and when
     * it is finally done, it takes the update n-value and assigns it as its
     * nright value. Because it is passed as a reference, the subsequent changes
     * in subrequests are held over to when control is returned so the nright
     * can be assigned.
     *
     * @param array &$arr   A reference to the data array, since we need to
     *                      be able to update the data in it
     * @param int    $id    The ID of the current node to process
     * @param int    $level The nlevel to assign to the current node
     * @param int   &$n     A reference to the running tally for the n-value
     */
    public function _generateTreeData(&$arr, $id, $level, &$n)
    {
        $arr[$id]['nlevel'] = $level;
        $arr[$id]['nleft']  = ++$n;

        // loop over the node's children and process their data
        // before assigning the nright value
        foreach ($arr[$id]['children'] as $child_id) {
            $this->_generateTreeData($arr, $child_id, $level + 1, $n);
        }
        $arr[$id]['nright'] = ++$n;
    }

    /**
     * @param string $permType
     *
     * @return bool|\Criteria
     */
    public function getCatRestrictCriteria($permType = 'public_access')
    {
        if (null !== $GLOBALS['xoopsUser'] && \is_object($GLOBALS['xoopsUser'])) {
            $permHandler       = $this->getPermHandler();
            $allowedCategories = $permHandler->getAuthorizedPublicCat($GLOBALS['xoopsUser'], $permType);

            $count = \count($allowedCategories);
            if ($count > 0) {
                $in = '(' . $allowedCategories[0];
                \array_shift($allowedCategories);
                foreach ($allowedCategories as $allowedCategory) {
                    $in .= ',' . $allowedCategory;
                }
                $in       .= ')';
                $criteria = new \Criteria('cat_id', $in, 'IN');
            } else {
                $criteria = new \Criteria('cat_id', '(0)', 'IN');
            }

            return $criteria;
        }

        return false;
    }

    /**
     * @return Extgallery\PublicPermHandler
     */
    public function getPermHandler()
    {
        return Extgallery\PublicPermHandler::getInstance();
    }
}