View file upload/src/addons/AddonsLab/Core/Xf2/Admin/CrudController.php

File size: 12.66Kb
<?php
/** 
This software is furnished under a license and may be used and copied
only  in  accordance  with  the  terms  of such  license and with the
inclusion of the above copyright notice.  This software  or any other
copies thereof may not be provided or otherwise made available to any
other person.  No title to and  ownership of the  software is  hereby
transferred.                                                         
                                                                     
You may not reverse  engineer, decompile, defeat  license  encryption
mechanisms, or  disassemble this software product or software product
license.  AddonsLab may terminate this license if you don't comply with
any of these terms and conditions.  In such event,  licensee  agrees 
to return licensor  or destroy  all copies of software  upon termination 
of the license.
*/


namespace AddonsLab\Core\Xf2\Admin;

use AddonsLab\Core\Xf2\Admin\Field\AbstractRow;
use AddonsLab\Core\Xf2\AdminManagerInterface;
use AddonsLab\Core\Xf2\CrudEntityInterface;
use XF\Admin\Controller\AbstractController;
use XF\Mvc\Entity\Entity;
use XF\Mvc\Entity\Finder;
use XF\Mvc\ParameterBag;
use XF\Mvc\Reply\AbstractReply;
use XF\Mvc\Reply\View;

abstract class CrudController extends AbstractController implements AdminManagerInterface
{
    /**
     * @param $action
     * @param ParameterBag $params
     * @throws \XF\Mvc\Reply\Exception
     */
    protected function preDispatchController($action, ParameterBag $params)
    {
        parent::preDispatchController($action, $params);

        if ($this->config()->getAdminPermissionId())
        {
            $this->assertAdminPermission($this->config()->getAdminPermissionId());
        }
    }

    protected function postDispatchController($action, ParameterBag $params, AbstractReply &$reply)
    {
        parent::postDispatchController($action, $params, $reply);

        if ($reply instanceof View)
        {
            $reply->setParams($this->_mergeDefaultParams($reply->getParams(), $action));
        }
    }

    public function actionIndex(ParameterBag $params)
    {
        return $this->reroutePath($this->config()->getRoutePrefix() . '/list');
    }

    public function actionList(ParameterBag $params)
    {
        $order = $this->filter('order', 'str');
        $direction = $this->filter('direction', 'str');

        $page = $this->filterPage();
        $perPage = $this->_getPerPage();

        $showingAll = $this->filter('all', 'bool');

        if ($showingAll)
        {
            $page = 1;
            $perPage = 5000;
        }

        $finder = $this->getFinder();

        $finder->limitByPage($page, $perPage);

        $filter = $this->filter('_xfFilter', array(
            'text' => 'str',
            'prefix' => 'bool'
        ));

        $this->_filterFinder($finder, $filter);
        $this->_orderFinder($finder, $order, $direction);

        $total = $finder->total();
        $items = $finder->fetch();

        $this->assertValidPage($page, $perPage, $total, $this->config()->getRoutePrefix() . '/list');

        if (
            !strlen($filter['text'])
            && $total == 1 && ($item = $items->first())
            && $this->config()->hasViewPage()
            && $this->_redirectOnSingleItem()
        )
        {
            return $this->redirect($this->buildLink($this->config()->getRoutePrefix() . '/edit', $item));
        }

        // give extending controllers ability to do conversion/preparation of items
        $items = array_map(array($this, '_convertEntity'), $items->toArray());

        $viewParams = array(
            'items' => $items,

            'total' => $total,
            'page' => $page,
            'perPage' => $perPage,

            'showingAll' => $showingAll,
            'showAll' => (!$showingAll && $total <= 5000),
            'filter' => $filter['text'],
            'order' => $order,
            'direction' => $direction
        );

        return $this->view(
            $this->config()->getViewName('List'),
            $this->config()->getTemplateName('list'),
            $viewParams
        );
    }

