PageRenderTime 58ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/titania/includes/library/automod/acp_mods.php

http://github.com/phpbb/customisation-db
PHP | 2172 lines | 1657 code | 351 blank | 164 comment | 303 complexity | 796129ffcf7006df563b15c0e974d289 MD5 | raw file
Possible License(s): AGPL-1.0

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

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

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