Differences Between: [Versions 400 and 401] [Versions 400 and 402] [Versions 400 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 declare(strict_types=1); 18 19 namespace core_reportbuilder\local\helpers; 20 21 use context_system; 22 use core_reportbuilder\local\filters\boolean_select; 23 use core_reportbuilder\local\filters\date; 24 use core_reportbuilder\local\filters\select; 25 use core_reportbuilder\local\filters\text; 26 use core_reportbuilder\local\helpers\database; 27 use core_reportbuilder\local\report\column; 28 use core_reportbuilder\local\report\filter; 29 use lang_string; 30 use profile_field_base; 31 use stdClass; 32 33 defined('MOODLE_INTERNAL') || die(); 34 35 global $CFG; 36 require_once($CFG->dirroot.'/user/profile/lib.php'); 37 38 /** 39 * Helper class for user profile fields. 40 * 41 * @package core_reportbuilder 42 * @copyright 2021 David Matamoros <davidmc@moodle.com> 43 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 44 */ 45 class user_profile_fields { 46 47 /** @var array user profile fields */ 48 private $userprofilefields; 49 50 /** @var string $entityname Name of the entity */ 51 private $entityname; 52 53 /** @var int $usertablefieldalias The user table/field alias */ 54 private $usertablefieldalias; 55 56 /** @var array additional joins */ 57 private $joins = []; 58 59 /** 60 * Class userprofilefields constructor. 61 * 62 * @param string $usertablefieldalias The user table/field alias used when adding columns and filters. 63 * @param string $entityname The entity name used when adding columns and filters. 64 */ 65 public function __construct(string $usertablefieldalias, string $entityname) { 66 $this->usertablefieldalias = $usertablefieldalias; 67 $this->entityname = $entityname; 68 $this->userprofilefields = $this->get_user_profile_fields(); 69 } 70 71 /** 72 * Retrieves the list of available/visible user profile fields 73 * 74 * @return profile_field_base[] 75 */ 76 private function get_user_profile_fields(): array { 77 return array_filter(profile_get_user_fields_with_data(0), static function(profile_field_base $profilefield): bool { 78 return $profilefield->is_visible(); 79 }); 80 } 81 82 /** 83 * Additional join that is needed. 84 * 85 * @param string $join 86 * @return self 87 */ 88 public function add_join(string $join): self { 89 $this->joins[trim($join)] = trim($join); 90 return $this; 91 } 92 93 /** 94 * Additional joins that are needed. 95 * 96 * @param array $joins 97 * @return self 98 */ 99 public function add_joins(array $joins): self { 100 foreach ($joins as $join) { 101 $this->add_join($join); 102 } 103 return $this; 104 } 105 106 /** 107 * Return joins 108 * 109 * @return string[] 110 */ 111 private function get_joins(): array { 112 return array_values($this->joins); 113 } 114 115 /** 116 * Return the user profile fields visible columns. 117 * 118 * @return column[] 119 */ 120 public function get_columns(): array { 121 global $DB; 122 123 $columns = []; 124 foreach ($this->userprofilefields as $profilefield) { 125 $userinfotablealias = database::generate_alias(); 126 127 $columntype = $this->get_user_field_type($profilefield->field->datatype); 128 129 $columnfieldsql = "{$userinfotablealias}.data"; 130 if ($DB->get_dbfamily() === 'oracle') { 131 $columnfieldsql = $DB->sql_order_by_text($columnfieldsql, 1024); 132 } 133 134 $column = (new column( 135 'profilefield_' . $profilefield->field->shortname, 136 new lang_string('customfieldcolumn', 'core_reportbuilder', 137 format_string($profilefield->field->name, true, 138 ['escape' => true, 'context' => context_system::instance()])), 139 $this->entityname 140 )) 141 ->add_joins($this->get_joins()) 142 ->add_join("LEFT JOIN {user_info_data} {$userinfotablealias} " . 143 "ON {$userinfotablealias}.userid = {$this->usertablefieldalias} " . 144 "AND {$userinfotablealias}.fieldid = {$profilefield->fieldid}") 145 ->add_field($columnfieldsql, 'data') 146 ->set_type($columntype) 147 ->set_is_sortable($columntype !== column::TYPE_LONGTEXT) 148 ->add_callback([$this, 'format_profile_field'], $profilefield); 149 150 $columns[] = $column; 151 } 152 153 return $columns; 154 } 155 156 /** 157 * Get custom user profile fields filters. 158 * 159 * @return filter[] 160 */ 161 public function get_filters(): array { 162 global $DB; 163 164 $filters = []; 165 foreach ($this->userprofilefields as $profilefield) { 166 $userinfotablealias = database::generate_alias(); 167 $field = "{$userinfotablealias}.data"; 168 $params = []; 169 170 switch ($profilefield->field->datatype) { 171 case 'checkbox': 172 $classname = boolean_select::class; 173 $fieldsql = "COALESCE(" . $DB->sql_cast_char2int($field, true) . ", 0)"; 174 break; 175 case 'datetime': 176 $classname = date::class; 177 $fieldsql = $DB->sql_cast_char2int($field, true); 178 break; 179 case 'menu': 180 $classname = select::class; 181 182 $emptyparam = database::generate_param_name(); 183 $fieldsql = "COALESCE(" . $DB->sql_compare_text($field, 255) . ", :{$emptyparam})"; 184 $params[$emptyparam] = ''; 185 186 break; 187 case 'text': 188 case 'textarea': 189 default: 190 $classname = text::class; 191 192 $emptyparam = database::generate_param_name(); 193 $fieldsql = "COALESCE(" . $DB->sql_compare_text($field, 255) . ", :{$emptyparam})"; 194 $params[$emptyparam] = ''; 195 196 break; 197 } 198 199 $filter = (new filter( 200 $classname, 201 'profilefield_' . $profilefield->field->shortname, 202 new lang_string('customfieldcolumn', 'core_reportbuilder', 203 format_string($profilefield->field->name, true, 204 ['escape' => false, 'context' => context_system::instance()])), 205 $this->entityname, 206 $fieldsql, 207 $params 208 )) 209 ->add_joins($this->get_joins()) 210 ->add_join("LEFT JOIN {user_info_data} {$userinfotablealias} " . 211 "ON {$userinfotablealias}.userid = {$this->usertablefieldalias} " . 212 "AND {$userinfotablealias}.fieldid = {$profilefield->fieldid}"); 213 214 // If menu type then set filter options as appropriate. 215 if ($profilefield->field->datatype === 'menu') { 216 $filter->set_options($profilefield->options); 217 } 218 219 $filters[] = $filter; 220 } 221 222 return $filters; 223 } 224 225 /** 226 * Get user profile field type for report. 227 * 228 * @param string $userfield user field. 229 * @return int the constant equivalent to this custom field type. 230 */ 231 protected function get_user_field_type(string $userfield): int { 232 switch ($userfield) { 233 case 'checkbox': 234 $customfieldtype = column::TYPE_BOOLEAN; 235 break; 236 case 'datetime': 237 $customfieldtype = column::TYPE_TIMESTAMP; 238 break; 239 case 'textarea': 240 $customfieldtype = column::TYPE_LONGTEXT; 241 break; 242 case 'menu': 243 case 'text': 244 default: 245 $customfieldtype = column::TYPE_TEXT; 246 break; 247 } 248 return $customfieldtype; 249 } 250 251 /** 252 * Formatter for a profile field. It formats the field according to its type. 253 * 254 * @param mixed $value 255 * @param stdClass $row 256 * @param profile_field_base $field 257 * @return string 258 */ 259 public static function format_profile_field($value, stdClass $row, profile_field_base $field): string { 260 // Special handling of checkboxes, we want to display their boolean state rather than the input element itself. 261 if (is_a($field, 'profile_field_checkbox')) { 262 return format::boolean_as_text($value); 263 } 264 265 $field->data = $value; 266 return (string) $field->display_data(); 267 } 268 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body