autocomplete.php 9.08 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?php
/**
 *
 * @package    mahara
 * @subpackage artefact
 * @author     Yuliya Bozhko <yuliya.bozhko@totaralms.com>
 * @author     Aaron Wells <aaronw@catalyst.net.nz>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later
 * @copyright  For copyright information on Mahara, please see the README file distributed with this software.
 *
 */

// To use in forms:
// 'ELEMENTNAME'  => array(
//      'type'         => 'autocomplete',
//      'title'        => TEXT,
//      'ajaxurl'      => Absolute URL of the Ajax callback script that provides the autocompletion results.
//                        This script should take params "q", "page" (the page number, starts at 1), and "sesskey".
//                        It should return a JSON-encoded object that must contain fields "more" (which indicates
//                        whether there are more results to page through) and "results", which will be an array
//                        of result objects each of which must have an "id", a "text", and optionally a "disabled" flag.
//                        {
//                           more : true,
//                           results : [
//                              {id:1, text:'first result'},
//                              {id:2, text:'second result'},
//                              {id:3, text:'disabled result', disabled:true},
//                           ]
//                        }
//      'initfunction' => A PHP function (name or inline function) that translates selected values ("id"'s) into (id, text)
//                        pairs. This is used when initially displaying the menu, because Pieforms only gives us the
//                        value/id of the selected element, but Select2 also needs to know its label/text.
//                        For a multi-select this method should take an array of IDs and return an array of objects with
//                        "id" and "text" fields. For a non-multi-select it should take a single ID and return a single
//                        object.
//      'defaultvalue' => The value (ID) of the initial selected element(s). For a multi-select this should be an array
//                        of IDs; for a non-multi-select it should be a single ID. NOTE: This is *just* the ID(s),
//                        not the label (i.e. "text"). The label will be retrieved by passing this to the "initfunction"
//                        callback method.
//      'description'  => TEXT, // Optional description printed under the menu
//      'multiple'     => false, // Indicates whether we should allow the user to select multiple items
//      'hint'         => TEXT, // Optional placeholder text displayed before anything is selected
//      'mininputlength' => Default 1. User must enter this many characters before a search is fired off
//      'allowclear'   => Default false. Only for non-multiple selects. Set to "true" to add a button that clears
//                        the user's selection
//      'width'        => Default 300px. How wide to make the dropdown. Passed to the select2 "width" parameter.
//                        Since we're doing Ajax calls, the menu will be a little glitchy because it can't tell
//                        how wide the widest item it will need to display is. So it's best to pass it a hard-coded width,
//                        that you expect will be wide enough to display your entries.
//      'extraparams'  => array(key => value, key2 => value2,  ...), // Optional additional configuration parameters for
//                        the select2 ajax library.
52
//      'inblockconfig' => If the field is a block config field we need to handle the js autocomplete js slightly differently
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// ),

defined('INTERNAL') || die();

/**
 * Autocomplete list selector element
 *
 * @param array    $element The element to render
 * @param Pieform  $form    The form to render the element for
 * @return string           The HTML for the element
 */