    public function actionAdd(ParameterBag $params)
    {
        $item = $this->_getDefaultItem();

        return $this->view(
            $this->config()->getViewName('Add'),
            $this->config()->getTemplateName('edit'),
            array(
                'item' => $item,
                'form' => $this->config()->getEditForm(),
                'redirect' => $this->filter('redirect', 'str', 'on')
            )
        );
    }

    public function actionEdit(ParameterBag $params)
    {
        $itemId = $params->get($this->config()->getPrimaryKeyName());

        $item = $this->_assertItemExists($itemId);

        if (!$item)
        {
            return $this->notFound();
        }

        return $this->view(
            $this->config()->getViewName('Edit'),
            $this->config()->getTemplateName('edit'),
            array(
                'item' => $item,
                'form' => $this->config()->getEditForm(),
                'redirect' => $this->filter('redirect', 'str', 'on')
            )
        );
    }

    public function actionSave(ParameterBag $params)
    {
        $this->assertPostOnly();

        if ($itemId = $params->get($this->config()->getPrimaryKeyName()))
        {
            $item = $this->_assertItemExists($itemId);
            $this->_assertCanEditItem($item);
        }
        else
        {
            $item = $this->_getDefaultItem();
            $this->_assertCanAddItem($item);
        }

        $this->_itemSaveProcess($item)->run();

        return $this->redirect($this->buildLink($this->config()->getRoutePrefix(), null, array('last_item_id' => $item->getItemPrimaryKey())));
    }

    public function actionToggle(ParameterBag $params)
    {
        $active = $this->filter('active', 'array');
        foreach ($active AS $itemId => $isActive)
        {
            $isActive = (bool)$isActive;

            $item = $this->_assertItemExists($itemId);
            $this->_assertCanEditItem($item);
            $item->active = $isActive;
            $item->save();
        }


        return $this->redirect($this->buildLink($this->config()->getRoutePrefix(), null, array('last_item_id' => $item->getItemPrimaryKey())));
    }

    public function actionDelete(ParameterBag $params)
    {
        $itemId = $params->get($this->config()->getPrimaryKeyName());

        $item = $this->_assertItemExists($itemId);

        $this->_assertCanDeleteItem($item);

        if (!$item->preDelete())
        {
            return $this->error($item->getErrors());
        }

        if ($this->isPost())
        {
            $this->_setupDelete($item);
            $item->delete();

            return $this->redirect(
                $this->getDynamicRedirectIfNot(
                    $this->buildLink($this->config()->getRoutePrefix() . '/edit', $item),
                    $this->buildLink($this->config()->getRoutePrefix() . '/list')
                )
            );
        }
        else
        {
            return $this->view(
                $this->config()->getViewName('Delete'),
                $this->config()->getTemplateName('delete'),
                array('item' => $item)
            );
        }
    }

    /**
     * @param CrudEntityInterface|Entity $item
     * Child controls can override this to customize deletion
     */
    protected function _setupDelete(CrudEntityInterface $item)
    {

    }

    /**
     * @param CrudEntityInterface|Entity $entity
     * @return \XF\Mvc\FormAction
     */
    protected function _itemSaveProcess(CrudEntityInterface $entity)
    {
        $form = $this->formAction();

        $inputData = $this->_getInputData();
        $entityName = $this->config()->getEntityName();
        $entityData = isset($inputData[$entityName]) ? $inputData[$entityName] : array();

        $form->setupEntityInput($entity, $entityData)
            ->validateEntity($entity)
            ->saveEntity($entity);

        foreach ($inputData as $relationName => $input)
        {
            if ($relationName === $this->config()->getEntityName())
            {
                continue;
            }

            $relation = $entity->getRelationOrDefault($relationName, true);
            $form->setupEntityInput($relation, $input);
        }

        return $form;
    }

