luyadev/luya-module-admin

View on GitHub
src/ngrest/base/ActiveWindow.php

Summary

Maintainability
A
3 hrs
Test Coverage
F
55%
<?php

namespace luya\admin\ngrest\base;

use luya\admin\components\Auth;
use luya\admin\ngrest\NgRestButtonConditionInterface;
use luya\admin\ngrest\NgRestPermissionLevelInterface;
use luya\Exception;
use luya\helpers\StringHelper;
use luya\helpers\Url;
use Yii;
use yii\base\InvalidCallException;
use yii\base\ViewContextInterface;
use yii\helpers\Inflector;

/**
 * Base class for all ActiveWindow classes.
 *
 * An ActiveWindow is basically a custom view which renders your data attached to a row in the CRUD grid table.
 *
 * @property integer $itemId The Id of the item
 * @property \luya\admin\ngrest\base\ActiveWindowView $view The view object
 * @property string $name Get the current Active Window Name
 * @property string $hashName Get an unique hased Active Window config name
 * @property \luya\admin\ngrest\base\NgRestModel $model The model evaluated by the `findOne` of the called ng rest model ActiveRecord.
 *
 * @author Basil Suter <basil@nadar.io>
 * @since 1.0.0
 */
abstract class ActiveWindow extends BaseActiveResponse implements ViewContextInterface, ActiveWindowInterface, NgRestButtonConditionInterface, NgRestPermissionLevelInterface
{
    /**
     * @var string $suffix The suffix to use for all classes
     */
    protected $suffix = 'ActiveWindow';

    /**
     * @var string The class name of the called class where the actice window is bound to.
     */
    public $ngRestModelClass;

    /**
     * @var string the module name in where the active window context is loaded, in order to find view files.
     */
    public $module;
    /**
     * @inheritdoc
     */
    public function init()
    {
        parent::init();

        if ($this->module === null) {
            throw new Exception('The ActiveWindow property \'module\' of '.static::class.' can not be null. You have to defined the module in where the ActiveWindow is defined. For example `public $module = \'@admin\';`');
        }
    }

    private $_model;

    /**
     * Get the model object from where the Active Window is attached to.
     *
     * @return \luya\admin\ngrest\base\NgRestModel Get the model of the called ngrest model ActiveRecord by it's itemId.
     */
    public function getModel()
    {
        if ($this->_model === null && $this->ngRestModelClass !== null) {
            $this->_model = call_user_func_array([$this->ngRestModelClass, 'ngRestByPrimaryKeyOne'], $this->itemIds);
        }

        return $this->_model;
    }

    private $_configHash;

    /**
     * @inheritdoc
     */
    public function setConfigHash($hash)
    {
        $this->_configHash = $hash;
    }

    /**
     * Return the config hash name from the setter method.
     *
     * @return string
     */
    public function getConfigHash()
    {
        return $this->_configHash;
    }

    private $_activeWindowHash;

    /**
     * @inheritdoc
     */
    public function setActiveWindowHash($hash)
    {
        $this->_activeWindowHash = $hash;
    }

    /**
     * Get the active window hash from the setter method.
     * @return string
     */
    public function getActiveWindowHash()
    {
        return $this->_activeWindowHash;
    }

    /**
     * Create an absolute link to a callback.
     *
     * This method is commonly used when returing data directly to the browser, there for the abolute url to a callback is required. Only logged in
     * users can view the callback url, but there is no other security about callbacks.
     *
     * @param string $callback The name of the callback without the callback prefix exmaple `createPdf` if the callback is `callbackCreatePdf()`.
     * @return string The absolute url to the callback.
     */
    public function createCallbackUrl($callback)
    {
        return Url::to([
            '/admin/'.$this->model->ngRestApiEndpoint().'/active-window-callback',
            'activeWindowCallback' => Inflector::camel2id($callback),
            'ngrestConfigHash' => $this->getConfigHash(),
            'activeWindowHash' => $this->getActiveWindowHash(),
        ], true);
    }

    /**
     *
     * MIME: https://wiki.selfhtml.org/wiki/Referenz:MIME-Typen
     * @param string $fileName
     * @param string $mimeType
     * @param string $content
     * @return string
     */
    public function createDownloadableFileUrl($fileName, $mimeType, $content)
    {
        $key = uniqid(microtime().Inflector::slug($fileName), true);

        if (!Yii::$app->cache->set(['download', $key], $content, 60 * 60)) {
            return false;
        }

        $menu = Yii::$app->adminmenu->getApiDetail($this->model->ngRestApiEndpoint());
        $route = str_replace("/index", "/export-download", $menu['route']);

        Yii::$app->session->set('tempNgRestFileName', $fileName);
        Yii::$app->session->set('tempNgRestFileKey', $key);
        Yii::$app->session->set('tempNgRestFileMime', $mimeType);

        $url = Url::toRoute(['/'.$route], true);
        $param = http_build_query(['key' => base64_encode($key), 'time' => time()]);

        if (StringHelper::contains('?', $url)) {
            return $url . "&" . $param;
        } else {
            return $url . "?" . $param;
        }
    }

