PageRenderTime 63ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/umil/umil.php

https://code.google.com/p/phpbbex/
PHP | 2353 lines | 1689 code | 259 blank | 405 comment | 180 complexity | 67f8ba241deb2a5c366b716fcc83f0fd MD5 | raw file
Possible License(s): AGPL-1.0
  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. * @version $Id$
  8. * @copyright (c) 2008 phpBB Group
  9. * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
  10. *
  11. */
  12. /**
  13. * @ignore
  14. */
  15. if (!defined('IN_PHPBB'))
  16. {
  17. exit;
  18. }
  19. define('UMIL_VERSION', '1.0.4');
  20. /**
  21. * Multicall instructions
  22. *
  23. * 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.
  24. *
  25. * 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.
  26. *
  27. * Example:
  28. * $umil->config_add(array(
  29. * array('config_name', 'config_value'),
  30. * array('config_name1', 'config_value1'),
  31. * array('config_name2', 'config_value2', true),
  32. * array('config_name3', 'config_value3', true),
  33. * );
  34. */
  35. /**
  36. * UMIL - Unified MOD Installation Library class
  37. *
  38. * Cache Functions
  39. * cache_purge($type = '', $style_id = 0)
  40. *
  41. * Config Functions:
  42. * config_exists($config_name, $return_result = false)
  43. * config_add($config_name, $config_value = '', $is_dynamic = false)
  44. * config_update($config_name, $config_value, $is_dynamic = false)
  45. * config_remove($config_name)
  46. *
  47. * Module Functions
  48. * module_exists($class, $parent, $module)
  49. * module_add($class, $parent = 0, $data = array())
  50. * module_remove($class, $parent = 0, $module = '')
  51. *
  52. * Permissions/Auth Functions
  53. * permission_exists($auth_option, $global = true)
  54. * permission_add($auth_option, $global = true)
  55. * permission_remove($auth_option, $global = true)
  56. * permission_set($name, $auth_option = array(), $type = 'role', $global = true, $has_permission = true)
  57. * permission_unset($name, $auth_option = array(), $type = 'role', $global = true)
  58. *
  59. * Table Functions
  60. * table_exists($table_name)
  61. * table_add($table_name, $table_data = array())
  62. * table_remove($table_name)
  63. *
  64. * Table Column Functions
  65. * table_column_exists($table_name, $column_name)
  66. * table_column_add($table_name, $column_name = '', $column_data = array())
  67. * table_column_update($table_name, $column_name = '', $column_data = array())
  68. * table_column_remove($table_name, $column_name = '')
  69. *
  70. * Table Key/Index Functions
  71. * table_index_exists($table_name, $index_name)
  72. * table_index_add($table_name, $index_name = '', $column = array())
  73. * table_index_remove($table_name, $index_name = '')
  74. *
  75. * Table Row Functions (note that these actions are not reversed automatically during uninstallation)
  76. * table_row_insert($table_name, $data = array())
  77. * table_row_remove($table_name, $data = array())
  78. * table_row_update($table_name, $data = array(), $new_data = array())
  79. *
  80. * Version Check Function
  81. * version_check($url, $path, $file)
  82. */
  83. class umil
  84. {
  85. /**
  86. * This will hold the text output for the inputted command (if the mod author would like to display the command that was ran)
  87. *
  88. * @var string
  89. */
  90. var $command = '';
  91. /**
  92. * This will hold the text output for the result of the command. $user->lang['SUCCESS'] if everything worked.
  93. *
  94. * @var string
  95. */
  96. var $result = '';
  97. /**
  98. * Auto run $this->display_results after running a command
  99. */
  100. var $auto_display_results = false;
  101. /**
  102. * Stand Alone option (this makes it possible to just use the single umil file and not worry about any language stuff
  103. */
  104. var $stand_alone = false;
  105. /**
  106. * Were any new permissions added (used in umil_frontend)?
  107. */
  108. var $permissions_added = false;
  109. /**
  110. * Database Object
  111. */
  112. var $db = false;
  113. /**
  114. * Database Tools Object
  115. */
  116. var $db_tools = false;
  117. /**
  118. * Do we want a custom prefix besides the phpBB table prefix? You *probably* should not change this...
  119. */
  120. var $table_prefix = false;
  121. /**
  122. * Constructor
  123. */
  124. function umil($stand_alone = false, $db = false)
  125. {
  126. // Setup $this->db
  127. if ($db !== false)
  128. {
  129. if (!is_object($db) || !method_exists($db, 'sql_query'))
  130. {
  131. trigger_error('Invalid $db Object');
  132. }
  133. $this->db = $db;
  134. }
  135. else
  136. {
  137. global $db;
  138. $this->db = $db;
  139. }
  140. // Setup $this->db_tools
  141. if (!class_exists('phpbb_db_tools'))
  142. {
  143. global $phpbb_root_path, $phpEx;
  144. include($phpbb_root_path . 'includes/db/db_tools.' . $phpEx);
  145. }
  146. $this->db_tools = new phpbb_db_tools($this->db);
  147. $this->stand_alone = $stand_alone;
  148. if (!$stand_alone)
  149. {
  150. global $config, $user, $phpbb_root_path, $phpEx;
  151. /* 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.
  152. if (method_exists('user', 'set_custom_lang_path'))
  153. {
  154. $user->set_custom_lang_path($phpbb_root_path . 'umil/language/');
  155. $user->add_lang('umil');
  156. $user->set_custom_lang_path($phpbb_root_path . 'language/');
  157. }
  158. else
  159. {*/
  160. // 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.
  161. if (isset($user->data['user_lang']) && file_exists("{$phpbb_root_path}umil/language/{$user->data['user_lang']}/umil.$phpEx"))
  162. {
  163. $path = $user->data['user_lang'];
  164. }
  165. else if (file_exists("{$phpbb_root_path}umil/language/" . basename($config['default_lang']) . "/umil.$phpEx"))
  166. {
  167. $path = basename($config['default_lang']);
  168. }
  169. else if (file_exists("{$phpbb_root_path}umil/language/en/umil.$phpEx"))
  170. {
  171. $path = 'en';
  172. }
  173. else
  174. {
  175. 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);
  176. }
  177. $user->add_lang('./../../umil/language/' . $path . '/umil');
  178. //}
  179. $user->add_lang(array('acp/common', 'acp/permissions'));
  180. // Check to see if a newer version is available.
  181. $info = $this->version_check('version.phpbb.com', '/umil', ((defined('PHPBB_QA')) ? 'umil_qa.txt' : 'umil.txt'));
  182. if (is_array($info) && isset($info[0]) && isset($info[1]))
  183. {
  184. if (version_compare(UMIL_VERSION, $info[0], '<'))
  185. {
  186. global $template;
  187. // Make sure user->setup() has been called
  188. if (empty($user->lang))
  189. {
  190. $user->setup();
  191. }
  192. page_header('', false);
  193. $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>';
  194. $template->assign_vars(array(
  195. 'S_BOARD_DISABLED' => true,
  196. 'L_BOARD_DISABLED' => sprintf($user->lang['UPDATE_UMIL'], $info[1]),
  197. ));
  198. }
  199. }
  200. }
  201. }
  202. /**
  203. * umil_start
  204. *
  205. * A function which runs (almost) every time a function here is ran
  206. */
  207. function umil_start()
  208. {
  209. global $user;
  210. // Set up the command. This will get the arguments sent to the function.
  211. $args = func_get_args();
  212. $this->command = call_user_func_array(array($this, 'get_output_text'), $args);
  213. $this->result = (isset($user->lang['SUCCESS'])) ? $user->lang['SUCCESS'] : 'SUCCESS';
  214. $this->db->sql_return_on_error(true);
  215. //$this->db->sql_transaction('begin');
  216. }
  217. /**
  218. * umil_end
  219. *
  220. * A function which runs (almost) every time a function here is ran
  221. */
  222. function umil_end()
  223. {
  224. global $user;
  225. // Set up the result. This will get the arguments sent to the function.
  226. $args = func_get_args();
  227. $result = call_user_func_array(array($this, 'get_output_text'), $args);
  228. $this->result = ($result) ? $result : $this->result;
  229. if ($this->db->sql_error_triggered)
  230. {
  231. if ($this->result == ((isset($user->lang['SUCCESS'])) ? $user->lang['SUCCESS'] : 'SUCCESS'))
  232. {
  233. $this->result = 'SQL ERROR ' . $this->db->sql_error_returned['message'];
  234. }
  235. else
  236. {
  237. $this->result .= '<br /><br />SQL ERROR ' . $this->db->sql_error_returned['message'];
  238. }
  239. //$this->db->sql_transaction('rollback');
  240. }
  241. else
  242. {
  243. //$this->db->sql_transaction('commit');
  244. }
  245. $this->db->sql_return_on_error(false);
  246. // Auto output if requested.
  247. if ($this->auto_display_results && method_exists($this, 'display_results'))
  248. {
  249. $this->display_results();
  250. }
  251. return '<strong>' . $this->command . '</strong><br />' . $this->result;
  252. }
  253. /**
  254. * Get text for output
  255. *
  256. * Takes the given arguments and prepares them for the UI
  257. *
  258. * First argument sent is used as the language key
  259. * Further arguments (if send) are used on the language key through vsprintf()
  260. *
  261. * @return string Returns the prepared string for output
  262. */
  263. function get_output_text()
  264. {
  265. global $user;
  266. // Set up the command. This will get the arguments sent to the function.
  267. $args = func_get_args();
  268. if (sizeof($args))
  269. {
  270. $lang_key = array_shift($args);
  271. if (sizeof($args))
  272. {
  273. $lang_args = array();
  274. foreach ($args as $arg)
  275. {
  276. $lang_args[] = (isset($user->lang[$arg])) ? $user->lang[$arg] : $arg;
  277. }
  278. return @vsprintf(((isset($user->lang[$lang_key])) ? $user->lang[$lang_key] : $lang_key), $lang_args);
  279. }
  280. else
  281. {
  282. return ((isset($user->lang[$lang_key])) ? $user->lang[$lang_key] : $lang_key);
  283. }
  284. }
  285. return '';
  286. }
  287. /**
  288. * Run Actions
  289. *
  290. * Do-It-All function that can do everything required for installing/updating/uninstalling a mod based on an array of actions and the versions.
  291. *
  292. * @param string $action The action. install|update|uninstall
  293. * @param array $versions The array of versions and the actions for each
  294. * @param string $version_config_name The name of the config setting which holds/will hold the currently installed version
  295. * @param string $version_select Added for the UMIL Auto system to allow you to select the version you want to install/update/uninstall to.
  296. */
  297. function run_actions($action, $versions, $version_config_name, $version_select = '')
  298. {
  299. // We will sort the actions to prevent issues from mod authors incorrectly listing the version numbers
  300. uksort($versions, 'version_compare');
  301. // Find the current version to install
  302. $current_version = '0.0.0';
  303. foreach ($versions as $version => $actions)
  304. {
  305. $current_version = $version;
  306. }
  307. $db_version = '';
  308. if ($this->config_exists($version_config_name))
  309. {
  310. global $config;
  311. $db_version = $config[$version_config_name];
  312. }
  313. // Set the action to install from update if nothing is currently installed
  314. if ($action == 'update' && !$db_version)
  315. {
  316. $action = 'install';
  317. }
  318. if ($action == 'install' || $action == 'update')
  319. {
  320. $version_installed = $db_version;
  321. foreach ($versions as $version => $version_actions)
  322. {
  323. // If we are updating
  324. if ($db_version && version_compare($version, $db_version, '<='))
  325. {
  326. continue;
  327. }
  328. if ($version_select && version_compare($version, $version_select, '>'))
  329. {
  330. break;
  331. }
  332. foreach ($version_actions as $method => $params)
  333. {
  334. if ($method == 'custom')
  335. {
  336. $this->_call_custom_function($params, $action, $version);
  337. }
  338. else
  339. {
  340. if (method_exists($this, $method))
  341. {
  342. call_user_func(array($this, $method), $params);
  343. }
  344. }
  345. }
  346. $version_installed = $version;
  347. }
  348. // update the version number or add it
  349. if ($this->config_exists($version_config_name))
  350. {
  351. $this->config_update($version_config_name, $version_installed);
  352. }
  353. else
  354. {
  355. $this->config_add($version_config_name, $version_installed);
  356. }
  357. }
  358. else if ($action == 'uninstall' && $db_version)
  359. {
  360. // reverse version list
  361. $versions = array_reverse($versions);
  362. foreach ($versions as $version => $version_actions)
  363. {
  364. // Uninstalling and this listed version is newer than installed
  365. if (version_compare($version, $db_version, '>'))
  366. {
  367. continue;
  368. }
  369. // Version selection stuff
  370. if ($version_select && version_compare($version, $version_select, '<='))
  371. {
  372. // update the version number
  373. $this->config_update($version_config_name, $version);
  374. break;
  375. }
  376. $cache_purge = false;
  377. $version_actions = array_reverse($version_actions);
  378. foreach ($version_actions as $method => $params)
  379. {
  380. if ($method == 'custom')
  381. {
  382. $this->_call_custom_function($params, $action, $version);
  383. }
  384. else
  385. {
  386. // 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).
  387. if ($method == 'cache_purge')
  388. {
  389. $cache_purge = $params;
  390. continue;
  391. }
  392. // A few things are not possible for uninstallations update actions and table_row actions
  393. if (strpos($method, 'update') !== false || strpos($method, 'table_insert') !== false || strpos($method, 'table_row_') !== false)
  394. {
  395. continue;
  396. }
  397. // reverse function call
  398. $method = str_replace(array('add', 'remove', 'temp'), array('temp', 'add', 'remove'), $method);
  399. $method = str_replace(array('set', 'unset', 'temp'), array('temp', 'set', 'unset'), $method);
  400. if (method_exists($this, $method))
  401. {
  402. call_user_func(array($this, $method), ((is_array($params) ? array_reverse($params) : $params)));
  403. }
  404. }
  405. }
  406. if ($cache_purge !== false)
  407. {
  408. $this->cache_purge($cache_purge);
  409. }
  410. }
  411. if (!$version_select)
  412. {
  413. // Unset the version number
  414. $this->config_remove($version_config_name);
  415. }
  416. }
  417. }
  418. /**
  419. * Call custom function helper
  420. */
  421. function _call_custom_function($functions, $action, $version)
  422. {
  423. if (!is_array($functions))
  424. {
  425. $functions = array($functions);
  426. }
  427. $return = '';
  428. foreach ($functions as $function)
  429. {
  430. if (function_exists($function))
  431. {
  432. // Must reset before calling the function
  433. $this->umil_start();
  434. $returned = call_user_func($function, $action, $version);
  435. if (is_string($returned))
  436. {
  437. $this->command = $this->get_output_text($returned);
  438. }
  439. else if (is_array($returned) && isset($returned['command']))
  440. {
  441. if (is_array($returned['command']))
  442. {
  443. $this->command = call_user_func_array(array($this, 'get_output_text'), $returned['command']);
  444. }
  445. else
  446. {
  447. $this->command = $this->get_output_text($returned['command']);
  448. }
  449. if (isset($returned['result']))
  450. {
  451. $this->result = $this->get_output_text($returned['result']);
  452. }
  453. }
  454. else
  455. {
  456. $this->command = $this->get_output_text('UNKNOWN');
  457. }
  458. $return .= $this->umil_end() . '<br />';
  459. }
  460. }
  461. return $return;
  462. }
  463. /**
  464. * Multicall Helper
  465. *
  466. * @param mixed $function Function name to call
  467. * @param mixed $params The parameters array
  468. *
  469. * @return bool True if we have done a multicall ($params is an array), false if not ($params is not an array)
  470. */
  471. function multicall($function, $params)
  472. {
  473. if (is_array($params) && !empty($params))
  474. {
  475. foreach ($params as $param)
  476. {
  477. if (!is_array($param))
  478. {
  479. call_user_func(array($this, $function), $param);
  480. }
  481. else
  482. {
  483. call_user_func_array(array($this, $function), $param);
  484. }
  485. }
  486. return true;
  487. }
  488. return false;
  489. }
  490. /**
  491. * Cache Purge
  492. *
  493. * This function is for purging either phpBB3â&#x20AC;&#x2122;s data cache, authorization cache, or the styles cache.
  494. *
  495. * @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.
  496. * @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)
  497. */
  498. function cache_purge($type = '', $style_id = 0)
  499. {
  500. global $auth, $cache, $user, $phpbb_root_path, $phpEx;
  501. // Multicall
  502. if ($this->multicall(__FUNCTION__, $type))
  503. {
  504. return;
  505. }
  506. $style_id = (int) $style_id;
  507. $type = (string) $type; // Prevent PHP bug.
  508. switch ($type)
  509. {
  510. case 'auth' :
  511. $this->umil_start('AUTH_CACHE_PURGE');
  512. $cache->destroy('_acl_options');
  513. $auth->acl_clear_prefetch();
  514. return $this->umil_end();
  515. break;
  516. case 'imageset' :
  517. if ($style_id == 0)
  518. {
  519. $return = array();
  520. $sql = 'SELECT imageset_id
  521. FROM ' . STYLES_IMAGESET_TABLE;
  522. $result = $this->db->sql_query($sql);
  523. while ($row = $this->db->sql_fetchrow($result))
  524. {
  525. $return[] = $this->cache_purge('imageset', $row['imageset_id']);
  526. }
  527. $this->db->sql_freeresult($result);
  528. return implode('<br /><br />', $return);
  529. }
  530. else
  531. {
  532. $sql = 'SELECT *
  533. FROM ' . STYLES_IMAGESET_TABLE . "
  534. WHERE imageset_id = $style_id";
  535. $result = $this->db->sql_query($sql);
  536. $imageset_row = $this->db->sql_fetchrow($result);
  537. $this->db->sql_freeresult($result);
  538. if (!$imageset_row)
  539. {
  540. $this->umil_start('IMAGESET_CACHE_PURGE', 'UNKNOWN');
  541. return $this->umil_end('FAIL');
  542. }
  543. $this->umil_start('IMAGESET_CACHE_PURGE', $imageset_row['imageset_name']);
  544. // The following is from includes/acp/acp_styles.php (edited)
  545. $sql_ary = array();
  546. $cfg_data_imageset = parse_cfg_file("{$phpbb_root_path}styles/{$imageset_row['imageset_path']}/imageset/imageset.cfg");
  547. $sql = 'DELETE FROM ' . STYLES_IMAGESET_DATA_TABLE . '
  548. WHERE imageset_id = ' . $style_id;
  549. $result = $this->db->sql_query($sql);
  550. foreach ($cfg_data_imageset as $image_name => $value)
  551. {
  552. if (strpos($value, '*') !== false)
  553. {
  554. if (substr($value, -1, 1) === '*')
  555. {
  556. list($image_filename, $image_height) = explode('*', $value);
  557. $image_width = 0;
  558. }
  559. else
  560. {
  561. list($image_filename, $image_height, $image_width) = explode('*', $value);
  562. }
  563. }
  564. else
  565. {
  566. $image_filename = $value;
  567. $image_height = $image_width = 0;
  568. }
  569. if (strpos($image_name, 'img_') === 0 && $image_filename)
  570. {
  571. $image_name = substr($image_name, 4);
  572. $sql_ary[] = array(
  573. 'image_name' => (string) $image_name,
  574. 'image_filename' => (string) $image_filename,
  575. 'image_height' => (int) $image_height,
  576. 'image_width' => (int) $image_width,
  577. 'imageset_id' => (int) $style_id,
  578. 'image_lang' => '',
  579. );
  580. }
  581. }
  582. $sql = 'SELECT lang_dir
  583. FROM ' . LANG_TABLE;
  584. $result = $this->db->sql_query($sql);
  585. while ($row = $this->db->sql_fetchrow($result))
  586. {
  587. if (@file_exists("{$phpbb_root_path}styles/{$imageset_row['imageset_path']}/imageset/{$row['lang_dir']}/imageset.cfg"))
  588. {
  589. $cfg_data_imageset_data = parse_cfg_file("{$phpbb_root_path}styles/{$imageset_row['imageset_path']}/imageset/{$row['lang_dir']}/imageset.cfg");
  590. foreach ($cfg_data_imageset_data as $image_name => $value)
  591. {
  592. if (strpos($value, '*') !== false)
  593. {
  594. if (substr($value, -1, 1) === '*')
  595. {
  596. list($image_filename, $image_height) = explode('*', $value);
  597. $image_width = 0;
  598. }
  599. else
  600. {
  601. list($image_filename, $image_height, $image_width) = explode('*', $value);
  602. }
  603. }
  604. else
  605. {
  606. $image_filename = $value;
  607. $image_height = $image_width = 0;
  608. }
  609. if (strpos($image_name, 'img_') === 0 && $image_filename)
  610. {
  611. $image_name = substr($image_name, 4);
  612. $sql_ary[] = array(
  613. 'image_name' => (string) $image_name,
  614. 'image_filename' => (string) $image_filename,
  615. 'image_height' => (int) $image_height,
  616. 'image_width' => (int) $image_width,
  617. 'imageset_id' => (int) $style_id,
  618. 'image_lang' => (string) $row['lang_dir'],
  619. );
  620. }
  621. }
  622. }
  623. }
  624. $this->db->sql_freeresult($result);
  625. $this->db->sql_multi_insert(STYLES_IMAGESET_DATA_TABLE, $sql_ary);
  626. $cache->destroy('sql', STYLES_IMAGESET_DATA_TABLE);
  627. return $this->umil_end();
  628. }
  629. break;
  630. //case 'imageset' :
  631. case 'template' :
  632. if ($style_id == 0)
  633. {
  634. $return = array();
  635. $sql = 'SELECT template_id
  636. FROM ' . STYLES_TEMPLATE_TABLE;
  637. $result = $this->db->sql_query($sql);
  638. while ($row = $this->db->sql_fetchrow($result))
  639. {
  640. $return[] = $this->cache_purge('template', $row['template_id']);
  641. }
  642. $this->db->sql_freeresult($result);
  643. return implode('<br /><br />', $return);
  644. }
  645. else
  646. {
  647. $sql = 'SELECT *
  648. FROM ' . STYLES_TEMPLATE_TABLE . "
  649. WHERE template_id = $style_id";
  650. $result = $this->db->sql_query($sql);
  651. $template_row = $this->db->sql_fetchrow($result);
  652. $this->db->sql_freeresult($result);
  653. if (!$template_row)
  654. {
  655. $this->umil_start('TEMPLATE_CACHE_PURGE', 'UNKNOWN');
  656. return $this->umil_end('FAIL');
  657. }
  658. $this->umil_start('TEMPLATE_CACHE_PURGE', $template_row['template_name']);
  659. // The following is from includes/acp/acp_styles.php
  660. if ($template_row['template_storedb'] && file_exists("{$phpbb_root_path}styles/{$template_row['template_path']}/template/"))
  661. {
  662. $filelist = array('' => array());
  663. $sql = 'SELECT template_filename, template_mtime
  664. FROM ' . STYLES_TEMPLATE_DATA_TABLE . "
  665. WHERE template_id = $style_id";
  666. $result = $this->db->sql_query($sql);
  667. while ($row = $this->db->sql_fetchrow($result))
  668. {
  669. // if (@filemtime("{$phpbb_root_path}styles/{$template_row['template_path']}/template/" . $row['template_filename']) > $row['template_mtime'])
  670. // {
  671. // get folder info from the filename
  672. if (($slash_pos = strrpos($row['template_filename'], '/')) === false)
  673. {
  674. $filelist[''][] = $row['template_filename'];
  675. }
  676. else
  677. {
  678. $filelist[substr($row['template_filename'], 0, $slash_pos + 1)][] = substr($row['template_filename'], $slash_pos + 1, strlen($row['template_filename']) - $slash_pos - 1);
  679. }
  680. // }
  681. }
  682. $this->db->sql_freeresult($result);
  683. $includes = array();
  684. foreach ($filelist as $pathfile => $file_ary)
  685. {
  686. foreach ($file_ary as $file)
  687. {
  688. if (!($fp = @fopen("{$phpbb_root_path}styles/{$template_row['template_path']}$pathfile$file", 'r')))
  689. {
  690. return $this->umil_end('FILE_COULD_NOT_READ', "{$phpbb_root_path}styles/{$template_row['template_path']}$pathfile$file");
  691. }
  692. $template_data = fread($fp, filesize("{$phpbb_root_path}styles/{$template_row['template_path']}$pathfile$file"));
  693. fclose($fp);
  694. if (preg_match_all('#<!-- INCLUDE (.*?\.html) -->#is', $template_data, $matches))
  695. {
  696. foreach ($matches[1] as $match)
  697. {
  698. $includes[trim($match)][] = $file;
  699. }
  700. }
  701. }
  702. }
  703. foreach ($filelist as $pathfile => $file_ary)
  704. {
  705. foreach ($file_ary as $file)
  706. {
  707. // Skip index.
  708. if (strpos($file, 'index.') === 0)
  709. {
  710. continue;
  711. }
  712. // We could do this using extended inserts ... but that could be one
  713. // heck of a lot of data ...
  714. $sql_ary = array(
  715. 'template_id' => (int) $style_id,
  716. 'template_filename' => "$pathfile$file",
  717. 'template_included' => (isset($includes[$file])) ? implode(':', $includes[$file]) . ':' : '',
  718. 'template_mtime' => (int) filemtime("{$phpbb_root_path}styles/{$template_row['template_path']}$pathfile$file"),
  719. 'template_data' => (string) file_get_contents("{$phpbb_root_path}styles/{$template_row['template_path']}$pathfile$file"),
  720. );
  721. $sql = 'UPDATE ' . STYLES_TEMPLATE_DATA_TABLE . ' SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . "
  722. WHERE template_id = $style_id
  723. AND template_filename = '" . $this->db->sql_escape("$pathfile$file") . "'";
  724. $this->db->sql_query($sql);
  725. }
  726. }
  727. unset($filelist);
  728. }
  729. // Purge the forum's cache as well.
  730. $cache->purge();
  731. return $this->umil_end();
  732. }
  733. break;
  734. //case 'template' :
  735. case 'theme' :
  736. if ($style_id == 0)
  737. {
  738. $return = array();
  739. $sql = 'SELECT theme_id
  740. FROM ' . STYLES_THEME_TABLE;
  741. $result = $this->db->sql_query($sql);
  742. while ($row = $this->db->sql_fetchrow($result))
  743. {
  744. $return[] = $this->cache_purge('theme', $row['theme_id']);
  745. }
  746. $this->db->sql_freeresult($result);
  747. return implode('<br /><br />', $return);
  748. }
  749. else
  750. {
  751. $sql = 'SELECT *
  752. FROM ' . STYLES_THEME_TABLE . "
  753. WHERE theme_id = $style_id";
  754. $result = $this->db->sql_query($sql);
  755. $theme_row = $this->db->sql_fetchrow($result);
  756. $this->db->sql_freeresult($result);
  757. if (!$theme_row)
  758. {
  759. $this->umil_start('THEME_CACHE_PURGE', 'UNKNOWN');
  760. return $this->umil_end('FAIL');
  761. }
  762. $this->umil_start('THEME_CACHE_PURGE', $theme_row['theme_name']);
  763. // The following is from includes/acp/acp_styles.php
  764. if ($theme_row['theme_storedb'] && file_exists("{$phpbb_root_path}styles/{$theme_row['theme_path']}/theme/stylesheet.css"))
  765. {
  766. $stylesheet = file_get_contents($phpbb_root_path . 'styles/' . $theme_row['theme_path'] . '/theme/stylesheet.css');
  767. // Match CSS imports
  768. $matches = array();
  769. preg_match_all('/@import url\(["\'](.*)["\']\);/i', $stylesheet, $matches);
  770. if (sizeof($matches))
  771. {
  772. foreach ($matches[0] as $idx => $match)
  773. {
  774. if (!file_exists("{$phpbb_root_path}styles/{$theme_row['theme_path']}/theme/{$matches[1][$idx]}"))
  775. {
  776. continue;
  777. }
  778. $content = trim(file_get_contents("{$phpbb_root_path}styles/{$theme_row['theme_path']}/theme/{$matches[1][$idx]}"));
  779. $stylesheet = str_replace($match, $content, $stylesheet);
  780. }
  781. }
  782. // adjust paths
  783. $db_theme_data = str_replace('./', 'styles/' . $theme_row['theme_path'] . '/theme/', $stylesheet);
  784. // Save CSS contents
  785. $sql_ary = array(
  786. 'theme_mtime' => (int) filemtime("{$phpbb_root_path}styles/{$theme_row['theme_path']}/theme/stylesheet.css"),
  787. 'theme_data' => $db_theme_data,
  788. );
  789. $sql = 'UPDATE ' . STYLES_THEME_TABLE . ' SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . "
  790. WHERE theme_id = $style_id";
  791. $this->db->sql_query($sql);
  792. $cache->destroy('sql', STYLES_THEME_TABLE);
  793. }
  794. return $this->umil_end();
  795. }
  796. break;
  797. //case 'theme' :
  798. default:
  799. $this->umil_start('CACHE_PURGE');
  800. $cache->purge();
  801. return $this->umil_end();
  802. break;
  803. }
  804. }
  805. /**
  806. * Config Exists
  807. *
  808. * This function is to check to see if a config variable exists or if it does not.
  809. *
  810. * @param string $config_name The name of the config setting you wish to check for.
  811. * @param bool $return_result - return the config value/default if true : default false.
  812. *
  813. * @return bool true/false if config exists
  814. */
  815. function config_exists($config_name, $return_result = false)
  816. {
  817. global $config, $cache;
  818. $sql = 'SELECT *
  819. FROM ' . CONFIG_TABLE . "
  820. WHERE config_name = '" . $this->db->sql_escape($config_name) . "'";
  821. $result = $this->db->sql_query($sql);
  822. $row = $this->db->sql_fetchrow($result);
  823. $this->db->sql_freeresult($result);
  824. if ($row)
  825. {
  826. if (!isset($config[$config_name]))
  827. {
  828. $config[$config_name] = $row['config_value'];
  829. if (!$row['is_dynamic'])
  830. {
  831. $cache->destroy('config');
  832. }
  833. }
  834. return ($return_result) ? $row : true;
  835. }
  836. // this should never happen, but if it does, we need to remove the config from the array
  837. if (isset($config[$config_name]))
  838. {
  839. unset($config[$config_name]);
  840. $cache->destroy('config');
  841. }
  842. return false;
  843. }
  844. /**
  845. * Config Add
  846. *
  847. * This function allows you to add a config setting.
  848. *
  849. * @param string $config_name The name of the config setting you would like to add
  850. * @param mixed $config_value The value of the config setting
  851. * @param bool $is_dynamic True if it is dynamic (changes very often) and should not be stored in the cache, false if not.
  852. *
  853. * @return result
  854. */
  855. function config_add($config_name, $config_value = '', $is_dynamic = false)
  856. {
  857. // Multicall
  858. if ($this->multicall(__FUNCTION__, $config_name))
  859. {
  860. return;
  861. }
  862. $this->umil_start('CONFIG_ADD', $config_name);
  863. if ($this->config_exists($config_name))
  864. {
  865. return $this->umil_end('CONFIG_ALREADY_EXISTS', $config_name);
  866. }
  867. set_config($config_name, $config_value, $is_dynamic);
  868. return $this->umil_end();
  869. }
  870. /**
  871. * Config Update
  872. *
  873. * This function allows you to update an existing config setting.
  874. *
  875. * @param string $config_name The name of the config setting you would like to update
  876. * @param mixed $config_value The value of the config setting
  877. * @param bool $is_dynamic True if it is dynamic (changes very often) and should not be stored in the cache, false if not.
  878. *
  879. * @return result
  880. */
  881. function config_update($config_name, $config_value = '', $is_dynamic = false)
  882. {
  883. // Multicall
  884. if ($this->multicall(__FUNCTION__, $config_name))
  885. {
  886. return;
  887. }
  888. $this->umil_start('CONFIG_UPDATE', $config_name);
  889. if (!$this->config_exists($config_name))
  890. {
  891. return $this->umil_end('CONFIG_NOT_EXIST', $config_name);
  892. }
  893. set_config($config_name, $config_value, $is_dynamic);
  894. return $this->umil_end();
  895. }
  896. /**
  897. * Config Remove
  898. *
  899. * This function allows you to remove an existing config setting.
  900. *
  901. * @param string $config_name The name of the config setting you would like to remove
  902. *
  903. * @return result
  904. */
  905. function config_remove($config_name)
  906. {
  907. global $cache, $config;
  908. // Multicall
  909. if ($this->multicall(__FUNCTION__, $config_name))
  910. {
  911. return;
  912. }
  913. $this->umil_start('CONFIG_REMOVE', $config_name);
  914. if (!$this->config_exists($config_name))
  915. {
  916. return $this->umil_end('CONFIG_NOT_EXIST', $config_name);
  917. }
  918. $sql = 'DELETE FROM ' . CONFIG_TABLE . " WHERE config_name = '" . $this->db->sql_escape($config_name) . "'";
  919. $this->db->sql_query($sql);
  920. unset($config[$config_name]);
  921. $cache->destroy('config');
  922. return $this->umil_end();
  923. }
  924. /**
  925. * Module Exists
  926. *
  927. * Check if a module exists
  928. *
  929. * @param string $class The module class(acp|mcp|ucp)
  930. * @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.
  931. * @param int|string $module The module_id|module_langname you would like to check for to see if it exists
  932. */
  933. function module_exists($class, $parent, $module)
  934. {
  935. // the main root directory should return true
  936. if (!$module)
  937. {
  938. return true;
  939. }
  940. $class = $this->db->sql_escape($class);
  941. $module = $this->db->sql_escape($module);
  942. $parent_sql = '';
  943. if ($parent !== false)
  944. {
  945. // Allows '' to be sent as 0
  946. $parent = (!$parent) ? 0 : $parent;
  947. if (!is_numeric($parent))
  948. {
  949. $sql = 'SELECT module_id FROM ' . MODULES_TABLE . "
  950. WHERE module_langname = '" . $this->db->sql_escape($parent) . "'
  951. AND module_class = '$class'";
  952. $result = $this->db->sql_query($sql);
  953. $row = $this->db->sql_fetchrow($result);
  954. $this->db->sql_freeresult($result);
  955. if (!$row)
  956. {
  957. return false;
  958. }
  959. $parent_sql = 'AND parent_id = ' . (int) $row['module_id'];
  960. }
  961. else
  962. {
  963. $parent_sql = 'AND parent_id = ' . (int) $parent;
  964. }
  965. }
  966. $sql = 'SELECT module_id FROM ' . MODULES_TABLE . "
  967. WHERE module_class = '$class'
  968. $parent_sql
  969. AND " . ((is_numeric($module)) ? 'module_id = ' . (int) $module : "module_langname = '$module'");
  970. $result = $this->db->sql_query($sql);
  971. $row = $this->db->sql_fetchrow($result);
  972. $this->db->sql_freeresult($result);
  973. if ($row)
  974. {
  975. return true;
  976. }
  977. return false;
  978. }
  979. /**
  980. * Module Add
  981. *
  982. * Add a new module
  983. *
  984. * @param string $class The module class(acp|mcp|ucp)
  985. * @param int|string $parent The parent module_id|module_langname (0 for no parent)
  986. * @param array $data an array of the data on the new module. This can be setup in two different ways.
  987. * 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,
  988. * 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.
  989. * array(
  990. * 'module_enabled' => 1,
  991. * 'module_display' => 1,
  992. * 'module_basename' => '',
  993. * 'module_class' => $class,
  994. * 'parent_id' => (int) $parent,
  995. * 'module_langname' => '',
  996. * 'module_mode' => '',
  997. * 'module_auth' => '',
  998. * )
  999. * 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.
  1000. * An example follows (this would insert the settings, log, and flag modes from the includes/acp/info/acp_asacp.php file):
  1001. * array(
  1002. * 'module_basename' => 'asacp',
  1003. * 'modes' => array('settings', 'log', 'flag'),
  1004. * )
  1005. * Optionally you may not send 'modes' and it will insert all of the modules in that info file.
  1006. * @param string|bool $include_path If you would like to use a custom include path, specify that here
  1007. */
  1008. function module_add($class, $parent = 0, $data = array(), $include_path = false)
  1009. {
  1010. global $cache, $user, $phpbb_root_path, $phpEx;
  1011. // Multicall
  1012. if ($this->multicall(__FUNCTION__, $class))
  1013. {
  1014. return;
  1015. }
  1016. // Prevent stupid things like trying to add a module with no name or any data on it
  1017. if (empty($data))
  1018. {
  1019. $this->umil_start('MODULE_ADD', $class, 'UNKNOWN');
  1020. return $this->umil_end('FAIL');
  1021. }
  1022. // Allows '' to be sent as 0
  1023. $parent = (!$parent) ? 0 : $parent;
  1024. // allow sending the name as a string in $data to create a category
  1025. if (!is_array($data))
  1026. {
  1027. $data = array('module_langname' => $data);
  1028. }
  1029. if (!isset($data['module_langname']))
  1030. {
  1031. // The "automatic" way
  1032. $basename = (isset($data['module_basename'])) ? $data['module_basename'] : '';
  1033. $basename = str_replace(array('/', '\\'), '', $basename);
  1034. $class = str_replace(array('/', '\\'), '', $class);
  1035. $info_file = "$class/info/{$class}_$basename.$phpEx";
  1036. // The manual and automatic ways both failed...
  1037. if (!file_exists((($include_path === false) ? $phpbb_root_path . 'includes/' : $include_path) . $info_file))
  1038. {
  1039. $this->umil_start('MODULE_ADD', $class, $info_file);
  1040. return $this->umil_end('FAIL');
  1041. }
  1042. $classname = "{$class}_{$basename}_info";
  1043. if (!class_exists($classname))
  1044. {
  1045. include((($include_path === false) ? $phpbb_root_path . 'includes/' : $include_path) . $info_file);
  1046. }
  1047. $info = new $classname;
  1048. $module = $info->module();
  1049. unset($info);
  1050. $result = '';
  1051. foreach ($module['modes'] as $mode => $module_info)
  1052. {
  1053. if (!isset($data['modes']) || in_array($mode, $data['modes']))
  1054. {
  1055. $new_module = array(
  1056. 'module_basename' => $basename,
  1057. 'module_langname' => $module_info['title'],
  1058. 'module_mode' => $mode,
  1059. 'module_auth' => $module_info['auth'],
  1060. 'module_display' => (isset($module_info['display'])) ? $module_info['display'] : true,
  1061. 'before' => (isset($module_info['before'])) ? $module_info['before'] : false,
  1062. 'after' => (isset($module_info['after'])) ? $module_info['after'] : false,
  1063. );
  1064. // Run the "manual" way with the data we've collected.
  1065. $result .= ((isset($data['spacer'])) ? $data['spacer'] : '<br />') . $this->module_add($class, $parent, $new_module);
  1066. }
  1067. }
  1068. return $result;
  1069. }
  1070. // The "manual" way
  1071. $this->umil_start('MODULE_ADD', $class, ((isset($user->lang[$data['module_langname']])) ? $user->lang[$data['module_langname']] : $data['module_langname']));
  1072. add_log('admin', 'LOG_MODULE_ADD', ((isset($user->lang[$data['module_langname']])) ? $user->lang[$data['module_langname']] : $data['module_langname']));
  1073. $class = $this->db->sql_escape($class);
  1074. if (!is_numeric($parent))
  1075. {
  1076. $sql = 'SELECT module_id FROM ' . MODULES_TABLE . "
  1077. WHERE module_langname = '" . $this->db->sql_escape($parent) . "'
  1078. AND module_class = '$class'";
  1079. $result = $this->db->sql_query($sql);
  1080. $row = $this->db->sql_fetchrow($result);
  1081. $this->db->sql_freeresult($result);
  1082. if (!$row)
  1083. {
  1084. return $this->umil_end('PARENT_NOT_EXIST');
  1085. }
  1086. $parent = $data['parent_id'] = $row['module_id'];
  1087. }
  1088. else if (!$this->module_exists($class, false, $parent))
  1089. {
  1090. return $this->umil_end('PARENT_NOT_EXIST');
  1091. }
  1092. if ($this->module_exists($class, $parent, $data['module_langname']))
  1093. {
  1094. return $this->umil_end('MODULE_ALREADY_EXIST');
  1095. }
  1096. if (!class_exists('acp_modules'))
  1097. {
  1098. include($phpbb_root_path . 'includes/acp/acp_modules.' . $phpEx);
  1099. $user->add_lang('acp/modules');
  1100. }
  1101. $acp_modules = new acp_modules();
  1102. $module_data = array(
  1103. 'module_enabled' => (isset($data['module_enabled'])) ? $data['module_enabled'] : 1,
  1104. 'module_display' => (isset($data['module_display'])) ? $data['module_display'] : 1,
  1105. 'module_basename' => (isset($data['module_basename'])) ? $data['module_basename'] : '',
  1106. 'module_class' => $class,
  1107. 'parent_id' => (int) $parent,
  1108. 'module_langname' => (isset($data['module_langname'])) ? $data['module_langname'] : '',
  1109. 'module_mode' => (isset($data['module_mode'])) ? $data['module_mode'] : '',
  1110. 'module_auth' => (isset($data['module_auth'])) ? $data['module_auth'] : '',
  1111. );
  1112. $result = $acp_modules->update_module_data($module_data, true);
  1113. // update_module_data can either return a string or an empty array...
  1114. if (is_string($result))
  1115. {
  1116. // Error
  1117. $this->result = $this->get_output_text($result);
  1118. }
  1119. else
  1120. {
  1121. // Success
  1122. // Move the module if requested above/below an existing one
  1123. if (isset($data['before']) && $data['before'])
  1124. {
  1125. $sql = 'SELECT left_id FROM ' . MODULES_TABLE . '
  1126. WHERE module_class = \'' . $class . '\'
  1127. AND parent_id = ' . (int) $parent . '
  1128. AND module_langname = \'' . $this->db->sql_escape($data['before']) . '\'';
  1129. $this->db->sql_query($sql);
  1130. $to_left = $this->db->sql_fetchfield('left_id');
  1131. $sql = 'UPDATE ' . MODULES_TABLE . " SET left_id = left_id + 2, right_id = right_id + 2
  1132. WHERE module_class = '$class'
  1133. AND left_id >= $to_left
  1134. AND left_id < {$module_data['left_id']}";
  1135. $this->db->sql_query($sql);
  1136. $sql = 'UPDATE ' . MODULES_TABLE . " SET left_id = $to_left, right_id = " . ($to_left + 1) . "
  1137. WHERE module_class = '$class'
  1138. AND module_id = {$module_data['module_id']}";
  1139. $this->db->sql_query($sql);
  1140. }
  1141. else if (isset($data['after']) && $data['after'])
  1142. {
  1143. $sql = 'SELECT right_id FROM ' . MODULES_TABLE . '
  1144. WHERE module_class = \'' . $class . '\'
  1145. AND parent_id = ' . (int) $parent . '
  1146. AND module_langname = \'' . $this->db->sql_escape($data['after']) . '\'';
  1147. $this->db->sql_query($sql);
  1148. $to_right = $this->db->sql_fetchfield('right_id');
  1149. $sql = 'UPDATE ' . MODULES_TABLE . " SET left_id = left_id + 2, right_id = right_id + 2
  1150. WHERE module_class = '$class'
  1151. AND left_id >= $to_right
  1152. AND left_id < {$module_data['left_id']}";
  1153. $this->db->sql_query($sql);
  1154. $sql = 'UPDATE ' . MODULES_TABLE . ' SET left_id = ' . ($to_right + 1) . ', right_id = ' . ($to_right + 2) . "
  1155. WHERE module_class = '$class'
  1156. AND module_id = {$module_data['module_id']}";
  1157. $this->db->sql_query($sql);
  1158. }
  1159. }
  1160. // Clear the Modules Cache
  1161. $cache->destroy("_modules_$class");
  1162. return $this->umil_end();
  1163. }
  1164. /**
  1165. * Module Remove
  1166. *
  1167. * Remove a module
  1168. *
  1169. * @param string $class The module class(acp|mcp|ucp)
  1170. * @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.
  1171. * @param int|string $module The module id|module_langname
  1172. * @param string|bool $include_path If you would like to use a custom include path, specify that here
  1173. */
  1174. function module_remove($class, $parent = 0, $module = '', $include_path = false)
  1175. {
  1176. global $cache, $user, $phpbb_root_path, $phpEx;
  1177. // Multicall
  1178. if ($this->multicall(__FUNCTION__, $class))
  1179. {
  1180. return;
  1181. }
  1182. // Imitation of module_add's "automatic" and "manual" method so the uninstaller works from the same set of instructions for umil_auto
  1183. if (is_array($module))
  1184. {
  1185. if (isset($module['module_langname']))
  1186. {
  1187. // Manual Method
  1188. return $this->module_remove($class, $parent, $module['module_langname'], $include_path);
  1189. }
  1190. // Failed.
  1191. if (!isset($module['module_basename']))
  1192. {
  1193. $this->umil_start('MODULE_REMOVE', $class, 'UNKNOWN');
  1194. return $this->umil_end('FAIL');
  1195. }
  1196. // Automatic method
  1197. $basename = str_replace(array('/', '\\'), '', $module['module_basename']);
  1198. $class = str_replace(array('/', '\\'), '', $class);
  1199. $info_file = "$class/info/{$class}_$basename.$phpEx";
  1200. if (!file_exists((($include_path === false) ? $phpbb_root_path . 'includes/' : $include_path) . $info_file))
  1201. {
  1202. $this->umil_start('MODULE_REMOVE', $class, $info_file);
  1203. return $this->umil_end('FAIL');
  1204. }
  1205. $classname = "{$class}_{$basename}_info";
  1206. if (!class_exists($classname))
  1207. {
  1208. include((($include_path === false) ? $phpbb_root_path . 'includes/' : $include_path) . $info_file);
  1209. }
  1210. $info = new $classname;
  1211. $module_info = $info->module();
  1212. unset($info);
  1213. $result = '';
  1214. foreach ($module_info['modes'] as $mode => $info)
  1215. {
  1216. if (!isset($module['modes']) || in_array($mode, $module['modes']))
  1217. {
  1218. $result .= $this->module_remove($class, $parent, $info['title']) . '<br />';
  1219. }
  1220. }
  1221. return $result;
  1222. }
  1223. else
  1224. {
  1225. $class = $this->db->sql_escape($class);
  1226. if (!$this->module_exists($class, $parent, $module))
  1227. {
  1228. $this->umil_start('MODULE_REMOVE', $class, ((isset($user->lang[$module])) ? $user->lang[$module] : $module));
  1229. return $this->umil_end('MODULE_NOT_EXIST');
  1230. }
  1231. $parent_sql = '';
  1232. if ($parent !== false)
  1233. {
  1234. // Allows '' to be sent as 0
  1235. $parent = (!$parent) ? 0 : $parent;
  1236. if (!is_numeric($parent))
  1237. {
  1238. $sql = 'SELECT module_id FROM ' . MODULES_TABLE . "
  1239. WHERE module_langname = '" . $this->db->sql_escape($parent) . "'
  1240. AND module_class = '$class'";
  1241. $result = $this->db->sql_query($sql);
  1242. $row = $this->db->sql_fetchrow($result);
  1243. $this->db->sql_freeresult($result);
  1244. // we know it exists from the module_exists check
  1245. $parent_sql = 'AND parent_id = ' . (int) $row['module_id'];
  1246. }
  1247. else
  1248. {
  1249. $parent_sql = 'AND parent_id = ' . (int) $parent;
  1250. }
  1251. }
  1252. $module_ids = array();
  1253. if (!is_numeric($module))
  1254. {
  1255. $module = $this->db->sql_escape($module);
  1256. $sql = 'SELECT module_id FROM ' . MODULES_TABLE . "
  1257. WHERE module_langname = '$module'
  1258. AND module_class = '$class'
  1259. $parent_sql";
  1260. $result = $this->db->sql_query($sql);
  1261. while ($row = $this->db->sql_fetchrow($result))
  1262. {
  1263. $module_ids[] = (int) $row['module_id'];
  1264. }
  1265. $this->db->sql_freeresult($result);
  1266. $module_name = $module;
  1267. }
  1268. else
  1269. {
  1270. $module = (int) $module;
  1271. $sql = 'SELECT module_langname FROM ' . MODULES_TABLE . "
  1272. WHERE module_id = $module
  1273. AND module_class = '$class'
  1274. $parent_sql";
  1275. $result = $this->db->sql_query($sql);
  1276. $row = $this->db->sql_fetchrow($result);
  1277. $this->db->sql_freeresult($result);
  1278. $module_name = $row['module_langname'];
  1279. $module_ids[] = $module;
  1280. }
  1281. $this->umil_start('MODULE_REMOVE', $class, ((isset($user->lang[$module_name])) ? $user->lang[$module_name] : $module_name));
  1282. add_log('admin', 'LOG_MODULE_REMOVED', ((isset($user->lang[$module_name])) ? $user->lang[$module_name] : $module_name));
  1283. if (!class_exists('acp_modules'))
  1284. {
  1285. include($phpbb_root_path . 'includes/acp/acp_modules.' . $phpEx);
  1286. $user->add_lang('acp/modules');
  1287. }
  1288. $acp_modules = new acp_modules();
  1289. $acp_modules->module_class = $class;
  1290. foreach ($module_ids as $module_id)
  1291. {
  1292. $result = $acp_modules->delete_module($module_id);
  1293. if (!empty($result))
  1294. {
  1295. if ($this->result == ((isset($user->lang['SUCCESS'])) ? $user->lang['SUCCESS'] : 'SUCCESS'))
  1296. {
  1297. $this->result = implode('<br />', $result);
  1298. }
  1299. else
  1300. {
  1301. $this->result .= '<br />' . implode('<br />', $result);
  1302. }
  1303. }
  1304. }
  1305. $cache->destroy("_modules_$class");
  1306. return $this->umil_end();
  1307. }
  1308. }
  1309. /**
  1310. * Permission Exists
  1311. *
  1312. * Check if a permission (auth) setting exists
  1313. *
  1314. * @param string $auth_option The name of the permission (auth) option
  1315. * @param bool $global True for checking a global permission setting, False for a local permission setting
  1316. *
  1317. * @return bool true if it exists, false if not
  1318. */
  1319. function permission_exists($auth_option, $global = true)
  1320. {
  1321. if ($global)
  1322. {
  1323. $type_sql = ' AND is_global = 1';
  1324. }
  1325. else
  1326. {
  1327. $type_sql = ' AND is_local = 1';
  1328. }
  1329. $sql = 'SELECT auth_option_id
  1330. FROM ' . ACL_OPTIONS_TABLE . "
  1331. WHERE auth_option = '" . $this->db->sql_escape($auth_option) . "'"
  1332. . $type_sql;
  1333. $result = $this->db->sql_query($sql);
  1334. $row = $this->db->sql_fetchrow($result);
  1335. $this->db->sql_freeresult($result);
  1336. if ($row)
  1337. {
  1338. return true;
  1339. }
  1340. return false;
  1341. }
  1342. /**
  1343. * Permission Add
  1344. *
  1345. * Add a permission (auth) option
  1346. *
  1347. * @param string $auth_option The name of the permission (auth) option
  1348. * @param bool $global True for checking a global permission setting, False for a local permission setting
  1349. *
  1350. * @return result
  1351. */
  1352. function permission_add($auth_option, $global = true)
  1353. {
  1354. // Multicall
  1355. if ($this->multicall(__FUNCTION__, $auth_option))
  1356. {
  1357. return;
  1358. }
  1359. $this->umil_start('PERMISSION_ADD', $auth_option);
  1360. if ($this->permission_exists($auth_option, $global))
  1361. {
  1362. return $this->umil_end('PERMISSION_ALREADY_EXISTS', $auth_option);
  1363. }
  1364. // We've added permissions, so set to true to notify the user.
  1365. $this->permissions_added = true;
  1366. if (!class_exists('auth_admin'))
  1367. {
  1368. global $phpbb_root_path, $phpEx;
  1369. include($phpbb_root_path . 'includes/acp/auth.' . $phpEx);
  1370. }
  1371. $auth_admin = new auth_admin();
  1372. // 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.
  1373. if ($this->permission_exists($auth_option, !$global))
  1374. {
  1375. $sql_ary = array(
  1376. 'is_global' => 1,
  1377. 'is_local' => 1,
  1378. );
  1379. $sql = 'UPDATE ' . ACL_OPTIONS_TABLE . '
  1380. SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . '
  1381. WHERE auth_option = \'' . $this->db->sql_escape($auth_option) . "'";
  1382. $this->db->sql_query($sql);
  1383. }
  1384. else
  1385. {
  1386. if ($global)
  1387. {
  1388. $auth_admin->acl_add_option(array('global' => array($auth_option)));
  1389. }
  1390. else
  1391. {
  1392. $auth_admin->acl_add_option(array('local' => array($auth_option)));
  1393. }
  1394. }
  1395. return $this->umil_end();
  1396. }
  1397. /**
  1398. * Permission Remove
  1399. *
  1400. * Remove a permission (auth) option
  1401. *
  1402. * @param string $auth_option The name of the permission (auth) option
  1403. * @param bool $global True for checking a global permission setting, False for a local permission setting
  1404. *
  1405. * @return result
  1406. */
  1407. function permission_remove($auth_option, $global = true)
  1408. {
  1409. global $auth, $cache;
  1410. // Multicall
  1411. if ($this->multicall(__FUNCTION__, $auth_option))
  1412. {
  1413. return;
  1414. }
  1415. $this->umil_start('PERMISSION_REMOVE', $auth_option);
  1416. if (!$this->permission_exists($auth_option, $global))
  1417. {
  1418. return $this->umil_end('PERMISSION_NOT_EXIST', $auth_option);
  1419. }
  1420. if ($global)
  1421. {
  1422. $type_sql = ' AND is_global = 1';
  1423. }
  1424. else
  1425. {
  1426. $type_sql = ' AND is_local = 1';
  1427. }
  1428. $sql = 'SELECT auth_option_id, is_global, is_local FROM ' . ACL_OPTIONS_TABLE . "
  1429. WHERE auth_option = '" . $this->db->sql_escape($auth_option) . "'" .
  1430. $type_sql;
  1431. $result = $this->db->sql_query($sql);
  1432. $row = $this->db->sql_fetchrow($result);
  1433. $this->db->sql_freeresult($result);
  1434. $id = $row['auth_option_id'];
  1435. // If it is a local and global permission, do not remove the row! :P
  1436. if ($row['is_global'] && $row['is_local'])
  1437. {
  1438. $sql = 'UPDATE ' . ACL_OPTIONS_TABLE . '
  1439. SET ' . (($global) ? 'is_global = 0' : 'is_local = 0') . '
  1440. WHERE auth_option_id = ' . $id;
  1441. $this->db->sql_query($sql);
  1442. }
  1443. else
  1444. {
  1445. // Delete time
  1446. $this->db->sql_query('DELETE FROM ' . ACL_GROUPS_TABLE . ' WHERE auth_option_id = ' . $id);
  1447. $this->db->sql_query('DELETE FROM ' . ACL_ROLES_DATA_TABLE . ' WHERE auth_option_id = ' . $id);
  1448. $this->db->sql_query('DELETE FROM ' . ACL_USERS_TABLE . ' WHERE auth_option_id = ' . $id);
  1449. $this->db->sql_query('DELETE FROM ' . ACL_OPTIONS_TABLE . ' WHERE auth_option_id = ' . $id);
  1450. }
  1451. // Purge the auth cache
  1452. $cache->destroy('_acl_options');
  1453. $auth->acl_clear_prefetch();
  1454. return $this->umil_end();
  1455. }
  1456. /**
  1457. * Add a new permission role
  1458. *
  1459. * @param string $role_name The new role name
  1460. * @param sting $role_type The type (u_, m_, a_)
  1461. */
  1462. function permission_role_add($role_name, $role_type = '', $role_description = '')
  1463. {
  1464. // Multicall
  1465. if ($this->multicall(__FUNCTION__, $role_name))
  1466. {
  1467. return;
  1468. }
  1469. $this->umil_start('PERMISSION_ROLE_ADD', $role_name);
  1470. $sql = 'SELECT role_id FROM ' . ACL_ROLES_TABLE . '
  1471. WHERE role_name = \'' . $this->db->sql_escape($role_name) . '\'';
  1472. $this->db->sql_query($sql);
  1473. $role_id = $this->db->sql_fetchfield('role_id');
  1474. if ($role_id)
  1475. {
  1476. return $this->umil_end('ROLE_ALREADY_EXISTS', $old_role_name);
  1477. }
  1478. $sql = 'SELECT MAX(role_order) AS max FROM ' . ACL_ROLES_TABLE . '
  1479. WHERE role_type = \'' . $this->db->sql_escape($role_type) . '\'';
  1480. $this->db->sql_query($sql);
  1481. $role_order = $this->db->sql_fetchfield('max');
  1482. $role_order = (!$role_order) ? 1 : $role_order + 1;
  1483. $sql_ary = array(
  1484. 'role_name' => $role_name,
  1485. 'role_description' => $role_description,
  1486. 'role_type' => $role_type,
  1487. 'role_order' => $role_order,
  1488. );
  1489. $sql = 'INSERT INTO ' . ACL_ROLES_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary);
  1490. $this->db->sql_query($sql);
  1491. return $this->umil_end();
  1492. }
  1493. /**
  1494. * Update the name on a permission role
  1495. *
  1496. * @param string $old_role_name The old role name
  1497. * @param string $new_role_name The new role name
  1498. */
  1499. function permission_role_update($old_role_name, $new_role_name = '')
  1500. {
  1501. // Multicall
  1502. if ($this->multicall(__FUNCTION__, $role_name))
  1503. {
  1504. return;
  1505. }
  1506. $this->umil_start('PERMISSION_ROLE_UPDATE', $old_role_name);
  1507. $sql = 'SELECT role_id FROM ' . ACL_ROLES_TABLE . '
  1508. WHERE role_name = \'' . $this->db->sql_escape($old_role_name) . '\'';
  1509. $this->db->sql_query($sql);
  1510. $role_id = $this->db->sql_fetchfield('role_id');
  1511. if (!$role_id)
  1512. {
  1513. return $this->umil_end('ROLE_NOT_EXIST', $old_role_name);
  1514. }
  1515. $sql = 'UPDATE ' . ACL_ROLES_TABLE . '
  1516. SET role_name = \'' . $this->db->sql_escape($new_role_name) . '\'
  1517. WHERE role_name = \'' . $this->db->sql_escape($old_role_name) . '\'';
  1518. $this->db->sql_query($sql);
  1519. return $this->umil_end();
  1520. }
  1521. /**
  1522. * Remove a permission role
  1523. *
  1524. * @param string $role_name The role name to remove
  1525. */
  1526. function permission_role_remove($role_name)
  1527. {
  1528. global $auth;
  1529. // Multicall
  1530. if ($this->multicall(__FUNCTION__, $role_name))
  1531. {
  1532. return;
  1533. }
  1534. $this->umil_start('PERMISSION_ROLE_REMOVE', $role_name);
  1535. $sql = 'SELECT role_id FROM ' . ACL_ROLES_TABLE . '
  1536. WHERE role_name = \'' . $this->db->sql_escape($role_name) . '\'';
  1537. $this->db->sql_query($sql);
  1538. $role_id = $this->db->sql_fetchfield('role_id');
  1539. if (!$role_id)
  1540. {
  1541. return $this->umil_end('ROLE_NOT_EXIST', $role_name);
  1542. }
  1543. $sql = 'DELETE FROM ' . ACL_ROLES_DATA_TABLE . '
  1544. WHERE role_id = ' . $role_id;
  1545. $this->db->sql_query($sql);
  1546. $sql = 'DELETE FROM ' . ACL_ROLES_TABLE . '
  1547. WHERE role_id = ' . $role_id;
  1548. $this->db->sql_query($sql);
  1549. $auth->acl_clear_prefetch();
  1550. return $this->umil_end();
  1551. }
  1552. /**
  1553. * Permission Set
  1554. *
  1555. * Allows you to set permissions for a certain group/role
  1556. *
  1557. * @param string $name The name of the role/group
  1558. * @param string|array $auth_option The auth_option or array of auth_options you would like to set
  1559. * @param string $type The type (role|group)
  1560. * @param bool $has_permission True if you want to give them permission, false if you want to deny them permission
  1561. */
  1562. function permission_set($name, $auth_option = array(), $type = 'role', $has_permission = true)
  1563. {
  1564. global $auth;
  1565. // Multicall
  1566. if ($this->multicall(__FUNCTION__, $name))
  1567. {
  1568. return;
  1569. }
  1570. if (!is_array($auth_option))
  1571. {
  1572. $auth_option = array($auth_option);
  1573. }
  1574. $new_auth = array();
  1575. $sql = 'SELECT auth_option_id FROM ' . ACL_OPTIONS_TABLE . '
  1576. WHERE ' . $this->db->sql_in_set('auth_option', $auth_option);
  1577. $result = $this->db->sql_query($sql);
  1578. while ($row = $this->db->sql_fetchrow($result))
  1579. {
  1580. $new_auth[] = $row['auth_option_id'];
  1581. }
  1582. $this->db->sql_freeresult($result);
  1583. if (!sizeof($new_auth))
  1584. {
  1585. return false;
  1586. }
  1587. $current_auth = array();
  1588. $type = (string) $type; // Prevent PHP bug.
  1589. switch ($type)
  1590. {
  1591. case 'role' :
  1592. $this->umil_start('PERMISSION_SET_ROLE', $name);
  1593. $sql = 'SELECT role_id FROM ' . ACL_ROLES_TABLE . '
  1594. WHERE role_name = \'' . $this->db->sql_escape($name) . '\'';
  1595. $this->db->sql_query($sql);
  1596. $role_id = $this->db->sql_fetchfield('role_id');
  1597. if (!$role_id)
  1598. {
  1599. return $this->umil_end('ROLE_NOT_EXIST');
  1600. }
  1601. $sql = 'SELECT auth_option_id, auth_setting FROM ' . ACL_ROLES_DATA_TABLE . '
  1602. WHERE role_id = ' . $role_id;
  1603. $result = $this->db->sql_query($sql);
  1604. while ($row = $this->db->sql_fetchrow($result))
  1605. {
  1606. $current_auth[$row['auth_option_id']] = $row['auth_setting'];
  1607. }
  1608. $this->db->sql_freeresult($result);
  1609. break;
  1610. case 'group' :
  1611. $sql = 'SELECT group_id FROM ' . GROUPS_TABLE . ' WHERE group_name = \'' . $this->db->sql_escape($name) . '\'';
  1612. $this->db->sql_query($sql);
  1613. $group_id = $this->db->sql_fetchfield('group_id');
  1614. if (!$group_id)
  1615. {
  1616. $this->umil_start('PERMISSION_SET_GROUP', $name);
  1617. return $this->umil_end('GROUP_NOT_EXIST');
  1618. }
  1619. // If the group has a role set for them we will add the requested permissions to that role.
  1620. $sql = 'SELECT auth_role_id FROM ' . ACL_GROUPS_TABLE . '
  1621. WHERE group_id = ' . $group_id . '
  1622. AND auth_role_id <> 0
  1623. AND forum_id = 0';
  1624. $this->db->sql_query($sql);
  1625. $role_id = $this->db->sql_fetchfield('auth_role_id');
  1626. if ($role_id)
  1627. {
  1628. $sql = 'SELECT role_name FROM ' . ACL_ROLES_TABLE . '
  1629. WHERE role_id = ' . $role_id;
  1630. $this->db->sql_query($sql);
  1631. $role_name = $this->db->sql_fetchfield('role_name');
  1632. return $this->permission_set($role_name, $auth_option, 'role', $has_permission);
  1633. }
  1634. $this->umil_start('PERMISSION_SET_GROUP', $name);
  1635. $sql = 'SELECT auth_option_id, auth_setting FROM ' . ACL_GROUPS_TABLE . '
  1636. WHERE group_id = ' . $group_id;
  1637. $result = $this->db->sql_query($sql);
  1638. while ($row = $this->db->sql_fetchrow($result))
  1639. {
  1640. $current_auth[$row['auth_option_id']] = $row['auth_setting'];
  1641. }
  1642. $this->db->sql_freeresult($result);
  1643. break;
  1644. }
  1645. $sql_ary = array();
  1646. switch ($type)
  1647. {
  1648. case 'role' :
  1649. foreach ($new_auth as $auth_option_id)
  1650. {
  1651. if (!isset($current_auth[$auth_option_id]))
  1652. {
  1653. $sql_ary[] = array(
  1654. 'role_id' => $role_id,
  1655. 'auth_option_id' => $auth_option_id,
  1656. 'auth_setting' => $has_permission,
  1657. );
  1658. }
  1659. }
  1660. $this->db->sql_multi_insert(ACL_ROLES_DATA_TABLE, $sql_ary);
  1661. break;
  1662. case 'group' :
  1663. foreach ($new_auth as $auth_option_id)
  1664. {
  1665. if (!isset($current_auth[$auth_option_id]))
  1666. {
  1667. $sql_ary[] = array(
  1668. 'group_id' => $group_id,
  1669. 'auth_option_id' => $auth_option_id,
  1670. 'auth_setting' => $has_permission,
  1671. );
  1672. }
  1673. }
  1674. $this->db->sql_multi_insert(ACL_GROUPS_TABLE, $sql_ary);
  1675. break;
  1676. }
  1677. $auth->acl_clear_prefetch();
  1678. return $this->umil_end();
  1679. }
  1680. /**
  1681. * Permission Unset
  1682. *
  1683. * Allows you to unset (remove) permissions for a certain group/role
  1684. *
  1685. * @param string $name The name of the role/group
  1686. * @param string|array $auth_option The auth_option or array of auth_options you would like to set
  1687. * @param string $type The type (role|group)
  1688. */
  1689. function permission_unset($name, $auth_option = array(), $type = 'role')
  1690. {
  1691. global $auth;
  1692. // Multicall
  1693. if ($this->multicall(__FUNCTION__, $name))
  1694. {
  1695. return;
  1696. }
  1697. if (!is_array($auth_option))
  1698. {
  1699. $auth_option = array($auth_option);
  1700. }
  1701. $to_remove = array();
  1702. $sql = 'SELECT auth_option_id FROM ' . ACL_OPTIONS_TABLE . '
  1703. WHERE ' . $this->db->sql_in_set('auth_option', $auth_option);
  1704. $result = $this->db->sql_query($sql);
  1705. while ($row = $this->db->sql_fetchrow($result))
  1706. {
  1707. $to_remove[] = $row['auth_option_id'];
  1708. }
  1709. $this->db->sql_freeresult($result);
  1710. if (!sizeof($to_remove))
  1711. {
  1712. return false;
  1713. }
  1714. $type = (string) $type; // Prevent PHP bug.
  1715. switch ($type)
  1716. {
  1717. case 'role' :
  1718. $this->umil_start('PERMISSION_UNSET_ROLE', $name);
  1719. $sql = 'SELECT role_id FROM ' . ACL_ROLES_TABLE . '
  1720. WHERE role_name = \'' . $this->db->sql_escape($name) . '\'';
  1721. $this->db->sql_query($sql);
  1722. $role_id = $this->db->sql_fetchfield('role_id');
  1723. if (!$role_id)
  1724. {
  1725. return $this->umil_end('ROLE_NOT_EXIST');
  1726. }
  1727. $sql = 'DELETE FROM ' . ACL_ROLES_DATA_TABLE . '
  1728. WHERE ' . $this->db->sql_in_set('auth_option_id', $to_remove);
  1729. $this->db->sql_query($sql);
  1730. break;
  1731. case 'group' :
  1732. $sql = 'SELECT group_id FROM ' . GROUPS_TABLE . ' WHERE group_name = \'' . $this->db->sql_escape($name) . '\'';
  1733. $this->db->sql_query($sql);
  1734. $group_id = $this->db->sql_fetchfield('group_id');
  1735. if (!$group_id)
  1736. {
  1737. $this->umil_start('PERMISSION_UNSET_GROUP', $name);
  1738. return $this->umil_end('GROUP_NOT_EXIST');
  1739. }
  1740. // If the group has a role set for them we will remove the requested permissions from that role.
  1741. $sql = 'SELECT auth_role_id FROM ' . ACL_GROUPS_TABLE . '
  1742. WHERE group_id = ' . $group_id . '
  1743. AND auth_role_id <> 0';
  1744. $this->db->sql_query($sql);
  1745. $role_id = $this->db->sql_fetchfield('auth_role_id');
  1746. if ($role_id)
  1747. {
  1748. $sql = 'SELECT role_name FROM ' . ACL_ROLES_TABLE . '
  1749. WHERE role_id = ' . $role_id;
  1750. $this->db->sql_query($sql);
  1751. $role_name = $this->db->sql_fetchfield('role_name');
  1752. return $this->permission_unset($role_name, $auth_option, 'role');
  1753. }
  1754. $this->umil_start('PERMISSION_UNSET_GROUP', $name);
  1755. $sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . '
  1756. WHERE ' . $this->db->sql_in_set('auth_option_id', $to_remove);
  1757. $this->db->sql_query($sql);
  1758. break;
  1759. }
  1760. $auth->acl_clear_prefetch();
  1761. return $this->umil_end();
  1762. }
  1763. /**
  1764. * Table Exists
  1765. *
  1766. * Check if a table exists in the DB or not
  1767. *
  1768. * @param string $table_name The table name to check for
  1769. *
  1770. * @return bool true if the table exists, false if not
  1771. */
  1772. function table_exists($table_name)
  1773. {
  1774. $this->get_table_name($table_name);
  1775. // Use sql_table_exists if available
  1776. if (method_exists($this->db_tools, 'sql_table_exists'))
  1777. {
  1778. $roe = $this->db->return_on_error;
  1779. $result = $this->db_tools->sql_table_exists($table_name);
  1780. // 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
  1781. if ($roe)
  1782. {
  1783. $this->db->sql_return_on_error(true);
  1784. }
  1785. return $result;
  1786. }
  1787. if (!function_exists('get_tables'))
  1788. {
  1789. global $phpbb_root_path, $phpEx;
  1790. include($phpbb_root_path . 'includes/functions_install.' . $phpEx);
  1791. }
  1792. $tables = get_tables($this->db);
  1793. if (in_array($table_name, $tables))
  1794. {
  1795. return true;
  1796. }
  1797. else
  1798. {
  1799. return false;
  1800. }
  1801. }
  1802. /**
  1803. * Table Add
  1804. *
  1805. * This only supports input from the array format of db_tools or create_schema_files.
  1806. */
  1807. function table_add($table_name, $table_data = array())
  1808. {
  1809. global $dbms, $user;
  1810. // Multicall
  1811. if ($this->multicall(__FUNCTION__, $table_name))
  1812. {
  1813. return;
  1814. }
  1815. /**
  1816. * $table_data can be empty when uninstalling a mod and table_remove was used, but no 2rd argument was given.
  1817. * 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
  1818. */
  1819. if (empty($table_data))
  1820. {
  1821. return;
  1822. }
  1823. $this->get_table_name($table_name);
  1824. $this->umil_start('TABLE_ADD', $table_name);
  1825. if ($this->table_exists($table_name))
  1826. {
  1827. return $this->umil_end('TABLE_ALREADY_EXISTS', $table_name);
  1828. }
  1829. if (!is_array($table_data))
  1830. {
  1831. return $this->umil_end('NO_TABLE_DATA');
  1832. }
  1833. if (!function_exists('get_available_dbms'))
  1834. {
  1835. global $phpbb_root_path, $phpEx;
  1836. include("{$phpbb_root_path}includes/functions_install.$phpEx");
  1837. }
  1838. /*
  1839. * This function has had numerous problems and is currently broken, so until phpBB uses it I will not be anymore
  1840. if (method_exists($this->db_tools, 'sql_create_table'))
  1841. {
  1842. // Added in 3.0.5
  1843. $this->db_tools->sql_create_table($table_name, $table_data);
  1844. }
  1845. else
  1846. {*/
  1847. $available_dbms = get_available_dbms($dbms);
  1848. $sql_query = $this->create_table_sql($table_name, $table_data);
  1849. $sql_query = split_sql_file($sql_query, $available_dbms[$dbms]['DELIM']);
  1850. foreach ($sql_query as $sql)
  1851. {
  1852. $this->db->sql_query($sql);
  1853. }
  1854. //}
  1855. return $this->umil_end();
  1856. }
  1857. /**
  1858. * Table Remove
  1859. *
  1860. * Delete/Drop a DB table
  1861. */
  1862. function table_remove($table_name)
  1863. {
  1864. // Multicall
  1865. if ($this->multicall(__FUNCTION__, $table_name))
  1866. {
  1867. return;
  1868. }
  1869. $this->get_table_name($table_name);
  1870. $this->umil_start('TABLE_REMOVE', $table_name);
  1871. if (!$this->table_exists($table_name))
  1872. {
  1873. return $this->umil_end('TABLE_NOT_EXIST', $table_name);
  1874. }
  1875. if (method_exists($this->db_tools, 'sql_table_drop'))
  1876. {
  1877. // Added in 3.0.5
  1878. $this->db_tools->sql_table_drop($table_name);
  1879. }
  1880. else
  1881. {
  1882. $this->db->sql_query('DROP TABLE ' . $table_name);
  1883. }
  1884. return $this->umil_end();
  1885. }
  1886. /**
  1887. * Table Column Exists
  1888. *
  1889. * Check to see if a column exists in a table
  1890. */
  1891. function table_column_exists($table_name, $column_name)
  1892. {
  1893. $this->get_table_name($table_name);
  1894. return $this->db_tools->sql_column_exists($table_name, $column_name);
  1895. }
  1896. /**
  1897. * Table Column Add
  1898. *
  1899. * Add a new column to a table.
  1900. */
  1901. function table_column_add($table_name, $column_name = '', $column_data = array())
  1902. {
  1903. // Multicall
  1904. if ($this->multicall(__FUNCTION__, $table_name))
  1905. {
  1906. return;
  1907. }
  1908. /**
  1909. * $column_data can be empty when uninstalling a mod and table_column_remove was used, but no 3rd argument was given.
  1910. * 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
  1911. */
  1912. if (empty($column_data))
  1913. {
  1914. return;
  1915. }
  1916. $this->get_table_name($table_name);
  1917. $this->umil_start('TABLE_COLUMN_ADD', $table_name, $column_name);
  1918. if ($this->table_column_exists($table_name, $column_name))
  1919. {
  1920. return $this->umil_end('TABLE_COLUMN_ALREADY_EXISTS', $table_name, $column_name);
  1921. }
  1922. $this->db_tools->sql_column_add($table_name, $column_name, $column_data);
  1923. return $this->umil_end();
  1924. }
  1925. /**
  1926. * Table Column Update
  1927. *
  1928. * Alter/Update a column in a table. You can not change a column name with this.
  1929. */
  1930. function table_column_update($table_name, $column_name = '', $column_data = array())
  1931. {
  1932. // Multicall
  1933. if ($this->multicall(__FUNCTION__, $table_name))
  1934. {
  1935. return;
  1936. }
  1937. $this->get_table_name($table_name);
  1938. $this->umil_start('TABLE_COLUMN_UPDATE', $table_name, $column_name);
  1939. if (!$this->table_column_exists($table_name, $column_name))
  1940. {
  1941. return $this->umil_end('TABLE_COLUMN_NOT_EXIST', $table_name, $column_name);
  1942. }
  1943. $this->db_tools->sql_column_change($table_name, $column_name, $column_data);
  1944. return $this->umil_end();
  1945. }
  1946. /**
  1947. * Table Column Remove
  1948. *
  1949. * Remove a column from a table
  1950. */
  1951. function table_column_remove($table_name, $column_name = '')
  1952. {
  1953. // Multicall
  1954. if ($this->multicall(__FUNCTION__, $table_name))
  1955. {
  1956. return;
  1957. }
  1958. $this->get_table_name($table_name);
  1959. $this->umil_start('TABLE_COLUMN_REMOVE', $table_name, $column_name);
  1960. if (!$this->table_column_exists($table_name, $column_name))
  1961. {
  1962. return $this->umil_end('TABLE_COLUMN_NOT_EXIST', $table_name, $column_name);
  1963. }
  1964. $this->db_tools->sql_column_remove($table_name, $column_name);
  1965. return $this->umil_end();
  1966. }
  1967. /**
  1968. * Table Index Exists
  1969. *
  1970. * Check if a table key/index exists on a table (can not check primary or unique)
  1971. */
  1972. function table_index_exists($table_name, $index_name)
  1973. {
  1974. $this->get_table_name($table_name);
  1975. $indexes = $this->db_tools->sql_list_index($table_name);
  1976. if (in_array($index_name, $indexes))
  1977. {
  1978. return true;
  1979. }
  1980. return false;
  1981. }
  1982. /**
  1983. * Table Index Add
  1984. *
  1985. * Add a new key/index to a table
  1986. */
  1987. function table_index_add($table_name, $index_name = '', $column = array())
  1988. {
  1989. global $config;
  1990. // Multicall
  1991. if ($this->multicall(__FUNCTION__, $table_name))
  1992. {
  1993. return;
  1994. }
  1995. // Let them skip the column field and just use the index name in that case as the column as well
  1996. if (empty($column))
  1997. {
  1998. $column = array($index_name);
  1999. }
  2000. $this->get_table_name($table_name);
  2001. $this->umil_start('TABLE_KEY_ADD', $table_name, $index_name);
  2002. if ($this->table_index_exists($table_name, $index_name))
  2003. {
  2004. return $this->umil_end('TABLE_KEY_ALREADY_EXIST', $table_name, $index_name);
  2005. }
  2006. if (!is_array($column))
  2007. {
  2008. $column = array($column);
  2009. }
  2010. // remove index l