-
Notifications
You must be signed in to change notification settings - Fork 13
Update for Modx3 #38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Update for Modx3 #38
Conversation
add icon for menu
Remove run
add file browser for task's type as file
1.5.0 version
|
Thanks for the pull request! Some cool new features in there, too. Looks good from a code review, only spotted very minor things I would want to change. I'll try to find the time to do some proper testing with this on both 2.x and 3.x in the next few weeks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR updates Scheduler to better support MODX 3 while introducing reliability and performance improvements.
- Add MODX 3 compatibility (service container bootstrap, namespaced processors, dedicated MODX 3 build script).
- Introduce task retry logic (max_retries/retry_delay), improved failure notifications (templateable emails), and fix a run-processor race condition with atomic status updates.
- Add composite index (status, timing) for faster task selection and several UI updates (history grid retry count, icons, and settings).
Reviewed changes
Copilot reviewed 28 out of 30 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| core/components/scheduler/processors/mgr/runs/remove.class.php | Minor formatting; no functional changes |
| core/components/scheduler/processors/mgr/runs/multiple.class.php | Minor formatting; no functional changes |
| core/components/scheduler/processors/mgr/runs/history.class.php | Coding style cleanup; update action icon |
| core/components/scheduler/model/schema/scheduler.mysql.schema.xml | Add retry fields, composite index for sTaskRun |
| core/components/scheduler/model/scheduler/staskrun.class.php | Safer/clearer timing rounding and parsing |
| core/components/scheduler/model/scheduler/stask.class.php | Retry handling, Throwable catching, templateable failure emails |
| core/components/scheduler/model/scheduler/sprocessortask.class.php | MODX 3 namespaced processors support and path resolution |
| core/components/scheduler/model/scheduler/mysql/staskrun.map.inc.php | Map retry_count and status_timing index |
| core/components/scheduler/model/scheduler/mysql/stask.map.inc.php | Map max_retries and retry_delay |
| core/components/scheduler/lexicon/ru/default.inc.php | New settings/labels for retries and email templating (RU) |
| core/components/scheduler/lexicon/en/default.inc.php | New settings/labels (EN); minor text edit |
| core/components/scheduler/index.class.php | MODX 2/3 initialization; JS includes formatting |
| core/components/scheduler/docs/changelog.txt | New 1.7.0 section and reworked chronology/content |
| core/components/scheduler/controllers/index.class.php | Minor formatting |
| core/components/scheduler/bootstrap.php | MODX 3 service bootstrap (registers scheduler service) |
| assets/components/scheduler/run.php | Atomic status update to prevent race condition; timing/cleanup |
| assets/components/scheduler/js/mgr/widgets/windows.tasks.js | UI updates incl. retry fields; minor cleanups |
| assets/components/scheduler/js/mgr/widgets/grid.history.js | History grid columns (retry_count), actions, and refactors |
| assets/components/scheduler/js/mgr/misc/scheduler.utils.js | Minor whitespace cleanup |
| assets/components/scheduler/js/mgr/combos.js | Style cleanup; no functional changes |
| assets/components/scheduler/css/bootstrap.buttons.css | Formatting |
| _build/validators/requirements.script.php | Version checks updated; MODX 2/3 compatibility |
| _build/resolvers/resolve.tables.php | Table create/remove with MODX 2/3 compatibility |
| _build/resolvers/resolve.dbchanges.php | Upgrades include retry fields and composite index |
| _build/data/transport.menu.php | Menu icon updated (font icon) |
| _build/data/settings.php | New settings: email template/subject, tasks_per_run |
| _build/build.transport.php | Bump version to 1.7.0 |
| _build/build.transport.modx3.php | New MODX 3 build script |
| _build/build.schema.modx3.php | New MODX 3 schema build helper |
| _bootstrap/index.php | Path handling cleanup |
Comments suppressed due to low confidence (1)
assets/components/scheduler/js/mgr/widgets/grid.history.js:302
- This property is overwritten by another property in the same object literal.
, removeRun: function (btn, e) {
const ids = this._getSelectedIds();
if (!ids.length) {
return false;
}
Ext.MessageBox.confirm(
_('ms2_menu_remove_title'),
ids.length > 1
? _('scheduler.run_multiple_remove_confirm')
: _('scheduler.run_remove_confirm'),
function (val) {
if (val === 'yes') {
this.runAction('remove');
}
}, this
);
},
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review for a chance to win a $100 gift card. Take the survey.
| return false; | ||
| } | ||
| Ext.MessageBox.confirm( | ||
| _('ms2_menu_remove_title'), |
Copilot
AI
Dec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The confirmation title key references a MiniShop2 lexicon ('ms2_menu_remove_title'). Use a Scheduler lexicon key instead (e.g., _('scheduler.run_remove') or add a dedicated key like _('scheduler.menu_remove_title')).
| _('ms2_menu_remove_title'), | |
| _('scheduler.run_remove'), |
| , removeRun: function (btn, e) { | ||
| const ids = this._getSelectedIds(); | ||
| if (!ids.length) { | ||
| return false; | ||
| } | ||
| Ext.MessageBox.confirm( | ||
| _('ms2_menu_remove_title'), | ||
| ids.length > 1 | ||
| ? _('scheduler.run_multiple_remove_confirm') | ||
| : _('scheduler.run_remove_confirm'), | ||
| function (val) { | ||
| if (val === 'yes') { | ||
| this.runAction('remove'); | ||
| } | ||
| }, this | ||
| ); | ||
| }, | ||
| runAction(method) { | ||
| const ids = this._getSelectedIds(); | ||
| if (!ids.length) { | ||
| return false; | ||
| } | ||
| MODx.Ajax.request({ | ||
| url: this.config.url, | ||
| params: { | ||
| action: 'mgr/runs/multiple', | ||
| method: method, | ||
| ids: Ext.util.JSON.encode(ids), | ||
| }, | ||
| listeners: { | ||
| success: { | ||
| fn: function () { | ||
| //noinspection JSUnresolvedFunction | ||
| this.refresh(); | ||
| }, scope: this | ||
| }, | ||
| failure: { | ||
| fn: function (response) { | ||
| MODx.msg.alert(_('error'), response.message); | ||
| }, scope: this | ||
| }, | ||
| } | ||
| }) | ||
| } |
Copilot
AI
Dec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This block duplicates removeRun/runAction which are already defined earlier in the same object. Having duplicate method names in the object literal is confusing and the latter silently overrides the former. Please remove one set and keep a single implementation.
| $this->addJavascript($this->scheduler->config['jsUrl'] . 'mgr/scheduler.class.js'); | ||
| $this->addJavascript($this->scheduler->config['jsUrl'] . 'mgr/combos.js'); | ||
| $this->addHtml('<script type="text/javascript"> | ||
| Ext.onReady(function() { | ||
| Scheduler.config = '.$this->modx->toJSON($this->scheduler->config).'; | ||
| Scheduler.config = ' . $this->modx->toJSON($this->scheduler->config) . '; | ||
| }); |
Copilot
AI
Dec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential null dereference: if the MODX 3 service lookup fails (e.g., service not registered), $this->scheduler is null and accessing ->config will fatal. Guard against a missing service and either fall back to getService() or return an error/notice before using $this->scheduler.
| $newRun->setTiming('+' . $retryDelay . ' seconds'); | ||
|
|
||
| if ($newRun->save()) { | ||
| $this->xpdo->log(\xPDO::LOG_LEVEL_INFO, | ||
| "[Scheduler] Retry {$newRun->get('retry_count')}/{$maxRetries} scheduled for task {$this->get('namespace')}::{$this->get('reference')}" | ||
| ); | ||
| } | ||
| } |
Copilot
AI
Dec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check the return value of setTiming(). If it returns false (e.g., invalid strtotime input), saving will schedule a run with an invalid or zero timing. Suggest: if (!$newRun->setTiming('+' . $retryDelay . ' seconds')) { log and return; } before save.
| $_lang['scheduler.run_update'] = "Update scheduled run"; | ||
| $_lang['scheduler.run_remove'] = "Remove scheduled run"; | ||
| $_lang['scheduler.runs_remove'] = "Remove scheduled runs"; | ||
| $_lang['scheduler.runs_remove'] = "Remove scheduled runs"; |
Copilot
AI
Dec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct double space in the string.
| $_lang['scheduler.runs_remove'] = "Remove scheduled runs"; | |
| $_lang['scheduler.runs_remove'] = "Remove scheduled runs"; |
| $this->modx->log(1, $e->getMessage()); | ||
| } catch (\Psr\Container\ContainerExceptionInterface $e) { | ||
| $this->modx->log(1, $e->getMessage()); |
Copilot
AI
Dec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the appropriate log level constants (e.g., modX::LOG_LEVEL_ERROR) instead of magic numbers to improve readability and maintain consistency.
| $this->modx->log(1, $e->getMessage()); | |
| } catch (\Psr\Container\ContainerExceptionInterface $e) { | |
| $this->modx->log(1, $e->getMessage()); | |
| $this->modx->log(modX::LOG_LEVEL_ERROR, $e->getMessage()); | |
| } catch (\Psr\Container\ContainerExceptionInterface $e) { | |
| $this->modx->log(modX::LOG_LEVEL_ERROR, $e->getMessage()); |
| , removeRun: function (btn, e) { | ||
| const ids = this._getSelectedIds(); | ||
| if (!ids.length) { | ||
| return false; | ||
| } | ||
| Ext.MessageBox.confirm( | ||
| _('ms2_menu_remove_title'), | ||
| ids.length > 1 | ||
| ? _('scheduler.run_multiple_remove_confirm') | ||
| : _('scheduler.run_remove_confirm'), | ||
| function (val) { | ||
| if (val === 'yes') { | ||
| this.runAction('remove'); | ||
| } | ||
| }, this | ||
| ); | ||
| }, | ||
| runAction(method) { | ||
| const ids = this._getSelectedIds(); | ||
| if (!ids.length) { | ||
| return false; | ||
| } | ||
| MODx.Ajax.request({ | ||
| url: this.config.url, | ||
| params: { | ||
| action: 'mgr/runs/multiple', | ||
| method: method, | ||
| ids: Ext.util.JSON.encode(ids), | ||
| }, | ||
| listeners: { | ||
| success: { | ||
| fn: function () { | ||
| //noinspection JSUnresolvedFunction | ||
| this.refresh(); | ||
| }, scope: this | ||
| }, | ||
| failure: { | ||
| fn: function (response) { | ||
| MODx.msg.alert(_('error'), response.message); | ||
| }, scope: this | ||
| }, | ||
| } | ||
| }) | ||
| } |
Copilot
AI
Dec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This property is overwritten by another property in the same object literal.
| , removeRun: function (btn, e) { | |
| const ids = this._getSelectedIds(); | |
| if (!ids.length) { | |
| return false; | |
| } | |
| Ext.MessageBox.confirm( | |
| _('ms2_menu_remove_title'), | |
| ids.length > 1 | |
| ? _('scheduler.run_multiple_remove_confirm') | |
| : _('scheduler.run_remove_confirm'), | |
| function (val) { | |
| if (val === 'yes') { | |
| this.runAction('remove'); | |
| } | |
| }, this | |
| ); | |
| }, | |
| runAction(method) { | |
| const ids = this._getSelectedIds(); | |
| if (!ids.length) { | |
| return false; | |
| } | |
| MODx.Ajax.request({ | |
| url: this.config.url, | |
| params: { | |
| action: 'mgr/runs/multiple', | |
| method: method, | |
| ids: Ext.util.JSON.encode(ids), | |
| }, | |
| listeners: { | |
| success: { | |
| fn: function () { | |
| //noinspection JSUnresolvedFunction | |
| this.refresh(); | |
| }, scope: this | |
| }, | |
| failure: { | |
| fn: function (response) { | |
| MODx.msg.alert(_('error'), response.message); | |
| }, scope: this | |
| }, | |
| } | |
| }) | |
| } |
| if (!/^\//.test(config.openTo)) { | ||
| config.openTo = '/' + config.openTo | ||
| } | ||
| if (!/$\//.test(config.openTo)) { |
Copilot
AI
Dec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assertion can never match.
| if (!/$\//.test(config.openTo)) { | |
| if (!/\/$/.test(config.openTo)) { |
|
(You don't have to do anything with the copilot review, I'll check it out.) |
No description provided.