Merge branch 'MDL-25637'
[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 = $this->db_decode($fields);
116                     if (empty($fields[$coursefield])) {
117                         // missing course info
118                         continue;
119                     }
120                     if (!$course = $DB->get_record('course', array($localcoursefield=>$fields[$coursefield]), 'id,visible')) {
121                         continue;
122                     }
123                     if (!$course->visible and $ignorehidden) {
124                         continue;
125                     }
127                     if (empty($fields[$rolefield]) or !isset($roles[$fields[$rolefield]])) {
128                         if (!$defaultrole) {
129                             // role is mandatory
130                             continue;
131                         }
132                         $roleid = $defaultrole;
133                     } else {
134                         $roleid = $roles[$fields[$rolefield]];
135                     }
137                     if (empty($enrols[$course->id])) {
138                         $enrols[$course->id] = array();
139                     }
140                     $enrols[$course->id][] = $roleid;
142                     if ($instance = $DB->get_record('enrol', array('courseid'=>$course->id, 'enrol'=>'database'), '*', IGNORE_MULTIPLE)) {
143                         $instances[$course->id] = $instance;
144                         continue;
145                     }
147                     $enrolid = $this->add_instance($course);
148                     $instances[$course->id] = $DB->get_record('enrol', array('id'=>$enrolid));
149                 }
150             }
151             $rs->Close();
152             $extdb->Close();
153         } else {
154             // bad luck, something is wrong with the db connection
155             $extdb->Close();
156             return;
157         }
159         // enrol user into courses and sync roles
160         foreach ($enrols as $courseid => $roles) {
161             if (!isset($instances[$courseid])) {
162                 // ignored
163                 continue;
164             }
165             $instance = $instances[$courseid];
167             if ($e = $DB->get_record('user_enrolments', array('userid'=>$user->id, 'enrolid'=>$instance->id))) {
168                 // reenable enrolment when previously disable enrolment refreshed
169                 if ($e->status == ENROL_USER_SUSPENDED) {
170                     $DB->set_field('user_enrolments', 'status', ENROL_USER_ACTIVE, array('enrolid'=>$instance->id, 'userid'=>$user->id));
171                 }
172             } else {
173                 $roleid = reset($roles);
174                 $this->enrol_user($instance, $user->id, $roleid);
175             }
177             if (!$context = get_context_instance(CONTEXT_COURSE, $instance->courseid)) {
178                 //weird
179                 continue;
180             }
181             $current = $DB->get_records('role_assignments', array('contextid'=>$context->id, 'userid'=>$user->id, 'component'=>'enrol_database', 'itemid'=>$instance->id), '', 'id, roleid');
183             $existing = array();
184             foreach ($current as $r) {
185                 if (in_array($r->id, $roles)) {
186                     $existing[$r->roleid] = $r->roleid;
187                 } else {
188                     role_unassign($r->roleid, $user->id, $context->id, 'enrol_database', $instance->id);
189                 }
190             }
191             foreach ($roles as $rid) {
192                 if (!isset($existing[$rid])) {
193                     role_assign($rid, $user->id, $context->id, 'enrol_database', $instance->id);
194                 }
195             }
196         }
198         // unenrol as necessary
199         $sql = "SELECT e.*, c.visible AS cvisible, ue.status AS ustatus
200                   FROM {enrol} e
201                   JOIN {user_enrolments} ue ON ue.enrolid = e.id
202                   JOIN {course} c ON c.id = e.courseid
203                  WHERE ue.userid = :userid AND e.enrol = 'database'";
204         $rs = $DB->get_recordset_sql($sql, array('userid'=>$user->id));
205         foreach ($rs as $instance) {
206             if (!$instance->cvisible and $ignorehidden) {
207                 continue;
208             }
210             if (!$context = get_context_instance(CONTEXT_COURSE, $instance->courseid)) {
211                 //weird
212                 continue;
213             }
215             if (!empty($enrols[$instance->courseid])) {
216                 // we want this user enrolled
217                 continue;
218             }
220             // deal with enrolments removed from external table
221             if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
222                 // unenrol
223                 $this->unenrol_user($instance, $user->id);
225             } else if ($unenrolaction == ENROL_EXT_REMOVED_KEEP) {
226                 // keep - only adding enrolments
228             } else if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND or $unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
229                 // disable
230                 if ($instance->ustatus != ENROL_USER_SUSPENDED) {
231                     $DB->set_field('user_enrolments', 'status', ENROL_USER_SUSPENDED, array('enrolid'=>$instance->id, 'userid'=>$user->id));
232                 }
233                 if ($unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
234                     role_unassign_all(array('contextid'=>$context->id, 'userid'=>$user->id, 'component'=>'enrol_database', 'itemid'=>$instance->id));
235                 }
236             }
237         }
238         $rs->close();
239     }
241     /**
242      * Forces synchronisation of all enrolments with external database.
243      *
244      * @return void
245      */
246     public function sync_enrolments() {
247         global $CFG, $DB;
249         // we do not create courses here intentionally because it requires full sync and is slow
250         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')) {
251             return;
252         }
254         // we may need a lot of memory here
255         @set_time_limit(0);
256         raise_memory_limit(MEMORY_HUGE);
258         $extdb = $this->db_init();
260         // second step is to sync instances and users
261         $table            = $this->get_config('remoteenroltable');
262         $coursefield      = strtolower($this->get_config('remotecoursefield'));
263         $userfield        = strtolower($this->get_config('remoteuserfield'));
264         $rolefield        = strtolower($this->get_config('remoterolefield'));
266         $localrolefield   = $this->get_config('localrolefield');
267         $localuserfield   = $this->get_config('localuserfield');
268         $localcoursefield = $this->get_config('localcoursefield');
270         $unenrolaction    = $this->get_config('unenrolaction');
271         $defaultrole      = $this->get_config('defaultrole');
273         // create roles mapping
274         $allroles = get_all_roles();
275         if (!isset($allroles[$defaultrole])) {
276             $defaultrole = 0;
277         }
278         $roles = array();
279         foreach ($allroles as $role) {
280             $roles[$role->$localrolefield] = $role->id;
281         }
283         // get a list of courses to be synced that are in external table
284         $externalcourses = array();
285         $sql = $this->db_get_sql($table, array(), array($coursefield), true);
286         if ($rs = $extdb->Execute($sql)) {
287             if (!$rs->EOF) {
288                 while ($mapping = $rs->FetchRow()) {
289                     $mapping = reset($mapping);
290                     $mapping = $this->db_decode($mapping);
291                     if (empty($mapping)) {
292                         // invalid mapping
293                         continue;
294                     }
295                     $externalcourses[$mapping] = true;
296                 }
297             }
298             $rs->Close();
299         } else {
300             debugging('Error while communicating with external enrolment database');
301             $extdb->Close();
302             return;
303         }
304         $preventfullunenrol = empty($externalcourses);
305         if ($preventfullunenrol and $unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
306             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.');
307         }
309         // first find all existing courses with enrol instance
310         $existing = array();
311         $sql = "SELECT c.id, c.visible, c.$localcoursefield AS mapping, e.id AS enrolid
312                   FROM {course} c
313                   JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'database')";
314         $rs = $DB->get_recordset_sql($sql); // watch out for idnumber duplicates
315         foreach ($rs as $course) {
316             if (empty($course->mapping)) {
317                 continue;
318             }
319             $existing[$course->mapping] = $course;
320         }
321         $rs->close();
323         // add necessary enrol instances that are not present yet
324         $params = array();
325         $localnotempty = "";
326         if ($localcoursefield !== 'id') {
327             $localnotempty =  "AND c.$localcoursefield <> :lcfe";
328             $params['lcfe'] = $DB->sql_empty();
329         }
330         $sql = "SELECT c.id, c.visible, c.$localcoursefield AS mapping
331                   FROM {course} c
332              LEFT JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'database')
333                  WHERE e.id IS NULL $localnotempty";
334         $rs = $DB->get_recordset_sql($sql, $params);
335         foreach ($rs as $course) {
336             if (empty($course->mapping)) {
337                 continue;
338             }
339             if (!isset($externalcourses[$course->mapping])) {
340                 // course not synced
341                 continue;
342             }
343             if (isset($existing[$course->mapping])) {
344                 // some duplicate, sorry
345                 continue;
346             }
347             $course->enrolid = $this->add_instance($course);
348             $existing[$course->mapping] = $course;
349         }
350         $rs->close();
352         // free memory
353         unset($externalcourses);
355         // sync enrolments
356         $ignorehidden = $this->get_config('ignorehiddencourses');
357         $sqlfields = array($userfield);
358         if ($rolefield) {
359             $sqlfields[] = $rolefield;
360         }
361         foreach ($existing as $course) {
362             if ($ignorehidden and !$course->visible) {
363                 continue;
364             }
365             if (!$instance = $DB->get_record('enrol', array('id'=>$course->enrolid))) {
366                 continue; //weird
367             }
368             $context = get_context_instance(CONTEXT_COURSE, $course->id);
370             // get current list of enrolled users with their roles
371             $current_roles  = array();
372             $current_status = array();
373             $user_mapping   = array();
374             $sql = "SELECT u.$localuserfield AS mapping, u.id, ue.status, ue.userid, ra.roleid
375                       FROM {user} u
376                       JOIN {user_enrolments} ue ON (ue.userid = u.id AND ue.enrolid = :enrolid)
377                       JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.itemid = ue.enrolid AND ra.component = 'enrol_database')
378                      WHERE u.deleted = 0";
379             $params = array('enrolid'=>$instance->id);
380             if ($localuserfield === 'username') {
381                 $sql .= " AND u.mnethostid = :mnethostid";
382                 $params['mnethostid'] = $CFG->mnet_localhost_id;
383             }
384             $rs = $DB->get_recordset_sql($sql, $params);
385             foreach ($rs as $ue) {
386                 $current_roles[$ue->userid][$ue->roleid] = $ue->roleid;
387                 $current_status[$ue->userid] = $ue->status;
388                 $user_mapping[$ue->mapping] = $ue->userid;
389             }
390             $rs->close();
392             // get list of users that need to be enrolled and their roles
393             $requested_roles = array();
394             $sql = $this->db_get_sql($table, array($coursefield=>$course->mapping), $sqlfields);
395             if ($rs = $extdb->Execute($sql)) {
396                 if (!$rs->EOF) {
397                     if ($localuserfield === 'username') {
398                         $usersearch = array('mnethostid'=>$CFG->mnet_localhost_id, 'deleted' =>0);
399                     }
400                     while ($fields = $rs->FetchRow()) {
401                         $fields = array_change_key_case($fields, CASE_LOWER);
402                         if (empty($fields[$userfield])) {
403                             //user identification is mandatory!
404                         }
405                         $mapping = $fields[$userfield];
406                         if (!isset($user_mapping[$mapping])) {
407                             $usersearch[$localuserfield] = $mapping;
408                             if (!$user = $DB->get_record('user', $usersearch, 'id', IGNORE_MULTIPLE)) {
409                                 // user does not exist or was deleted
410                                 continue;
411                             }
412                             $user_mapping[$mapping] = $user->id;
413                             $userid = $user->id;
414                         } else {
415                             $userid = $user_mapping[$mapping];
416                         }
417                         if (empty($fields[$rolefield]) or !isset($roles[$fields[$rolefield]])) {
418                             if (!$defaultrole) {
419                                 // role is mandatory
420                                 continue;
421                             }
422                             $roleid = $defaultrole;
423                         } else {
424                             $roleid = $roles[$fields[$rolefield]];
425                         }
427                         $requested_roles[$userid][$roleid] = $roleid;
428                     }
429                 }
430                 $rs->Close();
431             } else {
432                 debugging('Error while communicating with external enrolment database');
433                 $extdb->Close();
434                 return;
435             }
436             unset($user_mapping);
438             // enrol all users and sync roles
439             foreach ($requested_roles as $userid=>$userroles) {
440                 foreach ($userroles as $roleid) {
441                     if (empty($current_roles[$userid])) {
442                         $this->enrol_user($instance, $userid, $roleid);
443                         $current_roles[$userid][$roleid] = $roleid;
444                         $current_status[$userid] = ENROL_USER_ACTIVE;
445                     }
446                 }
448                 // unassign removed roles
449                 foreach($current_roles[$userid] as $cr) {
450                     if (empty($userroles[$cr])) {
451                         role_unassign($cr, $userid, $context->id, 'enrol_database', $instance->id);
452                         unset($current_roles[$userid][$cr]);
453                     }
454                 }
456                 // reenable enrolment when previously disable enrolment refreshed
457                 if ($current_status[$userid] == ENROL_USER_SUSPENDED) {
458                     $DB->set_field('user_enrolments', 'status', ENROL_USER_ACTIVE, array('enrolid'=>$instance->id, 'userid'=>$userid));
459                 }
460             }
462             // deal with enrolments removed from external table
463             if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
464                 if (!$preventfullunenrol) {
465                     // unenrol
466                     foreach ($current_status as $userid=>$status) {
467                         if (isset($requested_roles[$userid])) {
468                             continue;
469                         }
470                         $this->unenrol_user($instance, $userid);
471                     }
472                 }
474             } else if ($unenrolaction == ENROL_EXT_REMOVED_KEEP) {
475                 // keep - only adding enrolments
477             } else if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND or $unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
478                 // disable
479                 foreach ($current_status as $userid=>$status) {
480                     if (isset($requested_roles[$userid])) {
481                         continue;
482                     }
483                     if ($status != ENROL_USER_SUSPENDED) {
484                         $DB->set_field('user_enrolments', 'status', ENROL_USER_SUSPENDED, array('enrolid'=>$instance->id, 'userid'=>$userid));
485                     }
486                     if ($unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
487                         role_unassign_all(array('contextid'=>$context->id, 'userid'=>$userid, 'component'=>'enrol_database', 'itemid'=>$instance->id));
488                     }
489                 }
490             }
491         }
493         // close db connection
494         $extdb->Close();
495     }
497     /**
498      * Performs a full sync with external database.
499      *
500      * First it creates new courses if necessary, then
501      * enrols and unenrols users.
502      * @return void
503      */
504     public function sync_courses() {
505         global $CFG, $DB;
507         // make sure we sync either enrolments or courses
508         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')) {
509             return;
510         }
512         // we may need a lot of memory here
513         @set_time_limit(0);
514         raise_memory_limit(MEMORY_HUGE);
516         $extdb = $this->db_init();
518         // first create new courses
519         $table     = $this->get_config('newcoursetable');
520         $fullname  = strtolower($this->get_config('newcoursefullname'));
521         $shortname = strtolower($this->get_config('newcourseshortname'));
522         $idnumber  = strtolower($this->get_config('newcourseidnumber'));
523         $category  = strtolower($this->get_config('newcoursecategory'));
525         $sqlfields = array($fullname, $shortname);
526         if ($category) {
527             $sqlfields[] = $category;
528         }
529         if ($idnumber) {
530             $sqlfields[] = $idnumber;
531         }
532         $sql = $this->db_get_sql($table, array(), $sqlfields);
533         $createcourses = array();
534         if ($rs = $extdb->Execute($sql)) {
535             if (!$rs->EOF) {
536                 $courselist = array();
537                 while ($fields = $rs->FetchRow()) {
538                     $fields = array_change_key_case($fields, CASE_LOWER);
539                     if (empty($fields[$shortname]) or empty($fields[$fullname])) {
540                         //invalid record - these two are mandatory
541                         continue;
542                     }
543                     $fields = $this->db_decode($fields);
544                     if ($DB->record_exists('course', array('shortname'=>$fields[$shortname]))) {
545                         // already exists
546                         continue;
547                     }
548                     if ($idnumber and $DB->record_exists('course', array('idnumber'=>$fields[$idnumber]))) {
549                         // idnumber duplicates are not allowed
550                         continue;
551                     }
552                     if ($category and !$DB->record_exists('course_categories', array('id'=>$fields[$category]))) {
553                         // invalid category id, better to skip
554                         continue;
555                     }
556                     $course = new stdClass();
557                     $course->fullname  = $fields[$fullname];
558                     $course->shortname = $fields[$shortname];
559                     $course->idnumber  = $idnumber ? $fields[$idnumber] : NULL;
560                     $course->category  = $category ? $fields[$category] : NULL;
561                     $createcourses[] = $course;
562                 }
563             }
564             $rs->Close();
565         } else {
566             debugging('Error while communicating with external enrolment database');
567             $extdb->Close();
568             return;
569         }
570         if ($createcourses) {
571             require_once("$CFG->dirroot/course/lib.php");
573             $template        = $this->get_config('templatecourse');
574             $defaultcategory = $this->get_config('defaultcategory');
576             if ($template) {
577                 if ($template = $DB->get_record('course', array('shortname'=>$template))) {
578                     unset($template->id);
579                     unset($template->fullname);
580                     unset($template->shortname);
581                     unset($template->idnumber);
582                 } else {
583                     $template = new stdClass();
584                 }
585             } else {
586                 $template = new stdClass();
587             }
588             if (!$DB->record_exists('course_categories', array('id'=>$defaultcategory))) {
589                 $categories = $DB->get_records('course_categories', array(), 'sortorder', 'id', 0, 1);
590                 $first = reset($categories);
591                 $defaultcategory = $first->id;
592             }
594             foreach ($createcourses as $fields) {
595                 $newcourse = clone($template);
596                 $newcourse->fullname  = $fields->fullname;
597                 $newcourse->shortname = $fields->shortname;
598                 $newcourse->idnumber  = $fields->idnumber;
599                 $newcourse->category  = $fields->category ? $fields->category : $defaultcategory;
601                 create_course($newcourse);
602             }
604             unset($createcourses);
605             unset($template);
606         }
608         // close db connection
609         $extdb->Close();
610     }
612     protected function db_get_sql($table, array $conditions, array $fields, $distinct = false, $sort = "") {
613         $fields = $fields ? implode(',', $fields) : "*";
614         $where = array();
615         if ($conditions) {
616             foreach ($conditions as $key=>$value) {
617                 $value = $this->db_encode($this->db_addslashes($value));
619                 $where[] = "$key = '$value'";
620             }
621         }
622         $where = $where ? "WHERE ".implode(" AND ", $where) : "";
623         $sort = $sort ? "ORDER BY $sort" : "";
624         $distinct = $distinct ? "DISTINCT" : "";
625         $sql = "SELECT $distinct $fields
626                   FROM $table
627                  $where
628                   $sort";
630         return $sql;
631     }
633     protected function db_init() {
634         global $CFG;
636         require_once($CFG->libdir.'/adodb/adodb.inc.php');
638         // Connect to the external database (forcing new connection)
639         $extdb = ADONewConnection($this->get_config('dbtype'));
640         if ($this->get_config('debugdb')) {
641             $extdb->debug = true;
642             ob_start(); //start output buffer to allow later use of the page headers
643         }
645         $extdb->Connect($this->get_config('dbhost'), $this->get_config('dbuser'), $this->get_config('dbpass'), $this->get_config('dbname'), true);
646         $extdb->SetFetchMode(ADODB_FETCH_ASSOC);
647         if ($this->get_config('dbsetupsql')) {
648             $extdb->Execute($this->get_config('dbsetupsql'));
649         }
650         return $extdb;
651     }
653     protected function db_addslashes($text) {
654         // using custom made function for now
655         if ($this->get_config('dbsybasequoting')) {
656             $text = str_replace('\\', '\\\\', $text);
657             $text = str_replace(array('\'', '"', "\0"), array('\\\'', '\\"', '\\0'), $text);
658         } else {
659             $text = str_replace("'", "''", $text);
660         }
661         return $text;
662     }
664     protected function db_encode($text) {
665         $dbenc = $this->get_config('dbencoding');
666         if (empty($dbenc) or $dbenc == 'utf-8') {
667             return $text;
668         }
669         if (is_array($text)) {
670             foreach($text as $k=>$value) {
671                 $text[$k] = $this->db_encode($value);
672             }
673             return $text;
674         } else {
675             return textlib_get_instance()->convert($text, 'utf-8', $dbenc);
676         }
677     }
679     protected function db_decode($text) {
680         $dbenc = $this->get_config('dbencoding');
681         if (empty($dbenc) or $dbenc == 'utf-8') {
682             return $text;
683         }
684         if (is_array($text)) {
685             foreach($text as $k=>$value) {
686                 $text[$k] = $this->db_decode($value);
687             }
688             return $text;
689         } else {
690             return textlib_get_instance()->convert($text, $dbenc, 'utf-8');
691         }
692     }