Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

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