Source of file AdminEvensimpler.php

Size: 19,867 Bytes - Last Modified: 2016-05-18T03:08:27+02:00

buildproject/core/module_system/admin/AdminEvensimpler.php

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
<?php
/*"******************************************************************************************************
*   (c) 2007-2012 by Kajona, www.kajona.de                                                              *
*       Published under the GNU LGPL v2.1, see /system/licence_lgpl.txt                                 *
*-------------------------------------------------------------------------------------------------------*
*	$Id$	                            *
********************************************************************************************************/

namespace Kajona\System\Admin;

use Kajona\System\Admin\Formentries\FormentryHidden;
use Kajona\System\System\ArraySectionIterator;
use Kajona\System\System\Exception;
use Kajona\System\System\FilterBase;
use Kajona\System\System\Link;
use Kajona\System\System\Model;
use Kajona\System\System\ModelInterface;
use Kajona\System\System\Reflection;
use ReflectionMethod;


/**
 * Class holding common methods for extended and simplified admin-guis.
 * Compared to AdminSimple, this implementation is based on a declarative approach,
 * reducing the amount of code required to implement a modules' admin-views.
 *
 * Subclasses are able to declare actions and the matching objects with annotations.
 * Have a look at the demo-module on how to use it.
 *
 * @module module_system
 * @since 4.1
 *
 * @author tim.kiefer@kojikui.de, ph.wolfer@googlemail.com
 */
abstract class AdminEvensimpler extends AdminSimple
{
    const   STR_OBJECT_LIST_ANNOTATION = "@objectList";
    const   STR_OBJECT_NEW_ANNOTATION = "@objectNew";
    const   STR_OBJECT_EDIT_ANNOTATION = "@objectEdit";

    const   STR_OBJECT_LISTFILTER_ANNOTATION = "@objectFilter";

    private static $arrActionNameMapping = array(
        "list"   => self::STR_OBJECT_LIST_ANNOTATION,
        "new"    => self::STR_OBJECT_NEW_ANNOTATION,
        "edit"   => self::STR_OBJECT_EDIT_ANNOTATION,
        "save"   => self::STR_OBJECT_EDIT_ANNOTATION,
        "delete" => self::STR_OBJECT_EDIT_ANNOTATION
    );

    private $strCurObjectClassName;
    private $strCurObjectTypeName = "";

    private $strOriginalAction = "";

    /**
     * @var AdminFormgenerator
     */
    private $objCurAdminForm = null;


    /**
     * Redefined action-handler to match the declarative action to a "real", implemented action.
     * The permission handling and other stuff is checked by the base-class.
     *
     * @param string $strAction
     *
     * @return string
     */
    public function action($strAction = "")
    {
        if($strAction == "") {
            $strActionName = $this->getAction();
        }
        else {
            $strActionName = $strAction;
        }

        $this->strOriginalAction = $strActionName;

        if(!$this->checkMethodExistsInConcreteClass("action".ucfirst($strActionName))) {

            foreach(self::$arrActionNameMapping as $strAutoMatchAction => $strAnnotation) {
                $this->autoMatchAction($strAutoMatchAction, $strAnnotation, $strActionName);
            }
        }

        return parent::action($strActionName);
    }

    /**
     * Tries to get the name of an action (edit, delete, list, new, save) for a given object-type.
     * Example: Converts list to listOtherObject for the object DemoDemo if the annotation
     *          @ objectListOtherObject Kajona\Demo\System\DemoDemo is declared
     *
     * @param string $strAction
     * @param $objInstance
     *
     * @return string
     */
    protected function getActionNameForClass($strAction, $objInstance)
    {
        if(isset(self::$arrActionNameMapping[$strAction])) {
            $strAnnotationPrefix = self::$arrActionNameMapping[$strAction];

            if($strAction == "new") {
                return $strAction.$this->getStrCurObjectTypeName();
            }
            else {
                $objReflection = new Reflection($this);
                $arrAnnotations = $objReflection->getAnnotationsWithValueFromClass(get_class($objInstance));

                foreach($arrAnnotations as $strProperty) {
                    if(uniStrpos($strProperty, $strAnnotationPrefix) === 0) {
                        return $strAction.uniSubstr($strProperty, uniStrlen($strAnnotationPrefix));
                    }
                }
            }
        }

        return parent::getActionNameForClass($strAction, $objInstance);
    }


