<?php
/**
 * Control类从baseControl类继承而来，每个模块的control对象从control类集成。
 * 您可以对baseControl类进行扩展，扩展的逻辑可以定义在这个文件中。
 *
 * This control class extends from the baseControl class and extened by every module's control. 
 * You can extend the baseControl class by change this file.
 *
 * @package framework
 *
 * The author disclaims copyright to this source code.  In place of
 * a legal notice, here is a blessing:
 *
 *  May you do good and not evil.
 *  May you find forgiveness for yourself and forgive others.
 *  May you share freely, never taking more than you give.
 */
include FRAME_ROOT . '/base/control.class.php';
class control extends baseControl
{
    /**
     * Append editor fields to this module config from workflow.
     * 
     * @param  string $moduleName 
     * @param  string $methodName 
     * @param  string $appName 
     * @access public
     * @return void
     */
    public function __construct($moduleName = '', $methodName = '', $appName = '')
    {
        parent::__construct($moduleName, $methodName, $appName);

        if(RUN_MODE == 'install' || RUN_MODE == 'upgrade') return false;
        if(!$this->app->isBiz) return false;

        /* Append editor field to this module config from workflow. */
        $textareaFields = $this->dao->select('*')->from(TABLE_WORKFLOWFIELD)->where('module')->eq($this->moduleName)->andWhere('control')->eq('richtext')->andWhere('buildin')->eq('0')->fetchAll('field');
        if($textareaFields)
        {
            $editorIdList = array();
            foreach($textareaFields as $textareaField) $editorIdList[] = $textareaField->field;

            if(!isset($this->config->{$this->moduleName})) $this->config->{$this->moduleName} = new stdclass();
            if(!isset($this->config->{$this->moduleName}->editor)) $this->config->{$this->moduleName}->editor = new stdclass();
            if(!isset($this->config->{$this->moduleName}->editor->{$this->methodName})) $this->config->{$this->moduleName}->editor->{$this->methodName} = array('id' => '', 'tools' => 'simple');
            $this->config->{$this->moduleName}->editor->{$this->methodName}['id'] .= ',' . join(',', $editorIdList);
            trim($this->config->{$this->moduleName}->editor->{$this->methodName}['id'], ',');
        }

        if($_POST or strpos($this->methodName, 'import') !== false)
        {
            $workflow = $this->dao->select('*')->from(TABLE_WORKFLOW)->where('module')->eq($this->moduleName)->andWhere('buildin')->eq('1')->fetch();
            if($workflow)
            {
                $extendFields = $this->getFlowExportFields();
                if($extendFields)
                {
                    foreach($extendFields as $field => $filedName)
                    {
                        if(!empty($this->config->{$this->moduleName}->list->exportFields))    $this->config->{$this->moduleName}->list->exportFields     .= ',' . $field;
                        if(!empty($this->config->{$this->moduleName}->excel->templateFields)) $this->config->{$this->moduleName}->excel->templateFields[] = $field;
                    }
                }
            }
        }

        if($this->app->user->account != 'guest') $this->setDefaultPrivByWorkflow();
    }

    /**
     * Det default priv by workflow.
     * 
     * @access public
     * @return void
     */
    public function setDefaultPrivByWorkflow()
    {
        $actionList = $this->dao->select('module, action')->from(TABLE_WORKFLOWACTION)
            ->where('createdBy')->eq($this->app->user->account)
            ->andWhere('buildin')->eq('0')
            ->fetchGroup('module');

        if($actionList)
        {
            foreach($actionList as $module => $actions)
            {
                foreach($actions as $action) $this->app->user->rights[$module][$action->action] = 1;
            }
        }

        $labelList = $this->dao->select('module, code')->from(TABLE_WORKFLOWLABEL)
            ->where('createdBy')->eq($this->app->user->account)
            ->andWhere('buildin')->eq('0')
            ->fetchGroup('module');

        if($labelList)
        {
            foreach($labelList as $module => $labels)
            {
                foreach($labels as $label)
                {
                    $code = str_replace('browse', '', $label->code);
                    $this->app->user->rights[$module][$code] = 1;
                }
            }
        }

        return true;
    }