function pieform_element_autocomplete(Pieform $form, $element) {
    global $USER;
    $wwwroot = get_config('wwwroot');
67
68
    $lang = pieform_element_autocomplete_language();

69
70
71
72
73
74
75
76
77
    $smarty = smarty_core();

    $smarty->left_delimiter = '{{';
    $smarty->right_delimiter = '}}';

    $value = $form->get_value($element);
    $multiple = !empty($element['multiple']);

    if (!empty($element['initfunction'])) {
Robert Lyon's avatar
Robert Lyon committed
78
        $initvalues = call_user_func($element['initfunction'], $value);
79
80
    }
    else {
Robert Lyon's avatar
Robert Lyon committed
81
        $initvalues = '[]';
82
83
84
85
86
87
88
89
90
91
92
93
    }

    if (array_key_exists('mininputlength', $element)) {
        $mininputlength = $element['mininputlength'];
    }
    else {
        $mininputlength = 1;
    }

    $extraparams = '';
    if (!empty($element['extraparams'])) {
        foreach ($element['extraparams'] as $k => $v) {
94
            if (!is_numeric($v) && !preg_match('/^function/', $v)) {
95
96
97
98
99
100
101
102
103
                if (preg_match('/^\'(.*)\'$/', $v, $match)) {
                    $v = $match[1];
                }
                $element['extraparams'][$k] = json_encode($v);
            }
            $extraparams .= $k . ': ' . $element['extraparams'][$k] . ',';
        }
    }

104
105
    $renderresult = isset($element['extraparams']['renderresult']) ? $element['extraparams']['renderresult'] : null;

106
107
    $smarty->assign('id', $form->get_name() . '_' . $element['id']);
    $smarty->assign('name', $element['name']);
Robert Lyon's avatar
Robert Lyon committed
108
    $smarty->assign('initvalues', $initvalues);
109
110
111
112
113
114
    $smarty->assign('width', empty($element['width']) ? '300px' : $element['width']);
    $smarty->assign('multiple', $multiple ? 'true' : 'false');
    $smarty->assign('mininputlength', $mininputlength);
    $smarty->assign('allowclear', empty($element['allowclear']) ? 'false' : 'true');
    $smarty->assign('disabled', !empty($element['disabled']) ? 'true' : 'false');
    $smarty->assign('ajaxurl', $element['ajaxurl']);
115
    $smarty->assign('language', $lang);
116
117
118
    $smarty->assign('sesskey', $USER->get('sesskey'));
    $smarty->assign('hint', empty($element['hint']) ? get_string('defaulthint') : $element['hint']);
    $smarty->assign('extraparams', $extraparams);
119
    $smarty->assign('inblockconfig', !empty($element['inblockconfig']) ? 'true' : 'false');
120
    $smarty->assign('renderresult', $renderresult);
121
122
123
124
125
126
127
128
    if (isset($element['description'])) {
        $smarty->assign('describedby', $form->element_descriptors($element));
    }

    return $smarty->fetch('form/autocomplete.tpl');
}

/**
129
130
 * Returns the current language that the user is viewing the form with
 * or 'en' if a corresponding select2 lang file is not found.
131
 *
132
 * @return string $langstr  A valid lang string
133
 */
134
function pieform_element_autocomplete_language() {
135
136
137
138
139
    global $THEME;
    // Add language file if required.
    $lang = current_language();
    // Replace '_' with '-' which is used in select2.
    $lang = str_replace('_', '-', substr($lang, 0, ((substr_count($lang, '_') > 0) ? 5 : 2)));
140
141
    if ($lang != 'en' && file_exists(get_config('docroot') . "js/select2/i18n/{$lang}.js")) {
        return $lang;
142
143
144
145
    }
    else {
        // Try parent language pack, which, for example, would be 'pt' for 'pt-BR'.
        $lang = substr($lang, 0, 2);
146
147
        if ($lang != 'en' && file_exists(get_config('docroot') . "js/select2/i18n/{$lang}.js")) {
            return $lang;
148
149
150
        }
    }

151
152
153
154
155
156
157
158
159
    return 'en';
}

/**
 * Returns code to go in <head> for the given autocomplete instance
 *
 * @param array $element The element to get <head> code for
 * @return array         An array of HTML elements to go in the <head>
 */
160
function pieform_element_autocomplete_get_headdata() {
161
162
163
164
165
166
167
168
    global $THEME;
    $lang = pieform_element_autocomplete_language();
    $langfile = '';
    if ($lang != 'en') {
        $langfile = '<script type="application/javascript" src="' .
            get_config('wwwroot') . "js/select2/i18n/{$lang}.js" .
            '"></script>';
    }
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
    $r = <<<JS
{$langfile}
JS;
    return array($r);
}

/**
 * Translates the raw form data into PHP variables. Basically it just needs to
 * decide whether we should return an array (if this is a multi-select) or a
 * scalar (if this is not a multi-select)
 *
 * @param Pieform $form
 * @param array $element
 * @return mixed
 */
function pieform_element_autocomplete_get_value(Pieform $form, $element) {
    $global = ($form->get_property('method') == 'get') ? $_GET : $_POST;
    if (isset($element['value'])) {
        $values = $element['value'];
    }
    else if ($form->is_submitted() && isset($global[$element['name']])) {
        $values = $global[$element['name']];
    }
    else if (!$form->is_submitted() && isset($element['defaultvalue'])) {
        $values = $element['defaultvalue'];
    }
    else if (!empty($element['disabled']) && isset($element['defaultvalue'])) {
        $values = $element['defaultvalue'];
    }
    else {
        $values = null;
    }

    if (empty($element['multiple'])) {
        return $values;
    }
    else {
        // Defaultvalue will already be an array
        if (is_array($values)) {
            return $values;
        }

        // Values returned form the form will be a comma-separated list
        $r = explode(',', $values);
        if ($r === false) {
            return array();
        }
        else {
            return $r;
        }
    }
}