2 namespace wcf\data\cronjob
;
3 use wcf\data\cronjob\log\CronjobLogEditor
;
4 use wcf\data\TDatabaseObjectToggle
;
5 use wcf\data\user\User
;
6 use wcf\data\AbstractDatabaseObjectAction
;
7 use wcf\data\IToggleAction
;
8 use wcf\system\cronjob\CronjobScheduler
;
9 use wcf\system\cronjob\ICronjob
;
10 use wcf\system\exception\PermissionDeniedException
;
12 use wcf\util\DateUtil
;
15 * Executes cronjob-related actions.
17 * @author Tim Duesterhus, Alexander Ebert
18 * @copyright 2001-2019 WoltLab GmbH
19 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
20 * @package WoltLabSuite\Core\Data\Cronjob
22 * @method Cronjob create()
23 * @method CronjobEditor[] getObjects()
24 * @method CronjobEditor getSingleObject()
26 class CronjobAction
extends AbstractDatabaseObjectAction
implements IToggleAction
{
27 use TDatabaseObjectToggle
;
32 protected $className = CronjobEditor
::class;
37 protected $permissionsCreate = ['admin.management.canManageCronjob'];
42 protected $permissionsDelete = ['admin.management.canManageCronjob'];
47 protected $permissionsUpdate = ['admin.management.canManageCronjob'];
52 protected $allowGuestAccess = ['executeCronjobs'];
57 protected $requireACP = ['create', 'delete', 'update', 'toggle', 'execute'];
62 public function validateDelete() {
63 parent
::validateDelete();
65 foreach ($this->getObjects() as $cronjob) {
66 if (!$cronjob->isDeletable()) {
67 throw new PermissionDeniedException();
75 public function validateUpdate() {
76 parent
::validateUpdate();
78 foreach ($this->getObjects() as $cronjob) {
79 if (!$cronjob->isEditable()) {
80 throw new PermissionDeniedException();
88 public function validateToggle() {
89 parent
::validateUpdate();
91 foreach ($this->getObjects() as $cronjob) {
92 if (!$cronjob->canBeDisabled()) {
93 throw new PermissionDeniedException();
99 * Validates the 'execute' action.
101 public function validateExecute() {
102 parent
::validateUpdate();
108 public function execute() {
111 // switch session owner to 'system' during execution of cronjobs
112 $actualUser = WCF
::getUser();
113 WCF
::getSession()->changeUser(new User(null, ['userID' => 0, 'username' => 'System']), true);
114 WCF
::getSession()->disableUpdate();
117 foreach ($this->getObjects() as $key => $cronjob) {
118 // mark them as pending
119 $cronjob->update(['state' => Cronjob
::PENDING
]);
122 foreach ($this->getObjects() as $cronjob) {
123 // it now time for executing
124 $cronjob->update(['state' => Cronjob
::EXECUTING
]);
125 $className = $cronjob->className
;
127 /** @var ICronjob $executable */
128 $executable = new $className();
133 // check if all required options are set for cronjob to be executed
134 // note: a general log is created to avoid confusion why a cronjob
135 // apparently is not executed while that is indeed the correct internal
137 if ($cronjob->validateOptions()) {
139 $executable->execute(new Cronjob($cronjob->cronjobID
));
141 catch (\Exception
$exception) { }
144 CronjobLogEditor
::create([
145 'cronjobID' => $cronjob->cronjobID
,
146 'execTime' => TIME_NOW
,
147 'success' => $exception ?
0 : 1,
148 'error' => $exception ?
$exception->getMessage() : ''
151 // calculate next exec-time
152 $nextExec = $cronjob->getNextExec();
154 'lastExec' => TIME_NOW
,
155 'nextExec' => $nextExec,
156 'afterNextExec' => $cronjob->getNextExec($nextExec +
120)
161 if ($cronjob->failCount
< Cronjob
::MAX_FAIL_COUNT
) {
162 $data['failCount'] = $cronjob->failCount +
1;
165 // cronjob failed too often: disable it
166 if ($cronjob->failCount +
1 == Cronjob
::MAX_FAIL_COUNT
) {
167 $data['isDisabled'] = 1;
170 // if no error: reset fail counter
172 $data['failCount'] = 0;
174 // if cronjob has been disabled because of too many
175 // failed executions, enable it again
176 if ($cronjob->failCount
== Cronjob
::MAX_FAIL_COUNT
&& $cronjob->isDisabled
) {
177 $data['isDisabled'] = 0;
181 $cronjob->update($data);
183 // build the return value
184 if ($exception === null && !$cronjob->isDisabled
) {
185 $dateTime = DateUtil
::getDateTimeByTimestamp($nextExec);
186 $return[$cronjob->cronjobID
] = [
188 'formatted' => str_replace(
190 DateUtil
::format($dateTime, DateUtil
::TIME_FORMAT
),
193 DateUtil
::format($dateTime, DateUtil
::DATE_FORMAT
),
194 WCF
::getLanguage()->get('wcf.date.dateTimeFormat')
201 $cronjob->update(['state' => Cronjob
::READY
]);
203 // throw exception again to show error message
210 // switch session back to the actual user
211 WCF
::getSession()->changeUser($actualUser, true);
218 * Validates the 'executeCronjobs' action.
220 public function validateExecuteCronjobs() {
225 * Executes open cronjobs.
227 public function executeCronjobs() {
228 // switch session owner to 'system' during execution of cronjobs
229 WCF
::getSession()->changeUser(new User(null, ['userID' => 0, 'username' => 'System']), true);
230 WCF
::getSession()->disableUpdate();
232 CronjobScheduler
::getInstance()->executeCronjobs();