MDL-27375 add missing lowercasing of external column names
[moodle.git] / enrol / database / lib.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * Database enrolment plugin.
20  *
21  * This plugin synchronises enrolment and roles with external database table.
22  *
23  * @package    enrol
24  * @subpackage database
25  * @copyright  2010 Petr Skoda {@link http://skodak.org}
26  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27  */
29 defined('MOODLE_INTERNAL') || die();
31 /**
32  * Database enrolment plugin implementation.
33  * @author  Petr Skoda - based on code by Martin Dougiamas, Martin Langhoff and others
34  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35  */
36 class enrol_database_plugin extends enrol_plugin {
37     /**
38      * Is it possible to delete enrol instance via standard UI?
39      *
40      * @param object $instance
41      * @return bool
42      */
43     public function instance_deleteable($instance) {
44         if (!enrol_is_enabled('database')) {
45             return true;
46         }
47         if (!$this->get_config('dbtype') or !$this->get_config('dbhost') or !$this->get_config('remoteenroltable') or !$this->get_config('remotecoursefield') or !$this->get_config('remoteuserfield')) {
48             return true;
49         }
51         //TODO: connect to external system and make sure no users are to be enrolled in this course
52         return false;
53     }
55     /**
56      * Forces synchronisation of user enrolments with external database,
57      * does not create new courses.
58      *
59      * @param object $user user record
60      * @return void
61      */
62     public function sync_user_enrolments($user) {
63         global $CFG, $DB;
65         // we do not create courses here intentionally because it requires full sync and is slow
66         if (!$this->get_config('dbtype') or !$this->get_config('dbhost') or !$this->get_config('remoteenroltable') or !$this->get_config('remotecoursefield') or !$this->get_config('remoteuserfield')) {
67             return;
68         }
70         $table            = $this->get_config('remoteenroltable');
71         $coursefield      = strtolower($this->get_config('remotecoursefield'));
72         $userfield        = strtolower($this->get_config('remoteuserfield'));
73         $rolefield        = strtolower($this->get_config('remoterolefield'));
75         $localrolefield   = $this->get_config('localrolefield');
76         $localuserfield   = $this->get_config('localuserfield');
77         $localcoursefield = $this->get_config('localcoursefield');
79         $unenrolaction    = $this->get_config('unenrolaction');
80         $defaultrole      = $this->get_config('defaultrole');
82         $ignorehidden     = $this->get_config('ignorehiddencourses');
84         if (!is_object($user) or !property_exists($user, 'id')) {
85             throw new coding_exception('Invalid $user parameter in sync_user_enrolments()');
86         }
88         if (!property_exists($user, $localuserfield)) {
89             debugging('Invalid $user parameter in sync_user_enrolments(), missing '.$localuserfield);
90             $user = $DB->get_record('user', array('id'=>$user->id));
91         }
93         // create roles mapping
94         $allroles = get_all_roles();
95         if (!isset($allroles[$defaultrole])) {
96             $defaultrole = 0;
97         }
98         $roles = array();
99         foreach ($allroles as $role) {
100             $roles[$role->$localrolefield] = $role->id;
101         }
103         $enrols = array();
104         $instances = array();
106         $extdb = $this->db_init();
108         // read remote enrols and create instances
109         $sql = $this->db_get_sql($table, array($userfield=>$user->$localuserfield), array(), false);
111         if ($rs = $extdb->Execute($sql)) {
112             if (!$rs->EOF) {
113                 while ($fields = $rs->FetchRow()) {
114                     $fields = array_change_key_case($fields, CASE_LOWER);
115                     $fields = $this->db_decode($fields);
117                     if (empty($fields[$coursefield])) {
118                         // missing course info
119                         continue;
120                     }
121                     if (!$course = $DB->get_record('course', array($localcoursefield=>$fields[$coursefield]), 'id,visible')) {
122                         continue;
123                     }
124                     if (!$course->visible and $ignorehidden) {
125                         continue;
126                     }
128                     if (empty($fields[$rolefield]) or !isset($roles[$fields[$rolefield]])) {
129                         if (!$defaultrole) {
130                             // role is mandatory
131                             continue;
132                         }
133                         $roleid = $defaultrole;
134                     } else {
135                         $roleid = $roles[$fields[$rolefield]];
136                     }
138                     if (empty($enrols[$course->id])) {
139                         $enrols[$course->id] = array();
140                     }
141                     $enrols[$course->id][] = $roleid;
143                     if ($instance = $DB->get_record('enrol', array('courseid'=>$course->id, 'enrol'=>'database'), '*', IGNORE_MULTIPLE)) {
144                         $instances[$course->id] = $instance;
145                         continue;
146                     }
148                     $enrolid = $this->add_instance($course);
149                     $instances[$course->id] = $DB->get_record('enrol', array('id'=>$enrolid));
150                 }
151             }
152             $rs->Close();
153             $extdb->Close();
154         } else {
155             // bad luck, something is wrong with the db connection
156             $extdb->Close();
157             return;
158         }
160         // enrol user into courses and sync roles
161         foreach ($enrols as $courseid => $roles) {
162             if (!isset($instances[$courseid])) {
163                 // ignored
164                 continue;
165             }
166             $instance = $instances[$courseid];
168             if ($e = $DB->get_record('user_enrolments', array('userid'=>$user->id, 'enrolid'=>$instance->id))) {
169                 // reenable enrolment when previously disable enrolment refreshed
170                 if ($e->status == ENROL_USER_SUSPENDED) {
171                     $DB->set_field('user_enrolments', 'status', ENROL_USER_ACTIVE, array('enrolid'=>$instance->id, 'userid'=>$user->id));
172                 }
173             } else {
174                 $roleid = reset($roles);
175                 $this->enrol_user($instance, $user->id, $roleid);
176             }
178             if (!$context = get_context_instance(CONTEXT_COURSE, $instance->courseid)) {
179                 //weird
180                 continue;
181             }
182             $current = $DB->get_records('role_assignments', array('contextid'=>$context->id, 'userid'=>$user->id, 'component'=>'enrol_database', 'itemid'=>$instance->id), '', 'id, roleid');
184             $existing = array();
185             foreach ($current as $r) {
186                 if (in_array($r->id, $roles)) {
187                     $existing[$r->roleid] = $r->roleid;
188                 } else {
189                     role_unassign($r->roleid, $user->id, $context->id, 'enrol_database', $instance->id);
190                 }
191             }
192             foreach ($roles as $rid) {
193                 if (!isset($existing[$rid])) {
194                     role_assign($rid, $user->id, $context->id, 'enrol_database', $instance->id);
195                 }
196             }
197         }
199         // unenrol as necessary
200         $sql = "SELECT e.*, c.visible AS cvisible, ue.status AS ustatus
201                   FROM {enrol} e
202                   JOIN {user_enrolments} ue ON ue.enrolid = e.id
203                   JOIN {course} c ON c.id = e.courseid
204                  WHERE ue.userid = :userid AND e.enrol = 'database'";
205         $rs = $DB->get_recordset_sql($sql, array('userid'=>$user->id));
206         foreach ($rs as $instance) {
207             if (!$instance->cvisible and $ignorehidden) {
208                 continue;
209             }
211             if (!$context = get_context_instance(CONTEXT_COURSE, $instance->courseid)) {
212                 //weird
213                 continue;
214             }
216             if (!empty($enrols[$instance->courseid])) {
217                 // we want this user enrolled
218                 continue;
219             }
221             // deal with enrolments removed from external table
222             if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
223                 // unenrol
224                 $this->unenrol_user($instance, $user->id);
226             } else if ($unenrolaction == ENROL_EXT_REMOVED_KEEP) {
227                 // keep - only adding enrolments
229             } else if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND or $unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
230                 // disable
231                 if ($instance->ustatus != ENROL_USER_SUSPENDED) {
232                     $DB->set_field('user_enrolments', 'status', ENROL_USER_SUSPENDED, array('enrolid'=>$instance->id, 'userid'=>$user->id));
233                 }
234                 if ($unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
235                     role_unassign_all(array('contextid'=>$context->id, 'userid'=>$user->id, 'component'=>'enrol_database', 'itemid'=>$instance->id));
236                 }
237             }
238         }
239         $rs->close();
240     }
242     /**
243      * Forces synchronisation of all enrolments with external database.
244      *
245      * @return void
246      */
247     public function sync_enrolments() {
248         global $CFG, $DB;
250         // we do not create courses here intentionally because it requires full sync and is slow
251         if (!$this->get_config('dbtype') or !$this->get_config('dbhost') or !$this->get_config('remoteenroltable') or !$this->get_config('remotecoursefield') or !$this->get_config('remoteuserfield')) {
252             return;
253         }
255         // we may need a lot of memory here
256         @set_time_limit(0);
257         raise_memory_limit(MEMORY_HUGE);
259         $extdb = $this->db_init();
261         // second step is to sync instances and users
262         $table            = $this->get_config('remoteenroltable');
263         $coursefield      = strtolower($this->get_config('remotecoursefield'));
264         $userfield        = strtolower($this->get_config('remoteuserfield'));
265         $rolefield        = strtolower($this->get_config('remoterolefield'));
267         $localrolefield   = $this->get_config('localrolefield');
268         $localuserfield   = $this->get_config('localuserfield');
269         $localcoursefield = $this->get_config('localcoursefield');
271         $unenrolaction    = $this->get_config('unenrolaction');
272         $defaultrole      = $this->get_config('defaultrole');
274         // create roles mapping
275         $allroles = get_all_roles();
276         if (!isset($allroles[$defaultrole])) {
277             $defaultrole = 0;
278         }
279         $roles = array();
280         foreach ($allroles as $role) {
281             $roles[$role->$localrolefield] = $role->id;
282         }
284         // get a list of courses to be synced that are in external table
285         $externalcourses = array();
286         $sql = $this->db_get_sql($table, array(), array($coursefield), true);
287         if ($rs = $extdb->Execute($sql)) {
288             if (!$rs->EOF) {
289                 while ($mapping = $rs->FetchRow()) {
290                     $mapping = reset($mapping);
291                     $mapping = $this->db_decode($mapping);
292                     if (empty($mapping)) {
293                         // invalid mapping
294                         continue;
295                     }
296                     $externalcourses[$mapping] = true;
297                 }
298             }
299             $rs->Close();
300         } else {
301             debugging('Error while communicating with external enrolment database');
302             $extdb->Close();
303             return;
304         }
305         $preventfullunenrol = empty($externalcourses);
306         if ($preventfullunenrol and $unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
307             debugging('Preventing unenrolment of all current users, because it might result in major data loss, there has to be at least one record in external enrol table, sorry.');
308         }
310         // first find all existing courses with enrol instance
311         $existing = array();
312         $sql = "SELECT c.id, c.visible, c.$localcoursefield AS mapping, e.id AS enrolid
313                   FROM {course} c
314                   JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'database')";
315         $rs = $DB->get_recordset_sql($sql); // watch out for idnumber duplicates
316         foreach ($rs as $course) {
317             if (empty($course->mapping)) {
318                 continue;
319             }
320             $existing[$course->mapping] = $course;
321         }
322         $rs->close();
324         // add necessary enrol instances that are not present yet
325         $params = array();
326         $localnotempty = "";
327         if ($localcoursefield !== 'id') {
328             $localnotempty =  "AND c.$localcoursefield <> :lcfe";
329             $params['lcfe'] = $DB->sql_empty();
330         }
331         $sql = "SELECT c.id, c.visible, c.$localcoursefield AS mapping
332                   FROM {course} c
333              LEFT JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'database')
334                  WHERE e.id IS NULL $localnotempty";
335         $rs = $DB->get_recordset_sql($sql, $params);
336         foreach ($rs as $course) {
337             if (empty($course->mapping)) {
338                 continue;
339             }
340             if (!isset($externalcourses[$course->mapping])) {
341                 // course not synced
342                 continue;
343             }
344             if (isset($existing[$course->mapping])) {
345                 // some duplicate, sorry
346                 continue;
347             }
348             $course->enrolid = $this->add_instance($course);
349             $existing[$course->mapping] = $course;
350         }
351         $rs->close();
353         // free memory
354         unset($externalcourses);
356         // sync enrolments
357         $ignorehidden = $this->get_config('ignorehiddencourses');
358         $sqlfields = array($userfield);
359         if ($rolefield) {
360             $sqlfields[] = $rolefield;
361         }
362         foreach ($existing as $course) {
363             if ($ignorehidden and !$course->visible) {
364                 continue;
365             }
366             if (!$instance = $DB->get_record('enrol', array('id'=>$course->enrolid))) {
367                 continue; //weird
368             }
369             $context = get_context_instance(CONTEXT_COURSE, $course->id);
371             // get current list of enrolled users with their roles
372             $current_roles  = array();
373             $current_status = array();
374             $user_mapping   = array();
375             $sql = "SELECT u.$localuserfield AS mapping, u.id, ue.status, ue.userid, ra.roleid
376                       FROM {user} u
377                       JOIN {user_enrolments} ue ON (ue.userid = u.id AND ue.enrolid = :enrolid)
378                       JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.itemid = ue.enrolid AND ra.component = 'enrol_database')
379                      WHERE u.deleted = 0";
380             $params = array('enrolid'=>$instance->id);
381             if ($localuserfield === 'username') {
382                 $sql .= " AND u.mnethostid = :mnethostid";
383                 $params['mnethostid'] = $CFG->mnet_localhost_id;
384             }
385             $rs = $DB->get_recordset_sql($sql, $params);
386             foreach ($rs as $ue) {
387                 $current_roles[$ue->userid][$ue->roleid] = $ue->roleid;
388                 $current_status[$ue->userid] = $ue->status;
389                 $user_mapping[$ue->mapping] = $ue->userid;
390             }
391             $rs->close();
393             // get list of users that need to be enrolled and their roles
394             $requested_roles = array();
395             $sql = $this->db_get_sql($table, array($coursefield=>$course->mapping), $sqlfields);
396             if ($rs = $extdb->Execute($sql)) {
397                 if (!$rs->EOF) {
398                     if ($localuserfield === 'username') {
399                         $usersearch = array('mnethostid'=>$CFG->mnet_localhost_id, 'deleted' =>0);
400                     }
401                     while ($fields = $rs->FetchRow()) {
402                         $fields = array_change_key_case($fields, CASE_LOWER);
403                         if (empty($fields[$userfield])) {
404                             //user identification is mandatory!
405                         }
406                         $mapping = $fields[$userfield];
407                         if (!isset($user_mapping[$mapping])) {
408                             $usersearch[$localuserfield] = $mapping;
409                             if (!$user = $DB->get_record('user', $usersearch, 'id', IGNORE_MULTIPLE)) {
410                                 // user does not exist or was deleted
411                                 continue;
412                             }
413                             $user_mapping[$mapping] = $user->id;
414                             $userid = $user->id;
415                         } else {
416                             $userid = $user_mapping[$mapping];
417                         }
418                         if (empty($fields[$rolefield]) or !isset($roles[$fields[$rolefield]])) {
419                             if (!$defaultrole) {
420                                 // role is mandatory
421                                 continue;
422                             }
423                             $roleid = $defaultrole;
424                         } else {
425                             $roleid = $roles[$fields[$rolefield]];
426                         }
428                         $requested_roles[$userid][$roleid] = $roleid;
429                     }
430                 }
431                 $rs->Close();
432             } else {
433                 debugging('Error while communicating with external enrolment database');
434                 $extdb->Close();
435                 return;
436             }
437             unset($user_mapping);
439             // enrol all users and sync roles
440             foreach ($requested_roles as $userid=>$userroles) {
441                 foreach ($userroles as $roleid) {
442                     if (empty($current_roles[$userid])) {
443                         $this->enrol_user($instance, $userid, $roleid);
444                         $current_roles[$userid][$roleid] = $roleid;
445                         $current_status[$userid] = ENROL_USER_ACTIVE;
446                     }
447                 }
449                 // unassign removed roles
450                 foreach($current_roles[$userid] as $cr) {
451                     if (empty($userroles[$cr])) {
452                         role_unassign($cr, $userid, $context->id, 'enrol_database', $instance->id);
453                         unset($current_roles[$userid][$cr]);
454                     }
455                 }
457                 // reenable enrolment when previously disable enrolment refreshed
458                 if ($current_status[$userid] == ENROL_USER_SUSPENDED) {
459                     $DB->set_field('user_enrolments', 'status', ENROL_USER_ACTIVE, array('enrolid'=>$instance->id, 'userid'=>$userid));
460                 }
461             }
463             // deal with enrolments removed from external table
464             if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
465                 if (!$preventfullunenrol) {
466                     // unenrol
467                     foreach ($current_status as $userid=>$status) {
468                         if (isset($requested_roles[$userid])) {
469                             continue;
470                         }
471                         $this->unenrol_user($instance, $userid);
472                     }
473                 }
475             } else if ($unenrolaction == ENROL_EXT_REMOVED_KEEP) {
476                 // keep - only adding enrolments
478             } else if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND or $unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
479                 // disable
480                 foreach ($current_status as $userid=>$status) {
481                     if (isset($requested_roles[$userid])) {
482                         continue;
483                     }
484                     if ($status != ENROL_USER_SUSPENDED) {
485                         $DB->set_field('user_enrolments', 'status', ENROL_USER_SUSPENDED, array('enrolid'=>$instance->id, 'userid'=>$userid));
486                     }
487                     if ($unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
488                         role_unassign_all(array('contextid'=>$context->id, 'userid'=>$userid, 'component'=>'enrol_database', 'itemid'=>$instance->id));
489                     }
490                 }
491             }
492         }
494         // close db connection
495         $extdb->Close();
496     }
498     /**
499      * Performs a full sync with external database.
500      *
501      * First it creates new courses if necessary, then
502      * enrols and unenrols users.
503      * @return void
504      */
505     public function sync_courses() {
506         global $CFG, $DB;
508         // make sure we sync either enrolments or courses
509         if (!$this->get_config('dbtype') or !$this->get_config('dbhost') or !$this->get_config('newcoursetable') or !$this->get_config('newcoursefullname') or !$this->get_config('newcourseshortname')) {
510             return;
511         }
513         // we may need a lot of memory here
514         @set_time_limit(0);
515         raise_memory_limit(MEMORY_HUGE);
517         $extdb = $this->db_init();
519         // first create new courses
520         $table     = $this->get_config('newcoursetable');
521         $fullname  = strtolower($this->get_config('newcoursefullname'));
522         $shortname = strtolower($this->get_config('newcourseshortname'));
523         $idnumber  = strtolower($this->get_config('newcourseidnumber'));
524         $category  = strtolower($this->get_config('newcoursecategory'));
526         $sqlfields = array($fullname, $shortname);
527         if ($category) {
528             $sqlfields[] = $category;
529         }
530         if ($idnumber) {
531             $sqlfields[] = $idnumber;
532         }
533         $sql = $this->db_get_sql($table, array(), $sqlfields);
534         $createcourses = array();
535         if ($rs = $extdb->Execute($sql)) {
536             if (!$rs->EOF) {
537                 $courselist = array();
538                 while ($fields = $rs->FetchRow()) {
539                     $fields = array_change_key_case($fields, CASE_LOWER);
540                     if (empty($fields[$shortname]) or empty($fields[$fullname])) {
541                         //invalid record - these two are mandatory
542                         continue;
543                     }
544                     $fields = $this->db_decode($fields);
545                     if ($DB->record_exists('course', array('shortname'=>$fields[$shortname]))) {
546                         // already exists
547                         continue;
548                     }
549                     if ($idnumber and $DB->record_exists('course', array('idnumber'=>$fields[$idnumber]))) {
550                         // idnumber duplicates are not allowed
551                         continue;
552                     }
553                     if ($category and !$DB->record_exists('course_categories', array('id'=>$fields[$category]))) {
554                         // invalid category id, better to skip
555                         continue;
556                     }
557                     $course = new stdClass();
558                     $course->fullname  = $fields[$fullname];
559                     $course->shortname = $fields[$shortname];
560                     $course->idnumber  = $idnumber ? $fields[$idnumber] : NULL;
561                     $course->category  = $category ? $fields[$category] : NULL;
562                     $createcourses[] = $course;
563                 }
564             }
565             $rs->Close();
566         } else {
567             debugging('Error while communicating with external enrolment database');
568             $extdb->Close();
569             return;
570         }
571         if ($createcourses) {
572             require_once("$CFG->dirroot/course/lib.php");
574             $template        = $this->get_config('templatecourse');
575             $defaultcategory = $this->get_config('defaultcategory');
577             if ($template) {
578                 if ($template = $DB->get_record('course', array('shortname'=>$template))) {
579                     unset($template->id);
580                     unset($template->fullname);
581                     unset($template->shortname);
582                     unset($template->idnumber);
583                 } else {
584                     $template = new stdClass();
585                 }
586             } else {
587                 $template = new stdClass();
588             }
589             if (!$DB->record_exists('course_categories', array('id'=>$defaultcategory))) {
590                 $categories = $DB->get_records('course_categories', array(), 'sortorder', 'id', 0, 1);
591                 $first = reset($categories);
592                 $defaultcategory = $first->id;
593             }
595             foreach ($createcourses as $fields) {
596                 $newcourse = clone($template);
597                 $newcourse->fullname  = $fields->fullname;
598                 $newcourse->shortname = $fields->shortname;
599                 $newcourse->idnumber  = $fields->idnumber;
600                 $newcourse->category  = $fields->category ? $fields->category : $defaultcategory;
602                 create_course($newcourse);
603             }
605             unset($createcourses);
606             unset($template);
607         }
609         // close db connection
610         $extdb->Close();
611     }
613     protected function db_get_sql($table, array $conditions, array $fields, $distinct = false, $sort = "") {
614         $fields = $fields ? implode(',', $fields) : "*";
615         $where = array();
616         if ($conditions) {
617             foreach ($conditions as $key=>$value) {
618                 $value = $this->db_encode($this->db_addslashes($value));
620                 $where[] = "$key = '$value'";
621             }
622         }
623         $where = $where ? "WHERE ".implode(" AND ", $where) : "";
624         $sort = $sort ? "ORDER BY $sort" : "";
625         $distinct = $distinct ? "DISTINCT" : "";
626         $sql = "SELECT $distinct $fields
627                   FROM $table
628                  $where
629                   $sort";
631         return $sql;
632     }
634     protected function db_init() {
635         global $CFG;
637         require_once($CFG->libdir.'/adodb/adodb.inc.php');
639         // Connect to the external database (forcing new connection)
640         $extdb = ADONewConnection($this->get_config('dbtype'));
641         if ($this->get_config('debugdb')) {
642             $extdb->debug = true;
643             ob_start(); //start output buffer to allow later use of the page headers
644         }
646         $extdb->Connect($this->get_config('dbhost'), $this->get_config('dbuser'), $this->get_config('dbpass'), $this->get_config('dbname'), true);
647         $extdb->SetFetchMode(ADODB_FETCH_ASSOC);
648         if ($this->get_config('dbsetupsql')) {
649             $extdb->Execute($this->get_config('dbsetupsql'));
650         }
651         return $extdb;
652     }
654     protected function db_addslashes($text) {
655         // using custom made function for now
656         if ($this->get_config('dbsybasequoting')) {
657             $text = str_replace('\\', '\\\\', $text);
658             $text = str_replace(array('\'', '"', "\0"), array('\\\'', '\\"', '\\0'), $text);
659         } else {
660             $text = str_replace("'", "''", $text);
661         }
662         return $text;
663     }
665     protected function db_encode($text) {
666         $dbenc = $this->get_config('dbencoding');
667         if (empty($dbenc) or $dbenc == 'utf-8') {
668             return $text;
669         }
670         if (is_array($text)) {
671             foreach($text as $k=>$value) {
672                 $text[$k] = $this->db_encode($value);
673             }
674             return $text;
675         } else {
676             return textlib_get_instance()->convert($text, 'utf-8', $dbenc);
677         }
678     }
680     protected function db_decode($text) {
681         $dbenc = $this->get_config('dbencoding');
682         if (empty($dbenc) or $dbenc == 'utf-8') {
683             return $text;
684         }
685         if (is_array($text)) {
686             foreach($text as $k=>$value) {
687                 $text[$k] = $this->db_decode($value);
688             }
689             return $text;
690         } else {
691             return textlib_get_instance()->convert($text, $dbenc, 'utf-8');
692         }
693     }