    /**
     * @return array
     */
    protected function _getInputData()
    {
        $fields = $this->config()->getEditForm()->getFields();

        $fields = array_filter(
            $fields,
            function (AbstractRow $field)
            {
                return !in_array($field->getType(), array(
                    AbstractRow::type_info,
                    AbstractRow::type_custom,
                ), true);
            }
        );

        $inputData = array();

        /** @var AbstractRow $field */
        foreach ($fields AS $field)
        {
            $fieldId = $field->getId();
            $entityName = $field->getRelationName();
            if (!$entityName)
            {
                $entityName = $this->config()->getEntityName();
            }

            $entityClass = $this->config()->getFullEntityName($entityName);

            $fullEntityClass = \XF::app()->em()->getEntityClassName($entityClass);
            $inputStructure = $fullEntityClass::getInputStructure();
            $inputData[$entityName][$fieldId] = $inputStructure[$fieldId];
        }

        foreach ($inputData AS $entityName => $entityInfo)
        {
            $entityData = $this->filter($entityName, 'array');
            $inputData[$entityName] = $this->filterArray($entityData, $entityInfo);
        }

        return $inputData;
    }

    /**
     * @return Finder
     */
    public function getFinder()
    {
        $finder = \XF::finder($this->config()->getFullEntityName(
            $this->config()->getListEntityName()
        ))->with(
            $this->config()->getListWith()
        );

        return $finder;
    }

    /**
     * @return Entity|CrudEntityInterface
     */
    protected function _getDefaultItem()
    {
        return \XF::em()->create($this->config()->getFullEntityName());
    }

    protected function _orderFinder(Finder $finder, $order, $direction)
    {
        if ($order)
        {
            $finder->order($order, $direction);
        }
    }


    /**
     * @param Finder $finder
     * @param array $filter
     * Should setup the finder object based on filter submitted
     */
    protected function _filterFinder(Finder $finder, array $filter)
    {

    }

    /**
     * @param Entity $entity
     * @throws \XF\Mvc\Reply\Exception
     * Entity-specific permissions can be implemented by child classes
     */
    protected function _assertCanEditItem(CrudEntityInterface $entity)
    {
        if (!$this->config()->hasSavePage())
        {
            throw $this->exception(
                $this->noPermission()
            );
        }
    }

    protected function _assertCanDeleteItem(CrudEntityInterface $entity)
    {
        if (!$this->config()->hasDeletePage())
        {
            throw $this->exception(
                $this->noPermission()
            );
        }
    }

    /**
     * @param Entity $entity
     */
    protected function _assertCanAddItem(CrudEntityInterface $entity)
    {
        if (!$this->config()->hasAddPage())
        {
            throw $this->exception(
                $this->noPermission()
            );
        }
    }

    /**
     * @param $id
     * @param null $with
     * @param null $phraseKey
     * @return Entity|CrudEntityInterface
     * @throws \XF\Mvc\Reply\Exception
     */
    protected function _assertItemExists($id, $with = null, $phraseKey = null)
    {
        $with = $this->_getEffectiveWith($with);
        return $this->assertRecordExists($this->config()->getFullEntityName(), $id, $with, $phraseKey);
    }

    /**
     * @param Entity $entity
     * @return CrudEntityInterface|Entity
     */
    protected function _convertEntity(Entity $entity)
    {
        return $entity;
    }

    protected function _getEffectiveWith($with = null)
    {
        if ($with === null)
        {
            $with = array();
        }

        $with = array_merge($with, $this->config()->getDefaultWith());

        return $with;
    }

    protected function _mergeDefaultParams(array $params, $action)
    {
        // add some default variables we can use in templates directly
        $params['config'] = $this->config();
        $params['routePrefix'] = $this->config()->getRoutePrefix();
        $params['filterForm'] = $this->config()->getFilterForm();

        return $params;
    }

    /**
     * @return int
     */
    protected function _getPerPage()
    {
        return 20;
    }

    protected function _redirectOnSingleItem()
    {
        return true;
    }
}