Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

/*
* This file is part of the MODX Revolution package.
*
* Copyright (c) MODX, LLC
*
* For complete copyright and license information, see the COPYRIGHT and LICENSE
* files found in the top-level directory of this distribution.
*
* @package modx-test
*/

namespace MODX\Revolution\Tests\Processors\System;

use MODX\Revolution\MODxTestCase;
use MODX\Revolution\Processors\Processor;

/**
* Contract for Processor::logProgress console protocol (#16296).
*
* @group Processors
* @group System
*/
class ConsoleProgressProtocolTest extends MODxTestCase
{
public function testLogProgressUsesIsolatedPrefixAndClamps()
{
$source = file_get_contents(MODX_CORE_PATH . 'src/Revolution/Processors/Processor.php');
$this->assertStringContainsString("__MODX_PROGRESS__:indeterminate", $source);
$this->assertStringContainsString("'__MODX_PROGRESS__:' . \$current . ':' . \$total", $source);

$console = file_get_contents(MODX_CORE_PATH . 'src/Revolution/Processors/System/Console.php');
$this->assertStringContainsString("show_progress", $console);
$this->assertStringContainsString('__MODX_PROGRESS__:', $console);
$this->assertStringNotContainsString("strpos(\$message['msg'], 'PROGRESS:')", $console);

$stub = new class ($this->modx) extends Processor {
public function process()
{
return $this->success();
}
};
$stub->logProgress(0, 0);
$stub->logProgress(9, 5);
$this->assertTrue(true);
}
}
1 change: 1 addition & 0 deletions _build/test/phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
<directory>Tests/Processors/Context</directory>
<directory>Tests/Processors/Element</directory>
<directory>Tests/Processors/Resource</directory>
<directory>Tests/Processors/System</directory>
</testsuite>
<testsuite name="Transport">
<directory>Tests/Transport</directory>
Expand Down
1 change: 1 addition & 0 deletions core/lexicon/en/default.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
$_lang['confirm_unpublish'] = 'Un-publishing this document now will delete any (un)publishing dates that may have been set. If you wish to set or keep publish or unpublish dates, please choose to edit the document instead.\n\nProceed?';
$_lang['console'] = 'Console';
$_lang['console_download_output'] = 'Download Output to File';
$_lang['console_progress'] = 'Processing...';
$_lang['console_running'] = 'Console running...';
$_lang['content'] = 'Content';
$_lang['content_elements'] = 'Content Elements';
Expand Down
22 changes: 22 additions & 0 deletions core/src/Revolution/Processors/Processor.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,28 @@ public function getLanguageTopics()
return [];
}

/**
* Log progress for console progress bar. When registry logging is active (e.g. console),
* the message is read by System/Console and drives the optional progress bar.
*
* @param int $current Current step (1-based index or count).
* @param int $total Total steps. Use 0 for indeterminate progress.
* @return void
*/
public function logProgress(int $current, int $total = 0): void
{
$current = max(0, $current);
if ($total <= 0) {
$this->modx->log(modX::LOG_LEVEL_INFO, '__MODX_PROGRESS__:indeterminate');
return;
}
$total = max(1, $total);
if ($current > $total) {
$current = $total;
}
$this->modx->log(modX::LOG_LEVEL_INFO, '__MODX_PROGRESS__:' . $current . ':' . $total);
}

