* checked: If the initial status is checked.
* disabled: If toggle input is disabled.
* dataattributes: Array of name/value elements added as data-attributes.
+* title: Title text.
* label: Label text.
* labelclasses: Any extra classes added to the label container.
"name": "action",
"value": "toggle-status"
}],
+ "title": "Toggle Enabled",
"label": "Enable/disable status",
"labelclasses": "sr-only"
}
"name": "action",
"value": "toggle-status"
}],
+ "title": "Toggle Disabled",
"label": "Enable/disable status"
}
{{< /mustache >}}
* disabled
* labelmarkup: Label element code block.
* Should include *class="custom-control-label"*.
+* title: Title text.
* label: Label text.
* labelclasses: Any extra classes added to the label container.
$this->add_entity($userentity->add_joins([$cohortmemberjoin, $userjoin]));
- // Add all columns from entities to be available in custom reports.
- $this->add_columns_from_entity($cohortentity->get_entity_name());
- $this->add_columns_from_entity($cohortmemberentity->get_entity_name());
- $this->add_columns_from_entity($userentity->get_entity_name());
-
- // Add all filters from entities to be available in custom reports.
- $this->add_filters_from_entity($cohortentity->get_entity_name());
- $this->add_filters_from_entity($cohortmemberentity->get_entity_name());
- $this->add_filters_from_entity($userentity->get_entity_name());
-
- // Add all conditions from entities to be available in custom reports.
- $this->add_conditions_from_entity($cohortentity->get_entity_name());
- $this->add_conditions_from_entity($cohortmemberentity->get_entity_name());
- $this->add_conditions_from_entity($userentity->get_entity_name());
+ // Add all columns/filters/conditions from entities to be available in custom reports.
+ $this->add_all_from_entities();
}
/**
->add_join("JOIN {course_categories} {$coursecattablealias}
ON {$coursecattablealias}.id = {$coursetablealias}.category"));
- // Add all columns from entities to be available in custom reports.
- $this->add_columns_from_entity($coursecatentity->get_entity_name());
- $this->add_columns_from_entity($courseentity->get_entity_name());
-
- // Add all filters from entities to be available in custom reports.
- $this->add_filters_from_entity($coursecatentity->get_entity_name());
- $this->add_filters_from_entity($courseentity->get_entity_name());
-
- // Add all conditions from entities to be available in custom reports.
- $this->add_conditions_from_entity($coursecatentity->get_entity_name());
- $this->add_conditions_from_entity($courseentity->get_entity_name());
+ // Add all columns/filters/conditions from entities to be available in custom reports.
+ $this->add_all_from_entity($coursecatentity->get_entity_name());
+ $this->add_all_from_entity($courseentity->get_entity_name());
}
/**
* @return string html text, usually a form in a text box
*/
public function enrol_page_hook(stdClass $instance) {
- global $CFG, $USER, $OUTPUT, $PAGE, $DB;
+ return $this->show_payment_info($instance);
+ }
+
+ /**
+ * Returns optional enrolment instance description text.
+ *
+ * This is used in detailed course information.
+ *
+ *
+ * @param object $instance
+ * @return string short html text
+ */
+ public function get_description_text($instance) {
+ return $this->show_payment_info($instance);
+ }
+
+ /**
+ * Generates payment information to display on enrol/info page.
+ *
+ * @param stdClass $instance
+ * @return false|string
+ * @throws coding_exception
+ * @throws dml_exception
+ */
+ private function show_payment_info(stdClass $instance) {
+ global $USER, $OUTPUT, $DB;
ob_start();
$course = $DB->get_record('course', array('id' => $instance->courseid));
$context = context_course::instance($course->id);
- $shortname = format_string($course->shortname, true, array('context' => $context));
- $strloginto = get_string("loginto", "", $shortname);
- $strcourses = get_string("courses");
-
- // Pass $view=true to filter hidden caps if the user cannot see them.
- if ($users = get_users_by_capability($context, 'moodle/course:update', 'u.*', 'u.id ASC',
- '', '', '', '', false, true)) {
- $users = sort_by_roleassignment_authority($users, $context);
- $teacher = array_shift($users);
- } else {
- $teacher = false;
- }
-
if ( (float) $instance->cost <= 0 ) {
$cost = (float) $this->get_config('cost');
} else {
} else {
$data = [
- 'isguestuser' => isguestuser(),
+ 'isguestuser' => isguestuser() || !isloggedin(),
'cost' => \core_payment\helper::get_cost_as_string($cost, $instance->currency),
'instanceid' => $instance->id,
'description' => get_string('purchasedescription', 'enrol_fee',
And I navigate to "Plugins > Enrolments > Manage enrol plugins" in site administration
And I click on "Enable" "link" in the "Enrolment on payment" "table_row"
And I log out
-
- @javascript
- Scenario: Student can see the payment prompt on the course enrolment page
- When I log in as "manager1"
+ And I log in as "manager1"
And I am on the "Course 1" "enrolment methods" page
And I select "Enrolment on payment" from the "Add method" singleselect
And I set the following fields to these values:
| Currency | Euro |
And I press "Add method"
And I log out
- And I log in as "student1"
+
+ @javascript
+ Scenario: Student can see the payment prompt on the course enrolment page
+ When I log in as "student1"
And I am on course index
And I follow "Course 1"
Then I should see "This course requires a payment for entry."
And I press "Select payment type"
And I should see "PayPal" in the "Select payment type" "dialogue"
And I click on "Cancel" "button" in the "Select payment type" "dialogue"
+
+ Scenario: Guest can see the login prompt on the course enrolment page
+ When I log in as "guest"
+ And I am on course index
+ And I follow "Course 1"
+ Then I should see "This course requires a payment for entry."
+ And I should see "123.45"
+ And I should see "Log in to the site"
$courseid = required_param('id', PARAM_INT);
$groupid = optional_param('group', 0, PARAM_INT);
$groupingid = optional_param('grouping', 0, PARAM_INT);
+$dataformat = optional_param('dataformat', '', PARAM_ALPHA);
$returnurl = $CFG->wwwroot.'/group/index.php?id='.$courseid;
$rooturl = $CFG->wwwroot.'/group/overview.php?id='.$courseid;
WHERE g.courseid = :courseid
) grouped ON grouped.userid = u.id
$userfieldsjoin
- WHERE grouped.userid IS NULL";
+ WHERE grouped.userid IS NULL
+ ORDER BY $sort";
$params['courseid'] = $courseid;
$nogroupusers = $DB->get_records_sql($sql, array_merge($params, $userfieldsparams));
}
}
+// Export groups if requested.
+if ($dataformat !== '') {
+ $columnnames = array(
+ 'grouping' => $strgrouping,
+ 'group' => $strgroup,
+ 'firstname' => get_string('firstname'),
+ 'lastname' => get_string('lastname'),
+ );
+ $extrafields = \core_user\fields::get_identity_fields($context, false);
+ foreach ($extrafields as $field) {
+ $columnnames[$field] = \core_user\fields::get_display_name($field);
+ }
+ $alldata = array();
+ // Generate file name.
+ $shortname = format_string($course->shortname, true, array('context' => $context))."_groups";
+ $i = 0;
+ foreach ($members as $gpgid => $groupdata) {
+ if ($groupingid and $groupingid != $gpgid) {
+ if ($groupingid > 0 || $gpgid > 0) {
+ // Still show 'not in group' when 'no grouping' selected.
+ continue; // Do not export.
+ }
+ }
+ if ($gpgid < 0) {
+ // Display 'not in group' for grouping id == OVERVIEW_GROUPING_NO_GROUP.
+ if ($gpgid == OVERVIEW_GROUPING_NO_GROUP) {
+ $groupingname = $strnotingroup;
+ } else {
+ $groupingname = $strnotingrouping;
+ }
+ } else {
+ $groupingname = $groupings[$gpgid]->formattedname;
+ }
+ if (empty($groupdata)) {
+ $alldata[$i] = array_fill_keys(array_keys($columnnames), '');
+ $alldata[$i]['grouping'] = $groupingname;
+ $i++;
+ }
+ foreach ($groupdata as $gpid => $users) {
+ if ($groupid and $groupid != $gpid) {
+ continue;
+ }
+ if (empty($users)) {
+ $alldata[$i] = array_fill_keys(array_keys($columnnames), '');
+ $alldata[$i]['grouping'] = $groupingname;
+ $alldata[$i]['group'] = $groups[$gpid]->name;
+ $i++;
+ }
+ foreach ($users as $option => $user) {
+ $alldata[$i]['grouping'] = $groupingname;
+ $alldata[$i]['group'] = $groups[$gpid]->name;
+ $alldata[$i]['firstname'] = $user->firstname;
+ $alldata[$i]['lastname'] = $user->lastname;
+ foreach ($extrafields as $field) {
+ $alldata[$i][$field] = $user->$field;
+ }
+ $i++;
+ }
+ }
+ }
+
+ \core\dataformat::download_data(
+ $shortname,
+ $dataformat,
+ $columnnames,
+ $alldata,
+ function($record, $supportshtml) use ($extrafields) {
+ if ($supportshtml) {
+ foreach ($extrafields as $extrafield) {
+ $record[$extrafield] = s($record[$extrafield]);
+ }
+ }
+ return $record;
+ });
+ die;
+}
+
+// Main page content.
navigation_node::override_active_url(new moodle_url('/group/index.php', array('id'=>$courseid)));
$PAGE->navbar->add(get_string('overview', 'group'));
$printed = true;
}
+// Add buttons for exporting groups/groupings.
+echo $OUTPUT->download_dataformat_selector(get_string('exportgroupsgroupings', 'group'), 'overview.php', 'dataformat', [
+ 'id' => $courseid,
+ 'group' => $groupid,
+ 'grouping' => $groupingid,
+]);
+
echo $OUTPUT->footer();
$string['eventgroupinggroupunassigned'] = 'Group unassigned from grouping';
$string['eventgroupingupdated'] = 'Grouping updated';
$string['existingmembers'] = 'Existing members: {$a}';
+$string['exportgroupsgroupings'] = 'Export groups and groupings as';
$string['filtergroups'] = 'Filter groups by:';
$string['group'] = 'Group';
$string['groupaddedsuccesfully'] = 'Group {$a} added successfully';
* @param string $plugin (optional) the plugin scope, default null
* @return bool true or exception
*/
-function set_config($name, $value, $plugin=null) {
+function set_config($name, $value, $plugin = null) {
global $CFG, $DB;
- if (empty($plugin)) {
- if (!array_key_exists($name, $CFG->config_php_settings)) {
- // So it's defined for this invocation at least.
- if (is_null($value)) {
- unset($CFG->$name);
- } else {
- // Settings from db are always strings.
- $CFG->$name = (string)$value;
- }
- }
+ // Redirect to appropriate handler when value is null.
+ if ($value === null) {
+ return unset_config($name, $plugin);
+ }
- if ($DB->get_field('config', 'name', array('name' => $name))) {
- if ($value === null) {
- $DB->delete_records('config', array('name' => $name));
- } else {
- $DB->set_field('config', 'value', $value, array('name' => $name));
- }
- } else {
- if ($value !== null) {
- $config = new stdClass();
- $config->name = $name;
- $config->value = $value;
- $DB->insert_record('config', $config, false);
- }
- // When setting config during a Behat test (in the CLI script, not in the web browser
- // requests), remember which ones are set so that we can clear them later.
- if (defined('BEHAT_TEST')) {
- if (!property_exists($CFG, 'behat_cli_added_config')) {
- $CFG->behat_cli_added_config = [];
- }
- $CFG->behat_cli_added_config[$name] = true;
- }
- }
- if ($name === 'siteidentifier') {
- cache_helper::update_site_identifier($value);
- }
- cache_helper::invalidate_by_definition('core', 'config', array(), 'core');
+ // Set variables determining conditions and where to store the new config.
+ // Plugin config goes to {config_plugins}, core config goes to {config}.
+ $iscore = empty($plugin);
+ if ($iscore) {
+ // If it's for core config.
+ $table = 'config';
+ $conditions = ['name' => $name];
+ $invalidatecachekey = 'core';
} else {
- // Plugin scope.
- if ($id = $DB->get_field('config_plugins', 'id', array('name' => $name, 'plugin' => $plugin))) {
- if ($value===null) {
- $DB->delete_records('config_plugins', array('name' => $name, 'plugin' => $plugin));
- } else {
- $DB->set_field('config_plugins', 'value', $value, array('id' => $id));
- }
- } else {
- if ($value !== null) {
- $config = new stdClass();
- $config->plugin = $plugin;
- $config->name = $name;
- $config->value = $value;
- $DB->insert_record('config_plugins', $config, false);
- }
+ // If it's a plugin.
+ $table = 'config_plugins';
+ $conditions = ['name' => $name, 'plugin' => $plugin];
+ $invalidatecachekey = $plugin;
+ }
+
+ // DB handling - checks for existing config, updating or inserting only if necessary.
+ $invalidatecache = true;
+ $inserted = false;
+ $record = $DB->get_record($table, $conditions, 'id, value');
+ if ($record === false) {
+ // Inserts a new config record.
+ $config = new stdClass();
+ $config->name = $name;
+ $config->value = $value;
+ if (!$iscore) {
+ $config->plugin = $plugin;
}
- cache_helper::invalidate_by_definition('core', 'config', array(), $plugin);
+ $inserted = $DB->insert_record($table, $config, false);
+ } else if ($invalidatecache = ($record->value !== $value)) {
+ // Record exists - Check and only set new value if it has changed.
+ $DB->set_field($table, 'value', $value, ['id' => $record->id]);
+ }
+
+ if ($iscore && !isset($CFG->config_php_settings[$name])) {
+ // So it's defined for this invocation at least.
+ // Settings from db are always strings.
+ $CFG->$name = (string) $value;
+ }
+
+ // When setting config during a Behat test (in the CLI script, not in the web browser
+ // requests), remember which ones are set so that we can clear them later.
+ if ($iscore && $inserted && defined('BEHAT_TEST')) {
+ $CFG->behat_cli_added_config[$name] = true;
+ }
+
+ // Update siteidentifier cache, if required.
+ if ($iscore && $name === 'siteidentifier') {
+ cache_helper::update_site_identifier($value);
+ }
+
+ // Invalidate cache, if required.
+ if ($invalidatecache) {
+ cache_helper::invalidate_by_definition('core', 'config', [], $invalidatecachekey);
}
return true;
"name": "action",
"value": "toggle-reality"
}],
+ "title": "Title example",
"label": "Enable/disable reality",
"labelclasses": "sr-only"
}
{{#disabled}}disabled{{/disabled}}
{{/attributes}}>
{{$labelmarkup}}
- <label class="custom-control-label" for="{{$id}}{{id}}{{/id}}">
+ <label class="custom-control-label" for="{{$id}}{{id}}{{/id}}" {{#title}}data-toggle="tooltip" data-placement="top" title="{{title}}"{{/title}}>
<span class="{{$labelclasses}}{{labelclasses}}{{/labelclasses}}">{{$label}}{{label}}{{/label}}</span>
</label>
{{/labelmarkup}}
* @return void
*/
public function reset() {
+ $this->gradecategorycounter = 0;
+ $this->gradeitemcounter = 0;
+ $this->gradeoutcomecounter = 0;
$this->usercounter = 0;
$this->categorycount = 0;
+ $this->cohortcount = 0;
$this->coursecount = 0;
$this->scalecount = 0;
+ $this->groupcount = 0;
+ $this->groupingcount = 0;
+ $this->rolecount = 0;
+ $this->tagcount = 0;
foreach ($this->generators as $generator) {
$generator->reset();
}
return [$component, $name];
} else {
- throw new coding_exception('The page name most be in the form ' .
+ throw new coding_exception('The page name must be in the form ' .
'"{page-name}" for core pages, or "{component} > {page-name}" ' .
'for pages belonging to other components. ' .
'For example "Admin notifications" or "mod_quiz > View".');
toggleSchedule(reportId, scheduleToggle.dataset.id, scheduleStateToggle)
.then(() => {
const tableRow = scheduleToggle.closest('tr');
- tableRow.classList.toggle('dimmed_text');
+ tableRow.classList.toggle('text-muted');
scheduleToggle.dataset.state = scheduleStateToggle;
return $conditions;
}
+
+ /**
+ * Adds all columns/filters/conditions from the given entity to the report at once
+ *
+ * @param string $entityname
+ */
+ final protected function add_all_from_entity(string $entityname): void {
+ $this->add_columns_from_entity($entityname);
+ $this->add_filters_from_entity($entityname);
+ $this->add_conditions_from_entity($entityname);
+ }
+
+ /**
+ * Adds all columns/filters/conditions from all the entities added to the report at once
+ */
+ final protected function add_all_from_entities(): void {
+ foreach ($this->get_entities() as $entity) {
+ $this->add_all_from_entity($entity->get_entity_name());
+ }
+ }
}
use core_reportbuilder\table\custom_report_table_view;
use core_reportbuilder\table\custom_report_table_view_filterset;
use core_reportbuilder\local\helpers\report as report_helper;
+use core_table\local\filter\integer_filter;
/**
* Custom report exporter class
*/
protected static function define_related(): array {
return [
+ 'pagesize' => 'int?',
];
}
$table = custom_report_table::create($this->persistent->get('id'));
$table->set_filterset(new custom_report_table_filterset());
} else {
+ // We store the pagesize within the table filterset so that it's available between AJAX requests.
+ $filterset = new custom_report_table_view_filterset();
+ $filterset->add_filter(new integer_filter('pagesize', null, [$this->related['pagesize']]));
+
$table = custom_report_table_view::create($this->persistent->get('id'), $this->download);
- $table->set_filterset(new custom_report_table_view_filterset());
+ $table->set_filterset($filterset);
// Generate filters form if report contains any filters.
$source = $this->persistent->get('source');
namespace core_reportbuilder\local\entities;
+use context_helper;
use context_system;
+use context_user;
use html_writer;
use lang_string;
use moodle_url;
* @return array
*/
protected function get_default_table_aliases(): array {
- return ['user' => 'u'];
+ return [
+ 'user' => 'u',
+ 'context' => 'uctx',
+ ];
}
/**
*/
protected function get_all_columns(): array {
$usertablealias = $this->get_table_alias('user');
+ $contexttablealias = $this->get_table_alias('context');
$fullnameselect = self::get_name_fields_select($usertablealias);
$fullnamesort = explode(', ', $fullnameselect);
$countries = get_string_manager()->get_list_of_countries(true);
return $countries[$country] ?? '';
});
+ } else if ($userfield === 'description') {
+ // Select enough fields in order to format the column.
+ $column
+ ->add_join("LEFT JOIN {context} {$contexttablealias}
+ ON {$contexttablealias}.contextlevel = " . CONTEXT_USER . "
+ AND {$contexttablealias}.instanceid = {$usertablealias}.id")
+ ->add_fields("{$usertablealias}.descriptionformat, {$usertablealias}.id")
+ ->add_fields(context_helper::get_preload_record_columns_sql($contexttablealias));
}
$columns[] = $column;
protected function is_sortable(string $fieldname): bool {
// Some columns can't be sorted, like longtext or images.
$nonsortable = [
+ 'description',
'picture',
];
* @return string
*/
public function format($value, stdClass $row, string $fieldname): string {
+ global $CFG;
+
if ($this->get_user_field_type($fieldname) === column::TYPE_BOOLEAN) {
return format::boolean_as_text($value);
}
return format::userdate($value, $row);
}
+ if ($fieldname === 'description') {
+ if (empty($row->id)) {
+ return '';
+ }
+
+ require_once("{$CFG->libdir}/filelib.php");
+
+ context_helper::preload_from_record($row);
+ $context = context_user::instance($row->id);
+
+ $description = file_rewrite_pluginfile_urls($value, 'pluginfile.php', $context->id, 'user', 'profile', null);
+ return format_text($description, $row->descriptionformat, ['context' => $context->id]);
+ }
+
return s($value);
}
'email' => new lang_string('email'),
'city' => new lang_string('city'),
'country' => new lang_string('country'),
+ 'description' => new lang_string('description'),
'firstnamephonetic' => new lang_string('firstnamephonetic'),
'lastnamephonetic' => new lang_string('lastnamephonetic'),
'middlename' => new lang_string('middlename'),
*/
protected function get_user_field_type(string $userfield): int {
switch ($userfield) {
+ case 'description':
+ $fieldtype = column::TYPE_LONGTEXT;
+ break;
case 'confirmed':
case 'suspended':
$fieldtype = column::TYPE_BOOLEAN;
* @return filter[]
*/
protected function get_all_filters(): array {
+ global $DB;
+
$filters = [];
$tablealias = $this->get_table_alias('user');
// User fields filters.
$fields = $this->get_user_fields();
foreach ($fields as $field => $name) {
+ // Filtering isn't supported for LONGTEXT fields on Oracle.
+ if ($this->get_user_field_type($field) === column::TYPE_LONGTEXT &&
+ $DB->get_dbfamily() === 'oracle') {
+
+ continue;
+ }
+
$optionscallback = [static::class, 'get_options_for_' . $field];
if (is_callable($optionscallback)) {
$classname = select::class;
return ['', []];
}
- $paramdatefrom = database::generate_param_name();
- $paramdateto = database::generate_param_name();
-
+ // Generate parameters and SQL clause for the relative date comparison.
+ [$paramdatefrom, $paramdateto] = database::generate_param_names(2);
$sql = "{$fieldsql} >= :{$paramdatefrom} AND {$fieldsql} <= :{$paramdateto}";
+
[
$params[$paramdatefrom],
$params[$paramdateto],
return ['', []];
}
- $param = database::generate_param_name();
- $param2 = database::generate_param_name();
+ [$param, $param2] = database::generate_param_names(2);
+
$fieldsql = $this->filter->get_field_sql();
$params = $this->filter->get_field_params();
return static::GENERATE_ALIAS_PREFIX . ($aliascount++);
}
+
+ /**
+ * Generate multiple unique table/column aliases, see {@see generate_alias} for info
+ *
+ * @param int $count
+ * @return string[]
+ */
+ public static function generate_aliases(int $count): array {
+ return array_map([
+ static::class, 'generate_alias'
+ ], array_fill(0, $count, null));
+ }
+
/**
* Generates unique parameter name that must be used in generated SQL
*
return static::GENERATE_PARAM_PREFIX . ($paramcount++);
}
+ /**
+ * Generate multiple unique parameter names, see {@see generate_param_name} for info
+ *
+ * @param int $count
+ * @return string[]
+ */
+ public static function generate_param_names(int $count): array {
+ return array_map([
+ static::class, 'generate_param_name'
+ ], array_fill(0, $count, null));
+ }
+
/**
* Validate that parameter names were generated using {@see generate_param_name}.
*
/** @var string $downloadfilename Name of the downloaded file */
private $downloadfilename = '';
+ /** @var int Default paging size */
+ private $defaultperpage = self::DEFAULT_PAGESIZE;
+
/**
* Base report constructor
*
return $this->entities[$name];
}
+ /**
+ * Returns the list of all the entities added to the report
+ *
+ * @return entity_base[]
+ */
+ final protected function get_entities(): array {
+ return $this->entities;
+ }
+
/**
* Define a new entity for the report
*
public function get_context(): context {
return $this->report->get_context();
}
+
+ /**
+ * Set the default 'per page' size
+ *
+ * @param int $defaultperpage
+ */
+ public function set_default_per_page(int $defaultperpage): void {
+ $this->defaultperpage = $defaultperpage;
+ }
+
+ /**
+ * Default 'per page' size
+ *
+ * @return int
+ */
+ public function get_default_per_page(): int {
+ return $this->defaultperpage;
+ }
}
* @return string
*/
public function get_row_class(stdClass $row): string {
- return $row->enabled ? '' : 'dimmed_text';
+ return $row->enabled ? '' : 'text-muted';
}
/**
* @return string
*/
public function get_row_class(stdClass $row): string {
- return $this->report_source_valid($row->source) ? '' : 'dimmed_text';
+ return $this->report_source_valid($row->source) ? '' : 'text-muted';
}
/**
namespace core_reportbuilder\output;
+use core_reportbuilder\manager;
use core_reportbuilder\external\custom_report_exporter;
use core_reportbuilder\local\models\report;
use renderable;
* @return stdClass
*/
public function export_for_template(renderer_base $output): stdClass {
- $exporter = new custom_report_exporter($this->persistent, [], $this->editmode, $this->download);
+ $report = manager::get_report_from_persistent($this->persistent);
+
+ $exporter = new custom_report_exporter($this->persistent, [
+ 'pagesize' => $report->get_default_per_page(),
+ ], $this->editmode, $this->download);
return $exporter->export($output);
}
$prefix = database::generate_param_name() . '_';
[$insql, $inparams] = $DB->get_in_or_equal($roles, SQL_PARAMS_NAMED, $prefix);
- $contextid = database::generate_param_name();
- $ra = database::generate_alias();
- $ctx = database::generate_alias();
+ // Ensure parameter names and aliases are unique, as the same audience type can be added multiple times to a report.
+ $paramcontextid = database::generate_param_name();
+ [$roleassignments, $context] = database::generate_aliases(2);
$join = "
- JOIN {role_assignments} {$ra} ON {$ra}.userid = {$usertablealias}.id
- JOIN {context} {$ctx} ON {$ctx}.id = {$ra}.contextid";
+ JOIN {role_assignments} {$roleassignments} ON {$roleassignments}.userid = {$usertablealias}.id
+ JOIN {context} {$context} ON {$context}.id = {$roleassignments}.contextid";
- $where = "{$ra}.contextid = :{$contextid} AND {$ra}.roleid {$insql}";
+ $where = "{$roleassignments}.contextid = :{$paramcontextid} AND {$roleassignments}.roleid {$insql}";
- return [$join, $where, $inparams + [$contextid => context_system::instance()->id]];
+ return [$join, $where, $inparams + [$paramcontextid => context_system::instance()->id]];
}
/**
return '';
}
- /**
- * Default 'per page' size. Can be overridden by system reports to define a different paging value
- *
- * @return int
- */
- public function get_default_per_page(): int {
- return self::DEFAULT_PAGESIZE;
- }
-
/**
* Called before rendering each row. Can be overridden to pre-fetch/create objects and store them in the class, which can
* later be used in column and action callbacks
$this->initialbars(false);
$this->collapsible(false);
$this->pageable(true);
+ $this->set_default_per_page($this->report->get_default_per_page());
// Initialise table SQL properties.
$this->set_report_editing(static::REPORT_EDITING);
base_report_table::print_headers();
}
+ /**
+ * Override base implementation, return pagesize as defined in table filterset
+ *
+ * @return int
+ */
+ public function get_default_per_page(): int {
+ $filterset = $this->get_filterset();
+
+ return $filterset->get_filter('pagesize')->current();
+ }
+
/**
* Get the html for the download buttons
*
namespace core_reportbuilder\table;
+use core_table\local\filter\filterset;
+use core_table\local\filter\integer_filter;
+
/**
* Custom report dynamic table filterset class
*
* @copyright 2021 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-class custom_report_table_view_filterset extends custom_report_table_filterset {
+class custom_report_table_view_filterset extends filterset {
+
+ /**
+ * Get the required filters
+ *
+ * @return array.
+ */
+ public function get_required_filters(): array {
+ return [
+ 'pagesize' => integer_filter::class,
+ ];
+ }
}
And I am on the "My report" "reportbuilder > Editor" page logged in as "admin"
And I click on the "Schedules" dynamic tab
When I click on "Disable schedule" "field" in the "My schedule" "table_row"
- Then the "class" attribute of "My schedule" "table_row" should contain "dimmed_text"
+ Then the "class" attribute of "My schedule" "table_row" should contain "text-muted"
And I click on "Enable schedule" "field" in the "My schedule" "table_row"
Scenario: Edit report schedule
class database_test extends advanced_testcase {
/**
- * Test generating table alias and parameter names
+ * Test generating alias
*/
- public function test_generate_alias_params(): void {
- global $DB;
-
- $admin = core_user::get_user_by_username('admin');
+ public function test_generate_alias(): void {
+ $this->assertMatchesRegularExpression('/^rbalias(\d+)$/', database::generate_alias());
+ }
- $usertablealias = database::generate_alias();
- $usertablealiasjoin = database::generate_alias();
- $useridalias = database::generate_alias();
+ /**
+ * Test generating multiple aliases
+ */
+ public function test_generate_aliases(): void {
+ $aliases = database::generate_aliases(3);
- $paramuserid = database::generate_param_name();
- $paramuserdeleted = database::generate_param_name();
+ $this->assertCount(3, $aliases);
+ [$aliasone, $aliastwo, $aliasthree] = $aliases;
// Ensure they are different.
- $this->assertNotEquals($usertablealias, $usertablealiasjoin);
- $this->assertNotEquals($paramuserid, $paramuserdeleted);
+ $this->assertNotEquals($aliasone, $aliastwo);
+ $this->assertNotEquals($aliasone, $aliasthree);
+ $this->assertNotEquals($aliastwo, $aliasthree);
+ }
- $sql = "SELECT {$usertablealias}.id AS {$useridalias}
- FROM {user} {$usertablealias}
- JOIN {user} {$usertablealiasjoin} ON {$usertablealiasjoin}.id = {$usertablealias}.id
- WHERE {$usertablealias}.id = :{$paramuserid} AND {$usertablealias}.deleted = :{$paramuserdeleted}";
- $params = [$paramuserid => $admin->id, $paramuserdeleted => 0];
+ /**
+ * Test generating parameter name
+ */
+ public function test_generate_param_name(): void {
+ $this->assertMatchesRegularExpression('/^rbparam(\d+)$/', database::generate_param_name());
+ }
- $validated = database::validate_params($params);
- $this->assertTrue($validated);
+ /**
+ * Test generating multiple parameter names
+ */
+ public function test_generate_param_names(): void {
+ $params = database::generate_param_names(3);
- $record = $DB->get_record_sql($sql, $params);
- $this->assertEquals($admin->id, $record->{$useridalias});
+ $this->assertCount(3, $params);
+ [$paramone, $paramtwo, $paramthree] = $params;
+
+ // Ensure they are different.
+ $this->assertNotEquals($paramone, $paramtwo);
+ $this->assertNotEquals($paramone, $paramthree);
+ $this->assertNotEquals($paramtwo, $paramthree);
}
/**
* Test parameter validation
*/
public function test_validate_params(): void {
+ [$paramone, $paramtwo] = database::generate_param_names(2);
+
+ $params = [
+ $paramone => 1,
+ $paramtwo => 2,
+ ];
+
+ $this->assertTrue(database::validate_params($params));
+ }
+
+ /**
+ * Test parameter validation for invalid parameters
+ */
+ public function test_validate_params_invalid(): void {
$params = [
database::generate_param_name() => 1,
'invalidfoo' => 2,
$this->expectExceptionMessage('Invalid parameter names (invalidfoo, invalidbar)');
database::validate_params($params);
}
+
+ /**
+ * Generate aliases and parameters and confirm they can be used within a query
+ */
+ public function test_generated_data_in_query(): void {
+ global $DB;
+
+ // Unique aliases.
+ [
+ $usertablealias,
+ $userfieldalias,
+ ] = database::generate_aliases(2);
+
+ // Unique parameters.
+ [
+ $paramuserid,
+ $paramuserdeleted,
+ ] = database::generate_param_names(2);
+
+ // Simple query to retrieve the admin user.
+ $sql = "SELECT {$usertablealias}.id AS {$userfieldalias}
+ FROM {user} {$usertablealias}
+ WHERE {$usertablealias}.id = :{$paramuserid}
+ AND {$usertablealias}.deleted = :{$paramuserdeleted}";
+
+ $admin = core_user::get_user_by_username('admin');
+
+ $params = [
+ $paramuserid => $admin->id,
+ $paramuserdeleted => 0,
+ ];
+
+ $record = $DB->get_record_sql($sql, $params);
+ $this->assertEquals($admin->id, $record->{$userfieldalias});
+ }
}
--- /dev/null
+This file describes API changes in /reportbuilder/*
+Information provided here is intended especially for developers.
+
+=== 4.1 ===
+* 'set_default_per_page' and 'get_default_per_page' methods have been added to \local\report\base class
+ to manage the default displayed rows per page.
+* Added two new methods in the datasource class:
+ - add_all_from_entity() to add all columns/filters/conditions from the given entity to the report at once
+ - add_all_from_entities() to add all columns/filters/conditions from all the entities added to the report at once
+=======
+* New database helper methods for generating multiple unique values: `generate_aliases` and `generate_param_names`
$userparamguest => $CFG->siteguest,
]);
- // Add all columns from entities to be available in custom reports.
$this->add_entity($userentity);
+ // Add all columns/filters/conditions from entities to be available in custom reports.
$userentityname = $userentity->get_entity_name();
- $this->add_columns_from_entity($userentityname);
- $this->add_filters_from_entity($userentityname);
- $this->add_conditions_from_entity($userentityname);
+ $this->add_all_from_entity($userentityname);
}
/**