2 namespace wcf\data\cronjob
;
3 use wcf\data\cronjob\log\CronjobLogEditor
;
4 use wcf\data\user\User
;
5 use wcf\data\AbstractDatabaseObjectAction
;
6 use wcf\data\IToggleAction
;
7 use wcf\system\cronjob\CronjobScheduler
;
8 use wcf\system\cronjob\ICronjob
;
9 use wcf\system\exception\PermissionDeniedException
;
11 use wcf\util\DateUtil
;
14 * Executes cronjob-related actions.
16 * @author Tim Duesterhus, Alexander Ebert
17 * @copyright 2001-2018 WoltLab GmbH
18 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
19 * @package WoltLabSuite\Core\Data\Cronjob
21 * @method Cronjob create()
22 * @method CronjobEditor[] getObjects()
23 * @method CronjobEditor getSingleObject()
25 class CronjobAction
extends AbstractDatabaseObjectAction
implements IToggleAction
{
29 protected $className = CronjobEditor
::class;
34 protected $permissionsCreate = ['admin.management.canManageCronjob'];
39 protected $permissionsDelete = ['admin.management.canManageCronjob'];
44 protected $permissionsUpdate = ['admin.management.canManageCronjob'];
49 protected $allowGuestAccess = ['executeCronjobs'];
54 protected $requireACP = ['create', 'delete', 'update', 'toggle', 'execute'];
59 public function validateDelete() {
60 parent
::validateDelete();
62 foreach ($this->getObjects() as $cronjob) {
63 if (!$cronjob->isDeletable()) {
64 throw new PermissionDeniedException();
72 public function validateUpdate() {
73 parent
::validateUpdate();
75 foreach ($this->getObjects() as $cronjob) {
76 if (!$cronjob->isEditable()) {
77 throw new PermissionDeniedException();
85 public function validateToggle() {
86 parent
::validateUpdate();
88 foreach ($this->getObjects() as $cronjob) {
89 if (!$cronjob->canBeDisabled()) {
90 throw new PermissionDeniedException();
98 public function toggle() {
99 foreach ($this->getObjects() as $cronjob) {
101 'isDisabled' => $cronjob->isDisabled ?
0 : 1
107 * Validates the 'execute' action.
109 public function validateExecute() {
110 parent
::validateUpdate();
116 public function execute() {
119 // switch session owner to 'system' during execution of cronjobs
120 $actualUser = WCF
::getUser();
121 WCF
::getSession()->changeUser(new User(null, ['userID' => 0, 'username' => 'System']), true);
122 WCF
::getSession()->disableUpdate();
125 foreach ($this->getObjects() as $key => $cronjob) {
126 // mark them as pending
127 $cronjob->update(['state' => Cronjob
::PENDING
]);
130 foreach ($this->getObjects() as $cronjob) {
131 // it now time for executing
132 $cronjob->update(['state' => Cronjob
::EXECUTING
]);
133 $className = $cronjob->className
;
135 /** @var ICronjob $executable */
136 $executable = new $className();
141 // check if all required options are set for cronjob to be executed
142 // note: a general log is created to avoid confusion why a cronjob
143 // apparently is not executed while that is indeed the correct internal
145 if ($cronjob->validateOptions()) {
147 $executable->execute(new Cronjob($cronjob->cronjobID
));
149 catch (\Exception
$exception) { }
152 CronjobLogEditor
::create([
153 'cronjobID' => $cronjob->cronjobID
,
154 'execTime' => TIME_NOW
,
155 'success' => $exception ?
0 : 1,
156 'error' => $exception ?
$exception->getMessage() : ''
159 // calculate next exec-time
160 $nextExec = $cronjob->getNextExec();
162 'lastExec' => TIME_NOW
,
163 'nextExec' => $nextExec,
164 'afterNextExec' => $cronjob->getNextExec($nextExec +
120)
169 if ($cronjob->failCount
< Cronjob
::MAX_FAIL_COUNT
) {
170 $data['failCount'] = $cronjob->failCount +
1;
173 // cronjob failed too often: disable it
174 if ($cronjob->failCount +
1 == Cronjob
::MAX_FAIL_COUNT
) {
175 $data['isDisabled'] = 1;
178 // if no error: reset fail counter
180 $data['failCount'] = 0;
182 // if cronjob has been disabled because of too many
183 // failed executions, enable it again
184 if ($cronjob->failCount
== Cronjob
::MAX_FAIL_COUNT
&& $cronjob->isDisabled
) {
185 $data['isDisabled'] = 0;
189 $cronjob->update($data);
191 // build the return value
192 if ($exception === null && !$cronjob->isDisabled
) {
193 $dateTime = DateUtil
::getDateTimeByTimestamp($nextExec);
194 $return[$cronjob->cronjobID
] = [
196 'formatted' => str_replace(
198 DateUtil
::format($dateTime, DateUtil
::TIME_FORMAT
),
201 DateUtil
::format($dateTime, DateUtil
::DATE_FORMAT
),
202 WCF
::getLanguage()->get('wcf.date.dateTimeFormat')
209 $cronjob->update(['state' => Cronjob
::READY
]);
211 // throw exception again to show error message
218 // switch session back to the actual user
219 WCF
::getSession()->changeUser($actualUser, true);
226 * Validates the 'executeCronjobs' action.
228 public function validateExecuteCronjobs() {
233 * Executes open cronjobs.
235 public function executeCronjobs() {
236 // switch session owner to 'system' during execution of cronjobs
237 WCF
::getSession()->changeUser(new User(null, ['userID' => 0, 'username' => 'System']), true);
238 WCF
::getSession()->disableUpdate();
240 CronjobScheduler
::getInstance()->executeCronjobs();