    /**
     * Check if the user can execute this method on this object.
     *
     * @param  object $object
     * @param  string $message
     * @param  string $module
     * @param  string $method
     * @param  string $app
     * @access public
     * @return bool | array
     */
    public function checkPriv($object, $viewType = '', $message = '', $module = '', $method = '', $app = '')
    {
        if(!$module) $module = $this->app->getModuleName();
        if(!$app)    $app    = $this->app->getAppName();

        $model = $this->loadModel($module, $app);
        if(method_exists($model, 'isClickable'))
        {
            if(!$method) $method = $this->app->getMethodName();

            $access = $model->isClickable($object, $method);
            if(!$access)
            {
                if(!$viewType) $viewType = $this->viewType;
                if(!$message)  $message  = $this->lang->error->accessDenied;

                $isAPI  = defined('RUN_MODE') && RUN_MODE == 'api';
                $isJSON = $viewType == 'json';
                if($isAPI or $isJSON) return array('result' => 'fail', 'message' => $message);

                $locate     = helper::safe64Encode($this->server->http_referer);
                $noticeLink = $this->createLink('notice', 'index', "type=accessLimited&locate={$locate}");
                $this->locate($noticeLink);
            }
        }

        return true;
    }

    /**
     * 设置视图文件：主视图文件，扩展视图文件， 站点扩展视图文件，以及钩子脚本。
     * Set view files: the main file, extension view file, site extension view file and hook files.
     *
     * @param  string   $moduleName    module name
     * @param  string   $methodName    method name
     * @access public
     * @return string  the view file
     */
    public function setViewFile($moduleName, $methodName)
    {
        $moduleName = strtolower(trim($moduleName));
        $methodName = strtolower(trim($methodName));

        $modulePath  = $this->app->getModulePath($this->appName, $moduleName);
        $viewExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, 'view');

        $viewType     = ($this->viewType == 'mhtml' or $this->viewType == 'xhtml') ? 'html' : $this->viewType;
        $mainViewFile = $modulePath . 'view' . DS . $this->devicePrefix . $methodName . '.' . $viewType . '.php';

        /* If the main view file doesn't exist, set the device prefix to empty and reset the main view file. */
        if($this->viewType == 'mhtml' or $this->viewType == 'xhtml')
        {
            if(!file_exists($mainViewFile))
            {
                if($this->viewType == 'xhtml') $this->app->viewType = 'html';
                $this->devicePrefix = '';
                $mainViewFile = $modulePath . 'view' . DS . $this->devicePrefix . $methodName . '.' . $viewType . '.php';
            }
        }

        $viewFile = $mainViewFile;

