<?php
/**
 * The control file of cron of XXB.
 *
 * @copyright   Copyright 2009-2023 禅道软件（青岛）有限公司(ZenTao Software (Qingdao) Co., Ltd., www.zentao.net)
 * @license     ZOSL (https://zpl.pub/page/zoslv1.html)
 * @author      Yidong Wang <yidong@cnezsoft.com>
 * @package     cron
 * @version     $Id$
 * @link        https://xuanim.com
 */
class cron extends control
{
    /**
     * Index page.
     *
     * @access public
     * @return void
     */
    public function index()
    {
        $this->lang->menuGroups->cron = 'system';
        $this->lang->cron->menu       = $this->lang->system->menu;
        $this->lang->cron->menuOrder  = $this->lang->system->menuOrder;

        $this->view->title = $this->lang->cron->common;
        $this->view->crons = $this->cron->getCrons();
        $this->display();
    }

    /**
     * Turnon cron.
     *
     * @access public
     * @return void
     */
    public function turnon($confirm = 'no')
    {
        $turnon = empty($this->config->global->cron) ? 1 : 0;
        if(!$turnon and $confirm == 'no') die(js::confirm($this->lang->cron->confirmTurnon, inlink('turnon', "confirm=yes")) . js::reload('parent'));
        $this->loadModel('setting')->setItem('system.sys.common.global.cron', $turnon);
        die(js::reload('parent'));
    }

    /**
     * Create cron.
     *
     * @access public
     * @return void
     */
    public function create()
    {
        if($_POST)
        {
            $this->cron->create();
            if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError()));
            $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('index')));
        }

        $this->view->title = $this->lang->cron->create . $this->lang->cron->common;
        $this->display();
    }

    /**
     * Edit cron.
     *
     * @param  int    $cronID
     * @access public
     * @return void
     */
    public function edit($cronID)
    {
        if($_POST)
        {
            $this->cron->update($cronID);
            if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError()));
            $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('index')));
        }

        $this->view->title = $this->lang->cron->edit . $this->lang->cron->common;
        $this->view->cron  = $this->cron->getById($cronID);
        $this->display();
    }

    /**
     * Toggle run cron.
     *
     * @param  int    $cronID
     * @param  int    $status
     * @access public
     * @return void
     */
    public function toggle($cronID, $status)
    {
        $this->cron->changeStatus($cronID, $status);
        $this->locate(inlink('index'));
    }

    /**
     * Delete cron.
     *
     * @param  int    $cronID
     * @param  string $confirm
     * @access public
     * @return void
     */
    public function delete($cronID, $confirm = 'no')
    {
        $this->dao->delete()->from(TABLE_CRON)->where('id')->eq($cronID)->exec();
        if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::Error()));
        $this->send(array('result' => 'success'));
    }

    /**
     * Ajax exec cron.
     *
     * @access public
     * @return void
     */
    public function ajaxExec()
    {
        ignore_user_abort(true);
        set_time_limit(0);
        session_write_close();
        /* Check cron turnon. */
        if(empty($this->config->global->cron)) die();

        /* make cron status to running. */
        $configID = $this->cron->getConfigID();
        $configID = $this->cron->markCronStatus('running', $configID);

        /* Get and parse crons. */
        $crons       = $this->cron->getCrons('nostop');
        $parsedCrons = $this->cron->parseCron($crons);

        /* Update last time. */
        $this->cron->changeStatus(key($parsedCrons), 'normal', true);
        $startedTime = time();
        while(true)
        {
            dao::$cache = array();
            /* When cron is null then die. */
            if(empty($crons)) break;
            if(empty($parsedCrons)) break;
            if(!$this->config->global->cron) break;

            /* Run crons. */
            $now = new datetime('now');
            foreach($parsedCrons as $id => $cron)
            {
                $cronInfo = $this->cron->getById($id);
                /* Skip empty and stop cron.*/
                if(empty($cronInfo) or $cronInfo->status == 'stop') continue;
                /* Skip cron that status is running and run time is less than max. */
                if($cronInfo->status == 'running' and (time() - strtotime($cronInfo->lastTime)) < $this->config->cron->maxRunTime) continue;
                /* Skip cron that last time is more than this cron time. */
                if($cronInfo->lastTime > $cron['time']->format(DT_DATETIME1)) die();

                if($now > $cron['time'])
                {
                    $this->cron->changeStatus($id, 'running');
                    $parsedCrons[$id]['time'] = $cron['cron']->getNextRunDate();

                    /* Execution command. */
                    $output = '';
                    $return = '';
                    if($cron['command'])
                    {
                        if(isset($crons[$id]) and $crons[$id]->type == 'ranzhi')
                        {
                            parse_str($cron['command'], $customParams);
                            unset($customParams['moduleName']);
                            unset($customParams['methodName']);
                            unset($customParams['appName']);

                            parse_str($cron['command'], $params);
                            if(isset($params['moduleName']) and isset($params['methodName']) and isset($params['appName']))
                            {
                                $output = $this->fetch($params['moduleName'], $params['methodName'], $customParams, $params['appName']);
                            }
                        }
                        elseif(isset($crons[$id]) and $crons[$id]->type == 'system')
                        {
                            exec($cron['command'], $output, $return);
                            if($output) $output = join("\n", $output);
                        }

                        /* Save log. */
                        $log  = '';
                        $time = $now->format('G:i:s');
                        $log  = "$time task " .  $id . " executed,\ncommand: $cron[command].\nreturn : $return.\noutput : $output\n";
                        $this->cron->logCron($log);
                        unset($log);
                    }

                    /* Revert cron status. */
                    $this->cron->changeStatus($id, 'normal');
                }
            }

            /* Check whether the task change. */
            $newCrons = $this->cron->getCrons('nostop');
            $changed  = $this->cron->checkChange();
            if(count($newCrons) != count($crons) or $changed)
            {
                $crons       = $newCrons;
                $parsedCrons = $this->cron->parseCron($newCrons);
            }

            /* Sleep some seconds. */
            $sleepTime = 60 - ((time() - $now->getTimestamp()) % 60);
            sleep($sleepTime);

            /* Break while. */
            if(connection_status() != CONNECTION_NORMAL) break;
            if(((time() - $startedTime) / 3600 / 24) >= $this->config->cron->maxRunDays) break;
        }

        /* Revert cron status to stop. */
        $this->cron->markCronStatus('stop', $configID);
    }
}