    /**
     * Helper method to resolve the declared action to a real action, so to make list out of listOtherObject.
     * If possible, the current object-type class (based on the annotation is stored, too.
     *
     * @param string $strAutoMatchAction
     * @param string $strAnnotation
     * @param string $strActionName
     *
     * @return void
     */
    private function autoMatchAction($strAutoMatchAction, $strAnnotation, &$strActionName)
    {

        if(uniStrpos($strActionName, $strAutoMatchAction) === 0) {
            // Set name of current list object
            $this->setStrCurObjectTypeName(uniStrReplace($strAutoMatchAction, "", $strActionName));
            $strActionName = $strAutoMatchAction;

            $objReflection = new Reflection($this);
            $arrAnnotations = $objReflection->getAnnotationValuesFromClass($strAnnotation.$this->getStrCurObjectTypeName());
            if(count($arrAnnotations) > 0) {
                $this->setCurObjectClassName(reset($arrAnnotations));
            }
            else {
                $this->setCurObjectClassName(null);
            }
        }
    }

    /**
     * Check if method exists in concrete class and not only in AdminSimple
     *
     * @param string $strMethod
     *
     * @internal param $strActionName
     * @return bool
     */
    protected function checkMethodExistsInConcreteClass($strMethod)
    {

        if(method_exists($this, $strMethod)) {
            $objRefl = new ReflectionMethod($this, $strMethod);

            if($objRefl->class != "Kajona\\System\\Admin\\AdminEvensimpler") {
                return true;
            }
            else {
                return false;
            }
        }
        return false;
    }


    /**
     * Renders the form to create a new entry
     *
     * @throws Exception
     * @return string
     * @permissions edit
     */
    protected function actionNew()
    {
        $strType = $this->getCurObjectClassName();

        if(!is_null($strType)) {
            /** @var $objEdit ModelInterface|Model */
            $objEdit = new $strType();

            $objForm = AdminFormgeneratorFactory::getFormForModel($objEdit);
            if($objForm !== null) {
                $objEdit = $objForm->getObjSourceobject();
            }

            //reset the current object reference to an object created before (e.g. during actionSave)
            $objForm = $this->getAdminForm($objEdit);
            $objForm->getObjSourceobject()->setSystemid($this->getParam("systemid"));
            $objForm->addField(new FormentryHidden("", "mode"))->setStrValue("new");

            return $objForm->renderForm(Link::getLinkAdminHref($this->getArrModule("modul"), "save".$this->getStrCurObjectTypeName()));
        }
        else {
            throw new Exception("error creating new entry current object type not known ", Exception::$level_ERROR);
        }
    }


    /**
     * Renders the form to edit an existing entry
     *
     * @throws Exception
     * @return string
     * @permissions edit
     */
    protected function actionEdit()
    {

        //try 1: get the object type and names based on the current object
        $objInstance = $this->objFactory->getObject($this->getSystemid());

        if($objInstance == null) {
            throw new Exception("given object with system id {$this->getSystemid()} does not exist", Exception::$level_ERROR);
        }

        $strObjectTypeName = uniSubstr($this->getActionNameForClass("edit", $objInstance), 4);
        if($strObjectTypeName != "") {
            $strType = get_class($objInstance);
            $this->setCurObjectClassName($strType);
            $this->setStrCurObjectTypeName($strObjectTypeName);
        }

        //try 2: regular, oldschool resolving based on the current action-params
        $strType = $this->getCurObjectClassName();

        if(!is_null($strType)) {

            //reset the current object reference to an object created before (e.g. during actionSave)
            $objForm = AdminFormgeneratorFactory::getFormForModel($objInstance);
            if($objForm !== null) {
                $objInstance = $objForm->getObjSourceobject();
            }

            $objForm = $this->getAdminForm($objInstance);
            $objForm->addField(new FormentryHidden("", "mode"))->setStrValue("edit");

            return $objForm->renderForm(Link::getLinkAdminHref($this->getArrModule("modul"), "save".$this->getStrCurObjectTypeName()));
        }
        else {
            throw new Exception("error editing current object type not known ", Exception::$level_ERROR);
        }
    }


    /**
     * Checks if for the current $strCurObjectTypeName a filter object was defined in Annotation STR_OBJECT_LISTFILTER_ANNOTATION.
     *
     * @param $strCurObjectTypeName
     *
     * @return null|string
     */
    private function getObjectFilterClass($strCurObjectTypeName)
    {

        $objReflection = new Reflection($this);
        $arrAnnotations = $objReflection->getAnnotationValuesFromClass(self::STR_OBJECT_LISTFILTER_ANNOTATION.$strCurObjectTypeName);

        if(count($arrAnnotations) > 0) {
            return reset($arrAnnotations);
        }

        return null;
    }

