See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 401 and 402] [Versions 401 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 /** 18 * Contains class \core\output\inplace_editable 19 * 20 * @package core 21 * @category output 22 * @copyright 2016 Marina Glancy 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 namespace core\output; 27 28 use templatable; 29 use renderable; 30 use lang_string; 31 use pix_icon; 32 33 /** 34 * Class allowing to quick edit a title inline 35 * 36 * This class is used for displaying an element that can be in-place edited by the user. To display call: 37 * echo $OUTPUT->render($element); 38 * or 39 * echo $OUTPUT->render_from_template('core/inplace_editable', $element->export_for_template($OUTPUT)); 40 * 41 * Template core/inplace_editable will automatically load javascript module with the same name 42 * core/inplace_editable. Javascript module registers a click-listener on edit link and 43 * then replaces the displayed value with an input field. On "Enter" it sends a request 44 * to web service core_update_inplace_editable, which invokes the callback from the component. 45 * Any exception thrown by the web service (or callback) is displayed as an error popup. 46 * 47 * Callback {$component}_inplace_editable($itemtype, $itemid, $newvalue) must be present in the lib.php file of 48 * the component or plugin. It must return instance of this class. 49 * 50 * @package core 51 * @category output 52 * @copyright 2016 Marina Glancy 53 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 54 */ 55 class inplace_editable implements templatable, renderable { 56 57 /** 58 * @var string component responsible for diplsying/updating 59 */ 60 protected $component = null; 61 62 /** 63 * @var string itemtype inside the component 64 */ 65 protected $itemtype = null; 66 67 /** 68 * @var int identifier of the editable element (usually database id) 69 */ 70 protected $itemid = null; 71 72 /** 73 * @var string value of the editable element as it is present in the database 74 */ 75 protected $value = null; 76 77 /** 78 * @var string value of the editable element as it should be displayed, 79 * must be formatted and may contain links or other html tags 80 */ 81 protected $displayvalue = null; 82 83 /** 84 * @var string label for the input element (for screenreaders) 85 */ 86 protected $editlabel = null; 87 88 /** 89 * @var string hint for the input element (for screenreaders) 90 */ 91 protected $edithint = null; 92 93 /** 94 * @var pix_icon icon to use to toggle editing 95 */ 96 protected $editicon = null; 97 98 /** 99 * @var bool indicates if the current user is allowed to edit this element - set in constructor after permissions are checked 100 */ 101 protected $editable = false; 102 103 /** 104 * @var string type of the element - text, toggle or select 105 */ 106 protected $type = 'text'; 107 108 /** 109 * @var string options for the element, for example new value for the toggle or json-encoded list of options for select 110 */ 111 protected $options = ''; 112 113 /** 114 * Constructor. 115 * 116 * @param string $component name of the component or plugin responsible for the updating of the value (must declare callback) 117 * @param string $itemtype type of the item inside the component - each component/plugin may implement multiple inplace-editable elements 118 * @param int $itemid identifier of the item that can be edited in-place 119 * @param bool $editable whether this value is editable (check capabilities and editing mode), if false, only "displayvalue" 120 * will be displayed without anything else 121 * @param string $displayvalue what needs to be displayed to the user, it must be cleaned, with applied filters (call 122 * {@link format_string()}). It may be wrapped in an html link, contain icons or other decorations 123 * @param string $value what needs to be edited - usually raw value from the database, it may contain multilang tags 124 * @param lang_string|string $edithint hint (title) that will be displayed under the edit link 125 * @param lang_string|string $editlabel label for the input element in the editing mode (for screenreaders) 126 * @param pix_icon|null $editicon icon to use to toggle editing 127 */ 128 public function __construct($component, $itemtype, $itemid, $editable, 129 $displayvalue, $value = null, $edithint = null, $editlabel = null, ?pix_icon $editicon = null) { 130 $this->component = $component; 131 $this->itemtype = $itemtype; 132 $this->itemid = $itemid; 133 $this->editable = $editable; 134 $this->displayvalue = $displayvalue; 135 $this->value = $value; 136 $this->edithint = $edithint; 137 $this->editlabel = $editlabel; 138 $this->editicon = $editicon; 139 } 140 141 /** 142 * Sets the element type to be a toggle 143 * 144 * For toggle element $editlabel is not used. 145 * $displayvalue must be specified, it can have text or icons but can not contain html links. 146 * 147 * Toggle element can have two or more options. 148 * 149 * @param array $options toggle options as simple, non-associative array; defaults to array(0,1) 150 * @return self 151 */ 152 public function set_type_toggle($options = null) { 153 if ($options === null) { 154 $options = array(0, 1); 155 } 156 $options = array_values($options); 157 $idx = array_search($this->value, $options, true); 158 if ($idx === false) { 159 throw new \coding_exception('Specified value must be one of the toggle options'); 160 } 161 $nextvalue = ($idx < count($options) - 1) ? $idx + 1 : 0; 162 163 $this->type = 'toggle'; 164 $this->options = (string)$nextvalue; 165 return $this; 166 } 167 168 /** 169 * Sets the element type to be a dropdown 170 * 171 * For select element specifying $displayvalue is optional, if null it will 172 * be assumed that $displayvalue = $options[$value]. 173 * However displayvalue can still be specified if it needs icons and/or 174 * html links. 175 * 176 * If only one option specified, the element will not be editable. 177 * 178 * @param array $options associative array with dropdown options 179 * @return self 180 */ 181 public function set_type_select($options) { 182 if (!array_key_exists($this->value, $options)) { 183 throw new \coding_exception('Options for select element must contain an option for the specified value'); 184 } 185 if (count($options) < 2) { 186 $this->editable = false; 187 } 188 $this->type = 'select'; 189 190 $pairedoptions = []; 191 foreach ($options as $key => $value) { 192 $pairedoptions[] = [ 193 'key' => $key, 194 'value' => $value, 195 ]; 196 } 197 $this->options = json_encode($pairedoptions); 198 if ($this->displayvalue === null) { 199 $this->displayvalue = $options[$this->value]; 200 } 201 if ($this->editicon === null) { 202 $this->editicon = new pix_icon('t/expanded', (string) $this->edithint); 203 } 204 return $this; 205 } 206 207 /** 208 * Sets the element type to be an autocomplete field 209 * 210 * @param array $options associative array with dropdown options 211 * @param array $attributes associative array with attributes for autoselect field. See AMD module core/form-autocomplete. 212 * @return self 213 */ 214 public function set_type_autocomplete($options, $attributes) { 215 $this->type = 'autocomplete'; 216 217 $pairedoptions = []; 218 foreach ($options as $key => $value) { 219 $pairedoptions[] = [ 220 'key' => $key, 221 'value' => $value, 222 ]; 223 } 224 $this->options = json_encode(['options' => $pairedoptions, 'attributes' => $attributes]); 225 return $this; 226 } 227 228 /** 229 * Whether the link should contain all of the content or not. 230 */ 231 protected function get_linkeverything() { 232 if ($this->type === 'toggle') { 233 return true; 234 } 235 236 if (preg_match('#<a .*>.*</a>#', $this->displayvalue) === 1) { 237 return false; 238 } 239 240 return true; 241 } 242 243 /** 244 * Export this data so it can be used as the context for a mustache template (core/inplace_editable). 245 * 246 * @param renderer_base $output typically, the renderer that's calling this function 247 * @return array data context for a mustache template 248 */ 249 public function export_for_template(\renderer_base $output) { 250 if (!$this->editable) { 251 return array( 252 'displayvalue' => (string)$this->displayvalue 253 ); 254 } 255 256 if ($this->editicon === null) { 257 $this->editicon = new pix_icon('t/editstring', (string) $this->edithint); 258 } 259 260 return array( 261 'component' => $this->component, 262 'itemtype' => $this->itemtype, 263 'itemid' => $this->itemid, 264 'displayvalue' => (string)$this->displayvalue, 265 'value' => (string)$this->value, 266 'edithint' => (string)$this->edithint, 267 'editlabel' => (string)$this->editlabel, 268 'editicon' => $this->editicon->export_for_pix(), 269 'type' => $this->type, 270 'options' => $this->options, 271 'linkeverything' => $this->get_linkeverything() ? 1 : 0, 272 ); 273 } 274 275 /** 276 * Renders this element 277 * 278 * @param renderer_base $output typically, the renderer that's calling this function 279 * @return string 280 */ 281 public function render(\renderer_base $output) { 282 return $output->render_from_template('core/inplace_editable', $this->export_for_template($output)); 283 } 284 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body