diff --git a/lib/classes/output/stored_progress_bar.php b/lib/classes/output/stored_progress_bar.php index 41fd66c0c32..18f2e5a988a 100644 --- a/lib/classes/output/stored_progress_bar.php +++ b/lib/classes/output/stored_progress_bar.php @@ -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; + } + } diff --git a/lib/classes/task/stored_progress_task_trait.php b/lib/classes/task/stored_progress_task_trait.php index ae79ccf913e..2fc835f5554 100644 --- a/lib/classes/task/stored_progress_task_trait.php +++ b/lib/classes/task/stored_progress_task_trait.php @@ -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 */ 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. diff --git a/lib/templates/progress_bar.mustache b/lib/templates/progress_bar.mustache index 13190c416f3..984a4024773 100644 --- a/lib/templates/progress_bar.mustache +++ b/lib/templates/progress_bar.mustache @@ -31,7 +31,7 @@
-
 
+
{{message}}