    /**
     * Renders the general list of records
     *
     * @throws Exception
     * @return string
     * @permissions view
     */
    protected function actionList()
    {
        /** @var $strType ModelInterface|Model */
        $strType = $this->getCurObjectClassName();

        if(!is_null($strType)) {

            /* pass the internal action in order to get a proper paging */
            $strOriginalAction = $this->getAction();
            $this->setAction($this->strOriginalAction);


            /* Create filter for list */
            $objFilter = null;
            $strFilterForm = "";
            $strObjectFilterClass = $this->getObjectFilterClass($this->getStrCurObjectTypeName());
            if($strObjectFilterClass !== null) {
                /** @var FilterBase $objFilter */
                $objFilter = new $strObjectFilterClass();
                $objFilter = $objFilter::getOrCreateFromSession();
                $strFilterForm = $this->renderFilter($objFilter);
                if($strFilterForm === AdminFormgeneratorFilter::STR_FILTER_REDIRECT) {
                    return "";
                }
            }

            /* Create list */
            $objArraySectionIterator = new ArraySectionIterator($strType::getObjectCountFiltered($objFilter, $this->getSystemid()));
            $objArraySectionIterator->setPageNumber((int)($this->getParam("pv") != "" ? $this->getParam("pv") : 1));
            $objArraySectionIterator->setArraySection($strType::getObjectListFiltered($objFilter, $this->getSystemid(), $objArraySectionIterator->calculateStartPos(), $objArraySectionIterator->calculateEndPos()));

            /* Render list and filter */
            $strList = $this->renderList($objArraySectionIterator, false, "list".$this->getStrCurObjectTypeName());
            $strList = $strFilterForm.$strList;

            $this->setAction($strOriginalAction);
            return $strList;
        }
        else {
            throw new Exception("error loading list current object type not known ", Exception::$level_ERROR);
        }
    }


    /**
     * Renders the filter form.
     * If FilterBase::STR_FILTER_REDIRECT is being returned an adminReload with given filter url is being trigerred.
     *
     * @param FilterBase $objFilter
     * @param string|null $strFilterUrl
     * @param boolean $bitInitiallyVisible
     *
     * @return string
     */
    public function renderFilter(FilterBase $objFilter, $strFilterUrl = null, $bitInitiallyVisible = false)
    {

        if($strFilterUrl === null) {
            $strFilterUrl = Link::getLinkAdminHref($this->getArrModule("module"), $this->getAction(), "&systemid=".$this->getSystemid());
        }

        if($objFilter->getBitFilterUpdated()) {
            $this->adminReload($strFilterUrl);
            return AdminFormgeneratorFilter::STR_FILTER_REDIRECT;
        }

        $objFilterForm = new AdminFormgeneratorFilter($objFilter->getFilterId(), $objFilter);
        $objFilterForm->setBitInitiallyVisible($bitInitiallyVisible);

        $strFilterForm = $objFilterForm->renderForm($strFilterUrl);

        return $strFilterForm;
    }

    /**
     * Creates the admin-form for a given object. You should specify a @formGenerator annotation in your model if you
     * want to override the default form
     *
     * @param ModelInterface|Model $objInstance
     *
     * @return AdminFormgenerator
     */
    protected function getAdminForm(ModelInterface $objInstance)
    {
        return $this->objCurAdminForm = AdminFormgeneratorFactory::createByModel($objInstance);
    }

    /**
     * Updates the source-object based on the passed form-params
     * and synchronizes it with the database.
     *
     * @throws Exception
     * @return string "" in case of success
     * @permissions edit
     */
    protected function actionSave()
    {
        $strType = $this->getCurObjectClassName();
        $strSystemId = "";

        if(!is_null($strType)) {

            /** @var $objRecord ModelInterface|Model */
            $objRecord = null;

            if($this->getParam("mode") == "new") {
                $objRecord = new $strType();
                $strSystemId = $this->getSystemid();
            }
            elseif($this->getParam("mode") == "edit") {
                $objRecord = new $strType($this->getSystemid());
            }

            if($objRecord != null) {
                $objForm = $this->getAdminForm($objRecord);
                if(!$objForm->validateForm()) {
                    if($this->getParam("mode") === "new") {
                        return $this->actionNew();
                    }
                    if($this->getParam("mode") === "edit") {
                        return $this->actionEdit();
                    }
                }

                $objForm->updateSourceObject();
                $objRecord = $objForm->getObjSourceobject();

                $this->persistModel($objRecord, $strSystemId);

                $this->setSystemid($objRecord->getStrSystemid());

                $this->adminReload(Link::getLinkAdminHref($this->getArrModule("modul"), $this->getActionNameForClass("list", $objRecord), "&systemid=".$objRecord->getStrPrevId().($this->getParam("pe") != "" ? "&peClose=1&blockAction=1" : "")));
                return "";
            }
        }
        else {
            throw new Exception("error on saving current object type not known ", Exception::$level_ERROR);
        }


        return $this->getLang("commons_error_permissions");
    }

