Skip to content

Commit

Permalink
MDL-82715 customfield_number: Add automatically populated providers.
Browse files Browse the repository at this point in the history
Added number of activity provider and also hooks for plugins.
  • Loading branch information
ilyatregubov committed Sep 19, 2024
1 parent 09e56f2 commit c6b1680
Show file tree
Hide file tree
Showing 20 changed files with 1,259 additions and 14 deletions.
28 changes: 28 additions & 0 deletions .upgradenotes/MDL-82715-2024091706584681.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
issueNumber: MDL-82715
notes:
customfield_number:
- message: >+
New 'customfield_number\hook\add_custom_providers' hook has been added.
It allows automatic calculation of number course custom field.
Added new class
'\customfield_number\local\numberproviders\nofactivities'
that allows to automatically calculate number of activities of a given
type in a given course.
Added new webservice customfield_number_recalculate_value to recalculate
a value of number course custom field.
Added 'customfield_number\task\cron' cron task that recalculates
automatically calculated number course custom fields.
type: improved
11 changes: 11 additions & 0 deletions customfield/field/number/amd/build/recalculate.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions customfield/field/number/amd/build/recalculate.min.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

73 changes: 73 additions & 0 deletions customfield/field/number/amd/src/recalculate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* Allows to recalculate a single value on demand
*
* @module customfield_number/recalculate
* @author 2024 Marina Glancy
* @copyright 2024 Moodle Pty Ltd <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

import Ajax from 'core/ajax';
import Notification from 'core/notification';
import {addIconToContainer} from 'core/loadingicon';
import Pending from 'core/pending';

const SELECTORS = {
wrapper: '[data-fieldtype="wrapper"]',
value: '[data-fieldtype="value"]',
link: '[data-fieldtype="link"]',
};

let initialised = false;

