· 5 years ago · Mar 29, 2020, 10:58 AM
1<?php
2/**
3 *
4 * @author Nathan Guse (EXreaction) http://lithiumstudios.org
5 * @author David Lewis (Highway of Life) highwayoflife@gmail.com
6 * @package umil
7 * @copyright (c) 2008 phpBB Group
8 * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
9 *
10 */
11
12/**
13 * @ignore
14 */
15if (!defined('IN_PHPBB'))
16{
17 exit;
18}
19
20define('UMIL_VERSION', '1.0.5');
21
22/**
23* Multicall instructions
24*
25* With the "multicall" (as I am calling it) you can make a single function call and have it repeat the actions multiple times on information sent from an array.
26*
27* To do this (it does not work on the _exists functions), all you must do is send the first variable in the function call as an array and for each item, send an array for each of the variables in order.
28*
29* Example:
30* $umil->config_add(array(
31* array('config_name', 'config_value'),
32* array('config_name1', 'config_value1'),
33* array('config_name2', 'config_value2', true),
34* array('config_name3', 'config_value3', true),
35* );
36*/
37
38/**
39* UMIL - Unified MOD Installation Library class
40*
41* Cache Functions
42* cache_purge($type = '', $style_id = 0)
43*
44* Config Functions:
45* config_exists($config_name, $return_result = false)
46* config_add($config_name, $config_value = '', $is_dynamic = false)
47* config_update($config_name, $config_value, $is_dynamic = false)
48* config_remove($config_name)
49*
50* Module Functions
51* module_exists($class, $parent, $module)
52* module_add($class, $parent = 0, $data = array())
53* module_remove($class, $parent = 0, $module = '')
54*
55* Permissions/Auth Functions
56* permission_exists($auth_option, $global = true)
57* permission_add($auth_option, $global = true)
58* permission_remove($auth_option, $global = true)
59* permission_set($name, $auth_option = array(), $type = 'role', $global = true, $has_permission = true)
60* permission_unset($name, $auth_option = array(), $type = 'role', $global = true)
61*
62* Table Functions
63* table_exists($table_name)
64* table_add($table_name, $table_data = array())
65* table_remove($table_name)
66*
67* Table Column Functions
68* table_column_exists($table_name, $column_name)
69* table_column_add($table_name, $column_name = '', $column_data = array())
70* table_column_update($table_name, $column_name = '', $column_data = array())
71* table_column_remove($table_name, $column_name = '')
72*
73* Table Key/Index Functions
74* table_index_exists($table_name, $index_name)
75* table_index_add($table_name, $index_name = '', $column = array())
76* table_index_remove($table_name, $index_name = '')
77*
78* Table Row Functions (note that these actions are not reversed automatically during uninstallation)
79* table_row_insert($table_name, $data = array())
80* table_row_remove($table_name, $data = array())
81* table_row_update($table_name, $data = array(), $new_data = array())
82*
83* Version Check Function
84* version_check($url, $path, $file)
85*/
86class umil
87{
88 /**
89 * This will hold the text output for the inputted command (if the mod author would like to display the command that was ran)
90 *
91 * @var string
92 */
93 var $command = '';
94
95 /**
96 * This will hold the text output for the result of the command. $user->lang['SUCCESS'] if everything worked.
97 *
98 * @var string
99 */
100 var $result = '';
101
102 /**
103 * Auto run $this->display_results after running a command
104 */
105 var $auto_display_results = false;
106
107 /**
108 * Stand Alone option (this makes it possible to just use the single umil file and not worry about any language stuff
109 */
110 var $stand_alone = false;
111
112 /**
113 * Were any new permissions added (used in umil_frontend)?
114 */
115 var $permissions_added = false;
116
117 /**
118 * Database Object
119 */
120 var $db = false;
121
122 /**
123 * Database Tools Object
124 */
125 var $db_tools = false;
126
127 /**
128 * Do we want a custom prefix besides the phpBB table prefix? You *probably* should not change this...
129 */
130 var $table_prefix = false;
131
132 /**
133 * Constructor
134 */
135 function umil($stand_alone = false, $db = false)
136 {
137 // Setup $this->db
138 if ($db !== false)
139 {
140 if (!is_object($db) || !method_exists($db, 'sql_query'))
141 {
142 trigger_error('Invalid $db Object');
143 }
144
145 $this->db = $db;
146 }
147 else
148 {
149 global $db;
150 $this->db = $db;
151 }
152
153 // Setup $this->db_tools
154 if (!class_exists('phpbb_db_tools'))
155 {
156 global $phpbb_root_path, $phpEx;
157 include($phpbb_root_path . 'includes/db/db_tools.' . $phpEx);
158 }
159 $this->db_tools = new phpbb_db_tools($this->db);
160
161 $this->stand_alone = $stand_alone;
162
163 if (!$stand_alone)
164 {
165 global $config, $user, $phpbb_root_path, $phpEx;
166
167 /* Does not have the fall back option to use en/ if the user's language file does not exist, so we will not use it...unless that is changed.
168 if (method_exists('user', 'set_custom_lang_path'))
169 {
170 $user->set_custom_lang_path($phpbb_root_path . 'umil/language/');
171 $user->add_lang('umil');
172 $user->set_custom_lang_path($phpbb_root_path . 'language/');
173 }
174 else
175 {*/
176 // Include the umil language file. First we check if the language file for the user's language is available, if not we check if the board's default language is available, if not we use the english file.
177 if (isset($user->data['user_lang']) && file_exists("{$phpbb_root_path}umil/language/{$user->data['user_lang']}/umil.$phpEx"))
178 {
179 $path = $user->data['user_lang'];
180 }
181 else if (file_exists("{$phpbb_root_path}umil/language/" . basename($config['default_lang']) . "/umil.$phpEx"))
182 {
183 $path = basename($config['default_lang']);
184 }
185 else if (file_exists("{$phpbb_root_path}umil/language/en/umil.$phpEx"))
186 {
187 $path = 'en';
188 }
189 else
190 {
191 trigger_error('Language Files Missing.<br /><br />Please download the latest UMIL (Unified MOD Install Library) from: <a href="http://www.phpbb.com/mods/umil/">phpBB.com/mods/umil</a>', E_USER_ERROR);
192 }
193
194 $user->add_lang('./../../umil/language/' . $path . '/umil');
195 //}
196
197 $user->add_lang(array('acp/common', 'acp/permissions'));
198
199 // Check to see if a newer version is available.
200 $info = $this->version_check('version.phpbb.com', '/umil', ((defined('PHPBB_QA')) ? 'umil_qa.txt' : 'umil.txt'));
201 if (is_array($info) && isset($info[0]) && isset($info[1]) && defined('DEBUG'))
202 {
203 if (version_compare(UMIL_VERSION, $info[0], '<'))
204 {
205 global $template;
206
207 // Make sure user->setup() has been called
208 if (empty($user->lang))
209 {
210 $user->setup();
211 }
212
213 page_header('', false);
214
215 $user->lang['UPDATE_UMIL'] = (isset($user->lang['UPDATE_UMIL'])) ? $user->lang['UPDATE_UMIL'] : 'This version of UMIL is outdated.<br /><br />Please download the latest UMIL (Unified MOD Install Library) from: <a href="%1$s">%1$s</a>';
216 $template->assign_vars(array(
217 'S_BOARD_DISABLED' => true,
218 'L_BOARD_DISABLED' => sprintf($user->lang['UPDATE_UMIL'], $info[1]),
219 ));
220 }
221 }
222 }
223 }
224
225 /**
226 * umil_start
227 *
228 * A function which runs (almost) every time a function here is ran
229 */
230 function umil_start()
231 {
232 global $user;
233
234 // Set up the command. This will get the arguments sent to the function.
235 $args = func_get_args();
236 $this->command = call_user_func_array(array($this, 'get_output_text'), $args);
237
238 $this->result = (isset($user->lang['SUCCESS'])) ? $user->lang['SUCCESS'] : 'SUCCESS';
239 $this->db->sql_return_on_error(true);
240
241 //$this->db->sql_transaction('begin');
242 }
243
244 /**
245 * umil_end
246 *
247 * A function which runs (almost) every time a function here is ran
248 */
249 function umil_end()
250 {
251 global $user;
252
253 // Set up the result. This will get the arguments sent to the function.
254 $args = func_get_args();
255 $result = call_user_func_array(array($this, 'get_output_text'), $args);
256 $this->result = ($result) ? $result : $this->result;
257
258 if ($this->db->sql_error_triggered)
259 {
260 if ($this->result == ((isset($user->lang['SUCCESS'])) ? $user->lang['SUCCESS'] : 'SUCCESS'))
261 {
262 $this->result = 'SQL ERROR ' . $this->db->sql_error_returned['message'];
263 }
264 else
265 {
266 $this->result .= '<br /><br />SQL ERROR ' . $this->db->sql_error_returned['message'];
267 }
268
269 //$this->db->sql_transaction('rollback');
270 }
271 else
272 {
273 //$this->db->sql_transaction('commit');
274 }
275
276 $this->db->sql_return_on_error(false);
277
278 // Auto output if requested.
279 if ($this->auto_display_results && method_exists($this, 'display_results'))
280 {
281 $this->display_results();
282 }
283
284 return '<strong>' . $this->command . '</strong><br />' . $this->result;
285 }
286
287 /**
288 * Get text for output
289 *
290 * Takes the given arguments and prepares them for the UI
291 *
292 * First argument sent is used as the language key
293 * Further arguments (if send) are used on the language key through vsprintf()
294 *
295 * @return string Returns the prepared string for output
296 */
297 function get_output_text()
298 {
299 global $user;
300
301 // Set up the command. This will get the arguments sent to the function.
302 $args = func_get_args();
303 if (sizeof($args))
304 {
305 $lang_key = array_shift($args);
306
307 if (sizeof($args))
308 {
309 $lang_args = array();
310 foreach ($args as $arg)
311 {
312 $lang_args[] = (isset($user->lang[$arg])) ? $user->lang[$arg] : $arg;
313 }
314
315 return @vsprintf(((isset($user->lang[$lang_key])) ? $user->lang[$lang_key] : $lang_key), $lang_args);
316 }
317 else
318 {
319 return ((isset($user->lang[$lang_key])) ? $user->lang[$lang_key] : $lang_key);
320 }
321 }
322
323 return '';
324 }
325
326 /**
327 * Run Actions
328 *
329 * Do-It-All function that can do everything required for installing/updating/uninstalling a mod based on an array of actions and the versions.
330 *
331 * @param string $action The action. install|update|uninstall
332 * @param array $versions The array of versions and the actions for each
333 * @param string $version_config_name The name of the config setting which holds/will hold the currently installed version
334 * @param string $version_select Added for the UMIL Auto system to allow you to select the version you want to install/update/uninstall to.
335 */
336 function run_actions($action, $versions, $version_config_name, $version_select = '')
337 {
338 // We will sort the actions to prevent issues from mod authors incorrectly listing the version numbers
339 uksort($versions, 'version_compare');
340
341 // Find the current version to install
342 $current_version = '0.0.0';
343 foreach ($versions as $version => $actions)
344 {
345 $current_version = $version;
346 }
347
348 $db_version = '';
349 if ($this->config_exists($version_config_name))
350 {
351 global $config;
352 $db_version = $config[$version_config_name];
353 }
354
355 // Set the action to install from update if nothing is currently installed
356 if ($action == 'update' && !$db_version)
357 {
358 $action = 'install';
359 }
360
361 if ($action == 'install' || $action == 'update')
362 {
363 $version_installed = $db_version;
364 foreach ($versions as $version => $version_actions)
365 {
366 // If we are updating
367 if ($db_version && version_compare($version, $db_version, '<='))
368 {
369 continue;
370 }
371
372 if ($version_select && version_compare($version, $version_select, '>'))
373 {
374 break;
375 }
376
377 foreach ($version_actions as $method => $params)
378 {
379 if ($method == 'custom')
380 {
381 $this->_call_custom_function($params, $action, $version);
382 }
383 else
384 {
385 if (method_exists($this, $method))
386 {
387 call_user_func(array($this, $method), $params);
388 }
389 }
390 }
391
392 $version_installed = $version;
393 }
394
395 // update the version number or add it
396 if ($this->config_exists($version_config_name))
397 {
398 $this->config_update($version_config_name, $version_installed);
399 }
400 else
401 {
402 $this->config_add($version_config_name, $version_installed);
403 }
404 }
405 else if ($action == 'uninstall' && $db_version)
406 {
407 // reverse version list
408 $versions = array_reverse($versions);
409
410 foreach ($versions as $version => $version_actions)
411 {
412 // Uninstalling and this listed version is newer than installed
413 if (version_compare($version, $db_version, '>'))
414 {
415 continue;
416 }
417
418 // Version selection stuff
419 if ($version_select && version_compare($version, $version_select, '<='))
420 {
421 // update the version number
422 $this->config_update($version_config_name, $version);
423 break;
424 }
425
426 $cache_purge = false;
427 $version_actions = array_reverse($version_actions);
428 foreach ($version_actions as $method => $params)
429 {
430 if ($method == 'custom')
431 {
432 $this->_call_custom_function($params, $action, $version);
433 }
434 else
435 {
436 // This way we always run the cache purge at the end of the version (done for the uninstall because the instructions are reversed, which would cause the cache purge to be run at the beginning if it was meant to run at the end).
437 if ($method == 'cache_purge')
438 {
439 $cache_purge = $params;
440 continue;
441 }
442
443 // A few things are not possible for uninstallations update actions and table_row actions
444 if (strpos($method, 'update') !== false || strpos($method, 'table_insert') !== false || strpos($method, 'table_row_') !== false)
445 {
446 continue;
447 }
448
449 // reverse function call
450 $method = str_replace(array('add', 'remove', 'temp'), array('temp', 'add', 'remove'), $method);
451 $method = str_replace(array('set', 'unset', 'temp'), array('temp', 'set', 'unset'), $method);
452
453 if (method_exists($this, $method))
454 {
455 call_user_func(array($this, $method), ((is_array($params) ? array_reverse($params) : $params)));
456 }
457 }
458 }
459
460 if ($cache_purge !== false)
461 {
462 $this->cache_purge($cache_purge);
463 }
464 }
465
466 if (!$version_select)
467 {
468 // Unset the version number
469 $this->config_remove($version_config_name);
470 }
471 }
472 }
473
474 /**
475 * Call custom function helper
476 */
477 function _call_custom_function($functions, $action, $version)
478 {
479 if (!is_array($functions))
480 {
481 $functions = array($functions);
482 }
483
484 $return = '';
485
486 foreach ($functions as $function)
487 {
488 if (function_exists($function))
489 {
490 // Must reset before calling the function
491 $this->umil_start();
492
493 $returned = call_user_func($function, $action, $version);
494 if (is_string($returned))
495 {
496 $this->command = $this->get_output_text($returned);
497 }
498 else if (is_array($returned) && isset($returned['command']))
499 {
500 if (is_array($returned['command']))
501 {
502 $this->command = call_user_func_array(array($this, 'get_output_text'), $returned['command']);
503 }
504 else
505 {
506 $this->command = $this->get_output_text($returned['command']);
507 }
508
509 if (isset($returned['result']))
510 {
511 $this->result = $this->get_output_text($returned['result']);
512 }
513 }
514 else
515 {
516 $this->command = $this->get_output_text('UNKNOWN');
517 }
518
519 $return .= $this->umil_end() . '<br />';
520 }
521 }
522
523 return $return;
524 }
525
526 /**
527 * Multicall Helper
528 *
529 * @param mixed $function Function name to call
530 * @param mixed $params The parameters array
531 *
532 * @return bool True if we have done a multicall ($params is an array), false if not ($params is not an array)
533 */
534 function multicall($function, $params)
535 {
536 if (is_array($params) && !empty($params))
537 {
538 foreach ($params as $param)
539 {
540 if (!is_array($param))
541 {
542 call_user_func(array($this, $function), $param);
543 }
544 else
545 {
546 call_user_func_array(array($this, $function), $param);
547 }
548 }
549 return true;
550 }
551
552 return false;
553 }
554
555 /**
556 * Cache Purge
557 *
558 * This function is for purging either phpBB3’s data cache, authorization cache, or the styles cache.
559 *
560 * @param string $type The type of cache you want purged. Available types: auth, imageset, template, theme. Anything else sent will purge the forum's cache.
561 * @param int $style_id The id of the item you want purged (if the type selected is imageset/template/theme, 0 for all items in that section)
562 */
563 function cache_purge($type = '', $style_id = 0)
564 {
565 global $auth, $cache, $user, $phpbb_root_path, $phpEx;
566
567 // Multicall
568 if ($this->multicall(__FUNCTION__, $type))
569 {
570 return;
571 }
572
573 $style_id = (int) $style_id;
574 $type = (is_array($type)) ? '' : strval($type); // only pass strings to switch()
575
576 switch ($type)
577 {
578 case 'auth' :
579 $this->umil_start('AUTH_CACHE_PURGE');
580 $cache->destroy('_acl_options');
581 $auth->acl_clear_prefetch();
582
583 return $this->umil_end();
584 break;
585
586 case 'imageset' :
587 if ($style_id == 0)
588 {
589 $return = array();
590 $sql = 'SELECT imageset_id
591 FROM ' . STYLES_IMAGESET_TABLE;
592 $result = $this->db->sql_query($sql);
593 while ($row = $this->db->sql_fetchrow($result))
594 {
595 $return[] = $this->cache_purge('imageset', $row['imageset_id']);
596 }
597 $this->db->sql_freeresult($result);
598
599 return implode('<br /><br />', $return);
600 }
601 else
602 {
603 $sql = 'SELECT *
604 FROM ' . STYLES_IMAGESET_TABLE . "
605 WHERE imageset_id = $style_id";
606 $result = $this->db->sql_query($sql);
607 $imageset_row = $this->db->sql_fetchrow($result);
608 $this->db->sql_freeresult($result);
609
610 if (!$imageset_row)
611 {
612 $this->umil_start('IMAGESET_CACHE_PURGE', 'UNKNOWN');
613 return $this->umil_end('FAIL');
614 }
615
616 $this->umil_start('IMAGESET_CACHE_PURGE', $imageset_row['imageset_name']);
617
618 // The following is from includes/acp/acp_styles.php (edited)
619 $sql_ary = array();
620
621 $cfg_data_imageset = parse_cfg_file("{$phpbb_root_path}styles/{$imageset_row['imageset_path']}/imageset/imageset.cfg");
622
623 $sql = 'DELETE FROM ' . STYLES_IMAGESET_DATA_TABLE . '
624 WHERE imageset_id = ' . $style_id;
625 $result = $this->db->sql_query($sql);
626
627 foreach ($cfg_data_imageset as $image_name => $value)
628 {
629 if (strpos($value, '*') !== false)
630 {
631 if (substr($value, -1, 1) === '*')
632 {
633 list($image_filename, $image_height) = explode('*', $value);
634 $image_width = 0;
635 }
636 else
637 {
638 list($image_filename, $image_height, $image_width) = explode('*', $value);
639 }
640 }
641 else
642 {
643 $image_filename = $value;
644 $image_height = $image_width = 0;
645 }
646
647 if (strpos($image_name, 'img_') === 0 && $image_filename)
648 {
649 $image_name = substr($image_name, 4);
650
651 $sql_ary[] = array(
652 'image_name' => (string) $image_name,
653 'image_filename' => (string) $image_filename,
654 'image_height' => (int) $image_height,
655 'image_width' => (int) $image_width,
656 'imageset_id' => (int) $style_id,
657 'image_lang' => '',
658 );
659 }
660 }
661
662 $sql = 'SELECT lang_dir
663 FROM ' . LANG_TABLE;
664 $result = $this->db->sql_query($sql);
665
666 while ($row = $this->db->sql_fetchrow($result))
667 {
668 if (@file_exists("{$phpbb_root_path}styles/{$imageset_row['imageset_path']}/imageset/{$row['lang_dir']}/imageset.cfg"))
669 {
670 $cfg_data_imageset_data = parse_cfg_file("{$phpbb_root_path}styles/{$imageset_row['imageset_path']}/imageset/{$row['lang_dir']}/imageset.cfg");
671 foreach ($cfg_data_imageset_data as $image_name => $value)
672 {
673 if (strpos($value, '*') !== false)
674 {
675 if (substr($value, -1, 1) === '*')
676 {
677 list($image_filename, $image_height) = explode('*', $value);
678 $image_width = 0;
679 }
680 else
681 {
682 list($image_filename, $image_height, $image_width) = explode('*', $value);
683 }
684 }
685 else
686 {
687 $image_filename = $value;
688 $image_height = $image_width = 0;
689 }
690
691 if (strpos($image_name, 'img_') === 0 && $image_filename)
692 {
693 $image_name = substr($image_name, 4);
694 $sql_ary[] = array(
695 'image_name' => (string) $image_name,
696 'image_filename' => (string) $image_filename,
697 'image_height' => (int) $image_height,
698 'image_width' => (int) $image_width,
699 'imageset_id' => (int) $style_id,
700 'image_lang' => (string) $row['lang_dir'],
701 );
702 }
703 }
704 }
705 }
706 $this->db->sql_freeresult($result);
707
708 $this->db->sql_multi_insert(STYLES_IMAGESET_DATA_TABLE, $sql_ary);
709
710 $cache->destroy('sql', STYLES_IMAGESET_DATA_TABLE);
711
712 return $this->umil_end();
713 }
714 break;
715 //case 'imageset' :
716
717 case 'template' :
718 if ($style_id == 0)
719 {
720 $return = array();
721 $sql = 'SELECT template_id
722 FROM ' . STYLES_TEMPLATE_TABLE;
723 $result = $this->db->sql_query($sql);
724 while ($row = $this->db->sql_fetchrow($result))
725 {
726 $return[] = $this->cache_purge('template', $row['template_id']);
727 }
728 $this->db->sql_freeresult($result);
729
730 return implode('<br /><br />', $return);
731 }
732 else
733 {
734 $sql = 'SELECT *
735 FROM ' . STYLES_TEMPLATE_TABLE . "
736 WHERE template_id = $style_id";
737 $result = $this->db->sql_query($sql);
738 $template_row = $this->db->sql_fetchrow($result);
739 $this->db->sql_freeresult($result);
740
741 if (!$template_row)
742 {
743 $this->umil_start('TEMPLATE_CACHE_PURGE', 'UNKNOWN');
744 return $this->umil_end('FAIL');
745 }
746
747 $this->umil_start('TEMPLATE_CACHE_PURGE', $template_row['template_name']);
748
749 // The following is from includes/acp/acp_styles.php
750 if ($template_row['template_storedb'] && file_exists("{$phpbb_root_path}styles/{$template_row['template_path']}/template/"))
751 {
752 $filelist = array('' => array());
753
754 $sql = 'SELECT template_filename, template_mtime
755 FROM ' . STYLES_TEMPLATE_DATA_TABLE . "
756 WHERE template_id = $style_id";
757 $result = $this->db->sql_query($sql);
758
759 while ($row = $this->db->sql_fetchrow($result))
760 {
761// if (@filemtime("{$phpbb_root_path}styles/{$template_row['template_path']}/template/" . $row['template_filename']) > $row['template_mtime'])
762// {
763 // get folder info from the filename
764 if (($slash_pos = strrpos($row['template_filename'], '/')) === false)
765 {
766 $filelist[''][] = $row['template_filename'];
767 }
768 else
769 {
770 $filelist[substr($row['template_filename'], 0, $slash_pos + 1)][] = substr($row['template_filename'], $slash_pos + 1, strlen($row['template_filename']) - $slash_pos - 1);
771 }
772// }
773 }
774 $this->db->sql_freeresult($result);
775
776 $includes = array();
777 foreach ($filelist as $pathfile => $file_ary)
778 {
779 foreach ($file_ary as $file)
780 {
781 if (!($fp = @fopen("{$phpbb_root_path}styles/{$template_row['template_path']}$pathfile$file", 'r')))
782 {
783 return $this->umil_end('FILE_COULD_NOT_READ', "{$phpbb_root_path}styles/{$template_row['template_path']}$pathfile$file");
784 }
785 $template_data = fread($fp, filesize("{$phpbb_root_path}styles/{$template_row['template_path']}$pathfile$file"));
786 fclose($fp);
787
788 if (preg_match_all('#<!-- INCLUDE (.*?\.html) -->#is', $template_data, $matches))
789 {
790 foreach ($matches[1] as $match)
791 {
792 $includes[trim($match)][] = $file;
793 }
794 }
795 }
796 }
797
798 foreach ($filelist as $pathfile => $file_ary)
799 {
800 foreach ($file_ary as $file)
801 {
802 // Skip index.
803 if (strpos($file, 'index.') === 0)
804 {
805 continue;
806 }
807
808 // We could do this using extended inserts ... but that could be one
809 // heck of a lot of data ...
810 $sql_ary = array(
811 'template_id' => (int) $style_id,
812 'template_filename' => "$pathfile$file",
813 'template_included' => (isset($includes[$file])) ? implode(':', $includes[$file]) . ':' : '',
814 'template_mtime' => (int) filemtime("{$phpbb_root_path}styles/{$template_row['template_path']}$pathfile$file"),
815 'template_data' => (string) file_get_contents("{$phpbb_root_path}styles/{$template_row['template_path']}$pathfile$file"),
816 );
817
818 $sql = 'UPDATE ' . STYLES_TEMPLATE_DATA_TABLE . ' SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . "
819 WHERE template_id = $style_id
820 AND template_filename = '" . $this->db->sql_escape("$pathfile$file") . "'";
821 $this->db->sql_query($sql);
822 }
823 }
824 unset($filelist);
825 }
826
827 // Purge the forum's cache as well.
828 $cache->purge();
829
830 return $this->umil_end();
831 }
832 break;
833 //case 'template' :
834
835 case 'theme' :
836 if ($style_id == 0)
837 {
838 $return = array();
839 $sql = 'SELECT theme_id
840 FROM ' . STYLES_THEME_TABLE;
841 $result = $this->db->sql_query($sql);
842 while ($row = $this->db->sql_fetchrow($result))
843 {
844 $return[] = $this->cache_purge('theme', $row['theme_id']);
845 }
846 $this->db->sql_freeresult($result);
847
848 return implode('<br /><br />', $return);
849 }
850 else
851 {
852 $sql = 'SELECT *
853 FROM ' . STYLES_THEME_TABLE . "
854 WHERE theme_id = $style_id";
855 $result = $this->db->sql_query($sql);
856 $theme_row = $this->db->sql_fetchrow($result);
857 $this->db->sql_freeresult($result);
858
859 if (!$theme_row)
860 {
861 $this->umil_start('THEME_CACHE_PURGE', 'UNKNOWN');
862 return $this->umil_end('FAIL');
863 }
864
865 $this->umil_start('THEME_CACHE_PURGE', $theme_row['theme_name']);
866
867 // The following is from includes/acp/acp_styles.php
868 if ($theme_row['theme_storedb'] && file_exists("{$phpbb_root_path}styles/{$theme_row['theme_path']}/theme/stylesheet.css"))
869 {
870 $stylesheet = file_get_contents($phpbb_root_path . 'styles/' . $theme_row['theme_path'] . '/theme/stylesheet.css');
871
872 // Match CSS imports
873 $matches = array();
874 preg_match_all('/@import url\(["\'](.*)["\']\);/i', $stylesheet, $matches);
875
876 if (sizeof($matches))
877 {
878 foreach ($matches[0] as $idx => $match)
879 {
880 if (!file_exists("{$phpbb_root_path}styles/{$theme_row['theme_path']}/theme/{$matches[1][$idx]}"))
881 {
882 continue;
883 }
884
885 $content = trim(file_get_contents("{$phpbb_root_path}styles/{$theme_row['theme_path']}/theme/{$matches[1][$idx]}"));
886 $stylesheet = str_replace($match, $content, $stylesheet);
887 }
888 }
889
890 // adjust paths
891 $db_theme_data = str_replace('./', 'styles/' . $theme_row['theme_path'] . '/theme/', $stylesheet);
892
893 // Save CSS contents
894 $sql_ary = array(
895 'theme_mtime' => (int) filemtime("{$phpbb_root_path}styles/{$theme_row['theme_path']}/theme/stylesheet.css"),
896 'theme_data' => $db_theme_data,
897 );
898
899 $sql = 'UPDATE ' . STYLES_THEME_TABLE . ' SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . "
900 WHERE theme_id = $style_id";
901 $this->db->sql_query($sql);
902
903 $cache->destroy('sql', STYLES_THEME_TABLE);
904 }
905
906 return $this->umil_end();
907 }
908 break;
909 //case 'theme' :
910
911 default:
912 $this->umil_start('CACHE_PURGE');
913 $cache->purge();
914
915 return $this->umil_end();
916 break;
917 }
918 }
919
920 /**
921 * Config Exists
922 *
923 * This function is to check to see if a config variable exists or if it does not.
924 *
925 * @param string $config_name The name of the config setting you wish to check for.
926 * @param bool $return_result - return the config value/default if true : default false.
927 *
928 * @return bool true/false if config exists
929 */
930 function config_exists($config_name, $return_result = false)
931 {
932 global $config, $cache;
933
934 $sql = 'SELECT *
935 FROM ' . CONFIG_TABLE . "
936 WHERE config_name = '" . $this->db->sql_escape($config_name) . "'";
937 $result = $this->db->sql_query($sql);
938 $row = $this->db->sql_fetchrow($result);
939 $this->db->sql_freeresult($result);
940
941 if ($row)
942 {
943 if (!isset($config[$config_name]))
944 {
945 $config[$config_name] = $row['config_value'];
946
947 if (!$row['is_dynamic'])
948 {
949 $cache->destroy('config');
950 }
951 }
952
953 return ($return_result) ? $row : true;
954 }
955
956 // this should never happen, but if it does, we need to remove the config from the array
957 if (isset($config[$config_name]))
958 {
959 unset($config[$config_name]);
960 $cache->destroy('config');
961 }
962
963 return false;
964 }
965
966 /**
967 * Config Add
968 *
969 * This function allows you to add a config setting.
970 *
971 * @param string $config_name The name of the config setting you would like to add
972 * @param mixed $config_value The value of the config setting
973 * @param bool $is_dynamic True if it is dynamic (changes very often) and should not be stored in the cache, false if not.
974 *
975 * @return result
976 */
977 function config_add($config_name, $config_value = '', $is_dynamic = false)
978 {
979 // Multicall
980 if ($this->multicall(__FUNCTION__, $config_name))
981 {
982 return;
983 }
984
985 $this->umil_start('CONFIG_ADD', $config_name);
986
987 if ($this->config_exists($config_name))
988 {
989 return $this->umil_end('CONFIG_ALREADY_EXISTS', $config_name);
990 }
991
992 set_config($config_name, $config_value, $is_dynamic);
993
994 return $this->umil_end();
995 }
996
997 /**
998 * Config Update
999 *
1000 * This function allows you to update an existing config setting.
1001 *
1002 * @param string $config_name The name of the config setting you would like to update
1003 * @param mixed $config_value The value of the config setting
1004 * @param bool $is_dynamic True if it is dynamic (changes very often) and should not be stored in the cache, false if not.
1005 *
1006 * @return result
1007 */
1008 function config_update($config_name, $config_value = '', $is_dynamic = false)
1009 {
1010 // Multicall
1011 if ($this->multicall(__FUNCTION__, $config_name))
1012 {
1013 return;
1014 }
1015
1016 $this->umil_start('CONFIG_UPDATE', $config_name);
1017
1018 if (!$this->config_exists($config_name))
1019 {
1020 return $this->umil_end('CONFIG_NOT_EXIST', $config_name);
1021 }
1022
1023 set_config($config_name, $config_value, $is_dynamic);
1024
1025 return $this->umil_end();
1026 }
1027
1028 /**
1029 * Config Remove
1030 *
1031 * This function allows you to remove an existing config setting.
1032 *
1033 * @param string $config_name The name of the config setting you would like to remove
1034 *
1035 * @return result
1036 */
1037 function config_remove($config_name)
1038 {
1039 global $cache, $config;
1040
1041 // Multicall
1042 if ($this->multicall(__FUNCTION__, $config_name))
1043 {
1044 return;
1045 }
1046
1047 $this->umil_start('CONFIG_REMOVE', $config_name);
1048
1049 if (!$this->config_exists($config_name))
1050 {
1051 return $this->umil_end('CONFIG_NOT_EXIST', $config_name);
1052 }
1053
1054 $sql = 'DELETE FROM ' . CONFIG_TABLE . " WHERE config_name = '" . $this->db->sql_escape($config_name) . "'";
1055 $this->db->sql_query($sql);
1056
1057 unset($config[$config_name]);
1058 $cache->destroy('config');
1059
1060 return $this->umil_end();
1061 }
1062
1063 /**
1064 * Module Exists
1065 *
1066 * Check if a module exists
1067 *
1068 * @param string $class The module class(acp|mcp|ucp)
1069 * @param int|string|bool $parent The parent module_id|module_langname (0 for no parent). Use false to ignore the parent check and check class wide.
1070 * @param int|string $module The module_id|module_langname you would like to check for to see if it exists
1071 */
1072 function module_exists($class, $parent, $module)
1073 {
1074 // the main root directory should return true
1075 if (!$module)
1076 {
1077 return true;
1078 }
1079
1080 $class = $this->db->sql_escape($class);
1081 $module = $this->db->sql_escape($module);
1082
1083 $parent_sql = '';
1084 if ($parent !== false)
1085 {
1086 // Allows '' to be sent as 0
1087 $parent = (!$parent) ? 0 : $parent;
1088
1089 if (!is_numeric($parent))
1090 {
1091 $sql = 'SELECT module_id FROM ' . MODULES_TABLE . "
1092 WHERE module_langname = '" . $this->db->sql_escape($parent) . "'
1093 AND module_class = '$class'";
1094 $result = $this->db->sql_query($sql);
1095 $row = $this->db->sql_fetchrow($result);
1096 $this->db->sql_freeresult($result);
1097
1098 if (!$row)
1099 {
1100 return false;
1101 }
1102
1103 $parent_sql = 'AND parent_id = ' . (int) $row['module_id'];
1104 }
1105 else
1106 {
1107 $parent_sql = 'AND parent_id = ' . (int) $parent;
1108 }
1109 }
1110
1111 $sql = 'SELECT module_id FROM ' . MODULES_TABLE . "
1112 WHERE module_class = '$class'
1113 $parent_sql
1114 AND " . ((is_numeric($module)) ? 'module_id = ' . (int) $module : "module_langname = '$module'");
1115 $result = $this->db->sql_query($sql);
1116 $row = $this->db->sql_fetchrow($result);
1117 $this->db->sql_freeresult($result);
1118
1119 if ($row)
1120 {
1121 return true;
1122 }
1123
1124 return false;
1125 }
1126
1127 /**
1128 * Module Add
1129 *
1130 * Add a new module
1131 *
1132 * @param string $class The module class(acp|mcp|ucp)
1133 * @param int|string $parent The parent module_id|module_langname (0 for no parent)
1134 * @param array $data an array of the data on the new module. This can be setup in two different ways.
1135 * 1. The "manual" way. For inserting a category or one at a time. It will be merged with the base array shown a bit below,
1136 * but at the least requires 'module_langname' to be sent, and, if you want to create a module (instead of just a category) you must send module_basename and module_mode.
1137 * array(
1138 * 'module_enabled' => 1,
1139 * 'module_display' => 1,
1140 * 'module_basename' => '',
1141 * 'module_class' => $class,
1142 * 'parent_id' => (int) $parent,
1143 * 'module_langname' => '',
1144 * 'module_mode' => '',
1145 * 'module_auth' => '',
1146 * )
1147 * 2. The "automatic" way. For inserting multiple at a time based on the specs in the info file for the module(s). For this to work the modules must be correctly setup in the info file.
1148 * An example follows (this would insert the settings, log, and flag modes from the includes/acp/info/acp_asacp.php file):
1149 * array(
1150 * 'module_basename' => 'asacp',
1151 * 'modes' => array('settings', 'log', 'flag'),
1152 * )
1153 * Optionally you may not send 'modes' and it will insert all of the modules in that info file.
1154 * @param string|bool $include_path If you would like to use a custom include path, specify that here
1155 */
1156 function module_add($class, $parent = 0, $data = array(), $include_path = false)
1157 {
1158 global $cache, $user, $phpbb_root_path, $phpEx;
1159
1160 // Multicall
1161 if ($this->multicall(__FUNCTION__, $class))
1162 {
1163 return;
1164 }
1165
1166 // Prevent stupid things like trying to add a module with no name or any data on it
1167 if (empty($data))
1168 {
1169 $this->umil_start('MODULE_ADD', $class, 'UNKNOWN');
1170 return $this->umil_end('FAIL');
1171 }
1172
1173 // Allows '' to be sent as 0
1174 $parent = (!$parent) ? 0 : $parent;
1175
1176 // allow sending the name as a string in $data to create a category
1177 if (!is_array($data))
1178 {
1179 $data = array('module_langname' => $data);
1180 }
1181
1182 if (!isset($data['module_langname']))
1183 {
1184 // The "automatic" way
1185 $basename = (isset($data['module_basename'])) ? $data['module_basename'] : '';
1186 $basename = str_replace(array('/', '\\'), '', $basename);
1187 $class = str_replace(array('/', '\\'), '', $class);
1188 $info_file = "$class/info/{$class}_$basename.$phpEx";
1189
1190 // The manual and automatic ways both failed...
1191 if (!file_exists((($include_path === false) ? $phpbb_root_path . 'includes/' : $include_path) . $info_file))
1192 {
1193 $this->umil_start('MODULE_ADD', $class, $info_file);
1194 return $this->umil_end('FAIL');
1195 }
1196
1197 $classname = "{$class}_{$basename}_info";
1198
1199 if (!class_exists($classname))
1200 {
1201 include((($include_path === false) ? $phpbb_root_path . 'includes/' : $include_path) . $info_file);
1202 }
1203
1204 $info = new $classname;
1205 $module = $info->module();
1206 unset($info);
1207
1208 $result = '';
1209 foreach ($module['modes'] as $mode => $module_info)
1210 {
1211 if (!isset($data['modes']) || in_array($mode, $data['modes']))
1212 {
1213 $new_module = array(
1214 'module_basename' => $basename,
1215 'module_langname' => $module_info['title'],
1216 'module_mode' => $mode,
1217 'module_auth' => $module_info['auth'],
1218 'module_display' => (isset($module_info['display'])) ? $module_info['display'] : true,
1219 'before' => (isset($module_info['before'])) ? $module_info['before'] : false,
1220 'after' => (isset($module_info['after'])) ? $module_info['after'] : false,
1221 );
1222
1223 // Run the "manual" way with the data we've collected.
1224 $result .= ((isset($data['spacer'])) ? $data['spacer'] : '<br />') . $this->module_add($class, $parent, $new_module);
1225 }
1226 }
1227
1228 return $result;
1229 }
1230
1231 // The "manual" way
1232 $this->umil_start('MODULE_ADD', $class, ((isset($user->lang[$data['module_langname']])) ? $user->lang[$data['module_langname']] : $data['module_langname']));
1233 add_log('admin', 'LOG_MODULE_ADD', ((isset($user->lang[$data['module_langname']])) ? $user->lang[$data['module_langname']] : $data['module_langname']));
1234
1235 $class = $this->db->sql_escape($class);
1236
1237 if (!is_numeric($parent))
1238 {
1239 $sql = 'SELECT module_id FROM ' . MODULES_TABLE . "
1240 WHERE module_langname = '" . $this->db->sql_escape($parent) . "'
1241 AND module_class = '$class'";
1242 $result = $this->db->sql_query($sql);
1243 $row = $this->db->sql_fetchrow($result);
1244 $this->db->sql_freeresult($result);
1245
1246 if (!$row)
1247 {
1248 return $this->umil_end('PARENT_NOT_EXIST');
1249 }
1250
1251 $parent = $data['parent_id'] = $row['module_id'];
1252 }
1253 else if (!$this->module_exists($class, false, $parent))
1254 {
1255 return $this->umil_end('PARENT_NOT_EXIST');
1256 }
1257
1258 if ($this->module_exists($class, $parent, $data['module_langname']))
1259 {
1260 return $this->umil_end('MODULE_ALREADY_EXIST');
1261 }
1262
1263 if (!class_exists('acp_modules'))
1264 {
1265 include($phpbb_root_path . 'includes/acp/acp_modules.' . $phpEx);
1266 $user->add_lang('acp/modules');
1267 }
1268 $acp_modules = new acp_modules();
1269
1270 $module_data = array(
1271 'module_enabled' => (isset($data['module_enabled'])) ? $data['module_enabled'] : 1,
1272 'module_display' => (isset($data['module_display'])) ? $data['module_display'] : 1,
1273 'module_basename' => (isset($data['module_basename'])) ? $data['module_basename'] : '',
1274 'module_class' => $class,
1275 'parent_id' => (int) $parent,
1276 'module_langname' => (isset($data['module_langname'])) ? $data['module_langname'] : '',
1277 'module_mode' => (isset($data['module_mode'])) ? $data['module_mode'] : '',
1278 'module_auth' => (isset($data['module_auth'])) ? $data['module_auth'] : '',
1279 );
1280 $result = $acp_modules->update_module_data($module_data, true);
1281
1282 // update_module_data can either return a string or an empty array...
1283 if (is_string($result))
1284 {
1285 // Error
1286 $this->result = $this->get_output_text($result);
1287 }
1288 else
1289 {
1290 // Success
1291
1292 // Move the module if requested above/below an existing one
1293 if (isset($data['before']) && $data['before'])
1294 {
1295 $sql = 'SELECT left_id FROM ' . MODULES_TABLE . '
1296 WHERE module_class = \'' . $class . '\'
1297 AND parent_id = ' . (int) $parent . '
1298 AND module_langname = \'' . $this->db->sql_escape($data['before']) . '\'';
1299 $this->db->sql_query($sql);
1300 $to_left = $this->db->sql_fetchfield('left_id');
1301
1302 $sql = 'UPDATE ' . MODULES_TABLE . " SET left_id = left_id + 2, right_id = right_id + 2
1303 WHERE module_class = '$class'
1304 AND left_id >= $to_left
1305 AND left_id < {$module_data['left_id']}";
1306 $this->db->sql_query($sql);
1307
1308 $sql = 'UPDATE ' . MODULES_TABLE . " SET left_id = $to_left, right_id = " . ($to_left + 1) . "
1309 WHERE module_class = '$class'
1310 AND module_id = {$module_data['module_id']}";
1311 $this->db->sql_query($sql);
1312 }
1313 else if (isset($data['after']) && $data['after'])
1314 {
1315 $sql = 'SELECT right_id FROM ' . MODULES_TABLE . '
1316 WHERE module_class = \'' . $class . '\'
1317 AND parent_id = ' . (int) $parent . '
1318 AND module_langname = \'' . $this->db->sql_escape($data['after']) . '\'';
1319 $this->db->sql_query($sql);
1320 $to_right = $this->db->sql_fetchfield('right_id');
1321
1322 $sql = 'UPDATE ' . MODULES_TABLE . " SET left_id = left_id + 2, right_id = right_id + 2
1323 WHERE module_class = '$class'
1324 AND left_id >= $to_right
1325 AND left_id < {$module_data['left_id']}";
1326 $this->db->sql_query($sql);
1327
1328 $sql = 'UPDATE ' . MODULES_TABLE . ' SET left_id = ' . ($to_right + 1) . ', right_id = ' . ($to_right + 2) . "
1329 WHERE module_class = '$class'
1330 AND module_id = {$module_data['module_id']}";
1331 $this->db->sql_query($sql);
1332 }
1333 }
1334
1335 // Clear the Modules Cache
1336 $cache->destroy("_modules_$class");
1337
1338 return $this->umil_end();
1339 }
1340
1341 /**
1342 * Module Remove
1343 *
1344 * Remove a module
1345 *
1346 * @param string $class The module class(acp|mcp|ucp)
1347 * @param int|string|bool $parent The parent module_id|module_langname (0 for no parent). Use false to ignore the parent check and check class wide.
1348 * @param int|string $module The module id|module_langname
1349 * @param string|bool $include_path If you would like to use a custom include path, specify that here
1350 */
1351 function module_remove($class, $parent = 0, $module = '', $include_path = false)
1352 {
1353 global $cache, $user, $phpbb_root_path, $phpEx;
1354
1355 // Multicall
1356 if ($this->multicall(__FUNCTION__, $class))
1357 {
1358 return;
1359 }
1360
1361 // Imitation of module_add's "automatic" and "manual" method so the uninstaller works from the same set of instructions for umil_auto
1362 if (is_array($module))
1363 {
1364 if (isset($module['module_langname']))
1365 {
1366 // Manual Method
1367 return $this->module_remove($class, $parent, $module['module_langname'], $include_path);
1368 }
1369
1370 // Failed.
1371 if (!isset($module['module_basename']))
1372 {
1373 $this->umil_start('MODULE_REMOVE', $class, 'UNKNOWN');
1374 return $this->umil_end('FAIL');
1375 }
1376
1377 // Automatic method
1378 $basename = str_replace(array('/', '\\'), '', $module['module_basename']);
1379 $class = str_replace(array('/', '\\'), '', $class);
1380 $info_file = "$class/info/{$class}_$basename.$phpEx";
1381
1382 if (!file_exists((($include_path === false) ? $phpbb_root_path . 'includes/' : $include_path) . $info_file))
1383 {
1384 $this->umil_start('MODULE_REMOVE', $class, $info_file);
1385 return $this->umil_end('FAIL');
1386 }
1387
1388 $classname = "{$class}_{$basename}_info";
1389
1390 if (!class_exists($classname))
1391 {
1392 include((($include_path === false) ? $phpbb_root_path . 'includes/' : $include_path) . $info_file);
1393 }
1394
1395 $info = new $classname;
1396 $module_info = $info->module();
1397 unset($info);
1398
1399 $result = '';
1400 foreach ($module_info['modes'] as $mode => $info)
1401 {
1402 if (!isset($module['modes']) || in_array($mode, $module['modes']))
1403 {
1404 $result .= $this->module_remove($class, $parent, $info['title']) . '<br />';
1405 }
1406 }
1407 return $result;
1408 }
1409 else
1410 {
1411 $class = $this->db->sql_escape($class);
1412
1413 if (!$this->module_exists($class, $parent, $module))
1414 {
1415 $this->umil_start('MODULE_REMOVE', $class, ((isset($user->lang[$module])) ? $user->lang[$module] : $module));
1416 return $this->umil_end('MODULE_NOT_EXIST');
1417 }
1418
1419 $parent_sql = '';
1420 if ($parent !== false)
1421 {
1422 // Allows '' to be sent as 0
1423 $parent = (!$parent) ? 0 : $parent;
1424
1425 if (!is_numeric($parent))
1426 {
1427 $sql = 'SELECT module_id FROM ' . MODULES_TABLE . "
1428 WHERE module_langname = '" . $this->db->sql_escape($parent) . "'
1429 AND module_class = '$class'";
1430 $result = $this->db->sql_query($sql);
1431 $row = $this->db->sql_fetchrow($result);
1432 $this->db->sql_freeresult($result);
1433
1434 // we know it exists from the module_exists check
1435 $parent_sql = 'AND parent_id = ' . (int) $row['module_id'];
1436 }
1437 else
1438 {
1439 $parent_sql = 'AND parent_id = ' . (int) $parent;
1440 }
1441 }
1442
1443 $module_ids = array();
1444 if (!is_numeric($module))
1445 {
1446 $module = $this->db->sql_escape($module);
1447 $sql = 'SELECT module_id FROM ' . MODULES_TABLE . "
1448 WHERE module_langname = '$module'
1449 AND module_class = '$class'
1450 $parent_sql";
1451 $result = $this->db->sql_query($sql);
1452 while ($row = $this->db->sql_fetchrow($result))
1453 {
1454 $module_ids[] = (int) $row['module_id'];
1455 }
1456 $this->db->sql_freeresult($result);
1457
1458 $module_name = $module;
1459 }
1460 else
1461 {
1462 $module = (int) $module;
1463 $sql = 'SELECT module_langname FROM ' . MODULES_TABLE . "
1464 WHERE module_id = $module
1465 AND module_class = '$class'
1466 $parent_sql";
1467 $result = $this->db->sql_query($sql);
1468 $row = $this->db->sql_fetchrow($result);
1469 $this->db->sql_freeresult($result);
1470
1471 $module_name = $row['module_langname'];
1472 $module_ids[] = $module;
1473 }
1474
1475 $this->umil_start('MODULE_REMOVE', $class, ((isset($user->lang[$module_name])) ? $user->lang[$module_name] : $module_name));
1476 add_log('admin', 'LOG_MODULE_REMOVED', ((isset($user->lang[$module_name])) ? $user->lang[$module_name] : $module_name));
1477
1478 if (!class_exists('acp_modules'))
1479 {
1480 include($phpbb_root_path . 'includes/acp/acp_modules.' . $phpEx);
1481 $user->add_lang('acp/modules');
1482 }
1483 $acp_modules = new acp_modules();
1484 $acp_modules->module_class = $class;
1485
1486 foreach ($module_ids as $module_id)
1487 {
1488 $result = $acp_modules->delete_module($module_id);
1489 if (!empty($result))
1490 {
1491 if ($this->result == ((isset($user->lang['SUCCESS'])) ? $user->lang['SUCCESS'] : 'SUCCESS'))
1492 {
1493 $this->result = implode('<br />', $result);
1494 }
1495 else
1496 {
1497 $this->result .= '<br />' . implode('<br />', $result);
1498 }
1499 }
1500 }
1501
1502 $cache->destroy("_modules_$class");
1503
1504 return $this->umil_end();
1505 }
1506 }
1507
1508 /**
1509 * Permission Exists
1510 *
1511 * Check if a permission (auth) setting exists
1512 *
1513 * @param string $auth_option The name of the permission (auth) option
1514 * @param bool $global True for checking a global permission setting, False for a local permission setting
1515 *
1516 * @return bool true if it exists, false if not
1517 */
1518 function permission_exists($auth_option, $global = true)
1519 {
1520 // forum permissions shouldn't be set globally
1521 if (strpos($auth_option, 'f_') === 0)
1522 {
1523 $global = false;
1524 }
1525
1526 if ($global)
1527 {
1528 $type_sql = ' AND is_global = 1';
1529 }
1530 else
1531 {
1532 $type_sql = ' AND is_local = 1';
1533 }
1534
1535 $sql = 'SELECT auth_option_id
1536 FROM ' . ACL_OPTIONS_TABLE . "
1537 WHERE auth_option = '" . $this->db->sql_escape($auth_option) . "'"
1538 . $type_sql;
1539 $result = $this->db->sql_query($sql);
1540
1541 $row = $this->db->sql_fetchrow($result);
1542 $this->db->sql_freeresult($result);
1543
1544 if ($row)
1545 {
1546 return true;
1547 }
1548
1549 return false;
1550 }
1551
1552 /**
1553 * Permission Add
1554 *
1555 * Add a permission (auth) option
1556 *
1557 * @param string $auth_option The name of the permission (auth) option
1558 * @param bool $global True for checking a global permission setting, False for a local permission setting
1559 *
1560 * @return result
1561 */
1562 function permission_add($auth_option, $global = true)
1563 {
1564 // Multicall
1565 if ($this->multicall(__FUNCTION__, $auth_option))
1566 {
1567 return;
1568 }
1569
1570 $this->umil_start('PERMISSION_ADD', $auth_option);
1571
1572 // forum permissions shouldn't be set globally
1573 if (strpos($auth_option, 'f_') === 0)
1574 {
1575 $global = false;
1576 }
1577
1578 if ($this->permission_exists($auth_option, $global))
1579 {
1580 return $this->umil_end('PERMISSION_ALREADY_EXISTS', $auth_option);
1581 }
1582
1583 // We've added permissions, so set to true to notify the user.
1584 $this->permissions_added = true;
1585
1586 if (!class_exists('auth_admin'))
1587 {
1588 global $phpbb_root_path, $phpEx;
1589
1590 include($phpbb_root_path . 'includes/acp/auth.' . $phpEx);
1591 }
1592 $auth_admin = new auth_admin();
1593
1594 // We have to add a check to see if the !$global (if global, local, and if local, global) permission already exists. If it does, acl_add_option currently has a bug which would break the ACL system, so we are having a work-around here.
1595 if ($this->permission_exists($auth_option, !$global))
1596 {
1597 $sql_ary = array(
1598 'is_global' => 1,
1599 'is_local' => 1,
1600 );
1601 $sql = 'UPDATE ' . ACL_OPTIONS_TABLE . '
1602 SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . '
1603 WHERE auth_option = \'' . $this->db->sql_escape($auth_option) . "'";
1604 $this->db->sql_query($sql);
1605 }
1606 else
1607 {
1608 if ($global)
1609 {
1610 $auth_admin->acl_add_option(array('global' => array($auth_option)));
1611 }
1612 else
1613 {
1614 $auth_admin->acl_add_option(array('local' => array($auth_option)));
1615 }
1616 }
1617
1618 return $this->umil_end();
1619 }
1620
1621 /**
1622 * Permission Remove
1623 *
1624 * Remove a permission (auth) option
1625 *
1626 * @param string $auth_option The name of the permission (auth) option
1627 * @param bool $global True for checking a global permission setting, False for a local permission setting
1628 *
1629 * @return result
1630 */
1631 function permission_remove($auth_option, $global = true)
1632 {
1633 global $auth, $cache;
1634
1635 // Multicall
1636 if ($this->multicall(__FUNCTION__, $auth_option))
1637 {
1638 return;
1639 }
1640
1641 $this->umil_start('PERMISSION_REMOVE', $auth_option);
1642
1643 // forum permissions shouldn't be set globally
1644 if (strpos($auth_option, 'f_') === 0)
1645 {
1646 $global = false;
1647 }
1648
1649 if (!$this->permission_exists($auth_option, $global))
1650 {
1651 return $this->umil_end('PERMISSION_NOT_EXIST', $auth_option);
1652 }
1653
1654 if ($global)
1655 {
1656 $type_sql = ' AND is_global = 1';
1657 }
1658 else
1659 {
1660 $type_sql = ' AND is_local = 1';
1661 }
1662 $sql = 'SELECT auth_option_id, is_global, is_local FROM ' . ACL_OPTIONS_TABLE . "
1663 WHERE auth_option = '" . $this->db->sql_escape($auth_option) . "'" .
1664 $type_sql;
1665 $result = $this->db->sql_query($sql);
1666 $row = $this->db->sql_fetchrow($result);
1667 $this->db->sql_freeresult($result);
1668
1669 $id = $row['auth_option_id'];
1670
1671 // If it is a local and global permission, do not remove the row! :P
1672 if ($row['is_global'] && $row['is_local'])
1673 {
1674 $sql = 'UPDATE ' . ACL_OPTIONS_TABLE . '
1675 SET ' . (($global) ? 'is_global = 0' : 'is_local = 0') . '
1676 WHERE auth_option_id = ' . $id;
1677 $this->db->sql_query($sql);
1678 }
1679 else
1680 {
1681 // Delete time
1682 $this->db->sql_query('DELETE FROM ' . ACL_GROUPS_TABLE . ' WHERE auth_option_id = ' . $id);
1683 $this->db->sql_query('DELETE FROM ' . ACL_ROLES_DATA_TABLE . ' WHERE auth_option_id = ' . $id);
1684 $this->db->sql_query('DELETE FROM ' . ACL_USERS_TABLE . ' WHERE auth_option_id = ' . $id);
1685 $this->db->sql_query('DELETE FROM ' . ACL_OPTIONS_TABLE . ' WHERE auth_option_id = ' . $id);
1686 }
1687
1688 // Purge the auth cache
1689 $cache->destroy('_acl_options');
1690 $auth->acl_clear_prefetch();
1691
1692 return $this->umil_end();
1693 }
1694
1695 /**
1696 * Add a new permission role
1697 *
1698 * @param string $role_name The new role name
1699 * @param sting $role_type The type (u_, m_, a_)
1700 */
1701 function permission_role_add($role_name, $role_type = '', $role_description = '')
1702 {
1703 // Multicall
1704 if ($this->multicall(__FUNCTION__, $role_name))
1705 {
1706 return;
1707 }
1708
1709 $this->umil_start('PERMISSION_ROLE_ADD', $role_name);
1710
1711 $sql = 'SELECT role_id FROM ' . ACL_ROLES_TABLE . '
1712 WHERE role_name = \'' . $this->db->sql_escape($role_name) . '\'';
1713 $this->db->sql_query($sql);
1714 $role_id = $this->db->sql_fetchfield('role_id');
1715
1716 if ($role_id)
1717 {
1718 return $this->umil_end('ROLE_ALREADY_EXISTS', $old_role_name);
1719 }
1720
1721 $sql = 'SELECT MAX(role_order) AS max FROM ' . ACL_ROLES_TABLE . '
1722 WHERE role_type = \'' . $this->db->sql_escape($role_type) . '\'';
1723 $this->db->sql_query($sql);
1724 $role_order = $this->db->sql_fetchfield('max');
1725 $role_order = (!$role_order) ? 1 : $role_order + 1;
1726
1727 $sql_ary = array(
1728 'role_name' => $role_name,
1729 'role_description' => $role_description,
1730 'role_type' => $role_type,
1731 'role_order' => $role_order,
1732 );
1733
1734 $sql = 'INSERT INTO ' . ACL_ROLES_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary);
1735 $this->db->sql_query($sql);
1736
1737 return $this->umil_end();
1738 }
1739
1740 /**
1741 * Update the name on a permission role
1742 *
1743 * @param string $old_role_name The old role name
1744 * @param string $new_role_name The new role name
1745 */
1746 function permission_role_update($old_role_name, $new_role_name = '')
1747 {
1748 // Multicall
1749 if ($this->multicall(__FUNCTION__, $role_name))
1750 {
1751 return;
1752 }
1753
1754 $this->umil_start('PERMISSION_ROLE_UPDATE', $old_role_name);
1755
1756 $sql = 'SELECT role_id FROM ' . ACL_ROLES_TABLE . '
1757 WHERE role_name = \'' . $this->db->sql_escape($old_role_name) . '\'';
1758 $this->db->sql_query($sql);
1759 $role_id = $this->db->sql_fetchfield('role_id');
1760
1761 if (!$role_id)
1762 {
1763 return $this->umil_end('ROLE_NOT_EXIST', $old_role_name);
1764 }
1765
1766 $sql = 'UPDATE ' . ACL_ROLES_TABLE . '
1767 SET role_name = \'' . $this->db->sql_escape($new_role_name) . '\'
1768 WHERE role_name = \'' . $this->db->sql_escape($old_role_name) . '\'';
1769 $this->db->sql_query($sql);
1770
1771 return $this->umil_end();
1772 }
1773
1774 /**
1775 * Remove a permission role
1776 *
1777 * @param string $role_name The role name to remove
1778 */
1779 function permission_role_remove($role_name)
1780 {
1781 global $auth;
1782
1783 // Multicall
1784 if ($this->multicall(__FUNCTION__, $role_name))
1785 {
1786 return;
1787 }
1788
1789 $this->umil_start('PERMISSION_ROLE_REMOVE', $role_name);
1790
1791 $sql = 'SELECT role_id FROM ' . ACL_ROLES_TABLE . '
1792 WHERE role_name = \'' . $this->db->sql_escape($role_name) . '\'';
1793 $this->db->sql_query($sql);
1794 $role_id = $this->db->sql_fetchfield('role_id');
1795
1796 if (!$role_id)
1797 {
1798 return $this->umil_end('ROLE_NOT_EXIST', $role_name);
1799 }
1800
1801 $sql = 'DELETE FROM ' . ACL_ROLES_DATA_TABLE . '
1802 WHERE role_id = ' . $role_id;
1803 $this->db->sql_query($sql);
1804
1805 $sql = 'DELETE FROM ' . ACL_ROLES_TABLE . '
1806 WHERE role_id = ' . $role_id;
1807 $this->db->sql_query($sql);
1808
1809 $auth->acl_clear_prefetch();
1810
1811 return $this->umil_end();
1812 }
1813
1814 /**
1815 * Permission Set
1816 *
1817 * Allows you to set permissions for a certain group/role
1818 *
1819 * @param string $name The name of the role/group
1820 * @param string|array $auth_option The auth_option or array of auth_options you would like to set
1821 * @param string $type The type (role|group)
1822 * @param bool $has_permission True if you want to give them permission, false if you want to deny them permission
1823 */
1824 function permission_set($name, $auth_option = array(), $type = 'role', $has_permission = true)
1825 {
1826 global $auth;
1827
1828 // Multicall
1829 if ($this->multicall(__FUNCTION__, $name))
1830 {
1831 return;
1832 }
1833
1834 if (!is_array($auth_option))
1835 {
1836 $auth_option = array($auth_option);
1837 }
1838
1839 $new_auth = array();
1840 $sql = 'SELECT auth_option_id FROM ' . ACL_OPTIONS_TABLE . '
1841 WHERE ' . $this->db->sql_in_set('auth_option', $auth_option);
1842 $result = $this->db->sql_query($sql);
1843 while ($row = $this->db->sql_fetchrow($result))
1844 {
1845 $new_auth[] = $row['auth_option_id'];
1846 }
1847 $this->db->sql_freeresult($result);
1848
1849 if (!sizeof($new_auth))
1850 {
1851 return false;
1852 }
1853
1854 $current_auth = array();
1855
1856 $type = (string) $type; // Prevent PHP bug.
1857
1858 switch ($type)
1859 {
1860 case 'role' :
1861 $this->umil_start('PERMISSION_SET_ROLE', $name);
1862
1863 $sql = 'SELECT role_id FROM ' . ACL_ROLES_TABLE . '
1864 WHERE role_name = \'' . $this->db->sql_escape($name) . '\'';
1865 $this->db->sql_query($sql);
1866 $role_id = $this->db->sql_fetchfield('role_id');
1867
1868 if (!$role_id)
1869 {
1870 return $this->umil_end('ROLE_NOT_EXIST');
1871 }
1872
1873 $sql = 'SELECT auth_option_id, auth_setting FROM ' . ACL_ROLES_DATA_TABLE . '
1874 WHERE role_id = ' . $role_id;
1875 $result = $this->db->sql_query($sql);
1876 while ($row = $this->db->sql_fetchrow($result))
1877 {
1878 $current_auth[$row['auth_option_id']] = $row['auth_setting'];
1879 }
1880 $this->db->sql_freeresult($result);
1881 break;
1882
1883 case 'group' :
1884 $sql = 'SELECT group_id FROM ' . GROUPS_TABLE . ' WHERE group_name = \'' . $this->db->sql_escape($name) . '\'';
1885 $this->db->sql_query($sql);
1886 $group_id = $this->db->sql_fetchfield('group_id');
1887
1888 if (!$group_id)
1889 {
1890 $this->umil_start('PERMISSION_SET_GROUP', $name);
1891 return $this->umil_end('GROUP_NOT_EXIST');
1892 }
1893
1894 // If the group has a role set for them we will add the requested permissions to that role.
1895 $sql = 'SELECT auth_role_id FROM ' . ACL_GROUPS_TABLE . '
1896 WHERE group_id = ' . $group_id . '
1897 AND auth_role_id <> 0
1898 AND forum_id = 0';
1899 $this->db->sql_query($sql);
1900 $role_id = $this->db->sql_fetchfield('auth_role_id');
1901 if ($role_id)
1902 {
1903 $sql = 'SELECT role_name FROM ' . ACL_ROLES_TABLE . '
1904 WHERE role_id = ' . $role_id;
1905 $this->db->sql_query($sql);
1906 $role_name = $this->db->sql_fetchfield('role_name');
1907
1908 return $this->permission_set($role_name, $auth_option, 'role', $has_permission);
1909 }
1910
1911 $this->umil_start('PERMISSION_SET_GROUP', $name);
1912
1913 $sql = 'SELECT auth_option_id, auth_setting FROM ' . ACL_GROUPS_TABLE . '
1914 WHERE group_id = ' . $group_id;
1915 $result = $this->db->sql_query($sql);
1916 while ($row = $this->db->sql_fetchrow($result))
1917 {
1918 $current_auth[$row['auth_option_id']] = $row['auth_setting'];
1919 }
1920 $this->db->sql_freeresult($result);
1921 break;
1922 }
1923
1924 $sql_ary = array();
1925 switch ($type)
1926 {
1927 case 'role' :
1928 foreach ($new_auth as $auth_option_id)
1929 {
1930 if (!isset($current_auth[$auth_option_id]))
1931 {
1932 $sql_ary[] = array(
1933 'role_id' => $role_id,
1934 'auth_option_id' => $auth_option_id,
1935 'auth_setting' => $has_permission,
1936 );
1937 }
1938 }
1939
1940 $this->db->sql_multi_insert(ACL_ROLES_DATA_TABLE, $sql_ary);
1941 break;
1942
1943 case 'group' :
1944 foreach ($new_auth as $auth_option_id)
1945 {
1946 if (!isset($current_auth[$auth_option_id]))
1947 {
1948 $sql_ary[] = array(
1949 'group_id' => $group_id,
1950 'auth_option_id' => $auth_option_id,
1951 'auth_setting' => $has_permission,
1952 );
1953 }
1954 }
1955
1956 $this->db->sql_multi_insert(ACL_GROUPS_TABLE, $sql_ary);
1957 break;
1958 }
1959
1960 $auth->acl_clear_prefetch();
1961
1962 return $this->umil_end();
1963 }
1964
1965 /**
1966 * Permission Unset
1967 *
1968 * Allows you to unset (remove) permissions for a certain group/role
1969 *
1970 * @param string $name The name of the role/group
1971 * @param string|array $auth_option The auth_option or array of auth_options you would like to set
1972 * @param string $type The type (role|group)
1973 */
1974 function permission_unset($name, $auth_option = array(), $type = 'role')
1975 {
1976 global $auth;
1977
1978 // Multicall
1979 if ($this->multicall(__FUNCTION__, $name))
1980 {
1981 return;
1982 }
1983
1984 if (!is_array($auth_option))
1985 {
1986 $auth_option = array($auth_option);
1987 }
1988
1989 $to_remove = array();
1990 $sql = 'SELECT auth_option_id FROM ' . ACL_OPTIONS_TABLE . '
1991 WHERE ' . $this->db->sql_in_set('auth_option', $auth_option);
1992 $result = $this->db->sql_query($sql);
1993 while ($row = $this->db->sql_fetchrow($result))
1994 {
1995 $to_remove[] = $row['auth_option_id'];
1996 }
1997 $this->db->sql_freeresult($result);
1998
1999 if (!sizeof($to_remove))
2000 {
2001 return false;
2002 }
2003
2004 $type = (string) $type; // Prevent PHP bug.
2005
2006 switch ($type)
2007 {
2008 case 'role' :
2009 $this->umil_start('PERMISSION_UNSET_ROLE', $name);
2010
2011 $sql = 'SELECT role_id FROM ' . ACL_ROLES_TABLE . '
2012 WHERE role_name = \'' . $this->db->sql_escape($name) . '\'';
2013 $this->db->sql_query($sql);
2014 $role_id = $this->db->sql_fetchfield('role_id');
2015
2016 if (!$role_id)
2017 {
2018 return $this->umil_end('ROLE_NOT_EXIST');
2019 }
2020
2021 $sql = 'DELETE FROM ' . ACL_ROLES_DATA_TABLE . '
2022 WHERE ' . $this->db->sql_in_set('auth_option_id', $to_remove);
2023 $this->db->sql_query($sql);
2024 break;
2025
2026 case 'group' :
2027 $sql = 'SELECT group_id FROM ' . GROUPS_TABLE . ' WHERE group_name = \'' . $this->db->sql_escape($name) . '\'';
2028 $this->db->sql_query($sql);
2029 $group_id = $this->db->sql_fetchfield('group_id');
2030
2031 if (!$group_id)
2032 {
2033 $this->umil_start('PERMISSION_UNSET_GROUP', $name);
2034 return $this->umil_end('GROUP_NOT_EXIST');
2035 }
2036
2037 // If the group has a role set for them we will remove the requested permissions from that role.
2038 $sql = 'SELECT auth_role_id FROM ' . ACL_GROUPS_TABLE . '
2039 WHERE group_id = ' . $group_id . '
2040 AND auth_role_id <> 0';
2041 $this->db->sql_query($sql);
2042 $role_id = $this->db->sql_fetchfield('auth_role_id');
2043 if ($role_id)
2044 {
2045 $sql = 'SELECT role_name FROM ' . ACL_ROLES_TABLE . '
2046 WHERE role_id = ' . $role_id;
2047 $this->db->sql_query($sql);
2048 $role_name = $this->db->sql_fetchfield('role_name');
2049
2050 return $this->permission_unset($role_name, $auth_option, 'role');
2051 }
2052
2053 $this->umil_start('PERMISSION_UNSET_GROUP', $name);
2054
2055 $sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . '
2056 WHERE ' . $this->db->sql_in_set('auth_option_id', $to_remove);
2057 $this->db->sql_query($sql);
2058 break;
2059 }
2060
2061 $auth->acl_clear_prefetch();
2062
2063 return $this->umil_end();
2064 }
2065
2066 /**
2067 * Table Exists
2068 *
2069 * Check if a table exists in the DB or not
2070 *
2071 * @param string $table_name The table name to check for
2072 *
2073 * @return bool true if the table exists, false if not
2074 */
2075 function table_exists($table_name)
2076 {
2077 $this->get_table_name($table_name);
2078
2079 // Use sql_table_exists if available
2080 if (method_exists($this->db_tools, 'sql_table_exists'))
2081 {
2082 $roe = $this->db->return_on_error;
2083 $result = $this->db_tools->sql_table_exists($table_name);
2084
2085 // db_tools::sql_table_exists resets the return_on_error to false always after completing, so we must make sure we set it to true again if it was before
2086 if ($roe)
2087 {
2088 $this->db->sql_return_on_error(true);
2089 }
2090
2091 return $result;
2092 }
2093
2094 if (!function_exists('get_tables'))
2095 {
2096 global $phpbb_root_path, $phpEx;
2097 include($phpbb_root_path . 'includes/functions_install.' . $phpEx);
2098 }
2099
2100 $tables = get_tables($this->db);
2101
2102 if (in_array($table_name, $tables))
2103 {
2104 return true;
2105 }
2106 else
2107 {
2108 return false;
2109 }
2110 }
2111
2112 /**
2113 * Table Add
2114 *
2115 * This only supports input from the array format of db_tools or create_schema_files.
2116 */
2117 function table_add($table_name, $table_data = array())
2118 {
2119 global $dbms, $user;
2120
2121 // Multicall
2122 if ($this->multicall(__FUNCTION__, $table_name))
2123 {
2124 return;
2125 }
2126
2127 /**
2128 * $table_data can be empty when uninstalling a mod and table_remove was used, but no 2rd argument was given.
2129 * In that case we'll assume that it was a column previously added by the mod (if not the author should specify a 2rd argument) and skip this to prevent an error
2130 */
2131 if (empty($table_data))
2132 {
2133 return;
2134 }
2135
2136 $this->get_table_name($table_name);
2137
2138 $this->umil_start('TABLE_ADD', $table_name);
2139
2140 if ($this->table_exists($table_name))
2141 {
2142 return $this->umil_end('TABLE_ALREADY_EXISTS', $table_name);
2143 }
2144
2145 if (!is_array($table_data))
2146 {
2147 return $this->umil_end('NO_TABLE_DATA');
2148 }
2149
2150 if (!function_exists('get_available_dbms'))
2151 {
2152 global $phpbb_root_path, $phpEx;
2153 include("{$phpbb_root_path}includes/functions_install.$phpEx");
2154 }
2155
2156 /*
2157 * This function has had numerous problems and is currently broken, so until phpBB uses it I will not be anymore
2158 if (method_exists($this->db_tools, 'sql_create_table'))
2159 {
2160 // Added in 3.0.5
2161 $this->db_tools->sql_create_table($table_name, $table_data);
2162 }
2163 else
2164 {*/
2165 $available_dbms = get_available_dbms($dbms);
2166
2167 $sql_query = $this->create_table_sql($table_name, $table_data);
2168 $sql_query = split_sql_file($sql_query, $available_dbms[$dbms]['DELIM']);
2169
2170 foreach ($sql_query as $sql)
2171 {
2172 $this->db->sql_query($sql);
2173 }
2174 //}
2175
2176 return $this->umil_end();
2177 }
2178
2179 /**
2180 * Table Remove
2181 *
2182 * Delete/Drop a DB table
2183 */
2184 function table_remove($table_name)
2185 {
2186 // Multicall
2187 if ($this->multicall(__FUNCTION__, $table_name))
2188 {
2189 return;
2190 }
2191
2192 $this->get_table_name($table_name);
2193
2194 $this->umil_start('TABLE_REMOVE', $table_name);
2195
2196 if (!$this->table_exists($table_name))
2197 {
2198 return $this->umil_end('TABLE_NOT_EXIST', $table_name);
2199 }
2200
2201 if (method_exists($this->db_tools, 'sql_table_drop'))
2202 {
2203 // Added in 3.0.5
2204 $this->db_tools->sql_table_drop($table_name);
2205 }
2206 else
2207 {
2208 $this->db->sql_query('DROP TABLE ' . $table_name);
2209 }
2210
2211 return $this->umil_end();
2212 }
2213
2214 /**
2215 * Table Column Exists
2216 *
2217 * Check to see if a column exists in a table
2218 */
2219 function table_column_exists($table_name, $column_name)
2220 {
2221 $this->get_table_name($table_name);
2222
2223 return $this->db_tools->sql_column_exists($table_name, $column_name);
2224 }
2225
2226 /**
2227 * Table Column Add
2228 *
2229 * Add a new column to a table.
2230 */
2231 function table_column_add($table_name, $column_name = '', $column_data = array())
2232 {
2233 // Multicall
2234 if ($this->multicall(__FUNCTION__, $table_name))
2235 {
2236 return;
2237 }
2238
2239 /**
2240 * $column_data can be empty when uninstalling a mod and table_column_remove was used, but no 3rd argument was given.
2241 * In that case we'll assume that it was a column previously added by the mod (if not the author should specify a 3rd argument) and skip this to prevent an error
2242 */
2243 if (empty($column_data))
2244 {
2245 return;
2246 }
2247
2248 $this->get_table_name($table_name);
2249
2250 $this->umil_start('TABLE_COLUMN_ADD', $table_name, $column_name);
2251
2252 if ($this->table_column_exists($table_name, $column_name))
2253 {
2254 return $this->umil_end('TABLE_COLUMN_ALREADY_EXISTS', $table_name, $column_name);
2255 }
2256
2257 $this->db_tools->sql_column_add($table_name, $column_name, $column_data);
2258
2259 return $this->umil_end();
2260 }
2261
2262 /**
2263 * Table Column Update
2264 *
2265 * Alter/Update a column in a table. You can not change a column name with this.
2266 */
2267 function table_column_update($table_name, $column_name = '', $column_data = array())
2268 {
2269 // Multicall
2270 if ($this->multicall(__FUNCTION__, $table_name))
2271 {
2272 return;
2273 }
2274
2275 $this->get_table_name($table_name);
2276
2277 $this->umil_start('TABLE_COLUMN_UPDATE', $table_name, $column_name);
2278
2279 if (!$this->table_column_exists($table_name, $column_name))
2280 {
2281 return $this->umil_end('TABLE_COLUMN_NOT_EXIST', $table_name, $column_name);
2282 }
2283
2284 $this->db_tools->sql_column_change($table_name, $column_name, $column_data);
2285
2286 return $this->umil_end();
2287 }
2288
2289 /**
2290 * Table Column Remove
2291 *
2292 * Remove a column from a table
2293 */
2294 function table_column_remove($table_name, $column_name = '')
2295 {
2296 // Multicall
2297 if ($this->multicall(__FUNCTION__, $table_name))
2298 {
2299 return;
2300 }
2301
2302 $this->get_table_name($table_name);
2303
2304 $this->umil_start('TABLE_COLUMN_REMOVE', $table_name, $column_name);
2305
2306 if (!$this->table_column_exists($table_name, $column_name))
2307 {
2308 return $this->umil_end('TABLE_COLUMN_NOT_EXIST', $table_name, $column_name);
2309 }
2310
2311 $this->db_tools->sql_column_remove($table_name, $column_name);
2312
2313 return $this->umil_end();
2314 }
2315
2316 /**
2317 * Table Index Exists
2318 *
2319 * Check if a table key/index exists on a table (can not check primary or unique)
2320 */
2321 function table_index_exists($table_name, $index_name)
2322 {
2323 $this->get_table_name($table_name);
2324
2325 $indexes = $this->db_tools->sql_list_index($table_name);
2326
2327 if (in_array($index_name, $indexes))
2328 {
2329 return true;
2330 }
2331
2332 return false;
2333 }
2334
2335 /**
2336 * Table Index Add
2337 *
2338 * Add a new key/index to a table
2339 */
2340 function table_index_add($table_name, $index_name = '', $column = array())
2341 {
2342 global $config;
2343
2344 // Multicall
2345 if ($this->multicall(__FUNCTION__, $table_name))
2346 {
2347 return;
2348 }
2349
2350 // Let them skip the column field and just use the index name in that case as the column as well
2351 if (empty($column))
2352 {
2353 $column = array($index_name);
2354 }
2355
2356 $this->get_table_name($table_name);
2357
2358 $this->umil_start('TABLE_KEY_ADD', $table_name, $index_name);
2359
2360 if ($this->table_index_exists($table_name, $index_name))
2361 {
2362 return $this->umil_end('TABLE_KEY_ALREADY_EXIST', $table_name, $index_name);
2363 }
2364
2365 if (!is_array($column))
2366 {
2367 $column = array($column);
2368 }
2369
2370 // remove index length if we are before 3.0.8
2371 // the feature (required for some types when using MySQL4)
2372 // was added in that release (ticket PHPBB3-8944)
2373 if (version_compare($config['version'], '3.0.7-pl1', '<='))
2374 {
2375 $column = preg_replace('#:.*$#', '', $column);
2376 }
2377
2378 $this->db_tools->sql_create_index($table_name, $index_name, $column);
2379
2380 return $this->umil_end();
2381 }
2382
2383 /**
2384 * Table Index Remove
2385 *
2386 * Remove a key/index from a table
2387 */
2388 function table_index_remove($table_name, $index_name = '')
2389 {
2390 // Multicall
2391 if ($this->multicall(__FUNCTION__, $table_name))
2392 {
2393 return;
2394 }
2395
2396 $this->get_table_name($table_name);
2397
2398 $this->umil_start('TABLE_KEY_REMOVE', $table_name, $index_name);
2399
2400 if (!$this->table_index_exists($table_name, $index_name))
2401 {
2402 return $this->umil_end('TABLE_KEY_NOT_EXIST', $table_name, $index_name);
2403 }
2404
2405 $this->db_tools->sql_index_drop($table_name, $index_name);
2406
2407 return $this->umil_end();
2408 }
2409
2410 // Ignore, function was renamed to table_row_insert and keeping for backwards compatibility
2411 function table_insert($table_name, $data = array()) { $this->table_row_insert($table_name, $data); }
2412
2413 /**
2414 * Table Insert
2415 *
2416 * Insert data into a table
2417 */
2418 function table_row_insert($table_name, $data = array())
2419 {
2420 // Multicall
2421 if ($this->multicall(__FUNCTION__, $table_name))
2422 {
2423 return;
2424 }
2425
2426 $this->get_table_name($table_name);
2427
2428 $this->umil_start('TABLE_ROW_INSERT_DATA', $table_name);
2429
2430 if (!$this->table_exists($table_name))
2431 {
2432 return $this->umil_end('TABLE_NOT_EXIST', $table_name);
2433 }
2434
2435 $this->db->sql_multi_insert($table_name, $data);
2436
2437 return $this->umil_end();
2438 }
2439
2440 /**
2441 * Table Row Update
2442 *
2443 * Update a row in a table
2444 *
2445 * $data should be an array with the column names as keys and values as the items to check for each column. Example:
2446 * array('user_id' => 123, 'user_name' => 'test user') would become:
2447 * WHERE user_id = 123 AND user_name = 'test user'
2448 *
2449 * $new_data is the new data it will be updated to (same format as you'd enter into $db->sql_build_array('UPDATE' ).
2450 */
2451 function table_row_update($table_name, $data = array(), $new_data = array())
2452 {
2453 // Multicall
2454 if ($this->multicall(__FUNCTION__, $table_name))
2455 {
2456 return;
2457 }
2458
2459 if (!sizeof($data))
2460 {
2461 return $this->umil_end('FAIL');
2462 }
2463
2464 $this->get_table_name($table_name);
2465
2466 $this->umil_start('TABLE_ROW_UPDATE_DATA', $table_name);
2467
2468 if (!$this->table_exists($table_name))
2469 {
2470 return $this->umil_end('TABLE_NOT_EXIST', $table_name);
2471 }
2472
2473 $sql = 'UPDATE ' . $table_name . '
2474 SET ' . $this->db->sql_build_array('UPDATE', $new_data) . '
2475 WHERE ' . $this->db->sql_build_array('SELECT', $data);
2476 $this->db->sql_query($sql);
2477
2478 return $this->umil_end();
2479 }
2480
2481 /**
2482 * Table Row Remove
2483 *
2484 * Remove a row from a table
2485 *
2486 * $data should be an array with the column names as keys and values as the items to check for each column. Example:
2487 * array('user_id' => 123, 'user_name' => 'test user') would become:
2488 * WHERE user_id = 123 AND user_name = 'test user'
2489 */
2490 function table_row_remove($table_name, $data = array())
2491 {
2492 // Multicall
2493 if ($this->multicall(__FUNCTION__, $table_name))
2494 {
2495 return;
2496 }
2497
2498 if (!sizeof($data))
2499 {
2500 return $this->umil_end('FAIL');
2501 }
2502
2503 $this->get_table_name($table_name);
2504
2505 $this->umil_start('TABLE_ROW_REMOVE_DATA', $table_name);
2506
2507 if (!$this->table_exists($table_name))
2508 {
2509 return $this->umil_end('TABLE_NOT_EXIST', $table_name);
2510 }
2511
2512 $sql = 'DELETE FROM ' . $table_name . ' WHERE ' . $this->db->sql_build_array('SELECT', $data);
2513 $this->db->sql_query($sql);
2514
2515 return $this->umil_end();
2516 }
2517
2518 /**
2519 * Version Checker
2520 *
2521 * Format the file like the following:
2522 * http://www.phpbb.com/updatecheck/30x.txt
2523 *
2524 * @param string $url The url to access (ex: www.phpbb.com)
2525 * @param string $path The path to access (ex: /updatecheck)
2526 * @param string $file The name of the file to access (ex: 30x.txt)
2527 *
2528 * @return array|string Error Message if there was any error, or an array (each line in the file as a value)
2529 */
2530 function version_check($url, $path, $file, $timeout = 10, $port = 80)
2531 {
2532 if (!function_exists('get_remote_file'))
2533 {
2534 global $phpbb_root_path, $phpEx;
2535
2536 include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
2537 }
2538
2539 $errstr = $errno = '';
2540
2541 $info = get_remote_file($url, $path, $file, $errstr, $errno, $port, $timeout);
2542
2543 if ($info === false)
2544 {
2545 return $errstr . ' [ ' . $errno . ' ]';
2546 }
2547
2548 $info = str_replace("\r\n", "\n", $info);
2549 $info = explode("\n", $info);
2550
2551 return $info;
2552 }
2553
2554 /**
2555 * Create table SQL
2556 *
2557 * Create the SQL query for the specified DBMS on the fly from a create_schema_files type of table array
2558 *
2559 * @param string $table_name The name of the table
2560 * @param array $table_data The table data (formatted in the array format used by create_schema_files)
2561 * @param string $dbms The dbms this will be built for (for testing only, leave blank to use the current DBMS)
2562 *
2563 * @return The sql query to run for the submitted dbms to insert the table
2564 */
2565 function create_table_sql($table_name, $table_data, $dbms = '')
2566 {
2567 // To allow testing
2568 $dbms = ($dbms) ? $dbms : $this->db_tools->sql_layer;
2569
2570 // A list of types being unsigned for better reference in some db's
2571 $unsigned_types = array('UINT', 'UINT:', 'USINT', 'BOOL', 'TIMESTAMP');
2572 $supported_dbms = array('firebird', 'mssql', 'mssqlnative', 'mysql_40', 'mysql_41', 'oracle', 'postgres', 'sqlite');
2573
2574 $sql = '';
2575
2576 // Create Table statement
2577 $generator = $textimage = false;
2578
2579 switch ($dbms)
2580 {
2581 case 'mysql_40':
2582 case 'mysql_41':
2583 case 'firebird':
2584 case 'oracle':
2585 case 'sqlite':
2586 case 'postgres':
2587 $sql .= "CREATE TABLE {$table_name} (\n";
2588 break;
2589
2590 case 'mssql':
2591 case 'mssqlnative':
2592 $sql .= "CREATE TABLE [{$table_name}] (\n";
2593 break;
2594 }
2595
2596 // Table specific so we don't get overlap
2597 $modded_array = array();
2598
2599 // Write columns one by one...
2600 foreach ($table_data['COLUMNS'] as $column_name => $column_data)
2601 {
2602 // Get type
2603 if (strpos($column_data[0], ':') !== false)
2604 {
2605 list($orig_column_type, $column_length) = explode(':', $column_data[0]);
2606 if (!is_array($this->db_tools->dbms_type_map[$dbms][$orig_column_type . ':']))
2607 {
2608 $column_type = sprintf($this->db_tools->dbms_type_map[$dbms][$orig_column_type . ':'], $column_length);
2609 }
2610 else
2611 {
2612 if (isset($this->db_tools->dbms_type_map[$dbms][$orig_column_type . ':']['rule']))
2613 {
2614 switch ($this->db_tools->dbms_type_map[$dbms][$orig_column_type . ':']['rule'][0])
2615 {
2616 case 'div':
2617 $column_length /= $this->db_tools->dbms_type_map[$dbms][$orig_column_type . ':']['rule'][1];
2618 $column_length = ceil($column_length);
2619 $column_type = sprintf($this->db_tools->dbms_type_map[$dbms][$orig_column_type . ':'][0], $column_length);
2620 break;
2621 }
2622 }
2623
2624 if (isset($this->db_tools->dbms_type_map[$dbms][$orig_column_type . ':']['limit']))
2625 {
2626 switch ($this->db_tools->dbms_type_map[$dbms][$orig_column_type . ':']['limit'][0])
2627 {
2628 case 'mult':
2629 $column_length *= $this->db_tools->dbms_type_map[$dbms][$orig_column_type . ':']['limit'][1];
2630 if ($column_length > $this->db_tools->dbms_type_map[$dbms][$orig_column_type . ':']['limit'][2])
2631 {
2632 $column_type = $this->db_tools->dbms_type_map[$dbms][$orig_column_type . ':']['limit'][3];
2633 $modded_array[$column_name] = $column_type;
2634 }
2635 else
2636 {
2637 $column_type = sprintf($this->db_tools->dbms_type_map[$dbms][$orig_column_type . ':'][0], $column_length);
2638 }
2639 break;
2640 }
2641 }
2642 }
2643 $orig_column_type .= ':';
2644 }
2645 else
2646 {
2647 $orig_column_type = $column_data[0];
2648 $column_type = $this->db_tools->dbms_type_map[$dbms][$column_data[0]];
2649 if ($column_type == 'text' || $column_type == 'blob')
2650 {
2651 $modded_array[$column_name] = $column_type;
2652 }
2653 }
2654
2655 // Adjust default value if db-dependant specified
2656 if (is_array($column_data[1]))
2657 {
2658 $column_data[1] = (isset($column_data[1][$dbms])) ? $column_data[1][$dbms] : $column_data[1]['default'];
2659 }
2660
2661 switch ($dbms)
2662 {
2663 case 'mysql_40':
2664 case 'mysql_41':
2665 $sql .= "\t{$column_name} {$column_type} ";
2666
2667 // For hexadecimal values do not use single quotes
2668 if (!is_null($column_data[1]) && substr($column_type, -4) !== 'text' && substr($column_type, -4) !== 'blob')
2669 {
2670 $sql .= (strpos($column_data[1], '0x') === 0) ? "DEFAULT {$column_data[1]} " : "DEFAULT '{$column_data[1]}' ";
2671 }
2672 $sql .= 'NOT NULL';
2673
2674 if (isset($column_data[2]))
2675 {
2676 if ($column_data[2] == 'auto_increment')
2677 {
2678 $sql .= ' auto_increment';
2679 }
2680 else if ($dbms === 'mysql_41' && $column_data[2] == 'true_sort')
2681 {
2682 $sql .= ' COLLATE utf8_unicode_ci';
2683 }
2684 }
2685
2686 $sql .= ",\n";
2687 break;
2688
2689 case 'sqlite':
2690 if (isset($column_data[2]) && $column_data[2] == 'auto_increment')
2691 {
2692 $sql .= "\t{$column_name} INTEGER PRIMARY KEY ";
2693 $generator = $column_name;
2694 }
2695 else
2696 {
2697 $sql .= "\t{$column_name} {$column_type} ";
2698 }
2699
2700 $sql .= 'NOT NULL ';
2701 $sql .= (!is_null($column_data[1])) ? "DEFAULT '{$column_data[1]}'" : '';
2702 $sql .= ",\n";
2703 break;
2704
2705 case 'firebird':
2706 $sql .= "\t{$column_name} {$column_type} ";
2707
2708 if (!is_null($column_data[1]))
2709 {
2710 $sql .= 'DEFAULT ' . ((is_numeric($column_data[1])) ? $column_data[1] : "'{$column_data[1]}'") . ' ';
2711 }
2712
2713 $sql .= 'NOT NULL';
2714
2715 // This is a UNICODE column and thus should be given it's fair share
2716 if (preg_match('/^X?STEXT_UNI|VCHAR_(CI|UNI:?)/', $column_data[0]))
2717 {
2718 $sql .= ' COLLATE UNICODE';
2719 }
2720
2721 $sql .= ",\n";
2722
2723 if (isset($column_data[2]) && $column_data[2] == 'auto_increment')
2724 {
2725 $generator = $column_name;
2726 }
2727 break;
2728
2729 case 'mssql':
2730 case 'mssqlnative':
2731 if ($column_type == '[text]')
2732 {
2733 $textimage = true;
2734 }
2735
2736 $sql .= "\t[{$column_name}] {$column_type} ";
2737
2738 if (!is_null($column_data[1]))
2739 {
2740 // For hexadecimal values do not use single quotes
2741 if (strpos($column_data[1], '0x') === 0)
2742 {
2743 $sql .= 'DEFAULT (' . $column_data[1] . ') ';
2744 }
2745 else
2746 {
2747 $sql .= 'DEFAULT (' . ((is_numeric($column_data[1])) ? $column_data[1] : "'{$column_data[1]}'") . ') ';
2748 }
2749 }
2750
2751 if (isset($column_data[2]) && $column_data[2] == 'auto_increment')
2752 {
2753 $sql .= 'IDENTITY (1, 1) ';
2754 }
2755
2756 $sql .= 'NOT NULL';
2757 $sql .= " ,\n";
2758 break;
2759
2760 case 'oracle':
2761 $sql .= "\t{$column_name} {$column_type} ";
2762 $sql .= (!is_null($column_data[1])) ? "DEFAULT '{$column_data[1]}' " : '';
2763
2764 // In Oracle empty strings ('') are treated as NULL.
2765 // Therefore in oracle we allow NULL's for all DEFAULT '' entries
2766 $sql .= ($column_data[1] === '') ? ",\n" : "NOT NULL,\n";
2767
2768 if (isset($column_data[2]) && $column_data[2] == 'auto_increment')
2769 {
2770 $generator = $column_name;
2771 }
2772 break;
2773
2774 case 'postgres':
2775 $sql .= "\t{$column_name} {$column_type} ";
2776
2777 if (isset($column_data[2]) && $column_data[2] == 'auto_increment')
2778 {
2779 $sql .= "DEFAULT nextval('{$table_name}_seq'),\n";
2780
2781 // Make sure the sequence will be created before creating the table
2782 $sql = "CREATE SEQUENCE {$table_name}_seq;\n\n" . $sql;
2783 }
2784 else
2785 {
2786 $sql .= (!is_null($column_data[1])) ? "DEFAULT '{$column_data[1]}' " : '';
2787 $sql .= "NOT NULL";
2788
2789 // Unsigned? Then add a CHECK contraint
2790 if (in_array($orig_column_type, $unsigned_types))
2791 {
2792 $sql .= " CHECK ({$column_name} >= 0)";
2793 }
2794
2795 $sql .= ",\n";
2796 }
2797 break;
2798 }
2799 }
2800
2801 switch ($dbms)
2802 {
2803 case 'firebird':
2804 // Remove last line delimiter...
2805 $sql = substr($sql, 0, -2);
2806 $sql .= "\n);;\n\n";
2807 break;
2808
2809 case 'mssql':
2810 case 'mssqlnative':
2811 $sql = substr($sql, 0, -2);
2812 $sql .= "\n) ON [PRIMARY]" . (($textimage) ? ' TEXTIMAGE_ON [PRIMARY]' : '') . "\n";
2813 $sql .= "GO\n\n";
2814 break;
2815 }
2816
2817 // Write primary key
2818 if (isset($table_data['PRIMARY_KEY']))
2819 {
2820 if (!is_array($table_data['PRIMARY_KEY']))
2821 {
2822 $table_data['PRIMARY_KEY'] = array($table_data['PRIMARY_KEY']);
2823 }
2824
2825 switch ($dbms)
2826 {
2827 case 'mysql_40':
2828 case 'mysql_41':
2829 case 'postgres':
2830 $sql .= "\tPRIMARY KEY (" . implode(', ', $table_data['PRIMARY_KEY']) . "),\n";
2831 break;
2832
2833 case 'firebird':
2834 $sql .= "ALTER TABLE {$table_name} ADD PRIMARY KEY (" . implode(', ', $table_data['PRIMARY_KEY']) . ");;\n\n";
2835 break;
2836
2837 case 'sqlite':
2838 if ($generator === false || !in_array($generator, $table_data['PRIMARY_KEY']))
2839 {
2840 $sql .= "\tPRIMARY KEY (" . implode(', ', $table_data['PRIMARY_KEY']) . "),\n";
2841 }
2842 break;
2843
2844 case 'mssql':
2845 case 'mssqlnative':
2846 $sql .= "ALTER TABLE [{$table_name}] WITH NOCHECK ADD \n";
2847 $sql .= "\tCONSTRAINT [PK_{$table_name}] PRIMARY KEY CLUSTERED \n";
2848 $sql .= "\t(\n";
2849 $sql .= "\t\t[" . implode("],\n\t\t[", $table_data['PRIMARY_KEY']) . "]\n";
2850 $sql .= "\t) ON [PRIMARY] \n";
2851 $sql .= "GO\n\n";
2852 break;
2853
2854 case 'oracle':
2855 $sql .= "\tCONSTRAINT pk_{$table_name} PRIMARY KEY (" . implode(', ', $table_data['PRIMARY_KEY']) . "),\n";
2856 break;
2857 }
2858 }
2859
2860 switch ($dbms)
2861 {
2862 case 'oracle':
2863 // UNIQUE contrains to be added?
2864 if (isset($table_data['KEYS']))
2865 {
2866 foreach ($table_data['KEYS'] as $key_name => $key_data)
2867 {
2868 if (!is_array($key_data[1]))
2869 {
2870 $key_data[1] = array($key_data[1]);
2871 }
2872
2873 if ($key_data[0] == 'UNIQUE')
2874 {
2875 $sql .= "\tCONSTRAINT u_phpbb_{$key_name} UNIQUE (" . implode(', ', $key_data[1]) . "),\n";
2876 }
2877 }
2878 }
2879
2880 // Remove last line delimiter...
2881 $sql = substr($sql, 0, -2);
2882 $sql .= "\n)\n/\n\n";
2883 break;
2884
2885 case 'postgres':
2886 // Remove last line delimiter...
2887 $sql = substr($sql, 0, -2);
2888 $sql .= "\n);\n\n";
2889 break;
2890
2891 case 'sqlite':
2892 // Remove last line delimiter...
2893 $sql = substr($sql, 0, -2);
2894 $sql .= "\n);\n\n";
2895 break;
2896 }
2897
2898 // Write Keys
2899 if (isset($table_data['KEYS']))
2900 {
2901 foreach ($table_data['KEYS'] as $key_name => $key_data)
2902 {
2903 if (!is_array($key_data[1]))
2904 {
2905 $key_data[1] = array($key_data[1]);
2906 }
2907
2908 switch ($dbms)
2909 {
2910 case 'mysql_40':
2911 case 'mysql_41':
2912 $sql .= ($key_data[0] == 'INDEX') ? "\tKEY" : '';
2913 $sql .= ($key_data[0] == 'UNIQUE') ? "\tUNIQUE" : '';
2914 foreach ($key_data[1] as $key => $col_name)
2915 {
2916 if (isset($modded_array[$col_name]))
2917 {
2918 switch ($modded_array[$col_name])
2919 {
2920 case 'text':
2921 case 'blob':
2922 $key_data[1][$key] = $col_name . '(255)';
2923 break;
2924 }
2925 }
2926 }
2927 $sql .= ' ' . $key_name . ' (' . implode(', ', $key_data[1]) . "),\n";
2928 break;
2929
2930 case 'firebird':
2931 $sql .= ($key_data[0] == 'INDEX') ? 'CREATE INDEX' : '';
2932 $sql .= ($key_data[0] == 'UNIQUE') ? 'CREATE UNIQUE INDEX' : '';
2933
2934 $sql .= ' ' . $table_name . '_' . $key_name . ' ON ' . $table_name . '(' . implode(', ', $key_data[1]) . ");;\n";
2935 break;
2936
2937 case 'mssql':
2938 case 'mssqlnative':
2939 $sql .= ($key_data[0] == 'INDEX') ? 'CREATE INDEX' : '';
2940 $sql .= ($key_data[0] == 'UNIQUE') ? 'CREATE UNIQUE INDEX' : '';
2941 $sql .= " [{$key_name}] ON [{$table_name}]([" . implode('], [', $key_data[1]) . "]) ON [PRIMARY]\n";
2942 $sql .= "GO\n\n";
2943 break;
2944
2945 case 'oracle':
2946 if ($key_data[0] == 'UNIQUE')
2947 {
2948 continue;
2949 }
2950
2951 $sql .= ($key_data[0] == 'INDEX') ? 'CREATE INDEX' : '';
2952
2953 $sql .= " {$table_name}_{$key_name} ON {$table_name} (" . implode(', ', $key_data[1]) . ")\n";
2954 $sql .= "/\n";
2955 break;
2956
2957 case 'sqlite':
2958 $sql .= ($key_data[0] == 'INDEX') ? 'CREATE INDEX' : '';
2959 $sql .= ($key_data[0] == 'UNIQUE') ? 'CREATE UNIQUE INDEX' : '';
2960
2961 $sql .= " {$table_name}_{$key_name} ON {$table_name} (" . implode(', ', $key_data[1]) . ");\n";
2962 break;
2963
2964 case 'postgres':
2965 $sql .= ($key_data[0] == 'INDEX') ? 'CREATE INDEX' : '';
2966 $sql .= ($key_data[0] == 'UNIQUE') ? 'CREATE UNIQUE INDEX' : '';
2967
2968 $sql .= " {$table_name}_{$key_name} ON {$table_name} (" . implode(', ', $key_data[1]) . ");\n";
2969 break;
2970 }
2971 }
2972 }
2973
2974 switch ($dbms)
2975 {
2976 case 'mysql_40':
2977 // Remove last line delimiter...
2978 $sql = substr($sql, 0, -2);
2979 $sql .= "\n);\n\n";
2980 break;
2981
2982 case 'mysql_41':
2983 // Remove last line delimiter...
2984 $sql = substr($sql, 0, -2);
2985 $sql .= "\n) CHARACTER SET utf8 COLLATE utf8_bin;\n\n";
2986 break;
2987
2988 // Create Generator
2989 case 'firebird':
2990 if ($generator !== false)
2991 {
2992 $sql .= "\nCREATE GENERATOR {$table_name}_gen;;\n";
2993 $sql .= 'SET GENERATOR ' . $table_name . "_gen TO 0;;\n\n";
2994
2995 $sql .= 'CREATE TRIGGER t_' . $table_name . ' FOR ' . $table_name . "\n";
2996 $sql .= "BEFORE INSERT\nAS\nBEGIN\n";
2997 $sql .= "\tNEW.{$generator} = GEN_ID({$table_name}_gen, 1);\nEND;;\n\n";
2998 }
2999 break;
3000
3001 case 'oracle':
3002 if ($generator !== false)
3003 {
3004 $sql .= "\nCREATE SEQUENCE {$table_name}_seq\n/\n\n";
3005
3006 $sql .= "CREATE OR REPLACE TRIGGER t_{$table_name}\n";
3007 $sql .= "BEFORE INSERT ON {$table_name}\n";
3008 $sql .= "FOR EACH ROW WHEN (\n";
3009 $sql .= "\tnew.{$generator} IS NULL OR new.{$generator} = 0\n";
3010 $sql .= ")\nBEGIN\n";
3011 $sql .= "\tSELECT {$table_name}_seq.nextval\n";
3012 $sql .= "\tINTO :new.{$generator}\n";
3013 $sql .= "\tFROM dual;\nEND;\n/\n\n";
3014 }
3015 break;
3016 }
3017
3018 return $sql;
3019 }
3020
3021 /**
3022 * Get the real table name
3023 * By A_Jelly_Doughnut
3024 *
3025 * @param string $table_name The table name to get the real table name from
3026 */
3027 function get_table_name(&$table_name)
3028 {
3029 // Use the global table prefix if a custom one is not specified
3030 if ($this->table_prefix === false)
3031 {
3032 global $table_prefix;
3033 }
3034 else
3035 {
3036 $table_prefix = $this->table_prefix;
3037 }
3038
3039 static $constants = NULL;
3040
3041 if (is_null($constants))
3042 {
3043 $constants = get_defined_constants();
3044 }
3045
3046 /**
3047 * only do the replace if the table prefix is not already present
3048 * this is required since UMIL supports specifying a table via phpbb_foo
3049 * (where a replace would be needed)
3050 * or by FOO_TABLE (where a replace is already done at constant-define time)
3051 */
3052 if (!preg_match('#^' . preg_quote($table_prefix, '#') . '#', $table_name) || !in_array($table_name, $constants, true))
3053 {
3054 if ((strpos($table_name, $table_prefix) === 0) && (strlen($table_name) > strlen($table_prefix)))
3055 {
3056 /**
3057 * Do not replace phpbb_ with the prefix, if it is already at the beginning.
3058 * Otherwise we would replace the prefix "phpbb_umil" multiple times and
3059 * end up with phpbb_umilumilumil_tablename, if the constant is not defined.
3060 * See Bug #62646.
3061 */
3062 }
3063 else
3064 {
3065 $table_name = preg_replace('#^phpbb_#i', $table_prefix, $table_name);
3066 }
3067 }
3068 }
3069}
3070
3071?>