Proyectos de Subversion Moodle

Rev

| 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
/**
18
 * Bulk user registration functions
19
 *
20
 * @package    tool
21
 * @subpackage uploaduser
22
 * @copyright  2004 onwards Martin Dougiamas (http://dougiamas.com)
23
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
 
26
defined('MOODLE_INTERNAL') || die();
27
 
28
define('UU_USER_ADDNEW', 0);
29
define('UU_USER_ADDINC', 1);
30
define('UU_USER_ADD_UPDATE', 2);
31
define('UU_USER_UPDATE', 3);
32
 
33
define('UU_UPDATE_NOCHANGES', 0);
34
define('UU_UPDATE_FILEOVERRIDE', 1);
35
define('UU_UPDATE_ALLOVERRIDE', 2);
36
define('UU_UPDATE_MISSING', 3);
37
 
38
define('UU_BULK_NONE', 0);
39
define('UU_BULK_NEW', 1);
40
define('UU_BULK_UPDATED', 2);
41
define('UU_BULK_ALL', 3);
42
 
43
define('UU_PWRESET_NONE', 0);
44
define('UU_PWRESET_WEAK', 1);
45
define('UU_PWRESET_ALL', 2);
46
 
47
/**
48
 * Tracking of processed users.
49
 *
50
 * This class prints user information into a html table.
51
 *
52
 * @package    core
53
 * @subpackage admin
54
 * @copyright  2007 Petr Skoda  {@link http://skodak.org}
55
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
56
 */
57
class uu_progress_tracker {
58
    /** @var array */
59
    protected $_row;
60
 
61
    /**
62
     * The columns shown on the table.
63
     * @var array
64
     */
65
    public $columns = [];
66
    /** @var array column headers */
67
    protected $headers = [];
68
 
69
    /**
70
     * uu_progress_tracker constructor.
71
     */
72
    public function __construct() {
73
        $this->headers = [
74
            'status' => get_string('status'),
75
            'line' => get_string('uucsvline', 'tool_uploaduser'),
76
            'id' => 'ID',
77
            'username' => get_string('username'),
78
            'firstname' => get_string('firstname'),
79
            'lastname' => get_string('lastname'),
80
            'email' => get_string('email'),
81
            'password' => get_string('password'),
82
            'auth' => get_string('authentication'),
83
            'enrolments' => get_string('enrolments', 'enrol'),
84
            'suspended' => get_string('suspended', 'auth'),
85
            'theme' => get_string('theme'),
86
            'deleted' => get_string('delete'),
87
        ];
88
        $this->columns = array_keys($this->headers);
89
    }
90
 
91
    /**
92
     * Print table header.
93
     * @return void
94
     */
95
    public function start() {
96
        $ci = 0;
97
        echo '<table id="uuresults" class="generaltable boxaligncenter flexible-wrap" summary="'.get_string('uploadusersresult', 'tool_uploaduser').'">';
98
        echo '<tr class="heading r0">';
99
        foreach ($this->headers as $key => $header) {
100
            echo '<th class="header c'.$ci++.'" scope="col">'.$header.'</th>';
101
        }
102
        echo '</tr>';
103
        $this->_row = null;
104
    }
105
 
106
    /**
107
     * Flush previous line and start a new one.
108
     * @return void
109
     */
110
    public function flush() {
111
        if (empty($this->_row) or empty($this->_row['line']['normal'])) {
112
            // Nothing to print - each line has to have at least number
113
            $this->_row = array();
114
            foreach ($this->columns as $col) {
115
                $this->_row[$col] = array('normal'=>'', 'info'=>'', 'warning'=>'', 'error'=>'');
116
            }
117
            return;
118
        }
119
        $ci = 0;
120
        $ri = 1;
121
        echo '<tr class="r'.$ri.'">';
122
        foreach ($this->_row as $key=>$field) {
123
            foreach ($field as $type=>$content) {
124
                if ($field[$type] !== '') {
125
                    $field[$type] = '<span class="uu'.$type.'">'.$field[$type].'</span>';
126
                } else {
127
                    unset($field[$type]);
128
                }
129
            }
130
            echo '<td class="cell c'.$ci++.'">';
131
            if (!empty($field)) {
132
                echo implode('<br />', $field);
133
            } else {
134
                echo '&nbsp;';
135
            }
136
            echo '</td>';
137
        }
138
        echo '</tr>';
139
        foreach ($this->columns as $col) {
140
            $this->_row[$col] = array('normal'=>'', 'info'=>'', 'warning'=>'', 'error'=>'');
141
        }
142
    }
143
 
144
    /**
145
     * Add tracking info
146
     * @param string $col name of column
147
     * @param string $msg message
148
     * @param string $level 'normal', 'warning' or 'error'
149
     * @param bool $merge true means add as new line, false means override all previous text of the same type
150
     * @return void
151
     */
152
    public function track($col, $msg, $level = 'normal', $merge = true) {
153
        if (empty($this->_row)) {
154
            $this->flush(); //init arrays
155
        }
156
        if (!in_array($col, $this->columns)) {
157
            debugging('Incorrect column:'.$col);
158
            return;
159
        }
160
        if ($merge) {
161
            if ($this->_row[$col][$level] != '') {
162
                $this->_row[$col][$level] .='<br />';
163
            }
164
            $this->_row[$col][$level] .= $msg;
165
        } else {
166
            $this->_row[$col][$level] = $msg;
167
        }
168
    }
169
 
170
    /**
171
     * Print the table end
172
     * @return void
173
     */
174
    public function close() {
175
        $this->flush();
176
        echo '</table>';
177
    }
178
}
179
 