/**
* Return a success message from the processor.
* @param string $msg
Expand Down
7 changes: 7 additions & 0 deletions core/src/Revolution/Processors/System/ClearCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ public function process()
{
$this->runBeforeEvents();

// Show activity during the actual refresh (results are logged after).
$this->logProgress(0, 0);

$results = [];
$partitions = $this->getPartitions();
$this->modx->cacheManager->refresh($partitions, $results);
Expand All @@ -50,9 +53,13 @@ public function process()

$o = '';
sleep(1);
$total = count($results);
$index = 0;
$result = reset($results);
$partition = key($results);
while ($partition && $result) {
$index++;
$this->logProgress($index, $total);
switch ($partition) {
case 'auto_publish':
$this->modx->log(modX::LOG_LEVEL_INFO, $this->modx->lexicon('refresh_auto_publish'));
Expand Down
34 changes: 31 additions & 3 deletions core/src/Revolution/Processors/System/Console.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php

/*
* This file is part of MODX Revolution.
*
Expand Down Expand Up @@ -82,18 +83,45 @@ public function process()
'data' => '',
'complete' => false,
];
$showProgress = (bool)$this->getProperty('show_progress', false);
foreach ($messages as $messageKey => $message) {
if ($message['msg'] === 'COMPLETED') {
$response['complete'] = true;
continue;
}
if (!empty ($message['def'])) {
$progressPrefix = '__MODX_PROGRESS__:';
if ($showProgress && str_starts_with((string)$message['msg'], $progressPrefix)) {
$progressMsg = substr($message['msg'], strlen($progressPrefix));
if ($progressMsg === 'indeterminate') {
$response['progress'] = ['indeterminate' => true];
continue;
}
$parts = explode(':', $progressMsg, 2);
if (
count($parts) === 2
&& ctype_digit($parts[0])
&& ctype_digit($parts[1])
&& (int)$parts[1] > 0
) {
$current = (int)$parts[0];
$total = (int)$parts[1];
if ($current > $total) {
$current = $total;
}
$response['progress'] = [
'current' => $current,
'total' => $total,
];
}
continue;
}
if (!empty($message['def'])) {
$message['def'] .= ' ';
}
if (!empty ($message['file'])) {
if (!empty($message['file'])) {
$message['file'] = '@ ' . $message['file'] . ' ';
}
if (!empty ($message['line'])) {
if (!empty($message['line'])) {
$message['line'] = 'line ' . $message['line'] . ' ';
}
$response['data'] .= '<span class="' . strtolower($message['level']) . '">';
Expand Down
1 change: 1 addition & 0 deletions manager/assets/modext/core/modx.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ Ext.extend(MODx,Ext.Component,{
,topic: topic
,clear: true
,show_filename: 0
,showProgress: true
,listeners: {
'shutdown': {fn:function() {
if (this.fireEvent('afterClearCache')) {
Expand Down
31 changes: 31 additions & 0 deletions manager/assets/modext/widgets/core/modx.console.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,18 @@ MODx.Console = function(config) {
,height: 400
,width: 600
,refreshRate: 2
,showProgress: false
,cls: 'modx-window modx-console'
,items: [{
itemId: 'header'
,cls: 'modx-console-text'
,html: _('console_running')
,border: false
},{
xtype: 'progress'
,itemId: 'progressBar'
,hidden: true
,style: 'margin: 8px 0;'
},{
xtype: 'panel'
,itemId: 'body'
Expand Down Expand Up @@ -85,13 +91,17 @@ Ext.extend(MODx.Console,Ext.Window,{
,clear: false
,show_filename: this.config.show_filename || 0
,format: this.config.format || 'html_log'
,show_progress: this.config.showProgress ? 1 : 0
}
});
Ext.Direct.addProvider(this.provider);
Ext.Direct.on('message', this.onMessage, this);
}

,onMessage: function(e,p) {
if (this.config.showProgress && e.progress) {
this.updateProgressBar(e.progress);
}
var out = this.getComponent('body');
if (out) {
out.el.insertHtml('beforeEnd',e.data);
Expand All @@ -104,12 +114,33 @@ Ext.extend(MODx.Console,Ext.Window,{
delete e;
}

,updateProgressBar: function(progress) {
const bar = this.getComponent('progressBar');
if (!bar) { return; }
bar.show();
if (progress.indeterminate) {
bar.wait({ interval: 200, text: _('console_progress') });
} else if (progress.current != null && progress.total != null && progress.total > 0) {
bar.reset();
let pct = progress.current / progress.total;
if (pct < 0) { pct = 0; }
if (pct > 1) { pct = 1; }
const text = progress.current + ' / ' + progress.total;
bar.updateProgress(pct, text);
}
}

,onComplete: function() {
if (this.provider && this.provider.disconnect) {
try {
this.provider.disconnect();
} catch (e) {}
}
const bar = this.getComponent('progressBar');
if (bar) {
bar.reset();
bar.hide();
}
this.fbar.setDisabled(false);
this.keyMap.setDisabled(false);
}
Expand Down
Loading