/**
* Init
*/
export function init() {
if (initialised) {
return;
}

initialised = true;

document.addEventListener('click', (e) => {
const target = e.target.closest(SELECTORS.wrapper + " " + SELECTORS.link);
if (!target) {
return;
}
const el = target.closest(SELECTORS.wrapper).querySelector(SELECTORS.value);
if (!el) {
return;
}
e.preventDefault();
const fieldid = target.dataset.fieldid;
const instanceid = target.dataset.instanceid;

const pendingPromise = new Pending('recalculate_customfield_number');
addIconToContainer(el).then(() => {
return Ajax.call([{
methodname: 'customfield_number_recalculate_value',
args: {fieldid, instanceid}
}])[0];
}).then((data) => {
el.innerHTML = data.value;
pendingPromise.resolve();
return;
}).catch(Notification.exception);
});
}
27 changes: 23 additions & 4 deletions customfield/field/number/classes/data_controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,19 @@ public function datafield(): string {
* @param MoodleQuickForm $mform
*/
public function instance_form_definition(MoodleQuickForm $mform): void {
global $OUTPUT;

$field = $this->get_field();
$elementname = $this->get_form_element_name();
if (!$field->is_editable()) {
// Display the value as static text.
$instanceid = (int)$this->get('instanceid');
$data = ['value' => $this->export_value(), 'fieldid' => $field->get('id'), 'instanceid' => $instanceid];
$value = $OUTPUT->render_from_template('customfield_number/staticvalue', $data);
$mform->addElement('static', $elementname . '_static', $this->get_field()->get_formatted_name(),
$value);
return;
}

$mform->addElement('float', $elementname, $this->get_field()->get_formatted_name());
if (!$this->get('id')) {
Expand All @@ -63,8 +75,11 @@ public function instance_form_validation(array $data, array $files): array {
$errors = parent::instance_form_validation($data, $files);

$elementname = $this->get_form_element_name();
$elementvalue = $data[$elementname];

$elementvalue = '';
// Providers calculate values automatically, so nothing to validate.
if (!provider_base::instance($this->get_field())) {
$elementvalue = $data[$elementname];
}
$minimumvalue = $this->get_field()->get_configdata_property('minimumvalue') ?? '';
$maximumvalue = $this->get_field()->get_configdata_property('maximumvalue') ?? '';

Expand Down Expand Up @@ -107,6 +122,10 @@ protected function is_empty($value): bool {
* @return float|null
*/
public function get_default_value(): ?float {
// If a provider is available, use its default value.
if ($provider = provider_base::instance($this->get_field())) {
return $provider->get_default_value();
}
$defaultvalue = $this->get_field()->get_configdata_property('defaultvalue');
if ($this->is_empty($defaultvalue)) {
return null;
Expand All @@ -117,9 +136,9 @@ public function get_default_value(): ?float {
/**
* Returns value in a human-readable format
*
* @return string|null
* @return string|float|null
*/
public function export_value(): ?string {
public function export_value(): string|float|null {
/** @var field_controller $field */
$field = $this->get_field();
return $field->prepare_field_for_display($this->get_value(), $this->get_context());
Expand Down
97 changes: 97 additions & 0 deletions customfield/field/number/classes/external/recalculate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

declare(strict_types=1);

namespace customfield_number\external;

use core\exception\invalid_parameter_exception;
use core_external\external_function_parameters;
use core_external\external_single_structure;
use core_external\external_api;
use core_external\external_value;
use customfield_number\provider_base;

/**
* Implementation of web service customfield_number_recalculate_value
*
* @package customfield_number
* @author 2024 Marina Glancy
* @copyright 2024 Moodle Pty Ltd <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class recalculate extends external_api {

/**
* Describes the parameters for customfield_number_recalculate_value
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters([
'fieldid' => new external_value(PARAM_INT, 'Field id', VALUE_REQUIRED),
'instanceid' => new external_value(PARAM_INT, 'Instance id', VALUE_REQUIRED),
]);
}

/**
* Implementation of web service customfield_number_recalculate_value
*
* @param int $fieldid
* @param int $instanceid
* @return array
*/
public static function execute(int $fieldid, int $instanceid): array {
// Parameter validation.
['fieldid' => $fieldid, 'instanceid' => $instanceid] = self::validate_parameters(
self::execute_parameters(),
['fieldid' => $fieldid, 'instanceid' => $instanceid]
);

// Access validation.
$context = \context_system::instance();
self::validate_context($context);

$field = \core_customfield\field_controller::create($fieldid);
$provider = provider_base::instance($field);
if (!$provider) {
throw new invalid_parameter_exception('Invalid parameter');
}

$handler = $field->get_handler();
if (!$handler->can_edit($field, $instanceid)) {
throw new \moodle_exception('nopermissions', '', '', get_string('update'));
}

$provider->recalculate($instanceid);

$data = \core_customfield\api::get_instance_fields_data(
[$fieldid => $field], $instanceid)[$fieldid];

return ['value' => $data->export_value()];
}

/**
* Describe the return structure for customfield_number_recalculate_value
*
* @return external_single_structure
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([
'value' => new external_value(PARAM_RAW, 'Recalculated value (prepared for display)'),
]);
}
}
61 changes: 52 additions & 9 deletions customfield/field/number/classes/field_controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class field_controller extends \core_customfield\field_controller {

/**
* Add form elements for editing the custom field definition
*
Expand All @@ -41,6 +40,15 @@ public function config_form_definition(MoodleQuickForm $mform): void {
$mform->addElement('header', 'specificsettings', get_string('specificsettings', 'customfield_number'));
$mform->setExpanded('specificsettings');

$providers = provider_base::get_all_providers($this);
if (count($providers) > 0) {
$this->add_field_type_select($mform, $providers);
// Add form config elements for each provider.
foreach ($providers as $provider) {
$provider->config_form_definition($mform);
}
}

// Default value.
$mform->addElement('float', 'configdata[defaultvalue]', get_string('defaultvalue', 'core_customfield'));
if ($this->get_configdata_property('defaultvalue') === null) {
Expand Down Expand Up @@ -68,7 +76,7 @@ public function config_form_definition(MoodleQuickForm $mform): void {

// Display format settings.
// TODO: Change this after MDL-82996 fixed.
$randelname = 'str_' . random_string();
$randelname = 'str_display_format';
$mform->addGroup([], $randelname, html_writer::tag('h4', get_string('headerdisplaysettings', 'customfield_number')));

// Display template.
Expand All @@ -90,6 +98,22 @@ public function config_form_definition(MoodleQuickForm $mform): void {
}
}

/**
* Adds selector to provider for field population.
*
* @param MoodleQuickForm $mform
* @param array $providers
*/
protected function add_field_type_select(\MoodleQuickForm $mform, array $providers): void {
$autooptions = [];
foreach ($providers as $provider) {
$autooptions[get_class($provider)] = $provider->get_name();
}
$options = [get_string('genericfield', 'customfield_number')];
$options = array_merge($options, $autooptions);
$mform->addElement('select', 'configdata[fieldtype]', get_string('fieldtype', 'customfield_number'), $options);
}

/**
* Validate the data on the field configuration form
*
Expand All @@ -110,6 +134,11 @@ public function config_form_validation(array $data, $files = []): array {
$minimumvalue = $data['configdata']['minimumvalue'] ?? '';
$maximumvalue = $data['configdata']['maximumvalue'] ?? '';

foreach (provider_base::get_all_providers($this) as $provider) {
if (array_key_exists('fieldtype', $data["configdata"]) && $data["configdata"]["fieldtype"] == get_class($provider)) {
$errors = array_merge($errors, $provider->config_form_validation($data, $files));
}
}
// Early exit if neither maximum/minimum are specified.
if ($minimumvalue === '' && $maximumvalue === '') {
return $errors;
Expand Down Expand Up @@ -140,9 +169,9 @@ public function config_form_validation(array $data, $files = []): array {
*
* @param mixed $value
* @param context|null $context
* @return string|null
* @return string|float|null
*/
public function prepare_field_for_display(mixed $value, ?context $context = null): ?string {
public function prepare_field_for_display(mixed $value, ?context $context = null): string|null|float {
if ($value === null) {
return null;
}
Expand All @@ -154,12 +183,26 @@ public function prepare_field_for_display(mixed $value, ?context $context = null
}
} else {
// Let's format the value.
$value = format_float((float) $value, $decimalplaces);

// Apply the display format.
$format = $this->get_configdata_property('display') ?? '{value}';
$value = str_replace('{value}', $value, $format);
$provider = provider_base::instance($this);
if ($provider) {
$value = $provider->prepare_export_value($value, $context);
} else {
$value = format_float((float)$value, $decimalplaces);
// Apply the display format.
$format = $this->get_configdata_property('display') ?? '{value}';
$value = str_replace('{value}', $value, $format);
}
}
return format_string($value, true, ['context' => $context ?? system::instance()]);
}

/**
* Can the value of this field be manually editable in the edit forms
*
* @return bool
*/
public function is_editable(): bool {
return (string)$this->get_configdata_property('fieldtype') === '0';
}

}
Loading

0 comments on commit c6b1680

Please sign in to comment.