180
/**
181
 * Validation callback function - verified the column line of csv file.
182
 * Converts standard column names to lowercase.
183
 * @param csv_import_reader $cir
184
 * @param array $stdfields standard user fields
185
 * @param array $profilefields custom profile fields
186
 * @param moodle_url $returnurl return url in case of any error
187
 * @return array list of fields
188
 */
189
function uu_validate_user_upload_columns(csv_import_reader $cir, $stdfields, $profilefields, moodle_url $returnurl) {
190
    $columns = $cir->get_columns();
191
 
192
    if (empty($columns)) {
193
        $cir->close();
194
        $cir->cleanup();
195
        throw new \moodle_exception('cannotreadtmpfile', 'error', $returnurl);
196
    }
197
    if (count($columns) < 2) {
198
        $cir->close();
199
        $cir->cleanup();
200
        throw new \moodle_exception('csvfewcolumns', 'error', $returnurl);
201
    }
202
 
203
    // test columns
204
    $processed = array();
205
    $acceptedfields = [
206
        'category',
207
        'categoryrole',
208
        'cohort',
209
        'course',
210
        'enrolperiod',
211
        'enrolstatus',
212
        'enroltimestart',
213
        'group',
214
        'role',
215
        'sysrole',
216
        'type',
217
    ];
218
    $specialfieldsregex = "/^(" . implode('|', $acceptedfields) . ")\d+$/";
219
 
220
    foreach ($columns as $key=>$unused) {
221
        $field = $columns[$key];
222
        $field = trim($field);
223
        $lcfield = core_text::strtolower($field);
224
        if (in_array($field, $stdfields) or in_array($lcfield, $stdfields)) {
225
            // standard fields are only lowercase
226
            $newfield = $lcfield;
227
 
228
        } else if (in_array($field, $profilefields)) {
229
            // exact profile field name match - these are case sensitive
230
            $newfield = $field;
231
 
232
        } else if (in_array($lcfield, $profilefields)) {
233
            // hack: somebody wrote uppercase in csv file, but the system knows only lowercase profile field
234
            $newfield = $lcfield;
235
 
236
        } else if (preg_match($specialfieldsregex, $lcfield)) {
237
            // special fields for enrolments
238
            $newfield = $lcfield;
239
 
240
        } else {
241
            $cir->close();
242
            $cir->cleanup();
243
            throw new \moodle_exception('invalidfieldname', 'error', $returnurl, $field);
244
        }
245
        if (in_array($newfield, $processed)) {
246
            $cir->close();
247
            $cir->cleanup();
248
            throw new \moodle_exception('duplicatefieldname', 'error', $returnurl, $newfield);
249
        }
250
        $processed[$key] = $newfield;
251
    }
252
 
253
    return $processed;
254
}
255
 
256
/**
257
 * Increments username - increments trailing number or adds it if not present.
258
 * Varifies that the new username does not exist yet
259
 * @param string $username
260
 * @return incremented username which does not exist yet
261
 */
