Skip to content

Conversation

@biz87
Copy link

@biz87 biz87 commented Dec 1, 2025

No description provided.

@biz87 biz87 changed the title Подогнал под MODX3 + MiniShop3 Update for Modx3 Dec 1, 2025
@Mark-H
Copy link
Member

Mark-H commented Dec 1, 2025

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.

@Mark-H Mark-H requested a review from Copilot December 1, 2025 17:46
Copilot finished reviewing on behalf of Mark-H December 1, 2025 17:50
Copy link

Copilot AI left a 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

    , 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'),
Copy link

Copilot AI Dec 1, 2025

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')).

Suggested change
_('ms2_menu_remove_title'),
_('scheduler.run_remove'),

Copilot uses AI. Check for mistakes.
Comment on lines +286 to 329
, 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
},
}
})
}
Copy link

Copilot AI Dec 1, 2025

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.

Copilot uses AI. Check for mistakes.
Comment on lines +36 to 41
$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) . ';
});
Copy link

Copilot AI Dec 1, 2025

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.

Copilot uses AI. Check for mistakes.
Comment on lines +103 to +110
$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')}"
);
}
}
Copy link

Copilot AI Dec 1, 2025

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.

Copilot uses AI. Check for mistakes.
$_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";
Copy link

Copilot AI Dec 1, 2025

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.

Suggested change
$_lang['scheduler.runs_remove'] = "Remove scheduled runs";
$_lang['scheduler.runs_remove'] = "Remove scheduled runs";

Copilot uses AI. Check for mistakes.
Comment on lines +27 to +29
$this->modx->log(1, $e->getMessage());
} catch (\Psr\Container\ContainerExceptionInterface $e) {
$this->modx->log(1, $e->getMessage());
Copy link

Copilot AI Dec 1, 2025

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.

Suggested change
$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());

Copilot uses AI. Check for mistakes.
Comment on lines +286 to 329
, 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
},
}
})
}
Copy link

Copilot AI Dec 1, 2025

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.

Suggested change
, 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 uses AI. Check for mistakes.
if (!/^\//.test(config.openTo)) {
config.openTo = '/' + config.openTo
}
if (!/$\//.test(config.openTo)) {
Copy link

Copilot AI Dec 1, 2025

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.

Suggested change
if (!/$\//.test(config.openTo)) {
if (!/\/$/.test(config.openTo)) {

Copilot uses AI. Check for mistakes.
@Mark-H
Copy link
Member

Mark-H commented Dec 1, 2025

(You don't have to do anything with the copilot review, I'll check it out.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants