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

/forum/includes/acp/acp_mods.php

https://github.com/GreyTeardrop/socionicasys-forum
PHP | 2211 lines | 1686 code | 356 blank | 169 comment | 314 complexity | 7064d9ae3a19fc5c70918081d39d3d3a MD5 | raw file
Possible License(s): AGPL-1.0, LGPL-3.0, MPL-2.0-no-copyleft-exception

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. *
  4. * @package automod
  5. * @version $Id: acp_mods.php 249 2010-05-14 16:14:43Z jelly_doughnut $
  6. * @copyright (c) 2008 phpBB Group
  7. * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
  8. *
  9. */
  10. /**
  11. */
  12. if (!defined('IN_PHPBB'))
  13. {
  14. exit;
  15. }
  16. /**
  17. * @package automod
  18. */
  19. class acp_mods
  20. {
  21. var $u_action;
  22. var $parser;
  23. var $mod_root = '';
  24. var $mods_dir = '';
  25. var $edited_root = '';
  26. var $backup_root = '';
  27. function main($id, $mode)
  28. {
  29. global $config, $db, $user, $auth, $template, $cache;
  30. global $phpbb_root_path, $phpEx;
  31. global $method, $test_ftp_connection, $test_connection;
  32. include("{$phpbb_root_path}includes/functions_transfer.$phpEx");
  33. include("{$phpbb_root_path}includes/editor.$phpEx");
  34. include("{$phpbb_root_path}includes/functions_mods.$phpEx");
  35. include("{$phpbb_root_path}includes/mod_parser.$phpEx");
  36. // start the page
  37. $user->add_lang(array('install', 'acp/mods'));
  38. $this->tpl_name = 'acp_mods';
  39. $this->page_title = 'ACP_CAT_MODS';
  40. $this->mods_dir = $phpbb_root_path . 'store/mods';
  41. // get any url vars
  42. $action = request_var('action', '');
  43. $mod_id = request_var('mod_id', 0);
  44. $mod_url = request_var('mod_url', '');
  45. $parent = request_var('parent', 0);
  46. $mod_path = request_var('mod_path', '');
  47. if ($mod_path)
  48. {
  49. $mod_path = htmlspecialchars_decode($mod_path);
  50. $mod_dir = substr($mod_path, 1, strpos($mod_path, '/', 1));
  51. $this->mod_root = $this->mods_dir . '/' . $mod_dir;
  52. $this->backup_root = $this->mod_root . '_backups/';
  53. }
  54. switch ($mode)
  55. {
  56. case 'config':
  57. $ftp_method = request_var('ftp_method', $config['ftp_method']);
  58. if (!$ftp_method || !class_exists($ftp_method))
  59. {
  60. $ftp_method = 'ftp';
  61. $ftp_methods = transfer::methods();
  62. if (!in_array('ftp', $ftp_methods))
  63. {
  64. $ftp_method = $ftp_methods[0];
  65. }
  66. }
  67. if (isset($_POST['submit']) && check_form_key('acp_mods'))
  68. {
  69. $ftp_host = request_var('host', '');
  70. $ftp_username = request_var('username', '');
  71. $ftp_password = request_var('password', ''); // not stored, used to test connection
  72. $ftp_root_path = request_var('root_path', '');
  73. $ftp_port = request_var('port', 21);
  74. $ftp_timeout = request_var('timeout', 10);
  75. $write_method = request_var('write_method', 0);
  76. $file_perms = request_var('file_perms', '0644');
  77. $dir_perms = request_var('dir_perms', '0755');
  78. $compress_method = request_var('compress_method', '');
  79. $preview_changes = request_var('preview_changes', 0);
  80. $error = '';
  81. if ($write_method == WRITE_DIRECT)
  82. {
  83. // the very best method would be to check every file for is_writable
  84. if (!is_writable("{$phpbb_root_path}common.$phpEx") || !is_writable("{$phpbb_root_path}adm/style/acp_groups.html"))
  85. {
  86. $error = 'FILESYSTEM_NOT_WRITABLE';
  87. }
  88. }
  89. else if ($write_method == WRITE_FTP)
  90. {
  91. // check the correctness of FTP infos
  92. $test_ftp_connection = true;
  93. $test_connection = false;
  94. test_ftp_connection($ftp_method, $test_ftp_connection, $test_connection);
  95. if ($test_connection !== true)
  96. {
  97. $error = $test_connection;
  98. }
  99. }
  100. else if ($write_method == WRITE_MANUAL)
  101. {
  102. // the compress class requires write access to the /store/ dir
  103. if (!is_writable("{$phpbb_root_path}store/"))
  104. {
  105. $error = 'STORE_NOT_WRITABLE';
  106. }
  107. }
  108. if (empty($error))
  109. {
  110. set_config('ftp_method', $ftp_method);
  111. set_config('ftp_host', $ftp_host);
  112. set_config('ftp_username', $ftp_username);
  113. set_config('ftp_root_path', $ftp_root_path);
  114. set_config('ftp_port', $ftp_port);
  115. set_config('ftp_timeout', $ftp_timeout);
  116. set_config('write_method', $write_method);
  117. set_config('compress_method', $compress_method);
  118. set_config('preview_changes', $preview_changes);
  119. set_config('am_file_perms', $file_perms);
  120. set_config('am_dir_perms', $dir_perms);
  121. trigger_error($user->lang['MOD_CONFIG_UPDATED'] . adm_back_link($this->u_action));
  122. }
  123. else
  124. {
  125. $template->assign_var('ERROR', $user->lang[$error]);
  126. }
  127. }
  128. else if (isset($_POST['submit']) && !check_form_key('acp_mods'))
  129. {
  130. trigger_error($user->lang['FORM_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING);
  131. }
  132. add_form_key('acp_mods');
  133. // implicit else
  134. include("{$phpbb_root_path}includes/functions_compress.$phpEx");
  135. foreach (compress::methods() as $method)
  136. {
  137. $template->assign_block_vars('compress', array(
  138. 'METHOD' => $method,
  139. ));
  140. }
  141. $requested_data = call_user_func(array($ftp_method, 'data'));
  142. foreach ($requested_data as $data => $default)
  143. {
  144. $default = (!empty($config['ftp_' . $data])) ? $config['ftp_' . $data] : $default;
  145. $template->assign_block_vars('data', array(
  146. 'DATA' => $data,
  147. 'NAME' => $user->lang[strtoupper($ftp_method . '_' . $data)],
  148. 'EXPLAIN' => $user->lang[strtoupper($ftp_method . '_' . $data) . '_EXPLAIN'],
  149. 'DEFAULT' => (!empty($_REQUEST[$data])) ? request_var($data, '') : $default
  150. ));
  151. }
  152. $template->assign_vars(array(
  153. 'S_CONFIG' => true,
  154. 'U_CONFIG' => $this->u_action . '&amp;mode=config',
  155. 'UPLOAD_METHOD_FTP' => ($config['ftp_method'] == 'ftp') ? ' checked="checked"' : '',
  156. 'UPLOAD_METHOD_FSOCK'=> ($config['ftp_method'] == 'ftp_fsock') ? ' checked="checked"' : '',
  157. 'WRITE_DIRECT' => ($config['write_method'] == WRITE_DIRECT) ? ' checked="checked"' : '',
  158. 'WRITE_FTP' => ($config['write_method'] == WRITE_FTP) ? ' checked="checked"' : '',
  159. 'WRITE_MANUAL' => ($config['write_method'] == WRITE_MANUAL) ? ' checked="checked"' : '',
  160. 'WRITE_METHOD_DIRECT' => WRITE_DIRECT,
  161. 'WRITE_METHOD_FTP' => WRITE_FTP,
  162. 'WRITE_METHOD_MANUAL' => WRITE_MANUAL,
  163. 'AUTOMOD_VERSION' => $config['automod_version'],
  164. 'COMPRESS_METHOD' => $config['compress_method'],
  165. 'DIR_PERMS' => $config['am_dir_perms'],
  166. 'FILE_PERMS' => $config['am_file_perms'],
  167. 'PREVIEW_CHANGES_YES' => ($config['preview_changes']) ? ' checked="checked"' : '',
  168. 'PREVIEW_CHANGES_NO' => (!$config['preview_changes']) ? ' checked="checked"' : '',
  169. 'S_HIDE_FTP' => ($config['write_method'] == WRITE_FTP) ? false : true,
  170. ));
  171. break;
  172. case 'frontend':
  173. if ($config['write_method'] == WRITE_FTP)
  174. {
  175. $method = basename(request_var('method', $config['ftp_method']));
  176. if (!$method || !class_exists($method))
  177. {
  178. $method = 'ftp';
  179. $methods = transfer::methods();
  180. if (!in_array('ftp', $methods))
  181. {
  182. $method = $methods[0];
  183. }
  184. }
  185. $test_connection = false;
  186. $test_ftp_connection = request_var('test_connection', '');
  187. if (!empty($test_ftp_connection) || $action == 'install')
  188. {
  189. test_ftp_connection($method, $test_ftp_connection, $test_connection);
  190. // Make sure the login details are correct before continuing
  191. if ($test_connection !== true || !empty($test_ftp_connection))
  192. {
  193. $action = 'pre_install';
  194. $test_ftp_connection = true;
  195. }
  196. }
  197. }
  198. else if ($config['write_method'] == WRITE_MANUAL && !is_writable($phpbb_root_path . 'store/'))
  199. {
  200. $template->assign_var('S_WRITABLE_WARN', true);
  201. }
  202. switch ($action)
  203. {
  204. case 'pre_install':
  205. $this->pre_install($mod_path);
  206. break;
  207. case 'install':
  208. $this->install($mod_path, $parent);
  209. break;
  210. case 'pre_uninstall':
  211. case 'uninstall':
  212. $this->uninstall($action, $mod_id);
  213. break;
  214. case 'details':
  215. $mod_ident = ($mod_id) ? $mod_id : $mod_path;
  216. $this->list_details($mod_ident);
  217. break;
  218. case 'delete':
  219. $this->delete($mod_path);
  220. default:
  221. if (!$this->upload_mod())
  222. {
  223. $can_upload = (@ini_get('file_uploads') == '0' || strtolower(@ini_get('file_uploads')) == 'off' || !@extension_loaded('zlib')) ? false : true;
  224. $template->assign_vars(array(
  225. 'S_FRONTEND' => true,
  226. 'S_MOD_UPLOAD' => ($can_upload) ? true : false,
  227. 'U_UPLOAD' => $this->u_action,
  228. 'S_FORM_ENCTYPE' => ($can_upload) ? ' enctype="multipart/form-data"' : '',
  229. ));
  230. add_form_key('acp_mods_upload');
  231. $this->list_installed();
  232. $this->list_uninstalled();
  233. }
  234. break;
  235. case 'download':
  236. include ($phpbb_root_path . "includes/functions_compress.$phpEx");
  237. $editor = new editor_manual();
  238. $time = request_var('time', 0);
  239. // if for some reason the MOD isn't found in the DB...
  240. $download_name = 'mod_' . $time;
  241. $sql = 'SELECT mod_name FROM ' . MODS_TABLE . '
  242. WHERE mod_time = ' . $time;
  243. $result = $db->sql_query($sql);
  244. if ($row = $db->sql_fetchrow($result))
  245. {
  246. $download_name = str_replace(' ', '_', $row['mod_name']);
  247. }
  248. $editor->compress->download("{$phpbb_root_path}store/mod_$time", $download_name);
  249. exit;
  250. break;
  251. }
  252. return;
  253. break;
  254. }
  255. }
  256. /**
  257. * List all the installed mods
  258. */
  259. function list_installed()
  260. {
  261. global $db, $template;
  262. $sql = 'SELECT mod_id, mod_name
  263. FROM ' . MODS_TABLE . '
  264. ORDER BY mod_name ASC';
  265. $result = $db->sql_query($sql);
  266. while ($row = $db->sql_fetchrow($result))
  267. {
  268. $template->assign_block_vars('installed', array(
  269. 'MOD_ID' => $row['mod_id'],
  270. 'MOD_NAME' => $row['mod_name'],
  271. 'U_DETAILS' => $this->u_action . '&amp;action=details&amp;mod_id=' . $row['mod_id'],
  272. 'U_UNINSTALL' => $this->u_action . '&amp;action=pre_uninstall&amp;mod_id=' . $row['mod_id'])
  273. );
  274. }
  275. $db->sql_freeresult($result);
  276. return;
  277. }
  278. /**
  279. * List all mods available locally
  280. */
  281. function list_uninstalled()
  282. {
  283. global $phpbb_root_path, $db, $template;
  284. // get available MOD paths
  285. $available_mods = $this->find_mods($this->mods_dir, 1);
  286. if (!sizeof($available_mods['main']))
  287. {
  288. return;
  289. }
  290. // get installed MOD paths
  291. $installed_paths = array();
  292. $sql = 'SELECT mod_path
  293. FROM ' . MODS_TABLE;
  294. $result = $db->sql_query($sql);
  295. while ($row = $db->sql_fetchrow($result))
  296. {
  297. $installed_paths[] = $row['mod_path'];
  298. }
  299. $db->sql_freeresult($result);
  300. $mod_paths = array();
  301. foreach ($available_mods['main'] as $mod_info)
  302. {
  303. $mod_paths[] = $mod_info['href'];
  304. }
  305. // we don't care about any xml files not in the main directory
  306. $available_mods = array_diff($mod_paths, $installed_paths);
  307. unset($installed_paths);
  308. unset($mod_paths);
  309. // show only available MODs that paths aren't in the DB
  310. foreach ($available_mods as $file)
  311. {
  312. $details = $this->mod_details($file, false);
  313. $short_path = urlencode(str_replace($this->mods_dir, '', $details['MOD_PATH']));
  314. $template->assign_block_vars('uninstalled', array(
  315. 'MOD_NAME' => $details['MOD_NAME'],
  316. 'MOD_PATH' => $short_path,
  317. 'U_INSTALL' => $this->u_action . "&amp;action=pre_install&amp;mod_path=$short_path",
  318. 'U_DELETE' => $this->u_action . "&amp;action=delete&amp;mod_path=$short_path",
  319. 'U_DETAILS' => $this->u_action . "&amp;action=details&amp;mod_path=$short_path",
  320. ));
  321. }
  322. return;
  323. }
  324. /**
  325. * Lists mod details
  326. */
  327. function list_details($mod_ident)
  328. {
  329. global $template;
  330. $template->assign_vars(array(
  331. 'S_DETAILS' => true,
  332. 'U_BACK' => $this->u_action,
  333. ));
  334. $details = $this->mod_details($mod_ident, true);
  335. if (!empty($details['AUTHOR_DETAILS']))
  336. {
  337. foreach ($details['AUTHOR_DETAILS'] as $author_details)
  338. {
  339. $template->assign_block_vars('author_list', $author_details);
  340. }
  341. unset($details['AUTHOR_DETAILS']);
  342. }
  343. if (!empty($details['MOD_HISTORY']))
  344. {
  345. $template->assign_var('S_CHANGELOG', true);
  346. foreach ($details['MOD_HISTORY'] as $mod_version)
  347. {
  348. $template->assign_block_vars('changelog', array(
  349. 'VERSION' => $mod_version['VERSION'],
  350. 'DATE' => $mod_version['DATE'],
  351. ));
  352. foreach ($mod_version['CHANGES'] as $changes)
  353. {
  354. $template->assign_block_vars('changelog.changes', array(
  355. 'CHANGE' => $changes,
  356. ));
  357. }
  358. }
  359. }
  360. unset($details['MOD_HISTORY']);
  361. $template->assign_vars($details);
  362. if (!empty($details['AUTHOR_NOTES']))
  363. {
  364. $template->assign_var('S_AUTHOR_NOTES', true);
  365. }
  366. if (!empty($details['MOD_INSTALL_TIME']))
  367. {
  368. $template->assign_var('S_INSTALL_TIME', true);
  369. }
  370. return;
  371. }
  372. /**
  373. * Returns array of mod information
  374. */
  375. function mod_details($mod_ident, $find_children = true, $uninstall = false)
  376. {
  377. global $phpbb_root_path, $phpEx, $user, $template, $parent_id;
  378. if (is_int($mod_ident))
  379. {
  380. global $db, $user;
  381. $mod_id = (int) $mod_ident;
  382. $sql = 'SELECT *
  383. FROM ' . MODS_TABLE . "
  384. WHERE mod_id = $mod_id";
  385. $result = $db->sql_query($sql);
  386. if ($row = $db->sql_fetchrow($result))
  387. {
  388. // TODO: Yuck, get rid of this.
  389. $author_details = array();
  390. $author_details[0] = array(
  391. 'AUTHOR_NAME' => $row['mod_author_name'],
  392. 'AUTHOR_EMAIL' => $row['mod_author_email'],
  393. 'AUTHOR_WEBSITE' => $row['mod_author_url'],
  394. );
  395. $details = array(
  396. 'MOD_ID' => $row['mod_id'],
  397. 'MOD_PATH' => $row['mod_path'],
  398. 'MOD_INSTALL_TIME' => $user->format_date($row['mod_time']),
  399. // 'MOD_DEPENDENCIES' => unserialize($row['mod_dependencies']), // ?
  400. 'MOD_NAME' => htmlspecialchars($row['mod_name']),
  401. 'MOD_DESCRIPTION' => nl2br($row['mod_description']),
  402. 'MOD_VERSION' => $row['mod_version'],
  403. 'AUTHOR_NOTES' => nl2br($row['mod_author_notes']),
  404. 'AUTHOR_DETAILS' => $author_details,
  405. );
  406. // This is a check for any further XML files to go with this MOD.
  407. // Obviously, the files must not have been removed for this to work.
  408. if (($find_children || $uninstall) && file_exists($row['mod_path']))
  409. {
  410. $parent_id = $mod_id;
  411. $mod_path = $row['mod_path'];
  412. $actions = array();
  413. $mod_dir = dirname($mod_path);
  414. $this->mod_root = $mod_dir . '/';
  415. $this->backup_root = $this->mod_root . '_backups/';
  416. $ext = substr(strrchr($mod_path, '.'), 1);
  417. $this->parser = new parser($ext);
  418. $this->parser->set_file($mod_path);
  419. // Find and display the available MODX files
  420. $children = $this->find_children($mod_path);
  421. $elements = array('language' => array(), 'template' => array());
  422. $found_prosilver = false;
  423. if (!$uninstall)
  424. {
  425. $this->handle_contrib($children);
  426. $this->handle_language_prompt($children, $elements, 'details');
  427. $this->handle_template_prompt($children, $elements, 'details');
  428. // Now offer to install additional templates
  429. if (isset($children['template']) && sizeof($children['template']))
  430. {
  431. // These are the instructions included with the MOD
  432. foreach ($children['template'] as $template_name)
  433. {
  434. if (!is_array($template_name))
  435. {
  436. continue;
  437. }
  438. if ($template_name['realname'] == 'prosilver')
  439. {
  440. $found_prosilver = true;
  441. }
  442. if (file_exists($this->mod_root . $template_name['href']))
  443. {
  444. $xml_file = $template_name['href'];
  445. }
  446. else
  447. {
  448. $xml_file = str_replace($this->mods_dir, '', dirname($row['mod_path'])) . '/' . $template_name['href'];
  449. }
  450. $template->assign_block_vars('avail_templates', array(
  451. 'TEMPLATE_NAME' => $template_name['realname'],
  452. 'XML_FILE' => urlencode($xml_file),
  453. ));
  454. }
  455. }
  456. }
  457. else
  458. {
  459. if (isset($children['uninstall']) && sizeof($children['uninstall']))
  460. {
  461. // Override already exising actions with the ones
  462. global $rev_actions;
  463. $xml_file = dirname($row['mod_path']) . '/' . ltrim($children['uninstall'][0]['href'], './');
  464. $this->parser->set_file($xml_file);
  465. $rev_actions = $this->parser->get_actions();
  466. }
  467. }
  468. if (!$found_prosilver)
  469. {
  470. $template->assign_block_vars('avail_templates', array(
  471. 'TEMPLATE_NAME' => 'prosilver',
  472. 'XML_FILE' => basename($row['mod_path']),
  473. ));
  474. }
  475. $processed_templates = array('prosilver');
  476. $processed_templates += explode(',', $row['mod_template']);
  477. // now grab the templates that have not already been processed
  478. $sql = 'SELECT template_id, template_path FROM ' . STYLES_TEMPLATE_TABLE . '
  479. WHERE ' . $db->sql_in_set('template_name', $processed_templates, true);
  480. $result = $db->sql_query($sql);
  481. while ($row = $db->sql_fetchrow($result))
  482. {
  483. $template->assign_block_vars('board_templates', array(
  484. 'TEMPLATE_ID' => $row['template_id'],
  485. 'TEMPLATE_NAME' => $row['template_path'],
  486. ));
  487. }
  488. $s_hidden_fields = build_hidden_fields(array(
  489. 'action' => ($uninstall) ? 'uninstall' : 'install',
  490. 'parent' => $parent_id,
  491. ));
  492. $template->assign_vars(array(
  493. 'S_FORM_ACTION' => $this->u_action,
  494. 'S_HIDDEN_FIELDS' => $s_hidden_fields,
  495. ));
  496. add_form_key('acp_mods');
  497. }
  498. }
  499. else
  500. {
  501. trigger_error($user->lang['NO_MOD'] . adm_back_link($this->u_action), E_USER_WARNING);
  502. }
  503. $db->sql_freeresult($result);
  504. }
  505. else
  506. {
  507. $parent = request_var('parent', 0);
  508. if ($parent)
  509. {
  510. global $db;
  511. // reset the class parameters to refelect the proper directory
  512. $sql = 'SELECT mod_path FROM ' . MODS_TABLE . '
  513. WHERE mod_id = ' . (int) $parent;
  514. $result = $db->sql_query($sql);
  515. if ($row = $db->sql_fetchrow($result))
  516. {
  517. $this->mod_root = dirname($row['mod_path']) . '/';
  518. }
  519. }
  520. if (strpos($mod_ident, $this->mods_dir) === false)
  521. {
  522. $mod_ident = $this->mods_dir . $mod_ident;
  523. }
  524. if (!file_exists($mod_ident))
  525. {
  526. $mod_ident = str_replace($this->mods_dir, $this->mod_root, $mod_ident);
  527. }
  528. $mod_path = $mod_ident;
  529. $mod_parent = 0;
  530. $ext = substr(strrchr($mod_path, '.'), 1);
  531. $this->parser = new parser($ext);
  532. $this->parser->set_file($mod_path);
  533. $details = $this->parser->get_details();
  534. if ($find_children)
  535. {
  536. $actions = array();
  537. $children = $this->find_children($mod_path);
  538. $elements = array('language' => array(), 'template' => array());
  539. $this->handle_contrib($children);
  540. $this->handle_language_prompt($children, $elements, 'details');
  541. $this->handle_template_prompt($children, $elements, 'details');
  542. }
  543. }
  544. return $details;
  545. }
  546. /**
  547. * Returns complex array of all mod actions
  548. */
  549. function mod_actions($mod_ident)
  550. {
  551. global $phpbb_root_path, $phpEx;
  552. if (is_int($mod_ident))
  553. {
  554. global $db, $user;
  555. $sql = 'SELECT mod_actions
  556. FROM ' . MODS_TABLE . "
  557. WHERE mod_id = $mod_ident";
  558. $result = $db->sql_query($sql);
  559. $row = $db->sql_fetchrow($result);
  560. $db->sql_freeresult($result);
  561. if ($row)
  562. {
  563. return unserialize($row['mod_actions']);
  564. }
  565. else
  566. {
  567. trigger_error($user->lang['NO_MOD'] . adm_back_link($this->u_action), E_USER_WARNING);
  568. }
  569. }
  570. else
  571. {
  572. if (strpos($mod_ident, $this->mods_dir) === false)
  573. {
  574. $mod_ident = $this->mods_dir . $mod_ident;
  575. }
  576. if (!file_exists($mod_ident))
  577. {
  578. $mod_ident = str_replace($this->mods_dir, $this->mod_root, $mod_ident);
  579. }
  580. $this->parser->set_file($mod_ident);
  581. $actions = $this->parser->get_actions();
  582. }
  583. return $actions;
  584. }
  585. /**
  586. * Parses and displays all Edits, Copies, and SQL modifcations
  587. */
  588. function pre_install($mod_path)
  589. {
  590. global $phpbb_root_path, $phpEx, $template, $db, $config, $user, $method, $test_ftp_connection, $test_connection;
  591. // mod_path empty?
  592. if (empty($mod_path))
  593. {
  594. // ERROR
  595. return false;
  596. }
  597. $details = $this->mod_details($mod_path, false);
  598. $actions = $this->mod_actions($mod_path);
  599. $elements = array('language' => array(), 'template' => array());
  600. // check for "child" MODX files and attempt to decide which ones we need
  601. $children = $this->find_children($mod_path);
  602. $this->handle_language_prompt($children, $elements, 'pre_install');
  603. $this->handle_merge('language', $actions, $children, $elements['language']);
  604. $this->handle_template_prompt($children, $elements, 'pre_install');
  605. $this->handle_merge('template', $actions, $children, $elements['template']);
  606. $template->assign_vars(array(
  607. 'S_PRE_INSTALL' => true,
  608. 'MOD_PATH' => str_replace($this->mod_root, '', $mod_path),
  609. 'U_INSTALL' => $this->u_action . '&amp;action=install',
  610. 'U_BACK' => $this->u_action,
  611. ));
  612. $s_hidden_fields = '';
  613. // get FTP information if we need it
  614. if ($config['write_method'] == WRITE_FTP)
  615. {
  616. handle_ftp_details($method, $test_ftp_connection, $test_connection);
  617. }
  618. $s_hidden_fields .= build_hidden_fields(array('dependency_confirm' => !empty($_REQUEST['dependency_confirm'])));
  619. $template->assign_var('S_HIDDEN_FIELDS', $s_hidden_fields);
  620. $write_method = 'editor_' . determine_write_method(true);
  621. $editor = new $write_method();
  622. // Only display full actions if the user has requested them.
  623. if (!$config['preview_changes'] || ($editor->write_method == WRITE_FTP && empty($_REQUEST['password'])))
  624. {
  625. return;
  626. }
  627. $this->process_edits($editor, $actions, $details, false, true, false);
  628. return;
  629. }
  630. /**
  631. * Preforms all Edits, Copies, and SQL queries
  632. */
  633. function install($mod_path, $parent = 0)
  634. {
  635. global $phpbb_root_path, $phpEx, $db, $template, $user, $config, $cache, $dest_template;
  636. global $force_install, $mod_installed;
  637. // Are we forcing a template install?
  638. $dest_template = '';
  639. if (isset($_POST['template_submit']))
  640. {
  641. if (!check_form_key('acp_mods'))
  642. {
  643. trigger_error($user->lang['FORM_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING);
  644. }
  645. $mod_path = urldecode(request_var('source', ''));
  646. $dest_template = request_var('dest', '');
  647. if (preg_match('#.*install.*xml$#i', $mod_path))
  648. {
  649. $src_template = 'prosilver';
  650. }
  651. else
  652. {
  653. preg_match('#([a-z0-9]+)$#i', core_basename($mod_path), $match);
  654. $src_template = $match[1];
  655. unset ($match);
  656. }
  657. }
  658. // mod_path empty?
  659. if (empty($mod_path))
  660. {
  661. // ERROR
  662. return false;
  663. }
  664. if (request_var('method', ''))
  665. {
  666. set_config('ftp_method', request_var('method', ''));
  667. }
  668. set_config('ftp_host', request_var('host', ''));
  669. set_config('ftp_username', request_var('username', ''));
  670. set_config('ftp_root_path', request_var('root_path', ''));
  671. set_config('ftp_port', request_var('port', 21));
  672. set_config('ftp_timeout', request_var('timeout', 10));
  673. $details = $this->mod_details($mod_path, false);
  674. if (!$parent)
  675. {
  676. $sql = 'SELECT mod_name FROM ' . MODS_TABLE . "
  677. WHERE mod_name = '" . $db->sql_escape($details['MOD_NAME']) . "'";
  678. $result = $db->sql_query($sql);
  679. if ($row = $db->sql_fetchrow($result))
  680. {
  681. trigger_error('AM_MOD_ALREADY_INSTALLED');
  682. }
  683. }
  684. else if ($dest_template) // implicit && $parent
  685. {
  686. // Has this template already been processed?
  687. $sql = 'SELECT mod_name FROM ' . MODS_TABLE . "
  688. WHERE mod_id = $parent
  689. AND mod_template " . $db->sql_like_expression($db->any_char . $dest_template . $db->any_char);
  690. $result = $db->sql_query($sql);
  691. if ($row = $db->sql_fetchrow($result))
  692. {
  693. trigger_error('AM_MOD_ALREADY_INSTALLED');
  694. }
  695. }
  696. // NB: There could and should be cases to check for duplicated MODs and contribs
  697. // However, there is not appropriate book-keeping in place for those in 1.0.x
  698. add_form_key('acp_mods');
  699. $write_method = 'editor_' . determine_write_method(false);
  700. $editor = new $write_method();
  701. // get mod install root && make temporary edited folder root
  702. // $this->mod_root = dirname(str_replace($phpbb_root_path, '', $mod_path)) . '/';
  703. $this->edited_root = "{$this->mod_root}_edited/";
  704. $actions = $this->mod_actions($mod_path);
  705. if ($dest_template)
  706. {
  707. $sql = 'SELECT template_inherit_path FROM ' . STYLES_TEMPLATE_TABLE . "
  708. WHERE template_path = '" . $db->sql_escape($dest_template) . "'";
  709. $result = $db->sql_query($sql);
  710. global $dest_inherits;
  711. $dest_inherits = '';
  712. if ($row = $db->sql_fetchrow($result))
  713. {
  714. $dest_inherits = $row['template_inherit_path'];
  715. }
  716. $db->sql_freeresult($result);
  717. if (!empty($actions['EDITS']))
  718. {
  719. foreach ($actions['EDITS'] as $file => $edits)
  720. {
  721. if (strpos($file, 'styles/') === false)
  722. {
  723. unset($actions['EDITS'][$file]);
  724. }
  725. else if ($src_template != $dest_template)
  726. {
  727. $file_new = str_replace($src_template, $dest_template, $file);
  728. $actions['EDITS'][$file_new] = $edits;
  729. unset($actions['EDITS'][$file]);
  730. }
  731. }
  732. }
  733. if (!empty($actions['NEW_FILES']))
  734. {
  735. foreach ($actions['NEW_FILES'] as $src_file => $dest_file)
  736. {
  737. if (strpos($src_file, 'styles/') === false)
  738. {
  739. unset($actions['NEW_FILES']);
  740. }
  741. else
  742. {
  743. $actions['NEW_FILES'][$src_file] = str_replace($src_template, $dest_template, $dest_file);
  744. }
  745. }
  746. }
  747. }
  748. // only supporting one level of hierarchy here
  749. if (!$parent)
  750. {
  751. // check for "child" MODX files and attempt to decide which ones we need
  752. $children = $this->find_children($mod_path);
  753. $elements = array('language' => array(), 'template' => array());
  754. global $mode;
  755. $this->handle_dependency($children, $mode, $mod_path);
  756. $this->handle_language_prompt($children, $elements, 'install');
  757. $this->handle_merge('language', $actions, $children, $elements['language']);
  758. $this->handle_template_prompt($children, $elements, 'install');
  759. $this->handle_merge('template', $actions, $children, $elements['template']);
  760. }
  761. else
  762. {
  763. if ($dest_template)
  764. {
  765. $elements['template'] = array($dest_template);
  766. }
  767. else
  768. {
  769. $elements['language'] = array(core_basename($mod_path));
  770. }
  771. }
  772. $editor->create_edited_root($this->edited_root);
  773. $force_install = request_var('force', false);
  774. // handle all edits here
  775. $mod_installed = $this->process_edits($editor, $actions, $details, true, true, false);
  776. // Display Do-It-Yourself Actions...per the MODX spec, these should be displayed last
  777. if (!empty($actions['DIY_INSTRUCTIONS']))
  778. {
  779. $template->assign_var('S_DIY', true);
  780. if (!is_array($actions['DIY_INSTRUCTIONS']))
  781. {
  782. $actions['DIY_INSTRUCTIONS'] = array($actions['DIY_INSTRUCTIONS']);
  783. }
  784. foreach ($actions['DIY_INSTRUCTIONS'] as $instruction)
  785. {
  786. $template->assign_block_vars('diy_instructions', array(
  787. 'DIY_INSTRUCTION' => nl2br($instruction),
  788. ));
  789. }
  790. }
  791. if (!empty($actions['PHP_INSTALLER']))
  792. {
  793. $template->assign_vars(array(
  794. 'U_PHP_INSTALLER' => $phpbb_root_path . $actions['PHP_INSTALLER'],
  795. ));
  796. }
  797. if ($mod_installed || $force_install)
  798. {
  799. // Move edited files back
  800. $status = $editor->commit_changes($this->edited_root, '');
  801. if (is_string($status))
  802. {
  803. $mod_installed = false;
  804. $template->assign_block_vars('error', array(
  805. 'ERROR' => $status,
  806. ));
  807. }
  808. }
  809. // Finish by sending template data
  810. $template->assign_vars(array(
  811. 'S_INSTALL' => true,
  812. 'U_RETURN' => $this->u_action,
  813. 'U_BACK' => $this->u_action,
  814. ));
  815. // The editor class provides more pertinent information regarding edits
  816. // so we store that as the canonical version, used for uninstalling
  817. $actions['EDITS'] = $editor->mod_actions;
  818. $editor->clear_actions();
  819. // if MOD installed successfully, make a record.
  820. if (($mod_installed || $force_install) && !$parent)
  821. {
  822. // Insert database data
  823. $sql = 'INSERT INTO ' . MODS_TABLE . ' ' . $db->sql_build_array('INSERT', array(
  824. 'mod_time' => (int) $editor->install_time,
  825. // @todo: Are dependencies part of the MODX Spec?
  826. 'mod_dependencies' => '', //(string) serialize($details['MOD_DEPENDENCIES']),
  827. 'mod_name' => (string) $details['MOD_NAME'],
  828. 'mod_description' => (string) $details['MOD_DESCRIPTION'],
  829. 'mod_version' => (string) $details['MOD_VERSION'],
  830. 'mod_path' => (string) $details['MOD_PATH'],
  831. 'mod_author_notes' => (string) $details['AUTHOR_NOTES'],
  832. 'mod_author_name' => (string) $details['AUTHOR_DETAILS'][0]['AUTHOR_NAME'],
  833. 'mod_author_email' => (string) $details['AUTHOR_DETAILS'][0]['AUTHOR_EMAIL'],
  834. 'mod_author_url' => (string) $details['AUTHOR_DETAILS'][0]['AUTHOR_WEBSITE'],
  835. 'mod_actions' => (string) serialize($actions),
  836. 'mod_languages' => (string) (isset($elements['language']) && sizeof($elements['language'])) ? implode(',', $elements['language']) : '',
  837. 'mod_template' => (string) (isset($elements['template']) && sizeof($elements['template'])) ? implode(',', $elements['template']) : '',
  838. ));
  839. $db->sql_query($sql);
  840. $cache->purge();
  841. // Add log
  842. add_log('admin', 'LOG_MOD_ADD', $details['MOD_NAME']);
  843. }
  844. // in this case, we are installing an additional template or language
  845. else if (($mod_installed || $force_install) && $parent)
  846. {
  847. $sql = 'SELECT * FROM ' . MODS_TABLE . " WHERE mod_id = $parent";
  848. $result = $db->sql_query($sql);
  849. $row = $db->sql_fetchrow($result);
  850. $db->sql_freeresult($result);
  851. if (!$row)
  852. {
  853. trigger_error($user->lang['NO_MOD'] . adm_back_link($this->u_action));
  854. }
  855. $sql_ary = array(
  856. 'mod_version' => $details['MOD_VERSION'],
  857. );
  858. if (!empty($elements['language']))
  859. {
  860. $sql_ary['mod_languages'] = $row['mod_languages'] . ',' . implode(',', $elements['language']);
  861. }
  862. else
  863. {
  864. $sql_ary['mod_languages'] = $row['mod_languages'];
  865. }
  866. if (!empty($elements['template']))
  867. {
  868. $sql_ary['mod_template'] = $row['mod_template'] . ',' . implode(',', $elements['template']);
  869. }
  870. else
  871. {
  872. $sql_ary['mod_template'] = $row['mod_template'];
  873. }
  874. $sql_ary['mod_time'] = $editor->install_time;
  875. $prior_mod_actions = unserialize($row['mod_actions']);
  876. $sql_ary['mod_actions'] = serialize(array_merge_recursive($prior_mod_actions, $actions));
  877. unset($prior_mod_actions);
  878. $sql = 'UPDATE ' . MODS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $sql_ary) . "
  879. WHERE mod_id = $parent";
  880. $db->sql_query($sql);
  881. add_log('admin', 'LOG_MOD_CHANGE', htmlspecialchars_decode($row['mod_name']));
  882. }
  883. // there was an error we need to tell the user about
  884. else
  885. {
  886. $hidden_ary = array();
  887. if ($editor->write_method == WRITE_FTP)
  888. {
  889. $hidden_ary['method'] = $config['ftp_method'];
  890. if (empty($config['ftp_method']))
  891. {
  892. trigger_error('FTP_METHOD_ERROR');
  893. }
  894. $requested_data = call_user_func(array($config['ftp_method'], 'data'));
  895. foreach ($requested_data as $data => $default)
  896. {
  897. if ($data == 'password')
  898. {
  899. $config['ftp_password'] = request_var('password', '');
  900. }
  901. $default = (!empty($config['ftp_' . $data])) ? $config['ftp_' . $data] : $default;
  902. $hidden_ary[$data] = $default;
  903. }
  904. }
  905. if ($parent)
  906. {
  907. $hidden_ary['parent'] = $parent;
  908. }
  909. if ($dest_template)
  910. {
  911. $hidden_ary['dest'] = $dest_template;
  912. $hidden_ary['source'] = $mod_path;
  913. $hidden_ary['template_submit'] = true;
  914. }
  915. $template->assign_vars(array(
  916. 'S_ERROR' => true,
  917. 'S_HIDDEN_FIELDS' => build_hidden_fields($hidden_ary),
  918. 'U_RETRY' => $this->u_action . '&amp;action=install&amp;mod_path=' . $mod_path,
  919. ));
  920. }
  921. // if we forced the install of the MOD, we need to let the user know their board could be broken
  922. if ($force_install)
  923. {
  924. $template->assign_var('S_FORCE', true);
  925. }
  926. if ($mod_installed || $force_install)
  927. {
  928. $editor->commit_changes_final('mod_' . $editor->install_time, str_replace(' ', '_', $details['MOD_NAME']));
  929. }
  930. }
  931. /**
  932. * Uninstall/pre uninstall a mod
  933. */
  934. function uninstall($action, $mod_id)
  935. {
  936. global $phpbb_root_path, $phpEx, $db, $template, $user, $config, $force_install;
  937. global $method, $test_ftp_connection, $test_connection, $mod_uninstalled;
  938. // mod_id blank?
  939. if (!$mod_id)
  940. {
  941. // ERROR
  942. return false;
  943. }
  944. // set the class parameters to refelect the proper directory
  945. $sql = 'SELECT mod_path FROM ' . MODS_TABLE . '
  946. WHERE mod_id = ' . $mod_id;
  947. $result = $db->sql_query($sql);
  948. if ($row = $db->sql_fetchrow($result))
  949. {
  950. $this->mod_root = dirname($row['mod_path']) . '/';
  951. }
  952. $execute_edits = ($action == 'pre_uninstall') ? false : true;
  953. $write_method = 'editor_' . determine_write_method(!$execute_edits);
  954. $editor = new $write_method();
  955. // get mod install root && make temporary edited folder root
  956. $this->edited_root = "$this->mod_root{$mod_id}_uninst/";
  957. // get FTP information if we need it
  958. // using $config instead of $editor because write_method is forced to direct
  959. // when in preview mode
  960. $hidden_ary = array();
  961. if ($config['write_method'] == WRITE_FTP)
  962. {
  963. if($execute_edits && isset($_POST['password']))
  964. {
  965. $hidden_ary['method'] = $config['ftp_method'];
  966. if (empty($config['ftp_method']))
  967. {
  968. trigger_error('FTP_METHOD_ERROR');
  969. }
  970. $requested_data = call_user_func(array($config['ftp_method'], 'data'));
  971. foreach ($requested_data as $data => $default)
  972. {
  973. if ($data == 'password')
  974. {
  975. $config['ftp_password'] = request_var('password', '');
  976. }
  977. $default = (!empty($config['ftp_' . $data])) ? $config['ftp_' . $data] : $default;
  978. $hidden_ary[$data] = $default;
  979. }
  980. }
  981. handle_ftp_details($method, $test_ftp_connection, $test_connection);
  982. }
  983. $editor->create_edited_root($this->edited_root);
  984. $template->assign_vars(array(
  985. 'S_UNINSTALL' => $execute_edits,
  986. 'S_PRE_UNINSTALL' => !$execute_edits,
  987. 'S_HIDDEN_FIELDS' => build_hidden_fields($hidden_ary),
  988. 'L_FORCE_INSTALL' => $user->lang['FORCE_UNINSTALL'],
  989. 'MOD_ID' => $mod_id,
  990. 'U_UNINSTALL' => $this->u_action . '&amp;action=uninstall&amp;mod_id=' . $mod_id,
  991. 'U_RETRY' => $this->u_action . '&amp;action=uninstall&amp;mod_id=' . $mod_id,
  992. 'U_RETURN' => $this->u_action,
  993. 'U_BACK' => $this->u_action,
  994. ));
  995. // grab actions and details
  996. $details = $this->mod_details($mod_id, false, true);
  997. $actions = $this->mod_actions($mod_id);
  998. $force_install = request_var('force', false);
  999. // process the actions
  1000. $mod_uninstalled = $this->process_edits($editor, $actions, $details, $execute_edits, true, true);
  1001. if (!$execute_edits)
  1002. {
  1003. return;
  1004. }
  1005. $force_uninstall = request_var('force', false);
  1006. if ($mod_uninstalled || $force_uninstall)
  1007. {
  1008. // Move edited files back
  1009. $status = $editor->commit_changes($this->edited_root, '');
  1010. if (is_string($status))
  1011. {
  1012. $mod_uninstalled = false;
  1013. $template->assign_block_vars('error', array(
  1014. 'ERROR' => $status,
  1015. ));
  1016. }
  1017. }
  1018. if ($force_uninstall)
  1019. {
  1020. $template->assign_var('S_FORCE', true);
  1021. }
  1022. else if (!$mod_uninstalled)
  1023. {
  1024. $template->assign_var('S_ERROR', true);
  1025. }
  1026. if ($execute_edits && ($mod_uninstalled || $force_uninstall))
  1027. {
  1028. // Delete from DB
  1029. $sql = 'DELETE FROM ' . MODS_TABLE . '
  1030. WHERE mod_id = ' . $mod_id;
  1031. $db->sql_query($sql);
  1032. // Add log
  1033. add_log('admin', 'LOG_MOD_REMOVE', $details['MOD_NAME']);
  1034. $editor->commit_changes_final('mod_' . $editor->install_time, str_replace(' ', '_', $details['MOD_NAME']));
  1035. }
  1036. }
  1037. /**
  1038. * Returns array of available mod install files in dir (Recursive)
  1039. * @param $dir string - dir to search
  1040. * @param $recurse int - number of levels to recurse
  1041. */
  1042. function find_mods($dir, $recurse = false)
  1043. {
  1044. if ($recurse === false)
  1045. {
  1046. $mods = array('main' => array(), 'contrib' => array(), 'template' => array(), 'language' => array());
  1047. $recurse = 0;
  1048. }
  1049. else
  1050. {
  1051. static $mods = array('main' => array(), 'contrib' => array(), 'template' => array(), 'language' => array());
  1052. }
  1053. // ltrim shouldn't be needed, but some users had problems. See #44305
  1054. $dir = ltrim($dir, '/');
  1055. if (!file_exists($dir))
  1056. {
  1057. return array();
  1058. }
  1059. $dp = opendir($dir);
  1060. while (($file = readdir($dp)) !== false)
  1061. {
  1062. if ($file[0] != '.' && strpos("$dir/$file", '_edited') === false && strpos("$dir/$file", '_backups') === false)
  1063. {
  1064. // recurse - we don't want anything within the MODX "root" though
  1065. if ($recurse && !is_file("$dir/$file") && strpos("$dir/$file", 'root') === false)
  1066. {
  1067. $mods = array_merge($mods, $this->find_mods("$dir/$file", $recurse - 1));
  1068. }
  1069. // this might be better as an str function, especially being in a loop
  1070. else if (preg_match('#.*install.*xml$#i', $file) || (preg_match('#(contrib|templates|languages)#i', $dir, $match)) || ($recurse === 0 && strpos($file, '.xml') !== false))
  1071. {
  1072. // if this is an "extra" MODX file, make a record of it as such
  1073. // we are assuming the MOD follows MODX packaging standards here
  1074. if (strpos($file, '.xml') !== false && preg_match('#(contrib|templates|languages)#i', $dir, $match))
  1075. {
  1076. // Get rid of the S. This is a side effect of understanding
  1077. // MODX 1.0.x and 1.2.x.
  1078. $match[1] = rtrim($match[1], 's');
  1079. $mods[$match[1]][] = array(
  1080. 'href' => "$dir/$file",
  1081. 'realname' => core_basename($file),
  1082. 'title' => core_basename($file),
  1083. );
  1084. }
  1085. else
  1086. {
  1087. $check = end($mods['main']);
  1088. $check = $check['href'];
  1089. // we take the first file alphabetically with install in the filename
  1090. if (!$check || dirname($check) == $dir)
  1091. {
  1092. if (preg_match('#.*install.*xml$#i', $file) && preg_match('#.*install.*xml$#i', $check) && strnatcasecmp(basename($check), $file) > 0)
  1093. {
  1094. $index = max(0, sizeof($mods['main']) - 1);
  1095. $mods['main'][$index] = array(
  1096. 'href' => "$dir/$file",
  1097. 'realname' => core_basename($file),
  1098. 'title' => core_basename($file),
  1099. );
  1100. break;
  1101. }
  1102. else if (preg_match('#.*install.*xml$#i', $file) && !preg_match('#.*install.*xml$#i', $check))
  1103. {
  1104. $index = max(0, sizeof($mods['main']) - 1);
  1105. $mods['main'][$index] = array(
  1106. 'href' => "$dir/$file",
  1107. 'realname' => core_basename($file),
  1108. 'title' => core_basename($file),
  1109. );
  1110. break;
  1111. }
  1112. }
  1113. else
  1114. {
  1115. if (strpos($file, '.xml') !== false)
  1116. {
  1117. $mods['main'][] = array(
  1118. 'href' => "$dir/$file",
  1119. 'realname' => core_basename($file),
  1120. 'title' => core_basename($file),
  1121. );
  1122. }
  1123. }
  1124. }
  1125. }
  1126. }
  1127. }
  1128. closedir($dp);
  1129. return $mods;
  1130. }
  1131. function process_edits($editor, $actions, $details, $change = false, $display = true, $reverse = false)
  1132. {
  1133. global $template, $user, $db, $phpbb_root_path, $force_install, $mod_installed;
  1134. global $dest_inherits, $dest_template, $children;
  1135. $mod_installed = true;
  1136. if ($reverse)
  1137. {
  1138. global $rev_actions;
  1139. if (empty($rev_actions))
  1140. {
  1141. // maybe should allow for potential extensions here
  1142. $actions = parser::reverse_edits($actions);
  1143. }
  1144. else
  1145. {
  1146. $actions = $rev_actions;
  1147. unset($rev_actions);
  1148. }
  1149. }
  1150. $template->assign_vars(array(
  1151. 'S_DISPLAY_DETAILS' => (bool) $display,
  1152. 'S_CHANGE_FILES' => (bool) $change,
  1153. ));
  1154. if (!empty($details['AUTHOR_NOTES']) && $details['AUTHOR_NOTES'] != $user->lang['UNKNOWN_MOD_AUTHOR-NOTES'])
  1155. {
  1156. $template->assign_vars(array(
  1157. 'S_AUTHOR_NOTES' => true,
  1158. 'AUTHOR_NOTES' => nl2br($details['AUTHOR_NOTES']),
  1159. ));
  1160. }
  1161. // not all MODs will have edits (!)
  1162. if (isset($actions['EDITS']))
  1163. {
  1164. $template->assign_var('S_EDITS', true);
  1165. foreach ($actions['EDITS'] as $filename => $edits)
  1166. {
  1167. // see if the file to be opened actually exists
  1168. if (!file_exists("$phpbb_root_path$filename"))
  1169. {
  1170. $is_inherit = (strpos($filename, 'styles/') !== false && !empty($dest_inherits)) ? true : false;
  1171. $template->assign_block_vars('edit_files', array(
  1172. 'S_MISSING_FILE' => ($is_inherit) ? false : true,
  1173. 'INHERIT_MSG' => ($is_inherit) ? sprintf($user->lang['INHERIT_NO_CHANGE'], $dest_template, $dest_inherits) : '',
  1174. 'FILENAME' => $filename,
  1175. ));
  1176. $mod_installed = ($is_inherit) ? $mod_installed : false;
  1177. continue;
  1178. }
  1179. else
  1180. {
  1181. $template->assign_block_vars('edit_files', array(
  1182. 'FILENAME' => $filename,
  1183. ));
  1184. $status = $editor->open_file($filename, $this->backup_root);
  1185. if (is_string($status))
  1186. {
  1187. $template->assign_block_vars('error', array(
  1188. 'ERROR' => $status,
  1189. ));
  1190. $mod_installed = false;
  1191. continue;
  1192. }
  1193. foreach ($edits as $finds)
  1194. {
  1195. $comment = '';
  1196. foreach ($finds as $find => $commands)
  1197. {
  1198. if (isset($finds['comment']) && !$comment && $finds['comment'] != $user->lang['UNKNOWN_MOD_COMMENT'])
  1199. {
  1200. $comment = $finds['comment'];
  1201. unset($finds['comment']);
  1202. }
  1203. if ($find == 'comment')
  1204. {
  1205. continue;
  1206. }
  1207. $template->assign_block_vars('edit_files.finds', array(
  1208. 'FIND_STRING' => htmlspecialchars($find),
  1209. 'COMMENT' => htmlspecialchars($comment),
  1210. ));
  1211. $offset_ary = $editor->find($find);
  1212. // special case for FINDs with no action associated
  1213. if (is_null($commands))
  1214. {
  1215. continue;
  1216. }
  1217. foreach ($commands as $type => $contents)
  1218. {
  1219. if (!$offset_ary)
  1220. {
  1221. $offset_ary['start'] = $offset_ary['end'] = false;
  1222. }
  1223. $status = false;
  1224. $inline_template_ary = array();
  1225. $contents_orig = $contents;
  1226. switch (strtoupper($type))
  1227. {
  1228. case 'AFTER ADD':
  1229. $status = $editor->add_string($find, $contents, 'AFTER', $offset_ary['start'], $offset_ary['end']);
  1230. break;
  1231. case 'BEFORE ADD':
  1232. $status = $editor->add_string($find, $contents, 'BEFORE', $offset_ary['start'], $offset_ary['end']);
  1233. break;
  1234. case 'INCREMENT':
  1235. case 'OPERATION':
  1236. //$contents = "";
  1237. $status = $editor->inc_string($find, '', $contents);
  1238. break;
  1239. case 'REPLACE WITH':
  1240. $status = $editor->replace_string($find, $contents, $offset_ary['start'], $offset_ary['end']);
  1241. break;
  1242. case 'IN-LINE-EDIT':
  1243. // these aren't quite as straight forward. Still have multi-level arrays to sort through
  1244. $inline_comment = '';
  1245. foreach ($contents as $inline_edit_id => $inline_edit)
  1246. {
  1247. if ($inline_edit_id === 'inline-comment')
  1248. {
  1249. // This is a special case for tucking comments in the array
  1250. if ($inline_edit != $user->lang['UNKNOWN_MOD_INLINE-COMMENT'])
  1251. {
  1252. $inline_comment = $inline_edit;
  1253. }
  1254. continue;
  1255. }
  1256. foreach ($inline_edit as $inline_find => $inline_commands)
  1257. {
  1258. foreach ($inline_commands as $inline_action => $inline_contents)
  1259. {
  1260. // inline finds are pretty contankerous, so so them in the loop
  1261. $line = $editor->inline_find($find, $inline_find, $offset_ary['start'], $offset_ary['end']);
  1262. if (!$line)
  1263. {
  1264. // find failed
  1265. $status = $mod_installed = false;
  1266. $inline_template_ary[] = array(
  1267. 'FIND' => array(
  1268. 'S_SUCCESS' => $status,
  1269. 'NAME' => $user->lang[$type],
  1270. 'COMMAND' => htmlspecialchars($inline_find),
  1271. ),
  1272. 'ACTION' => array());
  1273. continue 2;
  1274. }
  1275. $inline_contents = $inline_contents[0];
  1276. $contents_orig = $inline_find;
  1277. switch (strtoupper($inline_action))
  1278. {
  1279. case 'IN-LINE-':
  1280. $editor->last_string_offset = $line['string_offset'] + $line['find_length'] - 1;
  1281. $status = true;
  1282. continue 2;
  1283. break;
  1284. case 'IN-LINE-BEFORE-ADD':
  1285. $status = $editor->inline_add($find, $inline_find, $inline_contents, 'BEFORE', $line['array_offset'], $line['string_offset'], $line['find_length']);
  1286. break;
  1287. case 'IN-LINE-AFTER-ADD':
  1288. $status = $editor->inline_add($find, $inline_find, $inline_contents, 'AFTER', $line['array_offset'], $line['string_offset'], $line['find_length']);
  1289. break;
  1290. case 'IN-LINE-REPLACE':
  1291. case 'IN-LINE-REPLACE-WITH':
  1292. $status = $editor->inline_replace($find, $inline_find, $inline_contents, $line['array_offset'], $line['string_offset'], $line['find_length']);
  1293. break;
  1294. case 'IN-LINE-OPERATION':
  1295. $status = $editor->inc_string($find, $inline_find, $inline_contents);
  1296. break;
  1297. default:
  1298. $message = sprintf($user->lang['UNRECOGNISED_COMMAND'], $inline_action);
  1299. trigger_error($message, E_USER_WARNING); // ERROR!
  1300. break;
  1301. }
  1302. $inline_template_ary[] = array(
  1303. 'FIND' => array(
  1304. 'S_SUCCESS' => $status,
  1305. 'NAME' => $user->lang[$type],
  1306. 'COMMAND' => (is_array($contents_orig)) ? $user->lang['INVALID_MOD_INSTRUCTION'] : htmlspecialchars($contents_orig),
  1307. ),
  1308. 'ACTION' => array(
  1309. 'S_SUCCESS' => $status,
  1310. 'NAME' => $user->lang[$inline_action],
  1311. 'COMMAND' => (is_array($inline_contents)) ? $user->lang['INVALID_MOD_INSTRUCTION'] : htmlspecialchars($inline_contents),
  1312. // 'COMMENT' => $inline_comment, (inline comments aren't actually part of the MODX spec)
  1313. ),
  1314. );
  1315. }
  1316. if (!$status)
  1317. {
  1318. $mod_installed = false;
  1319. }
  1320. $editor->close_inline_edit();
  1321. }
  1322. }
  1323. break;
  1324. default:
  1325. $message = sprintf($user->lang['UNRECOGNISED_COMMAND'], $action);
  1326. trigger_error($message, E_USER_WARNING); // ERROR!
  1327. break;
  1328. }
  1329. if (!$status)
  1330. {
  1331. $mod_installed = false;
  1332. }
  1333. if (sizeof($inline_template_ary))
  1334. {
  1335. foreach ($inline_template_ary as $inline_template)
  1336. {
  1337. // We must assign the vars for the FIND first
  1338. $template->assign_block_vars('edit_files.finds.actions', $inline_template['FIND']);
  1339. // And now the vars for the ACTION
  1340. $template->assign_block_vars('edit_files.finds.actions.inline', $inline_template['ACTION']);
  1341. }
  1342. $inline_template_ary = array();
  1343. }
  1344. else if (!is_array($contents_orig))
  1345. {
  1346. $template->assign_block_vars('edit_files.finds.actions', array(
  1347. 'S_SUCCESS' => $status,
  1348. 'NAME' => $user->lang[$type],
  1349. 'COMMAND' => htmlspecialchars($contents_orig),
  1350. ));
  1351. }
  1352. }
  1353. }
  1354. $editor->close_edit();
  1355. }
  1356. }
  1357. if ($change)
  1358. {
  1359. $status = $editor->close_file("{$this->edited_root}$filename");
  1360. if (is_string($status))
  1361. {
  1362. $template->assign_block_vars('error', array(
  1363. 'ERROR' => $status,
  1364. ));
  1365. $mod_installed = false;
  1366. }
  1367. }
  1368. }
  1369. } // end foreach
  1370. // Move included files
  1371. if (isset($actions['NEW_FILES']) && !empty($actions['NEW_FILES']) && $change && ($mod_installed || $force_install))
  1372. {
  1373. $template->assign_var('S_NEW_FILES', true);
  1374. foreach ($actions['NEW_FILES'] as $source => $target)
  1375. {
  1376. $status = $editor->copy_content($this->mod_root . str_replace('*.*', '', $source), str_replace('*.*', '', $target));
  1377. if ($status !== true && !is_null($status))
  1378. {
  1379. $mod_installed = false;
  1380. }
  1381. $template->assign_block_vars('new_files', array(
  1382. 'S_SUCCESS' => ($status === true) ? true : false,
  1383. 'S_NO_COPY_ATTEMPT' => (is_null($status)) ? true : false,
  1384. 'SOURCE' => $source,
  1385. 'TARGET' => $target,
  1386. ));
  1387. }
  1388. }
  1389. if (!empty($actions['DELETE_FILES']) && $change && ($mod_installed || $force_install))
  1390. {
  1391. foreach ($actions['DELETE_FILES'] as $file)
  1392. {
  1393. // purposely do not use !== false here, because we don't expect wildcards in position 0
  1394. if (strpos($file, '*.*'))
  1395. {
  1396. $file = str_replace('*.*', '', $file);
  1397. if (is_dir($phpbb_root_path . $file))
  1398. {
  1399. // recursively delete
  1400. recursive_unlink($phpbb_root_path . $file);
  1401. }
  1402. else
  1403. {
  1404. unlink($phpbb_root_path . $file);
  1405. }
  1406. }
  1407. else
  1408. {
  1409. // if there's no wildcard, we assume it is a single file
  1410. unlink($phpbb_root_path . $file);
  1411. }
  1412. }
  1413. }
  1414. // Perform SQL queries last -- Queries usually cannot be done a second
  1415. // time, so do them only if the edits were successful. Still complies
  1416. // with the MODX spec in this location
  1417. if (!empty($actions['SQL']) && ($mod_installed || $force_install || ($display && !$change)))
  1418. {
  1419. $template->assign_var('S_SQL', true);
  1420. parser::parse_sql($actions['SQL']);
  1421. $db->sql_return_on_error(true);
  1422. foreach ($actions['SQL'] as $query)
  1423. {
  1424. if ($change)
  1425. {
  1426. $query_success = $db->sql_query($query);
  1427. if ($query_success)
  1428. {
  1429. $template->assign_block_vars('sql_queries', array(
  1430. 'S_SUCCESS' => true,
  1431. 'QUERY' => $query,
  1432. ));
  1433. }
  1434. else
  1435. {
  1436. $error = $db->sql_error();
  1437. $template->assign_block_vars('sql_queries', array(
  1438. 'S_SUCCESS' => false,
  1439. 'QUERY' => $query,
  1440. 'ERROR_MSG' => $error['message'],
  1441. 'ERROR_CODE'=> $error['code'],
  1442. ));
  1443. $mod_installed = false;
  1444. }
  1445. }
  1446. else if ($display)
  1447. {
  1448. $template->assign_block_vars('sql_queries', array(
  1449. 'QUERY' => $query,
  1450. ));
  1451. }
  1452. }
  1453. $db->sql_return_on_error(false);
  1454. }
  1455. else
  1456. {
  1457. $template->assign_var('S_SQL', false);
  1458. }
  1459. return $mod_installed;
  1460. }
  1461. /**
  1462. * Search on the file system for other .xml files that belong to this MOD
  1463. * @param string $mod_path - path to the "main" MODX file, relative to phpBB Root
  1464. */
  1465. function find_children($mod_path)
  1466. {
  1467. $children = array();
  1468. if ($this->parser->get_modx_version() == 1.2)
  1469. {
  1470. // TODO: eww, yuck ... processing the XML agaiā€¦

Large files files are truncated, but you can click here to view the full file