262
function uu_increment_username($username) {
263
    global $DB, $CFG;
264
 
265
    if (!preg_match_all('/(.*?)([0-9]+)$/', $username, $matches)) {
266
        $username = $username.'2';
267
    } else {
268
        $username = $matches[1][0].($matches[2][0]+1);
269
    }
270
 
271
    if ($DB->record_exists('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id))) {
272
        return uu_increment_username($username);
273
    } else {
274
        return $username;
275
    }
276
}
277
 
278
/**
279
 * Check if default field contains templates and apply them.
280
 * @param string template - potential tempalte string
281
 * @param object user object- we need username, firstname and lastname
282
 * @return string field value
283
 */
284
function uu_process_template($template, $user) {
285
    if (is_array($template)) {
286
        // hack for for support of text editors with format
287
        $t = $template['text'];
288
    } else {
289
        $t = $template;
290
    }
291
    if (strpos($t, '%') === false) {
292
        return $template;
293
    }
294
 
295
    $username  = isset($user->username)  ? $user->username  : '';
296
    $firstname = isset($user->firstname) ? $user->firstname : '';
297
    $lastname  = isset($user->lastname)  ? $user->lastname  : '';
298
 
299
    $callback = partial('uu_process_template_callback', $username, $firstname, $lastname);
300
 
301
    $result = preg_replace_callback('/(?<!%)%([+-~])?(\d)*([flu])/', $callback, $t);
302
 
303
    if (is_null($result)) {
304
        return $template; //error during regex processing??
305
    }
306
 
307
    if (is_array($template)) {
308
        $template['text'] = $result;
309
        return $t;
310
    } else {
311
        return $result;
312
    }
313
}
314
 
315
/**
316
 * Internal callback function.
317
 */
318
function uu_process_template_callback($username, $firstname, $lastname, $block) {
319
    switch ($block[3]) {
320
        case 'u':
321
            $repl = $username;
322
            break;
323
        case 'f':
324
            $repl = $firstname;
325
            break;
326
        case 'l':
327
            $repl = $lastname;
328
            break;
329
        default:
330
            return $block[0];
331
    }
332
 
333
    switch ($block[1]) {
334
        case '+':
335
            $repl = core_text::strtoupper($repl);
336
            break;
337
        case '-':
338
            $repl = core_text::strtolower($repl);
339
            break;
340
        case '~':
341
            $repl = core_text::strtotitle($repl);
342
            break;
343
    }
344
 
345
    if (!empty($block[2])) {
346
        $repl = core_text::substr($repl, 0 , $block[2]);
347
    }
348
 
349
    return $repl;
350
}
351
 
352
/**
353
 * Returns list of auth plugins that are enabled and known to work.
354
 *
355
 * If ppl want to use some other auth type they have to include it
356
 * in the CSV file next on each line.
357
 *
358
 * @return array type=>name
359
 */
360
function uu_supported_auths() {
361
    // Get all the enabled plugins.
362
    $plugins = get_enabled_auth_plugins();
363
    $choices = array();
364
    foreach ($plugins as $plugin) {
365
        $objplugin = get_auth_plugin($plugin);
366
        // If the plugin can not be manually set skip it.
367
        if (!$objplugin->can_be_manually_set()) {
368
            continue;
369
        }
370
        $choices[$plugin] = get_string('pluginname', "auth_{$plugin}");
371
    }
372
 
373
    return $choices;
374
}
375
 
376
/**
377
 * Returns list of roles that are assignable in courses
378
 * @return array
379
 */
380
function uu_allowed_roles() {
381
    // let's cheat a bit, frontpage is guaranteed to exist and has the same list of roles ;-)
382
    $roles = get_assignable_roles(context_course::instance(SITEID), ROLENAME_ORIGINALANDSHORT);
383
    return array_reverse($roles, true);
384
}
385
 
386
/**
387
 * Returns assignable roles for current user using short role name and role ID as index.
388
 * This function is no longer called without parameters.
389
 *
390
 * @param int|null $categoryid Id of the category to get roles for.
391
 * @param int|null $courseid Id of the course to get roles for.
392
 * @return array
393
 */
394
function uu_allowed_roles_cache(?int $categoryid = null, ?int $courseid = null): array {
395
    if (!is_null($categoryid) && !is_null($courseid)) {
396
        return [];
397
    } else if (is_null($categoryid) && !is_null($courseid)) {
398
        $allowedroles = get_assignable_roles(context_course::instance($courseid), ROLENAME_SHORT);
399
    } else if (is_null($courseid) && !is_null($categoryid)) {
400
        $allowedroles = get_assignable_roles(context_coursecat::instance($categoryid), ROLENAME_SHORT);
401
    } else {
402
        $allowedroles = get_assignable_roles(context_course::instance(SITEID), ROLENAME_SHORT);
403
    }
404
 
405
    $rolecache = [];
406
    // A role can be searched for by its ID or by its shortname.
407
    foreach ($allowedroles as $rid=>$rname) {
408
        $rolecache[$rid] = new stdClass();
409
        $rolecache[$rid]->id   = $rid;
410
        $rolecache[$rid]->name = $rname;
411
        // Since numeric short names are allowed, to avoid replacement of another role, we only accept non-numeric values.
412
        if (!is_numeric($rname)) {
413
            $rolecache[$rname] = new stdClass();
414
            $rolecache[$rname]->id   = $rid;
415
            $rolecache[$rname]->name = $rname;
416
        }
417
    }
418
    return $rolecache;
419
}
420
 
421
/**
422
 * Returns mapping of all system roles using short role name as index.
423
 * @return array
424
 */
425
function uu_allowed_sysroles_cache() {
426
    $allowedroles = get_assignable_roles(context_system::instance(), ROLENAME_SHORT);
427
    $rolecache = [];
428
    foreach ($allowedroles as $rid => $rname) {
429
        $rolecache[$rid] = new stdClass();
430
        $rolecache[$rid]->id   = $rid;
431
        $rolecache[$rid]->name = $rname;
432
        if (!is_numeric($rname)) { // Only non-numeric shortnames are supported!
433
            $rolecache[$rname] = new stdClass();
434
            $rolecache[$rname]->id   = $rid;
435
            $rolecache[$rname]->name = $rname;
436
        }
437
    }
438
    return $rolecache;
439
}
440
 
441
/**
442
 * Pre process custom profile data, and update it with corrected value
443
 *
444
 * @param stdClass $data user profile data
445
 * @return stdClass pre-processed custom profile data
446
 */
447
function uu_pre_process_custom_profile_data($data) {
448
    global $CFG;
449
    require_once($CFG->dirroot . '/user/profile/lib.php');
450
    $fields = profile_get_user_fields_with_data(0);
451
 
452
    // find custom profile fields and check if data needs to converted.
453
    foreach ($data as $key => $value) {
454
        if (preg_match('/^profile_field_/', $key)) {
455
            $shortname = str_replace('profile_field_', '', $key);
456
            if ($fields) {
457
                foreach ($fields as $formfield) {
458
                    if ($formfield->get_shortname() === $shortname && method_exists($formfield, 'convert_external_data')) {
459
                        $data->$key = $formfield->convert_external_data($value);
460
                    }
461
                }
462
            }
463
        }
464
    }
465
    return $data;
466
}
467
 
468
/**
469
 * Checks if data provided for custom fields is correct
470
 * Currently checking for custom profile field or type menu
471
 *
472
 * @param array $data user profile data
473
 * @param array $profilefieldvalues Used to track previous profile field values to ensure uniqueness is observed
474
 * @return bool true if no error else false
475
 */
476
function uu_check_custom_profile_data(&$data, array &$profilefieldvalues = []) {
477
    global $CFG;
478
    require_once($CFG->dirroot.'/user/profile/lib.php');
479
 
480
    $noerror = true;
481
    $testuserid = null;
482
 
483
    if (!empty($data['username'])) {
484
        if (preg_match('/id=(.*)"/i', $data['username'], $result)) {
485
            $testuserid = $result[1];
486
        }
487
    }
488
    $profilefields = profile_get_user_fields_with_data(0);
489
    // Find custom profile fields and check if data needs to converted.
490
    foreach ($data as $key => $value) {
491
        if (preg_match('/^profile_field_/', $key)) {
492
            $shortname = str_replace('profile_field_', '', $key);
493
            foreach ($profilefields as $formfield) {
494
                if ($formfield->get_shortname() === $shortname) {
495
                    if (method_exists($formfield, 'convert_external_data') &&
496
                            is_null($formfield->convert_external_data($value))) {
497
                        $data['status'][] = get_string('invaliduserfield', 'error', $shortname);
498
                        $noerror = false;
499
                    }
500
 
501
                    // Ensure unique field value doesn't already exist in supplied data.
502
                    $formfieldunique = $formfield->is_unique() && ($value !== '' || $formfield->is_required());
503
                    if ($formfieldunique && array_key_exists($shortname, $profilefieldvalues) &&
504
                            (array_search($value, $profilefieldvalues[$shortname]) !== false)) {
505
 
506
                        $data['status'][] = get_string('valuealreadyused') . " ({$key})";
507
                        $noerror = false;
508
                    }
509
 
510
                    // Check for duplicate value.
511
                    if (method_exists($formfield, 'edit_validate_field') ) {
512
                        $testuser = new stdClass();
513
                        $testuser->{$key} = $value;
514
                        $testuser->id = $testuserid;
515
                        $err = $formfield->edit_validate_field($testuser);
516
                        if (!empty($err[$key])) {
517
                            $data['status'][] = $err[$key].' ('.$key.')';
518
                            $noerror = false;
519
                        }
520
                    }
521
 
522
                    // Record value of unique field, so it can be compared for duplicates.
523
                    if ($formfieldunique) {
524
                        $profilefieldvalues[$shortname][] = $value;
525
                    }
526
                }
527
            }
528
        }
529
    }
530
    return $noerror;
531
}