    /**
     * Method which persists the record to the database
     *
     * @param Model $objModel
     * @param boolean $strPrevId
     *
     * @throws Exception
     */
    protected function persistModel(Model $objModel, $strPrevId = false)
    {
        $objModel->updateObjectToDb($strPrevId);
    }

    /**
     * Builds the object-path of the currently selected record.
     * Used to render the path-navigation in the backend.
     * Therefore the path from the current record up to the module-record is created based on the
     * common prev-id relation.
     * Each node is rendered using getOutputNaviEntry, so you may overwrite getOutputNaviEntry in
     * order to create the links based on an object.
     *
     * @return array
     * @see AdminEvensimpler::getOutputNaviEntry()
     */
    protected function getArrOutputNaviEntries()
    {

        $strOldAction = $this->getAction();
        $this->setAction($this->strOriginalAction);
        $arrPathLinks = parent::getArrOutputNaviEntries();
        $this->setAction($strOldAction);

        $arrPath = $this->getPathArray($this->getSystemid());

        // Render additional navigation path entries for child objects.
        foreach($arrPath as $strOneSystemid) {

            if(!validateSystemid($strOneSystemid)) {
                continue;
            }

            $objInstance = $this->objFactory->getObject($strOneSystemid);
            if($objInstance != null) {
                $objEntry = $this->getOutputNaviEntry($objInstance);
                if($objEntry != null) {
//                    $arrLink = splitUpLink($objEntry);
//                    if(uniStrlen($arrLink["name"] > 50))
//                        $objEntry = uniStrReplace($arrLink["name"], uniStrTrim($arrLink["name"], 50), $objEntry);
                    $arrPathLinks[] = $objEntry;
                }
            }

        }

        return $arrPathLinks;
    }


    /**
     * Overwrite to generate path navigation entries for the given object.
     * If not overwritten, the entries will be skipped and won't be included into the
     * path navigation.
     *
     * @param ModelInterface $objInstance
     *
     * @return string Navigation link.
     */
    protected function getOutputNaviEntry(ModelInterface $objInstance)
    {
        return null;
    }

    /**
     * Internal redefinition in order to match the internal, "real" action to
     * the action based on the passed, declarative action.
     *
     * @return string
     */
    protected function getQuickHelp()
    {
        $strOldAction = $this->getAction();
        $this->setAction($this->strOriginalAction);
        $strQuickhelp = parent::getQuickHelp();
        $this->setAction($strOldAction);
        return $strQuickhelp;
    }


    /**
     * Internal redefinition in order to match the internal, "real" action to
     * the action based on the passed, declarative action.
     *
     * @return string
     */
    protected function getOutputActionTitle()
    {
        if($this->getStrCurObjectTypeName() == "") {
            return $this->getOutputModuleTitle();
        }
        else {
            return $this->getLang($this->getObjLang()->stringToPlaceholder("modul_titel_".$this->getStrCurObjectTypeName()));
        }
    }


    public function setStrCurObjectTypeName($strCurObjectTypeName)
    {
        $this->strCurObjectTypeName = $strCurObjectTypeName;
    }

    public function getStrCurObjectTypeName()
    {
        return $this->strCurObjectTypeName;
    }

    public function setCurObjectClassName($strCurObjectTyp)
    {
        $this->strCurObjectClassName = $strCurObjectTyp;
    }

    public function getCurObjectClassName()
    {
        return $this->strCurObjectClassName;
    }

    /**
     * @param AdminFormgenerator $objCurAdminForm
     *
     * @deprecated
     */
    public function setObjCurAdminForm($objCurAdminForm)
    {
        $this->objCurAdminForm = $objCurAdminForm;
    }

    /**
     * @return AdminFormgenerator
     * @deprecated
     */
    public function getObjCurAdminForm()
    {
        return $this->objCurAdminForm;
    }


}