        if(!empty($viewExtPath))
        {
            $commonExtViewFile = $viewExtPath['common'] . $this->devicePrefix . $methodName . ".{$viewType}.php";
            $siteExtViewFile   = empty($viewExtPath['site']) ? '' : $viewExtPath['site'] . $this->devicePrefix . $methodName . ".{$viewType}.php";

            $viewFile = file_exists($commonExtViewFile) ? $commonExtViewFile : $mainViewFile;
            $viewFile = (!empty($siteExtViewFile) and file_exists($siteExtViewFile)) ? $siteExtViewFile : $viewFile;
            if(!is_file($viewFile)) $this->app->triggerError("the view file $viewFile not found", __FILE__, __LINE__, $exit = true);

            $commonExtHookFiles = glob($viewExtPath['common'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php");
            $siteExtHookFiles   = empty($viewExtPath['site']) ? '' : glob($viewExtPath['site'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php");
            $extHookFiles       = array_merge((array) $commonExtHookFiles, (array) $siteExtHookFiles);
        }

        if(!empty($extHookFiles)) return array('viewFile' => $viewFile, 'hookFiles' => $extHookFiles);
        return $viewFile;
    }

    /**
     * 默认渲染方法，适用于viewType = html的时候。
     * Default parse method when viewType != json, like html.
     *
     * @param string $moduleName    module name
     * @param string $methodName    method name
     * @access public
     * @return void
     */
    public function parseDefault($moduleName, $methodName)
    {
        /**
         * 设置视图文件。(PHP7有一个bug，不能直接$viewFile = $this->setViewFile())。
         * Set viewFile. (Can't assign $viewFile = $this->setViewFile() directly because one php7's bug.)
         */
        $results  = $this->setViewFile($moduleName, $methodName);
        $viewFile = $results;
        if(is_array($results)) extract($results);

        /**
         * 获得当前页面的CSS和JS。
         * Get css and js codes for current method.
         */
        $css = $this->getCSS($moduleName, $methodName);
        $js  = $this->getJS($moduleName, $methodName);
        /* If the js or css file doesn't exist, set the device prefix to empty and reset the js or css file. */
        if($this->viewType == 'xhtml')
        {
            if(!$css)
            {
                $this->devicePrefix = '';
                $css = $this->getCSS($moduleName, $methodName);
            }
            if(!$js)
            {
                $this->devicePrefix = '';
                $js = $this->getJS($moduleName, $methodName);
            }
        }
        if($css) $this->view->pageCSS = $css;
        if($js)  $this->view->pageJS  = $js;

        /**
         * 切换到视图文件所在的目录，以保证视图文件里面的include语句能够正常运行。
         * Change the dir to the view file to keep the relative pathes work.
         */
        $currentPWD = getcwd();
        chdir(dirname($viewFile));

        /**
         * 使用extract安定ob方法渲染$viewFile里面的代码。
         * Use extract and ob functions to eval the codes in $viewFile.
         */
        extract((array)$this->view);
        ob_start();
        include $viewFile;
        if(isset($hookFiles)) foreach($hookFiles as $hookFile) if(file_exists($hookFile)) include $hookFile;
        $this->output .= ob_get_contents();
        ob_end_clean();

        /**
         * 渲染完毕后，再切换回之前的路径。
         * At the end, chang the dir to the previous.
         */
        chdir($currentPWD);
    }

    /**
     * 获取一个方法的输出内容，这样我们可以在一个方法里获取其他模块方法的内容。
     * 如果模块名为空，则调用该模块、该方法；如果设置了模块名，调用指定模块指定方法。
     *
     * Get the output of one module's one method as a string, thus in one module's method, can fetch other module's content.
     * If the module name is empty, then use the current module and method. If set, use the user defined module and method.
     *
     * @param   string  $moduleName    module name.
     * @param   string  $methodName    method name.
     * @param   array   $params        params.
     * @access  public
     * @return  string  the parsed html.
     */
    public function fetch($moduleName = '', $methodName = '', $params = array(), $appName = '')
    {
        if($moduleName == '') $moduleName = $this->moduleName;
        if($methodName == '') $methodName = $this->methodName;
        if($appName    == '') $appName    = $this->appName;
        if($moduleName == $this->moduleName and $methodName == $this->methodName) 
        {
            $this->parse($moduleName, $methodName);
            return $this->output;
        }

        $currentPWD = getcwd();

        /**
         * 设置引用的文件和路径。
         * Set the pathes and files to included.
         **/
        $modulePath        = $this->app->getModulePath($appName, $moduleName);
        $moduleControlFile = $modulePath . 'control.php';
        $actionExtPath     = $this->app->getModuleExtPath($appName, $moduleName, 'control');
        $file2Included     = $moduleControlFile;

        if(!empty($actionExtPath))
        {
            $commonActionExtFile = $actionExtPath['common'] . strtolower($methodName) . '.php';
            $file2Included       = file_exists($commonActionExtFile) ? $commonActionExtFile : $moduleControlFile;

            if(!empty($actionExtPath['site']))
            {
                $siteActionExtFile = $actionExtPath['site'] . strtolower($methodName) . '.php';
                $file2Included     = file_exists($siteActionExtFile) ? $siteActionExtFile : $file2Included;
            }
        }

        /**
         * 加载控制器文件。
         * Load the control file. 
         */
        if(!is_file($file2Included)) $this->app->triggerError("The control file $file2Included not found", __FILE__, __LINE__, $exit = true);
        chdir(dirname($file2Included));
        if($moduleName != $this->moduleName) helper::import($file2Included);

        /**
         * 设置调用的类名。
         * Set the name of the class to be called. 
         */
        $className = class_exists("my$moduleName") ? "my$moduleName" : $moduleName;
        if(!class_exists($className)) $this->app->triggerError(" The class $className not found", __FILE__, __LINE__, $exit = true);

        /**
         * 解析参数，创建模块control对象。
         * Parse the params, create the $module control object. 
         */
        if(!is_array($params)) parse_str($params, $params);
        $module = new $className($moduleName, $methodName, $appName);

        /**
         * 调用对应方法，使用ob方法获取输出内容。
         * Call the method and use ob function to get the output. 
         */
        ob_start();
        call_user_func_array(array($module, $methodName), $params);
        $output = ob_get_contents();
        ob_end_clean();

        /**
         * 返回内容。
         * Return the content. 
         */
        unset($module);

        chdir($currentPWD);
        return $output;
    }

    /**
     * Build operate menu of a module.
     *
     * @param  object $object
     * @param  string $type
     * @access public
     * @return string
     */
    public function buildOperateMenu($object, $type = 'browse')
    {
        if(!$this->app->isBiz) return false;

        $moduleName = $this->moduleName;
        return $this->$moduleName->buildOperateMenu($object, $type);
    }

    /**
     * Execute hooks of a method.
     *
     * @param  int    $objectID     The id of an object.
     * @access public
     * @return void
     */
    public function executeHooks($objectID)
    {
        if(!$this->app->isBiz) return false;

        $moduleName = $this->moduleName;
        return $this->$moduleName->executeHooks($objectID);
    }

    /**
     * Set workflow export fields 
     * 
     * @param  array  $fields 
     * @access public
     * @return array
     */
    public function getFlowExportFields()
    {
        if(!$this->app->isBiz) return array();

        $moduleName = $this->moduleName;
        return $this->$moduleName->getFlowExportFields();
    }

    /**
     * Print extend fields.
     *
     * @param  object $object
     * @param  string $type     The parent component which fileds displayed in. It should be table or div.
     * @param  string $extras   The extra params.
     *                          columns=2|3|5           Number of the columns merged to display the fields. The default is 1.
     *                          position=left|right     The position which the fields displayed in a page.
     *                          inForm=0|1              The fields displayed in a form or not. The default is 1.
     *                          inCell=0|1              The fields displayed in a div with class cell or not. The default is 0.
     * @param  bool   $print
     * @access public
     * @return void
     */
    public function printExtendFields($object, $type, $extras = '', $print = true)
    {
        if(!$this->app->isBiz) return false;

        $moduleName = $this->app->getModuleName();
        $methodName = $this->app->getMethodName();
        $fields     = $this->loadModel('flow')->printFields($moduleName, $methodName, $object, $type, $extras);
        if(!$print) return $fields;

        $jsRoot = $this->config->webRoot . "js/";
        $picker = file_get_contents($this->app->getBasePath() . 'app/sys/common/view/picker.html.php');
        $picker = substr($picker, strpos($picker, '<style>'));

        js::import($jsRoot . 'picker/min.js');
        css::import($jsRoot . 'picker/min.css');

        echo $picker . $fields;
    }

    /**
     * Process status of an object according to its subStatus.
     *
     * @param  string $module   order | contract | purchasecontract | customer | provider | contact | leads | product | invoice | feedback
     * @param  object $record   a record of above modules.
     * @access public
     * @return string
     */
    public function processStatus($module, $record)
    {
        $moduleName = $this->moduleName;

        return $this->$moduleName->processStatus($module, $record);
    }
}