    /**
     * If no label value is provided via getter/setter, this value is used.
     *
     * You can override this method in order to provide a default label for your Active Window.
     *
     * @return boolean|string
     */
    public function defaultLabel()
    {
        return false;
    }

    private $_label;

    /**
     * Setter method for the Label.
     *
     * @param string $label The active window label.
     */
    public function setLabel($label)
    {
        $this->_label = $label;
    }

    /**
     * @inheritdoc
     */
    public function getLabel()
    {
        return empty($this->_label) ? $this->defaultLabel() : $this->_label;
    }

    /**
     * @inheritdoc
     */
    public function getTitle()
    {
        return $this->getLabel();
    }

    /**
     * If no extenion is set, this value is used.
     *
     * You can override this method in order to provide a default icon for your Active Window.
     *
     * @return string
     */
    public function defaultIcon()
    {
        return 'extension';
    }

    private $_icon;

    /**
     * Setter method for the icon
     * @param string $icon
     */
    public function setIcon($icon)
    {
        $this->_icon = $icon;
    }

    /**
     * @inheritdoc
     */
    public function getIcon()
    {
        return empty($this->_icon) ? $this->defaultIcon() : $this->_icon;
    }

    private $_name;

    /**
     * Get the ActiveWindow name based on its class short name.
     *
     * @return string
     */
    public function getName()
    {
        if ($this->_name === null) {
            $this->_name = ((new \ReflectionClass($this))->getShortName());
        }

        return $this->_name;
    }

    private $_viewFolderName;

    /**
     * Get the folder name where the views for this ActiveWindow should be stored.
     *
     * @return string
     */
    public function getViewFolderName()
    {
        if ($this->_viewFolderName === null) {
            $name = $this->getName();

            if (StringHelper::endsWith($name, $this->suffix, false)) {
                $name = substr($name, 0, -(strlen($this->suffix)));
            }

            $this->_viewFolderName = strtolower($name);
        }

        return $this->_viewFolderName;
    }

    private $_hashName;

    /**
     * Get a unique identifier hash based on the name and config values like icon and label.
     *
     * @return string
     */
    public function getHashName()
    {
        if ($this->_hashName === null) {
            $this->_hashName = sha1(static::class);
        }

        return $this->_hashName;
    }

    /**
     * Return the view path for view context.
     *
     * {@inheritDoc}
     * @see \yii\base\ViewContextInterface::getViewPath()
     */
    public function getViewPath()
    {
        $module = $this->module;

        if (!str_starts_with($module, '@')) {
            $module = '@'.$module;
        }

        return implode(DIRECTORY_SEPARATOR, [Yii::getAlias($module), 'views', 'aws', $this->getViewFolderName()]);
    }

    private $_view;

    /**
     * Get the view object to render templates.
     *
     * @return \luya\admin\ngrest\base\ActiveWindowView
     */
    public function getView()
    {
        if ($this->_view === null) {
            $this->_view = new ActiveWindowView();
        }

        return $this->_view;
    }

    /**
     * Render a template with its name and params based on the view folder path.
     *
     * @param string $name The view file to render
     * @param array $params Optional params to assign into the view
     * @return string
     */
    public function render($name, array $params = [])
    {
        return $this->getView()->render($name, $params, $this);
    }

    private $_itemId;

    /**
     * @inheritdoc
     */
    public function setItemId($id)
    {
        $this->_itemId = $id;
    }

    /**
     * @inheritdoc
     */
    public function getItemId()
    {
        return $this->getIsCompositeItem() ? $this->getItemIds() : $this->getItemIds()[0];
    }

    private $_condition;

    /**
     * @inheritdoc
     */
    public function setCondition($condition)
    {
        $this->_condition = $condition;
    }

    /**
     * @inheritdoc
     */
    public function getCondition()
    {
        return empty($this->_condition) ? '' : $this->_condition;
    }

    private int $_permissionLevel = Auth::CAN_UPDATE;

    /**
     * @inheritdoc
     */
    public function setPermissionLevel($permissionLevel)
    {
        $this->_permissionLevel = (int) $permissionLevel;
    }

    /**
     * @inheritdoc
     */
    public function getPermissionLevel()
    {
        return $this->_permissionLevel;
    }

    /**
     * @inheritdoc
     */
    public function getIsCompositeItem()
    {
        return count($this->getItemIds()) > 1 ? true : false;
    }

    /**
     * @inheritdoc
     */
    public function getItemIds()
    {
        if (empty($this->_itemId)) {
            throw new InvalidCallException("Unable to determine the active window item id.");
        }

        return explode(",", $this->_itemId);
    }
}