MDL-81714 output: Update stored progress bar APIs

Expand the methods available in the stored_progress_bar output component
and stored_progress_task_trait to allow a progress bar to be created in
a "pending" state before the associated task is executed.
This commit is contained in:
Mark Johnson 2024-05-14 16:26:41 +01:00
parent 91e69cee7f
commit b94b1ed5ff
No known key found for this signature in database
GPG Key ID: EB30E1468CFAE242
3 changed files with 114 additions and 25 deletions

View File

@ -29,6 +29,9 @@ class stored_progress_bar extends progress_bar {
/** @var bool Can use output buffering. */
protected static $supportsoutputbuffering = true;
/** @var bool Flag to indicate the Javascript module has been initialised already. */
protected static $jsloaded = false;
/** @var int DB record ID */
protected $recordid;
@ -41,15 +44,19 @@ class stored_progress_bar extends progress_bar {
/**
* This overwrites the progress_bar::__construct method.
*
* The stored progress bar does not need to check NO_OUTPUT_BUFFERING since it outputs to the page
* then polls for updates asynchronously, rather than waiting for synchronous updates in later output.
*
* @param string $idnumber
* @param int $width The suggested width.
* @param bool $autostart Whether to start the progress bar right away.
*/
public function __construct($idnumber) {
public function __construct(string $idnumber, int $width = 0, bool $autostart = true) {
$this->clock = \core\di::get(\core\clock::class);
// Construct from the parent.
parent::__construct($idnumber, 0, true);
parent::__construct($idnumber, $width, $autostart);
}
/**
@ -125,10 +132,10 @@ class stored_progress_bar extends progress_bar {
/**
* Set the time we started the process.
*
* @param int $value
* @param ?int $value
* @return void
*/
protected function set_time_started(int $value): void {
protected function set_time_started(?int $value): void {
$this->timestart = $value;
}
@ -185,17 +192,33 @@ class stored_progress_bar extends progress_bar {
return $this->message;
}
/**
* Initialise Javascript for stored progress bars.
*
* The javascript polls the status of all progress bars on the page, so it only needs to be initialised once.
*
* @return void
*/
public function init_js(): void {
global $PAGE;
if (self::$jsloaded) {
return;
}
$PAGE->requires->js_call_amd('core/stored_progress', 'init', [
self::get_timeout(),
]);
self::$jsloaded = true;
}
/**
* Get the content to display the progress bar and start polling via AJAX
*
* @return string
*/
public function get_content(): string {
global $CFG, $PAGE, $OUTPUT;
global $OUTPUT;
$PAGE->requires->js_call_amd('core/stored_progress', 'init', [
self::get_timeout(),
]);
$this->init_js();
$context = $this->export_for_template($OUTPUT);
return $OUTPUT->render_from_template('core/progress_bar', $context);
@ -208,11 +231,15 @@ class stored_progress_bar extends progress_bar {
* @return array
*/
public function export_for_template(\renderer_base $output): array {
$class = 'stored-progress-bar';
if (empty($this->timestart)) {
$class .= ' stored-progress-notstarted';
}
return [
'id' => $this->recordid,
'idnumber' => $this->idnumber,
'width' => $this->width,
'class' => 'stored-progress-bar',
'class' => $class,
'value' => $this->percent,
'message' => $this->message,
'error' => $this->haserrored,
@ -233,8 +260,19 @@ class stored_progress_bar extends progress_bar {
$OUTPUT->render_progress_bar($this);
}
// Delete any existing records for this.
$this->clear_records();
$record = $DB->get_record('stored_progress', ['idnumber' => $this->idnumber]);
if ($record) {
if ($record->timestart == 0) {
// Set the timestart now and return.
$record->timestart = $this->timestart;
$DB->update_record('stored_progress', $record);
$this->recordid = $record->id;
return $this->recordid;
} else {
// Delete any existing records for this.
$this->clear_records();
}
}
// Create new progress record.
$this->recordid = $DB->insert_record('stored_progress', [
@ -362,4 +400,25 @@ class stored_progress_bar extends progress_bar {
return $CFG->progresspollinterval ?? 5;
}
/**
* Store a progress bar record in a pending state.
*
* @return int ID of the DB record
*/
public function store_pending(): int {
global $DB;
// Delete any existing records for this.
$this->clear_records();
// Create new progress record.
$this->recordid = $DB->insert_record('stored_progress', [
'idnumber' => $this->idnumber,
'timestart' => $this->timestart,
'message' => '',
]);
return $this->recordid;
}
}

View File

@ -16,6 +16,10 @@
namespace core\task;
use core\progress\db_updater;
use core\progress\stored;
use core\output\stored_progress_bar;
/**
* Trait to use in tasks to automatically add stored progress functionality.
*
@ -25,10 +29,44 @@ namespace core\task;
* @author Conn Warwicker <conn.warwicker@catalyst-eu.net>
*/
trait stored_progress_task_trait {
/** @var \core\output\stored_progress_bar|null $progress */
/** @var ?stored_progress_bar $progress */
protected $progress = null;
/**
* Construct a unique name for the progress bar.
*
* For adhoc tasks, this will need the ID in it. For scheduled tasks just the class name.
*
* @return string
*/
protected function get_progress_name(): string {
if (method_exists($this, 'get_id')) {
return get_class($this) . '_' . $this->get_id();
} else {
return get_class($this);
}
}
/**
* Initialise a stored progress record.
*/
public function initialise_stored_progress(): void {
$this->progress = new stored_progress_bar(
stored_progress_bar::convert_to_idnumber($this->get_progress_name()),
autostart: false,
);
$this->progress->store_pending();
}
/**
* Get a stored object for the stored progress record.
*
* @return stored
*/
public function get_progress(): stored {
return new stored($this->progress);
}
/**
* Start a stored progress bar implementation for the task this trait is used in.
*
@ -40,16 +78,8 @@ trait stored_progress_task_trait {
// To get around the issue in MDL-80770, we are manually setting the renderer to cli.
$OUTPUT = $PAGE->get_renderer('core', null, 'cli');
// Construct a unique name for the progress bar.
// For adhoc tasks, this will need the ID in it. For scheduled tasks just the class name.
if (method_exists($this, 'get_id')) {
$name = get_class($this) . '_' . $this->get_id();
} else {
$name = get_class($this);
}
$this->progress = new \core\output\stored_progress_bar(
\core\output\stored_progress_bar::convert_to_idnumber($name)
$this->progress = new stored_progress_bar(
stored_progress_bar::convert_to_idnumber($this->get_progress_name())
);
// Start the progress.

View File

@ -31,7 +31,7 @@
</div>
<div class="d-flex">
<div style="flex: 1 1 0; min-width: 0;">
<div id="{{idnumber}}_status" class="text-truncate">&nbsp;</div>
<div id="{{idnumber}}_status" class="text-truncate">{{message}}</div>
</div>
<div class="text-end ps-3" style="flex: 0 0 content">
<span id="{{idnumber}}_estimate" class="">&nbsp;</span>