/includes/library/automod/acp_mods.php
PHP | 2955 lines | 2150 code | 414 blank | 391 comment | 414 complexity | 8880f6bf54492fe40d789c61a9dc5aa2 MD5 | raw file
Possible License(s): AGPL-1.0
- <?php
- /**
- *
- * @package automod
- * @copyright (c) 2008 phpBB Group
- * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License, version 2
- *
- */
- /**
- */
- if (!defined('IN_PHPBB'))
- {
- exit;
- }
- /**
- * @package automod
- */
- class acp_mods
- {
- var $u_action;
- var $parser;
- var $mod_root = '';
- var $store_dir = '';
- var $mods_dir = '';
- var $edited_root = '';
- var $backup_root = '';
- var $sort_key = '';
- var $sort_dir = '';
- function main($id, $mode)
- {
- global $config, $db, $user, $auth, $template, $cache;
- global $phpbb_root_path, $phpEx;
- global $ftp_method, $test_ftp_connection, $test_connection, $sort_key, $sort_dir;
- include("{$phpbb_root_path}includes/functions_transfer.$phpEx");
- include("{$phpbb_root_path}includes/editor.$phpEx");
- include("{$phpbb_root_path}includes/functions_mods.$phpEx");
- include("{$phpbb_root_path}includes/mod_parser.$phpEx");
- // start the page
- $user->add_lang(array('install', 'acp/mods'));
- $this->tpl_name = 'acp_mods';
- $this->page_title = 'ACP_CAT_MODS';
- $this->store_dir = $phpbb_root_path . 'store';
- $this->mods_dir = $phpbb_root_path . 'store/mods';
- // get any url vars
- $action = request_var('action', '');
- $mod_id = request_var('mod_id', 0);
- $mod_url = request_var('mod_url', '');
- $parent = request_var('parent', 0);
- //sort keys
- $sort_key = request_var('sk','t');
- $sort_dir = request_var('sd', 'a');
- $mod_path = request_var('mod_path', '');
- if ($mod_path)
- {
- $mod_path = htmlspecialchars_decode($mod_path); // "/my_mod/install.xml" or "/./contrib/blah.xml"
- $mod_dir = substr($mod_path, 1, strpos($mod_path, '/', 1)); // "my_mod/"
- $this->mod_root = $this->mods_dir . '/' . $mod_dir; // "./../store/mods/my_mod/"
- $this->backup_root = "{$this->mod_root}_backups/"; // "./../store/mods/my_mod/_backups/"
- $this->edited_root = "{$this->mod_root}_edited/"; // "./../store/mods/my_mod/_edited/"
- }
- switch ($mode)
- {
- case 'config':
- $ftp_method = request_var('ftp_method', $config['ftp_method']);
- if (!$ftp_method || !class_exists($ftp_method))
- {
- $ftp_method = 'ftp';
- $ftp_methods = transfer::methods();
- if (!in_array('ftp', $ftp_methods))
- {
- $ftp_method = $ftp_methods[0];
- }
- }
- if (isset($_POST['submit']) && check_form_key('acp_mods'))
- {
- $ftp_host = request_var('host', '');
- $ftp_username = request_var('username', '');
- $ftp_password = request_var('password', ''); // not stored, used to test connection
- $ftp_root_path = request_var('root_path', '');
- $ftp_port = request_var('port', 21);
- $ftp_timeout = request_var('timeout', 10);
- $write_method = request_var('write_method', 0);
- $file_perms = request_var('file_perms', '0644');
- $dir_perms = request_var('dir_perms', '0755');
- $compress_method = request_var('compress_method', '');
- $preview_changes = request_var('preview_changes', 0);
- $error = '';
- if ($write_method == WRITE_DIRECT)
- {
- // the very best method would be to check every file for is_writable
- if (!is_writable("{$phpbb_root_path}common.$phpEx") || !is_writable("{$phpbb_root_path}adm/style/acp_groups.html"))
- {
- $error = 'FILESYSTEM_NOT_WRITABLE';
- }
- }
- else if ($write_method == WRITE_FTP)
- {
- // check the correctness of FTP infos
- $test_ftp_connection = true;
- $test_connection = false;
- test_ftp_connection($ftp_method, $test_ftp_connection, $test_connection);
- if ($test_connection !== true)
- {
- $error = $test_connection;
- }
- }
- else if ($write_method == WRITE_MANUAL)
- {
- // the compress class requires write access to the store/ dir
- if (!is_writable($this->store_dir))
- {
- $error = 'STORE_NOT_WRITABLE';
- }
- }
- if (empty($error))
- {
- set_config('ftp_method', $ftp_method);
- set_config('ftp_host', $ftp_host);
- set_config('ftp_username', $ftp_username);
- set_config('ftp_root_path', $ftp_root_path);
- set_config('ftp_port', $ftp_port);
- set_config('ftp_timeout', $ftp_timeout);
- set_config('write_method', $write_method);
- set_config('compress_method', $compress_method);
- set_config('preview_changes', $preview_changes);
- set_config('am_file_perms', $file_perms);
- set_config('am_dir_perms', $dir_perms);
- trigger_error($user->lang['MOD_CONFIG_UPDATED'] . adm_back_link($this->u_action));
- }
- else
- {
- $template->assign_var('ERROR', $user->lang[$error]);
- }
- }
- else if (isset($_POST['submit']) && !check_form_key('acp_mods'))
- {
- trigger_error($user->lang['FORM_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING);
- }
- add_form_key('acp_mods');
- // implicit else
- include("{$phpbb_root_path}includes/functions_compress.$phpEx");
- foreach (compress::methods() as $compress_method)
- {
- $template->assign_block_vars('compress', array(
- 'METHOD' => $compress_method,
- ));
- }
- $requested_data = call_user_func(array($ftp_method, 'data'));
- foreach ($requested_data as $data => $default)
- {
- $default = (!empty($config['ftp_' . $data])) ? $config['ftp_' . $data] : $default;
- $template->assign_block_vars('data', array(
- 'DATA' => $data,
- 'NAME' => $user->lang[strtoupper($ftp_method . '_' . $data)],
- 'EXPLAIN' => $user->lang[strtoupper($ftp_method . '_' . $data) . '_EXPLAIN'],
- 'DEFAULT' => (!empty($_REQUEST[$data])) ? request_var($data, '') : $default
- ));
- }
- $template->assign_vars(array(
- 'S_CONFIG' => true,
- 'U_CONFIG' => $this->u_action . '&mode=config',
- 'UPLOAD_METHOD_FTP' => ($config['ftp_method'] == 'ftp') ? ' checked="checked"' : '',
- 'UPLOAD_METHOD_FSOCK'=> ($config['ftp_method'] == 'ftp_fsock') ? ' checked="checked"' : '',
- 'WRITE_DIRECT' => ($config['write_method'] == WRITE_DIRECT) ? ' checked="checked"' : '',
- 'WRITE_FTP' => ($config['write_method'] == WRITE_FTP) ? ' checked="checked"' : '',
- 'WRITE_MANUAL' => ($config['write_method'] == WRITE_MANUAL) ? ' checked="checked"' : '',
- 'WRITE_METHOD_DIRECT' => WRITE_DIRECT,
- 'WRITE_METHOD_FTP' => WRITE_FTP,
- 'WRITE_METHOD_MANUAL' => WRITE_MANUAL,
- 'AUTOMOD_VERSION' => $config['automod_version'],
- 'COMPRESS_METHOD' => $config['compress_method'],
- 'DIR_PERMS' => $config['am_dir_perms'],
- 'FILE_PERMS' => $config['am_file_perms'],
- 'PREVIEW_CHANGES_YES' => ($config['preview_changes']) ? ' checked="checked"' : '',
- 'PREVIEW_CHANGES_NO' => (!$config['preview_changes']) ? ' checked="checked"' : '',
- 'S_HIDE_FTP' => ($config['write_method'] == WRITE_FTP) ? false : true,
- ));
- break;
- case 'frontend':
- if ($config['write_method'] == WRITE_FTP)
- {
- $ftp_method = basename(request_var('method', $config['ftp_method']));
- if (!$ftp_method || !class_exists($ftp_method))
- {
- $ftp_method = 'ftp';
- $ftp_methods = transfer::methods();
- if (!in_array('ftp', $ftp_methods))
- {
- $ftp_method = $ftp_methods[0];
- }
- }
- $test_connection = false;
- $test_ftp_connection = request_var('test_connection', '');
- if (!empty($test_ftp_connection) || in_array($action, array('install', 'uninstall', 'upload_mod', 'delete_mod')))
- {
- test_ftp_connection($ftp_method, $test_ftp_connection, $test_connection);
- // Make sure the login details are correct before continuing
- if ($test_connection !== true || !empty($test_ftp_connection))
- {
- $action = 'pre_' . $action;
- }
- }
- }
- // store/ needs to be world-writable even when FTP is the write method,
- // for extracting uploaded mod zip files
- if (!is_writable($this->store_dir))
- {
- $template->assign_var('S_STORE_WRITABLE_WARN', true);
- }
- // Otherwise, store/mods/ needs to be writable
- else if ($config['write_method'] != WRITE_FTP && !is_writable($this->mods_dir))
- {
- $template->assign_var('S_MODS_WRITABLE_WARN', true);
- }
- switch ($action)
- {
- case 'pre_install':
- case 'install':
- $this->install($action, $mod_path, $parent);
- break;
- case 'pre_uninstall':
- case 'uninstall':
- $this->uninstall($action, $mod_id, $parent);
- break;
- case 'details':
- $mod_ident = ($mod_id) ? $mod_id : $mod_path;
- $this->list_details($mod_ident);
- break;
- case 'pre_delete_mod':
- case 'delete_mod':
- $this->delete_mod($action, $mod_path);
- break;
- case 'pre_upload_mod':
- case 'upload_mod':
- default:
- $action = (isset($action)) ? $action : '';
- if (!$this->upload_mod($action))
- {
- $this->list_installed();
- $this->list_uninstalled();
- }
- break;
- case 'download':
- include ($phpbb_root_path . "includes/functions_compress.$phpEx");
- $editor = new editor_manual();
- $time = request_var('time', 0);
- // if for some reason the MOD isn't found in the DB...
- $download_name = 'mod_' . $time;
- $sql = 'SELECT mod_name FROM ' . MODS_TABLE . '
- WHERE mod_time = ' . $time;
- $result = $db->sql_query($sql);
- if ($row = $db->sql_fetchrow($result))
- {
- // Always use the English name except for showing the user.
- $mod_name = localize_title($row['mod_name'], 'en');
- $download_name = str_replace(' ', '_', $mod_name);
- }
- $editor->compress->download("{$this->store_dir}/mod_$time", $download_name);
- exit;
- break;
- }
- return;
- break;
- }
- }
- /**
- * List all the installed mods
- */
- function list_installed()
- {
- global $db, $template, $user, $sort_key, $sort_dir;
- $sort_by_text = array('u' => $user->lang['SORT_NAME'], 't' => $user->lang['SORT_DATE']);
- ($sort_key == 't')? $sort='mod_time' & $s_sort_key='mod_time': $sort='mod_name' & $s_sort_key='mod_name';
- ($sort_dir == 'd') ? $dir='DESC' & $s_sort_dir='DESC' : $dir='ASC' & $s_sort_dir='ASC';
- $limit_days = array();
- $s_limit_days=$sort_days=$s_limit_days = $u_sort_param = '';
- gen_sort_selects($limit_days, $sort_by_text, $sort_days, $sort_key, $sort_dir, $s_limit_days, $s_sort_key, $s_sort_dir, $u_sort_param);
- $template->assign_vars(array(
- 'S_SORT_KEY' => $s_sort_key,
- 'S_SORT_DIR' => $s_sort_dir,
- 'U_SORT_ACTION' => $this->u_action ."&$u_sort_param"
- ));
- // The MOD name is a array so it can't be used as sort key directly.
- $sql_sort = ($sort_key == 't') ? " ORDER BY mod_time $dir" : '';
- $sql = 'SELECT mod_name, mod_id, mod_time
- FROM ' . MODS_TABLE .
- $sql_sort;
- $result = $db->sql_query($sql);
- $mod_ary = $db->sql_fetchrowset($result);
- $db->sql_freeresult($result);
- foreach ($mod_ary as $key => $row)
- {
- if (($name_ary = @unserialize($row['mod_name'])) === false)
- {
- $name_ary['en'] = $row['mod_name'];
- }
- $mod_ary[$key]['mod_name'] = $name_ary;
- }
- if ($sort_key != 't')
- {
- $sort_ary = array();
- foreach ($mod_ary as $key => $row)
- {
- $sort_ary[$key] = $row['mod_name']['en'];
- }
- if ($sort_dir == 'd')
- {
- arsort($sort_ary, SORT_STRING);
- }
- else
- {
- asort($sort_ary, SORT_STRING);
- }
- foreach ($sort_ary as $key => $name)
- {
- $sort_ary[$key] = $mod_ary[$key];
- }
- $mod_ary = $sort_ary;
- unset($sort_ary);
- }
- foreach ($mod_ary as $row)
- {
- $mod_name = localize_title($row['mod_name'], $user->data['user_lang']);
- $template->assign_block_vars('installed', array(
- 'MOD_ID' => $row['mod_id'],
- 'MOD_NAME' => htmlspecialchars($mod_name),
- 'MOD_TIME' => $user->format_date($row['mod_time']),
- 'U_DETAILS' => $this->u_action . '&action=details&mod_id=' . $row['mod_id'],
- 'U_UNINSTALL' => $this->u_action . '&action=pre_uninstall&mod_id=' . $row['mod_id'])
- );
- }
- return;
- }
- /**
- * List all mods available locally
- */
- function list_uninstalled()
- {
- global $phpbb_root_path, $db, $template, $config, $user;
- // get available MOD paths
- $available_mods = $this->find_mods($this->mods_dir, 1);
- if (!sizeof($available_mods['main']))
- {
- return;
- }
- // get installed MOD paths
- $installed_paths = array();
- $sql = 'SELECT mod_path
- FROM ' . MODS_TABLE;
- $result = $db->sql_query($sql);
- while ($row = $db->sql_fetchrow($result))
- {
- $installed_paths[] = $row['mod_path'];
- }
- $db->sql_freeresult($result);
- $mod_paths = array();
- foreach ($available_mods['main'] as $mod_info)
- {
- $mod_paths[] = $mod_info['href'];
- }
- // we don't care about any xml files not in the main directory
- $available_mods = array_diff($mod_paths, $installed_paths);
- unset($installed_paths);
- unset($mod_paths);
- // show only available MODs that paths aren't in the DB
- foreach ($available_mods as $file)
- {
- $details = $this->mod_details($file, false);
- $short_path = urlencode(str_replace($this->mods_dir, '', $details['MOD_PATH']));
- $mod_name = localize_title($details['MOD_NAME'], $user->data['user_lang']);
- $template->assign_block_vars('uninstalled', array(
- 'MOD_NAME' => htmlspecialchars($mod_name),
- 'MOD_PATH' => $short_path,
- 'PHPBB_VERSION' => $details['PHPBB_VERSION'],
- 'S_PHPBB_VESION' => ($details['PHPBB_VERSION'] != $config['version']) ? true : false,
- 'U_INSTALL' => $this->u_action . "&action=pre_install&mod_path=$short_path",
- 'U_DELETE' => $this->u_action . "&action=pre_delete_mod&mod_path=$short_path",
- 'U_DETAILS' => $this->u_action . "&action=details&mod_path=$short_path",
- ));
- }
- return;
- }
- /**
- * Lists mod details
- */
- function list_details($mod_ident)
- {
- global $template, $config, $user;
- $template->assign_vars(array(
- 'S_DETAILS' => true,
- 'U_BACK' => $this->u_action,
- ));
- $details = $this->mod_details($mod_ident, true);
- if (!is_int($mod_ident) && $details['PHPBB_VERSION'] != $config['version'])
- {
- $version_warnig = sprintf($user->lang['VERSION_WARNING'], $details['PHPBB_VERSION'], $config['version']);
- $template->assign_vars(array(
- 'VERSION_WARNING' => $version_warnig,
- 'S_PHPBB_VESION' => true,
- ));
- }
- if (!empty($details['AUTHOR_DETAILS']))
- {
- foreach ($details['AUTHOR_DETAILS'] as $author_details)
- {
- $template->assign_block_vars('author_list', $author_details);
- }
- unset($details['AUTHOR_DETAILS']);
- }
- // Display Do-It-Yourself Actions...per the MODX spec,
- // Need to handle the languag later, but it's not saved for now.
- if (!empty($details['DIY_INSTRUCTIONS']))
- {
- $template->assign_var('S_DIY', true);
- if (!is_array($details['DIY_INSTRUCTIONS']))
- {
- $details['DIY_INSTRUCTIONS'] = array($details['DIY_INSTRUCTIONS']);
- }
- foreach ($details['DIY_INSTRUCTIONS'] as $instruction)
- {
- $template->assign_block_vars('diy_instructions', array(
- 'DIY_INSTRUCTION' => nl2br($instruction),
- ));
- }
- }
- if (!empty($details['MOD_HISTORY']))
- {
- $template->assign_var('S_CHANGELOG', true);
- foreach ($details['MOD_HISTORY'] as $mod_version)
- {
- $template->assign_block_vars('changelog', array(
- 'VERSION' => $mod_version['VERSION'],
- 'DATE' => $mod_version['DATE'],
- ));
- foreach ($mod_version['CHANGES'] as $changes)
- {
- $template->assign_block_vars('changelog.changes', array(
- 'CHANGE' => $changes,
- ));
- }
- }
- }
- unset($details['MOD_HISTORY']);
- $details['MOD_NAME'] = localize_title($details['MOD_NAME'], $user->data['user_lang']);
- $details['MOD_NAME'] = htmlspecialchars($details['MOD_NAME']);
- $template->assign_vars($details);
- if (!empty($details['AUTHOR_NOTES']))
- {
- $template->assign_var('S_AUTHOR_NOTES', true);
- }
- if (!empty($details['MOD_INSTALL_TIME']))
- {
- $template->assign_var('S_INSTALL_TIME', true);
- }
- return;
- }
- /**
- * Returns array of mod information
- */
- function mod_details($mod_ident, $find_children = true, $uninstall = false)
- {
- global $phpbb_root_path, $phpEx, $user, $template, $parent_id;
- if (is_int($mod_ident))
- {
- global $db, $user;
- $mod_id = (int) $mod_ident;
- $sql = 'SELECT *
- FROM ' . MODS_TABLE . "
- WHERE mod_id = $mod_id";
- $result = $db->sql_query($sql);
- if ($row = $db->sql_fetchrow($result))
- {
- // TODO: Yuck, get rid of this.
- $author_details = array();
- $author_details[0] = array(
- 'AUTHOR_NAME' => $row['mod_author_name'],
- 'AUTHOR_EMAIL' => $row['mod_author_email'],
- 'AUTHOR_WEBSITE' => $row['mod_author_url'],
- );
- $actions = unserialize($row['mod_actions']);
- $details = array(
- 'MOD_ID' => $row['mod_id'],
- 'MOD_PATH' => $row['mod_path'],
- 'MOD_INSTALL_TIME' => $user->format_date($row['mod_time']),
- // 'MOD_DEPENDENCIES' => unserialize($row['mod_dependencies']), // ?
- 'MOD_NAME' => $row['mod_name'],
- 'MOD_DESCRIPTION' => nl2br($row['mod_description']),
- 'MOD_VERSION' => $row['mod_version'],
- 'DIY_INSTRUCTIONS' => (!empty($actions['DIY_INSTRUCTIONS'])) ? $actions['DIY_INSTRUCTIONS'] : '',
- 'AUTHOR_NOTES' => nl2br($row['mod_author_notes']),
- 'AUTHOR_DETAILS' => $author_details,
- );
- // This is a check for any further XML files to go with this MOD.
- // Obviously, the files must not have been removed for this to work.
- if (($find_children || $uninstall) && file_exists($row['mod_path']))
- {
- $parent_id = $mod_id;
- $mod_path = $row['mod_path'];
- $actions = array();
- $mod_dir = dirname($mod_path);
- $this->mod_root = $mod_dir . '/';
- $ext = substr(strrchr($mod_path, '.'), 1);
- $this->parser = new parser($ext);
- $this->parser->set_file($mod_path);
- // Find and display the available MODX files
- $children = $this->find_children($mod_path);
- $elements = array('language' => array(), 'template' => array());
- $found_prosilver = false;
- if (!$uninstall)
- {
- $this->handle_contrib($children);
- $this->handle_language_prompt($children, $elements, 'details');
- $this->handle_template_prompt($children, $elements, 'details');
- // Now offer to install additional templates
- if (isset($children['template']) && sizeof($children['template']))
- {
- // These are the instructions included with the MOD
- foreach ($children['template'] as $template_name)
- {
- if (!is_array($template_name))
- {
- continue;
- }
- if ($template_name['realname'] == 'prosilver')
- {
- $found_prosilver = true;
- }
- if (file_exists($this->mod_root . $template_name['href']))
- {
- $xml_file = $template_name['href'];
- }
- else
- {
- $xml_file = str_replace($this->mods_dir, '', $mod_dir) . '/' . $template_name['href'];
- }
- $template->assign_block_vars('avail_templates', array(
- 'TEMPLATE_NAME' => $template_name['realname'],
- 'XML_FILE' => urlencode($xml_file),
- ));
- }
- }
- }
- else
- {
- if (isset($children['uninstall']) && sizeof($children['uninstall']))
- {
- // Override already exising actions with the ones
- global $rev_actions;
- $xml_file = $mod_dir . '/' . ltrim($children['uninstall'][0]['href'], './');
- $this->parser->set_file($xml_file);
- $rev_actions = $this->parser->get_actions();
- }
- }
- if (!$found_prosilver)
- {
- $template->assign_block_vars('avail_templates', array(
- 'TEMPLATE_NAME' => 'prosilver',
- 'XML_FILE' => basename($mod_path),
- ));
- }
- $processed_templates = array('prosilver');
- $processed_templates += explode(',', $row['mod_template']);
- /*
- // now grab the templates that have not already been processed
- $sql = 'SELECT template_id, template_path FROM ' . STYLES_TEMPLATE_TABLE . '
- WHERE ' . $db->sql_in_set('template_name', $processed_templates, true);
- $result = $db->sql_query($sql);
- while ($row = $db->sql_fetchrow($result))
- {
- $template->assign_block_vars('board_templates', array(
- 'TEMPLATE_ID' => $row['template_id'],
- 'TEMPLATE_NAME' => $row['template_path'],
- ));
- }
- */
- $s_hidden_fields = build_hidden_fields(array(
- 'action' => 'install',
- 'parent' => $parent_id,
- ));
- $template->assign_vars(array(
- 'S_FORM_ACTION' => $this->u_action,
- 'S_HIDDEN_FIELDS' => $s_hidden_fields,
- ));
- add_form_key('acp_mods');
- }
- }
- else
- {
- trigger_error($user->lang['NO_MOD'] . adm_back_link($this->u_action), E_USER_WARNING);
- }
- $db->sql_freeresult($result);
- }
- else
- {
- $parent = request_var('parent', 0);
- if ($parent)
- {
- global $db;
- // reset the class parameters to refelect the proper directory
- $sql = 'SELECT mod_path FROM ' . MODS_TABLE . '
- WHERE mod_id = ' . (int) $parent;
- $result = $db->sql_query($sql);
- if ($row = $db->sql_fetchrow($result))
- {
- $this->mod_root = dirname($row['mod_path']) . '/';
- }
- }
- if (strpos($mod_ident, $this->mods_dir) === false)
- {
- $mod_ident = $this->mods_dir . $mod_ident;
- }
- if (!file_exists($mod_ident))
- {
- $mod_ident = str_replace($this->mods_dir, $this->mod_root, $mod_ident);
- }
- $mod_path = $mod_ident;
- $mod_parent = 0;
- $ext = substr(strrchr($mod_path, '.'), 1);
- $this->parser = new parser($ext);
- $this->parser->set_file($mod_path);
- $details = $this->parser->get_details();
- if ($find_children)
- {
- $actions = array();
- $children = $this->find_children($mod_path);
- $elements = array('language' => array(), 'template' => array());
- $this->handle_contrib($children);
- $this->handle_language_prompt($children, $elements, 'details');
- $this->handle_template_prompt($children, $elements, 'details');
- }
- }
- return $details;
- }
- /**
- * Returns complex array of all mod actions
- */
- function mod_actions($mod_ident)
- {
- global $phpbb_root_path, $phpEx;
- if (is_int($mod_ident))
- {
- global $db, $user;
- $sql = 'SELECT mod_actions, mod_name
- FROM ' . MODS_TABLE . "
- WHERE mod_id = $mod_ident";
- $result = $db->sql_query($sql);
- $row = $db->sql_fetchrow($result);
- $db->sql_freeresult($result);
- if ($row)
- {
- $mod_actions = unserialize($row['mod_actions']);
- if (@unserialize($row['mod_name']) === false)
- {
- // On version 1.0.1 and later the mod name is a serialized array.
- // Earlier it was a string so unserialize will fail.
- $mod_actions['EDITS'] = $this->update_edits($mod_actions['EDITS']);
- }
- return($mod_actions);
- }
- else
- {
- trigger_error($user->lang['NO_MOD'] . adm_back_link($this->u_action), E_USER_WARNING);
- }
- }
- else
- {
- if (strpos($mod_ident, $this->mods_dir) === false)
- {
- $mod_ident = $this->mods_dir . $mod_ident;
- }
- if (!file_exists($mod_ident))
- {
- $mod_ident = str_replace($this->mods_dir, $this->mod_root, $mod_ident);
- }
- $this->parser->set_file($mod_ident);
- $actions = $this->parser->get_actions();
- }
- return $actions;
- }
- /**
- * Updates inline edits for MODs installed before AutoMOD 1.0.1.
- *
- * @param array $mod_edits, MOD actions directly from the DB.
- * @return mixed uppdated array or false on error.
- */
- function update_edits($mod_edits)
- {
- if (empty($mod_edits))
- {
- return(false);
- }
- $updated_ary = array();
- foreach ($mod_edits as $file => $edits)
- {
- $updated_ary[$file] = array();
- $inline = false;
- $key = 0;
- $old_find = $find_line = '';
- foreach ($edits as $edit)
- {
- foreach ($edit as $find => $action)
- {
- $first_key = key($action); // The first key contains the action or "in-line-edit".
- if ($first_key != 'in-line-edit')
- {
- if ($inline)
- {
- $updated_ary[$file][$key++][$find_line] = array('in-line-edit' => $inline_edit);
- $inline_edit = array();
- $inline = false;
- $old_find = $find_line = '';
- }
- $updated_ary[$file][$key++][$find] = $action;
- $inline = false;
- continue;
- }
- $inline = true;
- $inline_edit = (empty($inline_edit)) ? array() : $inline_edit;
- if (!empty($old_find) && !$this->same_line($old_find, $find, $action['in-line-edit']))
- {
- $updated_ary[$file][$key++][$find_line] = array('in-line-edit' => $inline_edit);
- $inline_edit = array();
- }
- $find_line = $find;
- $old_find = $find;
- $inline_edit[] = $action['in-line-edit'];
- }
- }
- if ($inline && !empty($inline_edit))
- {
- $updated_ary[$file][$key++][$find_line] = array('in-line-edit' => $inline_edit);
- $inline = false;
- $inline_edit = array();
- $old_find = $find_line = '';
- }
- }
- return($updated_ary);
- }
- /**
- * Tries to check if two inline edits are editing the same line.
- *
- * @param string $prev, the find from the previous in-line-edit.
- * @param string $find, the find for the current in-line-edit.
- * @param array $action, the current edit array.
- * @return bool true for identical lines, otherwise false
- */
- function same_line($prev_find, $find, $action)
- {
- if (empty($prev_find) || empty($find))
- {
- // If both are empty something is wrong.
- return(false);
- }
- // The first key in $action is the in-line-find string
- $edit_ary = reset($action); // Array with what to do and what to remove.
- $inline_find = key($action); // String to find in $find.
- $add_ary = reset($edit_ary); // $add_ary[0] contains the string to remove from $find
- $add_str = $add_ary[0];
- $inline_action = key($edit_ary); // What to do.
- // The actions currently supported are in-line-before-add and in-line-after-add.
- // replace and delete will be added later.
- switch ($inline_action)
- {
- case 'in-line-replace':
- // There are no positions stored in the DB so we can not be sure that there
- // is only one occasion of the string added instead of the search string.
- // Keeping count on the previous edits still don't give a 100% guaranty that
- // we are in the right place in the string.
- $compare = str_replace($add_str, $inline_find, $find);
- break;
- case 'in-line-before-add':
- $pos = strpos($find, $inline_find);
- $len = strlen($add_str);
- $start = $pos - $len;
- $compare = substr_replace($find, '', $start, $len);
- break;
- case 'in-line-after-add':
- $pos = strpos($find, $inline_find);
- $start = $pos + strlen($inline_find);
- $compare = substr_replace($find, '', $start, strlen($add_str));
- break;
- case 'inline-remove':
- default:
- // inline-remove don't yet work to install with AutoMOD so I assume nobody
- // is trying to remove it either in MODs installed with 1.0.0.1 or earlier.
- return(false);
- break;
- }
- $check = ($compare == $prev_find) ? true : false;
- return($check);
- }
- /**
- * Install/pre-install a mod
- * Preforms all Edits, Copies, and SQL queries
- */
- function install($action, $mod_path, $parent = 0)
- {
- global $phpbb_root_path, $phpEx, $db, $template, $user, $config, $cache, $dest_template;
- global $force_install, $mod_installed;
- // Are we forcing a template install?
- $dest_template = $mod_contribs = $mod_language = '';
- if (isset($_POST['template_submit']))
- {
- if (!check_form_key('acp_mods'))
- {
- trigger_error($user->lang['FORM_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING);
- }
- $mod_path = urldecode(request_var('source', ''));
- $dest_template = request_var('dest', '');
- if (preg_match('#.*install.*xml$#i', $mod_path))
- {
- $src_template = 'prosilver';
- }
- else
- {
- preg_match('#([a-z0-9]+)$#i', core_basename($mod_path), $match);
- $src_template = $match[1];
- unset ($match);
- }
- }
- if (empty($mod_path))
- {
- return false; // ERROR
- }
- $details = $this->mod_details($mod_path, false);
- if (!$parent)
- {
- // $details['MOD_NAME'] is a array and not knowing what language was used when the MOD was installed,
- // if it was installed with AutoMOD 1.0.0. So need to check all.
- $sql_where = '';
- foreach ($details['MOD_NAME'] as $mod_name)
- {
- $sql_where .= (($sql_where == '') ? ' mod_name ' : ' OR mod_name ') . $db->sql_like_expression($db->any_char . $mod_name . $db->any_char);
- }
- $sql = 'SELECT mod_name FROM ' . MODS_TABLE . "
- WHERE $sql_where";
- $result = $db->sql_query($sql);
- if ($row = $db->sql_fetchrow($result))
- {
- trigger_error('AM_MOD_ALREADY_INSTALLED');
- }
- }
- else if ($dest_template) // implicit && $parent
- {
- // Has this template already been processed?
- $sql = 'SELECT mod_name
- FROM ' . MODS_TABLE . "
- WHERE mod_id = $parent
- AND mod_template " . $db->sql_like_expression($db->any_char . $dest_template . $db->any_char);
- $result = $db->sql_query($sql);
- if ($row = $db->sql_fetchrow($result))
- {
- trigger_error('AM_MOD_ALREADY_INSTALLED');
- }
- $db->sql_freeresult($result);
- }
- // NB: There could and should be cases to check for duplicated MODs and contribs
- // However, there is not appropriate book-keeping in place for those in 1.0.x
- // grab installed contrib and language items from the database
- if ($parent)
- {
- $modx_type = request_var('type', '');
- if ($modx_type == 'lang')
- {
- $sql = 'SELECT mod_name
- FROM ' . MODS_TABLE . "
- WHERE mod_id = $parent
- AND mod_languages " . $db->sql_like_expression($db->any_char . $mod_path . $db->any_char);
- $result = $db->sql_query($sql);
- if ($row = $db->sql_fetchrow($result))
- {
- trigger_error('AM_MOD_ALREADY_INSTALLED');
- }
- else
- {
- $mod_language = $mod_path;
- }
- $db->sql_freeresult($result);
- }
- elseif ($modx_type == 'contrib')
- {
- $sql = 'SELECT mod_name
- FROM ' . MODS_TABLE . "
- WHERE mod_id = $parent
- AND mod_contribs " . $db->sql_like_expression($db->any_char . $mod_path . $db->any_char);
- $result = $db->sql_query($sql);
- if ($row = $db->sql_fetchrow($result))
- {
- trigger_error('AM_MOD_ALREADY_INSTALLED');
- }
- else
- {
- $mod_contribs = $mod_path;
- }
- $db->sql_freeresult($result);
- }
- }
- $execute_edits = ($action == 'pre_install') ? false : true;
- $write_method = 'editor_' . determine_write_method(!$execute_edits);
- $editor = new $write_method();
- // get FTP information if we need it (or initialize array $hidden_ary)
- $hidden_ary = get_connection_info(!$execute_edits);
- $actions = $this->mod_actions($mod_path);
- if ($dest_template)
- {
- $sql = 'SELECT template_inherit_path FROM ' . STYLES_TEMPLATE_TABLE . "
- WHERE template_path = '" . $db->sql_escape($dest_template) . "'";
- $result = $db->sql_query($sql);
- global $dest_inherits;
- $dest_inherits = '';
- if ($row = $db->sql_fetchrow($result))
- {
- $dest_inherits = $row['template_inherit_path'];
- }
- $db->sql_freeresult($result);
- if (!empty($actions['EDITS']))
- {
- foreach ($actions['EDITS'] as $file => $edits)
- {
- if (strpos($file, 'styles/') === false)
- {
- unset($actions['EDITS'][$file]);
- }
- else if ($src_template != $dest_template)
- {
- $file_new = str_replace($src_template, $dest_template, $file);
- $actions['EDITS'][$file_new] = $edits;
- unset($actions['EDITS'][$file]);
- }
- }
- }
- if (!empty($actions['NEW_FILES']))
- {
- foreach ($actions['NEW_FILES'] as $src_file => $dest_file)
- {
- if (strpos($src_file, 'styles/') === false)
- {
- unset($actions['NEW_FILES']);
- }
- else
- {
- $actions['NEW_FILES'][$src_file] = str_replace($src_template, $dest_template, $dest_file);
- }
- }
- }
- }
- // only supporting one level of hierarchy here
- if (!$parent)
- {
- // check for "child" MODX files and attempt to decide which ones we need
- $children = $this->find_children($mod_path);
- $elements = array('language' => array(), 'template' => array());
- if ($execute_edits)
- {
- global $mode;
- $this->handle_dependency($children, $mode, $mod_path);
- }
- $this->handle_language_prompt($children, $elements, $action);
- $this->handle_merge('language', $actions, $children, $elements['language']);
- $this->handle_template_prompt($children, $elements, $action);
- $this->handle_merge('template', $actions, $children, $elements['template']);
- }
- else
- {
- if ($dest_template)
- {
- $elements['template'] = array($dest_template);
- }
- elseif ($mod_language)
- {
- $elements['language'] = array($mod_path);
- }
- else
- {
- $elements['contrib'] = array($mod_path);
- }
- }
- $template->assign_vars(array(
- 'S_INSTALL' => $execute_edits,
- 'S_PRE_INSTALL' => !$execute_edits,
- 'MOD_PATH' => str_replace($this->mod_root, '', $mod_path),
- 'U_INSTALL' => $this->u_action . '&action=install' . ($parent ? "&parent=$parent" : ''),
- 'U_RETURN' => $this->u_action,
- 'U_BACK' => $this->u_action,
- ));
- if ($execute_edits)
- {
- $editor->create_edited_root($this->edited_root);
- $force_install = request_var('force', false);
- }
- $display = ($execute_edits || $config['preview_changes']) ? true : false;
- // process the actions
- $mod_installed = $this->process_edits($editor, $actions, $details, $execute_edits, $display, false);
- if (!$execute_edits)
- {
- $s_hidden_fields = array('dependency_confirm' => !empty($_REQUEST['dependency_confirm']));
- if ($dest_template)
- {
- $s_hidden_fields['dest'] = $dest_template;
- $s_hidden_fields['source'] = $mod_path;
- $s_hidden_fields['template_submit'] = true;
- }
- if ($parent)
- {
- $s_hidden_fields['type'] = $modx_type;
- }
- $template->assign_var('S_HIDDEN_FIELDS', build_hidden_fields($s_hidden_fields));
- add_form_key('acp_mods');
- return;
- } // end pre_install
- // Display Do-It-Yourself Actions...per the MODX spec, these should be displayed last
- if (!empty($actions['DIY_INSTRUCTIONS']))
- {
- $template->assign_var('S_DIY', true);
- if (!is_array($actions['DIY_INSTRUCTIONS']))
- {
- $actions['DIY_INSTRUCTIONS'] = array($actions['DIY_INSTRUCTIONS']);
- }
- foreach ($actions['DIY_INSTRUCTIONS'] as $instruction)
- {
- $template->assign_block_vars('diy_instructions', array(
- 'DIY_INSTRUCTION' => nl2br($instruction),
- ));
- }
- }
- if (!empty($actions['PHP_INSTALLER']))
- {
- $template->assign_vars(array(
- 'U_PHP_INSTALLER' => $phpbb_root_path . $actions['PHP_INSTALLER'],
- ));
- }
- if ($mod_installed || $force_install)
- {
- // Move edited files back
- $status = $editor->commit_changes($this->edited_root, '');
- if (is_string($status))
- {
- $mod_installed = false;
- $template->assign_block_vars('error', array(
- 'ERROR' => $status,
- ));
- }
- }
- // The editor class provides more pertinent information regarding edits
- // so we store that as the canonical version, used for uninstalling
- $actions['EDITS'] = $editor->mod_actions;
- $editor->clear_actions();
- // if MOD installed successfully, make a record.
- if (($mod_installed || $force_install) && !$parent)
- {
- $mod_name = (is_array($details['MOD_NAME'])) ? serialize($details['MOD_NAME']) : $details['MOD_NAME'];
- // Insert database data
- $sql = 'INSERT INTO ' . MODS_TABLE . ' ' . $db->sql_build_array('INSERT', array(
- 'mod_time' => (int) $editor->install_time,
- // @todo: Are dependencies part of the MODX Spec?
- 'mod_dependencies' => '', //(string) serialize($details['MOD_DEPENDENCIES']),
- 'mod_name' => (string) $mod_name,
- 'mod_description' => (string) $details['MOD_DESCRIPTION'],
- 'mod_version' => (string) $details['MOD_VERSION'],
- 'mod_path' => (string) $details['MOD_PATH'],
- 'mod_author_notes' => (string) $details['AUTHOR_NOTES'],
- 'mod_author_name' => (string) $details['AUTHOR_DETAILS'][0]['AUTHOR_NAME'],
- 'mod_author_email' => (string) $details['AUTHOR_DETAILS'][0]['AUTHOR_EMAIL'],
- 'mod_author_url' => (string) $details['AUTHOR_DETAILS'][0]['AUTHOR_WEBSITE'],
- 'mod_actions' => (string) serialize($actions),
- 'mod_languages' => (string) (isset($elements['language']) && sizeof($elements['language'])) ? implode(',', $elements['language']) : '',
- 'mod_template' => (string) (isset($elements['template']) && sizeof($elements['template'])) ? implode(',', $elements['template']) : '',
- 'mod_contribs' => (string) (isset($elements['contrib']) && sizeof($elements['contrib'])) ? implode(',', $elements['contrib']) : '',
- ));
- $db->sql_query($sql);
- $cache->purge();
- // Add log
- $mod_name = localize_title($details['MOD_NAME'], 'en');
- add_log('admin', 'LOG_MOD_ADD', $mod_name);
- }
- // in this case, we are installing an additional template or language
- else if (($mod_installed || $force_install) && $parent)
- {
- $sql = 'SELECT * FROM ' . MODS_TABLE . " WHERE mod_id = $parent";
- $result = $db->sql_query($sql);
- $row = $db->sql_fetchrow($result);
- $db->sql_freeresult($result);
- if (!$row)
- {
- trigger_error($user->lang['NO_MOD'] . adm_back_link($this->u_action));
- }
- $sql_ary = array(
- 'mod_version' => $details['MOD_VERSION'],
- );
- if (!empty($elements['language']))
- {
- $sql_ary['mod_languages'] = (!empty($row['mod_languages'])) ? $row['mod_languages'] . ',' : '';
- $sql_ary['mod_languages'] .= implode(',', $elements['language']);
- }
- else
- {
- $sql_ary['mod_languages'] = $row['mod_languages'];
- }
- if (!empty($elements['template']))
- {
- $sql_ary['mod_template'] = $row['mod_template'] . ',' . implode(',', $elements['template']);
- }
- else
- {
- $sql_ary['mod_template'] = $row['mod_template'];
- }
- if (!empty($elements['contrib']))
- {
- $sql_ary['mod_contribs'] = (!empty($row['mod_contribs'])) ? $row['mod_contribs'] . ',' : '';
- $sql_ary['mod_contribs'] .= implode(',', $elements['contrib']);
- }
- else
- {
- $sql_ary['mod_contribs'] = $row['mod_contribs'];
- }
- $sql_ary['mod_time'] = $editor->install_time;
- $prior_mod_actions = unserialize($row['mod_actions']);
- $sql_ary['mod_actions'] = serialize(array_merge_recursive($prior_mod_actions, $actions));
- unset($prior_mod_actions);
- $sql = 'UPDATE ' . MODS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $sql_ary) . "
- WHERE mod_id = $parent";
- $db->sql_query($sql);
- add_log('admin', 'LOG_MOD_CHANGE', htmlspecialchars_decode($row['mod_name']));
- }
- // there was an error we need to tell the user about
- else
- {
- add_form_key('acp_mods');
- if ($parent)
- {
- $hidden_ary['parent'] = $parent;
- }
- if ($dest_template)
- {
- $hidden_ary['dest'] = $dest_template;
- $hidden_ary['source'] = $mod_path;
- $hidden_ary['template_submit'] = true;
- }
- if ($mod_language || $mod_contribs)
- {
- $hidden_ary['type'] = $modx_type;
- }
- $template->assign_vars(array(
- 'S_ERROR' => true,
- 'S_HIDDEN_FIELDS' => build_hidden_fields($hidden_ary),
- 'U_RETRY' => $this->u_action . '&action=install&mod_path=' . $mod_path,
- ));
- }
- // if we forced the install of the MOD, we need to let the user know their board could be broken
- if ($force_install)
- {
- $template->assign_var('S_FORCE', true);
- }
- if ($mod_installed || $force_install)
- {
- // $editor->commit_changes_final don't do anything ATM, but to be compatible with future versions
- $mod_name = localize_title($details['MOD_NAME'], 'en');
- $editor->commit_changes_final('mod_' . $editor->install_time, str_replace(' ', '_', $mod_name));
- }
- }
- /**
- * Uninstall/pre uninstall a mod
- */
- function uninstall($action, $mod_id, $parent)
- {
- global $phpbb_root_path, $phpEx, $db, $template, $user, $config;
- global $force_install, $mod_uninstalled;
- if (!$mod_id && !$parent)
- {
- return false; // ERROR
- }
- // the MOD is more important than additional MODx files
- if ($parent == $mod_id)
- {
- $parent = 0;
- }
- if ($parent)
- {
- // grab installed contrib and language items from the database
- $sql = 'SELECT mod_languages, mod_contribs
- FROM ' . MODS_TABLE . "
- WHERE mod_id = $parent";
- $result = $db->sql_query($sql);
- if ($row = $db->sql_fetchrow($result))
- {
- $mod_path = request_var('mod_path', '');
- if (in_array($mod_path, explode(',', $row['mod_languages'])))
- {
- $elements['languages'] = $mod_path;
- }
- elseif (in_array($mod_path, explode(',', $row['mod_contribs'])))
- {
- $elements['contrib'] = $mod_path;
- }
- else
- {
- trigger_error('AM_MOD_NOT_INSTALLED');
- }
- }
- else
- {
- return false;
- }
- }
- // set the class parameters to reflect the proper directory
- $sql = 'SELECT mod_path FROM ' . MODS_TABLE . '
- WHERE mod_id = ' . (($mod_id) ? $mod_id : $parent);
- $result = $db->sql_query($sql);
- if ($row = $db->sql_fetchrow($result))
- {
- $this->mod_root = dirname($row['mod_path']) . '/';
- $this->edited_root = "{$this->mod_root}_edited/";
- }
- else
- {
- return false; // ERROR
- }
- $execute_edits = ($action == 'pre_uninstall') ? false : true;
- $write_method = 'editor_' . determine_write_method(!$execute_edits);
- $editor = new $write_method();
- // get FTP information if we need it (or initialize array $hidden_ary)
- $hidden_ary = get_connection_info(!$execute_edits);
- if ($parent)
- {
- $hidden_ary['parent'] = $parent;
- $hidden_ary['mod_path'] = $mod_path;
- }
- $template->assign_vars(array(
- 'S_UNINSTALL' => $execute_edits,
- 'S_PRE_UNINSTALL' => !$execute_edits,
- 'L_FORCE_INSTALL' => $user->lang['FORCE_UNINSTALL'],
- 'MOD_ID' => $mod_id,
- 'U_UNINSTALL' => ($parent) ? $this->u_action . "&action=uninstall&parent=$parent&mod_path=$mod_path" : $this->u_action . '&action=uninstall&mod_id=' . $mod_id,
- 'U_RETURN' => $this->u_action,
- 'U_BACK' => $this->u_action,
- 'S_HIDDEN_FIELDS' => build_hidden_fields($hidden_ary),
- ));
- // grab actions and details
- if (!$parent)
- {
- $details = $this->mod_details($mod_id, false, true);
- $actions = $this->mod_actions($mod_id);
- }
- else
- {
- $details = $this->mod_details($mod_path, false);
- $actions = $this->mod_actions($mod_path);
- }
- if ($execute_edits)
- {
- $editor->create_edited_root($this->edited_root);
- $force_install = $force_uninstall = request_var('force', false);
- }
- $display = ($execute_edits || $config['preview_changes']) ? true : false;
- // cleanup edits if we forced the install on a contrib or language
- if ($parent)
- {
- if (isset($actions['EDITS']))
- {
- foreach ($actions['EDITS'] as $file => $edit_ary)
- {
- foreach ($edit_ary as $edit_id => $edit)
- {
- foreach ($edit as $find => $action_ary)
- {
- if (empty($action_ary))
- {
- unset($actions['EDITS'][$file][$edit_id][$find]);
- }
- }
- }
- }
- }
- }
- // process the actions
- $mod_uninstalled = $this->process_edits($editor, $actions, $details, $execute_edits, $display, true);
- if (!$execute_edits)
- {
- return;
- } // end pre_uninstall
- if (($mod_uninstalled || $force_uninstall) && !$parent)
- {
- // Move edited files back
- $status = $editor->commit_changes($this->edited_root, '');
- if (is_string($status))
- {
- $mod_uninstalled = false;
- $template->assign_block_vars('error', array(
- 'ERROR' => $status,
- ));
- }
- }
- /*
- elseif (($mod_uninstalled || $force_uninstall) && $parent)
- {
- // Only update the database entries and don't move any files back
- $sql = 'SELECT * FROM ' . MODS_TABLE . " WHERE mod_id = $parent";
- $result = $db->sql_query($sql);
- $row = $db->sql_fetchrow($result);
- $db->sql_freeresult($result);
- if (!$row)
- {
- trigger_error($user->lang['NO_MOD'] . adm_back_link($this->u_action));
- }
- $sql_ary = array(
- 'mod_version' => $details['MOD_VERSION'],
- );
- if (!empty($elements['languages']))
- {
- $sql_ary['mod_languages'] = explode(',', $row['mod_languages']);
- foreach ($sql_ary['mod_languages'] as $key => $value)
- {
- if ($value != $elements['languages']);
- {
- unset($sql_ary['mod_languages'][$key]);
- }
- }
- $sql_ary['mod_languages'] = implode(',', $sql_ary['mod_languages']);
- }
- else
- {
- $sql_ary['mod_languages'] = $row['mod_languages'];
- }
- // let's just not support uninstalling styles edits
- $sql_ary['mod_template'] = $row['mod_template'];
- if (!empty($elements['contrib']))
- {
- $sql_ary['mod_contribs'] = explode(',', $row['mod_contribs']);
- foreach ($sql_ary['mod_contribs'] as $key => $value)
- {
- if ($value != $elements['contrib']);
- {
- unset($sql_ary['mod_contribs'][$key]);
- }
- }
- $sql_ary['mod_contribs'] = implode(',', $sql_ary['mod_contribs']);
- }
- else
- {
- $sql_ary['mod_contribs'] = $row['mod_contribs'];
- }
- $sql_ary['mod_time'] = $row['mod_time'];
- //$prior_mod_actions = unserialize($row['mod_actions']);
- //$sql_ary['mod_actions'] = serialize(array_merge_recursive($prior_mod_actions, $actions));
- $sql_ary['mod_actions'] = $row['mod_actions'];
- //unset($prior_mod_actions);
- $sql = 'UPDATE ' . MODS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $sql_ary) . "
- WHERE mod_id = $parent";
- $db->sql_query($sql);
- $mod_name = localize_title($row['mod_name'], $user->data['user_lang']);
- add_log('admin', 'LOG_MOD_CHANGE', htmlspecialchars_decode($mod_name));
- }
- */
- // if we forced uninstall of the MOD, we need to let the user know their board could be broken
- if ($force_uninstall)
- {
- $template->assign_var('S_FORCE', true);
- }
- /*
- else if (!$mod_uninstalled)
- {
- add_form_key('acp_mods');
- $template->assign_vars(array(
- 'S_ERROR' => true,
- 'S_HIDDEN_FIELDS' => build_hidden_fields($hidden_ary),
- 'U_RETRY' => $this->u_action . '&action=uninstall&mod_id=' . $mod_id,
- ));
- }
- */
- if ($mod_uninstalled || $force_uninstall)
- {
- // Delete from DB
- $sql = 'DELETE FROM ' . MODS_TABLE . '
- WHERE mod_id = ' . $mod_id;
- $db->sql_query($sql);
- // Add log
- $mod_name = localize_title($details['MOD_NAME'], 'en');
- $mod_name = htmlspecialchars_decode($mod_name);
- add_log('admin', 'LOG_MOD_REMOVE', $mod_name);
- $mod_name = localize_title($details['MOD_NAME'], 'en');
- $editor->commit_changes_final('mod_' . $editor->install_time, str_replace(' ', '_', $mod_name));
- }
- }
- /**
- * Returns array of available mod install files in dir (Recursive)
- * @param $dir string - dir to search
- * @param $recurse int - number of levels to recurse
- */
- function find_mods($dir, $recurse = false)
- {
- global $user;
- if ($recurse === false)
- {
- $mods = array('main' => array(), 'contrib' => array(), 'template' => array(), 'language' => array());
- $recurse = 0;
- }
- else
- {
- static $mods = array('main' => array(), 'contrib' => array(), 'template' => array(), 'language' => array());
- }
- // ltrim shouldn't be needed, but some users had problems. See #44305
- $dir = ltrim($dir, '/');
- if (!file_exists($dir))
- {
- return array();
- }
- if (!is_readable($dir))
- {
- trigger_error(sprintf($user->lang['NEED_READ_PERMISSIONS'], $dir), E_USER_WARNING);
- }
- $dp = opendir($dir);
- while (($file = readdir($dp)) !== false)
- {
- if ($file[0] != '.' && strpos("$dir/$file", '_edited') === false && strpos("$dir/$file", '_backups') === false)
- {
- // recurse - we don't want anything within the MODX "root" though
- if ($recurse && !is_file("$dir/$file") && strpos("$dir/$file", 'root') === false)
- {
- $mods = array_merge($mods, $this->find_mods("$dir/$file", $recurse - 1));
- }
- // this might be better as an str function, especially being in a loop
- else if (preg_match('#.*install.*xml$#i', $file) || (preg_match('#(contrib|templates|languages)#i', $dir, $match)) || ($recurse === 0 && strpos($file, '.xml') !== false))
- {
- // if this is an "extra" MODX file, make a record of it as such
- // we are assuming the MOD follows MODX packaging standards here
- if (strpos($file, '.xml') !== false && preg_match('#(contrib|templates|languages)#i', $dir, $match))
- {
- // Get rid of the S. This is a side effect of understanding
- // MODX 1.0.x and 1.2.x.
- $match[1] = rtrim($match[1], 's');
- $mods[$match[1]][] = array(
- 'href' => "$dir/$file",
- 'realname' => core_basename($file),
- 'title' => core_basename($file),
- );
- }
- else
- {
- $check = end($mods['main']);
- $check = $check['href'];
- // we take the first file alphabetically with install in the filename
- if (!$check || dirname($check) == $dir)
- {
- if (preg_match('#.*install.*xml$#i', $file) && preg_match('#.*install.*xml$#i', $check) && strnatcasecmp(basename($check), $file) > 0)
- {
- $index = max(0, sizeof($mods['main']) - 1);
- $mods['main'][$index] = array(
- 'href' => "$dir/$file",
- 'realname' => core_basename($file),
- 'title' => core_basename($file),
- );
- break;
- }
- else if (preg_match('#.*install.*xml$#i', $file) && !preg_match('#.*install.*xml$#i', $check))
- {
- $index = max(0, sizeof($mods['main']) - 1);
- $mods['main'][$index] = array(
- 'href' => "$dir/$file",
- 'realname' => core_basename($file),
- 'title' => core_basename($file),
- );
- break;
- }
- }
- else
- {
- if (strpos($file, '.xml') !== false)
- {
- $mods['main'][] = array(
- 'href' => "$dir/$file",
- 'realname' => core_basename($file),
- 'title' => core_basename($file),
- );
- }
- }
- }
- }
- }
- }
- closedir($dp);
- return $mods;
- }
- function process_edits($editor, $actions, $details, $change = false, $display = true, $reverse = false)
- {
- global $template, $user, $db, $phpbb_root_path, $force_install, $mod_installed;
- global $dest_inherits, $dest_template, $children, $config;
- $mod_installed = true;
- if ($reverse)
- {
- global $rev_actions;
- if (empty($rev_actions))
- {
- // maybe should allow for potential extensions here
- $actions = parser::reverse_edits($actions);
- }
- else
- {
- $actions = $rev_actions;
- unset($rev_actions);
- }
- }
- $template->assign_vars(array(
- 'S_DISPLAY_DETAILS' => (bool) $display,
- 'S_CHANGE_FILES' => (bool) $change,
- ));
- /*
- if (!empty($details['PHPBB_VERSION']) && $details['PHPBB_VERSION'] != $config['version'])
- {
- $version_warnig = sprintf($user->lang['VERSION_WARNING'], $details['PHPBB_VERSION'], $config['version']);
- $template->assign_vars(array(
- 'VERSION_WARNING' => $version_warnig,
- 'S_PHPBB_VESION' => true,
- ));
- }
- */
- if (!empty($details['AUTHOR_NOTES']) && $details['AUTHOR_NOTES'] != $user->lang['UNKNOWN_MOD_AUTHOR-NOTES'])
- {
- $template->assign_vars(array(
- 'S_AUTHOR_NOTES' => true,
- 'AUTHOR_NOTES' => nl2br($details['AUTHOR_NOTES']),
- ));
- }
- // not all MODs will have edits (!)
- if (isset($actions['EDITS']))
- {
- $template->assign_var('S_EDITS', true);
- foreach ($actions['EDITS'] as $filename => $edits)
- {
- // see if the file to be opened actually exists
- if (!file_exists("$phpbb_root_path$filename"))
- {
- $is_inherit = (strpos($filename, 'styles/') !== false && !empty($dest_inherits)) ? true : false;
- $template->assign_block_vars('edit_files', array(
- 'S_MISSING_FILE' => ($is_inherit) ? false : true,
- 'INHERIT_MSG' => ($is_inherit) ? sprintf($user->lang['INHERIT_NO_CHANGE'], $dest_template, $dest_inherits) : '',
- 'FILENAME' => $filename,
- ));
- $mod_installed = ($is_inherit) ? $mod_installed : false;
- continue;
- }
- else
- {
- $template->assign_block_vars('edit_files', array(
- 'S_SUCCESS' => false,
- 'FILENAME' => $filename,
- ));
- // If installing - not pre_install nor (pre_)uninstall, backup the file
- // This is to make sure it works with editor_ftp because write_method is
- // forced to direct when in preview modes, and ignored in editor_manual!
- if ($change && !$reverse)
- {
- $status = $editor->open_file($filename, $this->backup_root);
- }
- else
- {
- $status = $editor->open_file($filename);
- }
- $edit_success = true;
- if (is_string($status))
- {
- $template->assign_block_vars('error', array(
- 'ERROR' => $status,
- ));
- $mod_installed = false;
- continue;
- }
- $edit_success = true;
- foreach ($edits as $finds)
- {
- $comment = '';
- foreach ($finds as $find => $commands)
- {
- if (isset($finds['comment']) && !$comment && $finds['comment'] != $user->lang['UNKNOWN_MOD_COMMENT'])
- {
- $comment = $finds['comment'];
- unset($finds['comment']);
- }
- if ($find == 'comment')
- {
- continue;
- }
- $find_tpl = array(
- 'FIND_STRING' => htmlspecialchars($find),
- 'COMMENT' => htmlspecialchars($comment),
- );
- $offset_ary = $editor->find($find);
- // special case for FINDs with no action associated
- if (is_null($commands))
- {
- continue;
- }
- foreach ($commands as $type => $contents)
- {
- if (!$offset_ary)
- {
- $offset_ary['start'] = $offset_ary['end'] = false;
- }
- $status = false;
- $inline_template_ary = array();
- $contents_orig = $contents;
- switch (strtoupper($type))
- {
- case 'AFTER ADD':
- $status = $editor->add_string($find, $contents, 'AFTER', $offset_ary['start'], $offset_ary['end']);
- break;
- case 'BEFORE ADD':
- $status = $editor->add_string($find, $contents, 'BEFORE', $offset_ary['start'], $offset_ary['end']);
- break;
- case 'INCREMENT':
- case 'OPERATION':
- //$contents = "";
- $status = $editor->inc_string($find, '', $contents);
- break;
- case 'REPLACE WITH':
- $status = $editor->replace_string($find, $contents, $offset_ary['start'], $offset_ary['end']);
- break;
- case 'IN-LINE-EDIT':
- // these aren't quite as straight forward. Still have multi-level arrays to sort through
- $inline_comment = '';
- foreach ($contents as $inline_edit_id => $inline_edit)
- {
- if ($inline_edit_id === 'inline-comment')
- {
- // This is a special case for tucking comments in the array
- if ($inline_edit != $user->lang['UNKNOWN_MOD_INLINE-COMMENT'])
- {
- $inline_comment = $inline_edit;
- }
- continue;
- }
- foreach ($inline_edit as $inline_find => $inline_commands)
- {
- foreach ($inline_commands as $inline_action => $inline_contents)
- {
- // inline finds are pretty cantankerous, so do them in the loop
- $line = $editor->inline_find($find, $inline_find, $offset_ary['start'], $offset_ary['end']);
- if (!$line)
- {
- // find failed
- $status = $mod_installed = false;
- $inline_template_ary[] = array(
- 'FIND' => array(
- 'S_SUCCESS' => $status,
- 'NAME' => $user->lang[$type],
- 'COMMAND' => htmlspecialchars($inline_find),
- ),
- 'ACTION' => array());
- continue 2;
- }
- $inline_contents = $inline_contents[0];
- $contents_orig = $inline_find;
- switch (strtoupper($inline_action))
- {
- case 'IN-LINE-':
- $editor->last_string_offset = $line['string_offset'] + $line['find_length'] - 1;
- $status = true;
- continue 2;
- break;
- case 'IN-LINE-BEFORE-ADD':
- $status = $editor->inline_add($find, $inline_find, $inline_contents, 'BEFORE', $line['array_offset'], $line['string_offset'], $line['find_length']);
- break;
- case 'IN-LINE-AFTER-ADD':
- $status = $editor->inline_add($find, $inline_find, $inline_contents, 'AFTER', $line['array_offset'], $line['string_offset'], $line['find_length']);
- break;
- case 'IN-LINE-REPLACE':
- case 'IN-LINE-REPLACE-WITH':
- $status = $editor->inline_replace($find, $inline_find, $inline_contents, $line['array_offset'], $line['string_offset'], $line['find_length']);
- break;
- case 'IN-LINE-OPERATION':
- $status = $editor->inc_string($find, $inline_find, $inline_contents);
- break;
- default:
- $message = sprintf($user->lang['UNRECOGNISED_COMMAND'], $inline_action);
- trigger_error($message, E_USER_WARNING); // ERROR!
- break;
- }
- $inline_template_ary[] = array(
- 'FIND' => array(
- 'S_SUCCESS' => $status,
- 'NAME' => $user->lang[$type],
- 'COMMAND' => (is_array($contents_orig)) ? $user->lang['INVALID_MOD_INSTRUCTION'] : htmlspecialchars($contents_orig),
- ),
- 'ACTION' => array(
- 'S_SUCCESS' => $status,
- 'NAME' => $user->lang[$inline_action],
- 'COMMAND' => (is_array($inline_contents)) ? $user->lang['INVALID_MOD_INSTRUCTION'] : htmlspecialchars($inline_contents),
- // 'COMMENT' => $inline_comment, (inline comments aren't actually part of the MODX spec)
- ),
- );
- }
- if (!$status)
- {
- $mod_installed = false;
- }
- $editor->close_inline_edit();
- }
- }
- break;
- default:
- $message = sprintf($user->lang['UNRECOGNISED_COMMAND'], $type);
- trigger_error($message, E_USER_WARNING); // ERROR!
- break;
- }
- $template->assign_block_vars('edit_files.finds', array_merge($find_tpl, array(
- 'S_SUCCESS' => $status,
- )));
- if (!$status)
- {
- $edit_success = false;
- $mod_installed = false;
- }
- if (sizeof($inline_template_ary))
- {
- foreach ($inline_template_ary as $inline_template)
- {
- // We must assign the vars for the FIND first
- $template->assign_block_vars('edit_files.finds.actions', $inline_template['FIND']);
- // And now the vars for the ACTION
- $template->assign_block_vars('edit_files.finds.actions.inline', $inline_template['ACTION']);
- }
- $inline_template_ary = array();
- }
- else if (!is_array($contents_orig))
- {
- $template->assign_block_vars('edit_files.finds.actions', array(
- 'S_SUCCESS' => $status,
- 'NAME' => $user->lang[$type],
- 'COMMAND' => htmlspecialchars($contents_orig),
- ));
- }
- }
- }
- $editor->close_edit();
- }
- $template->alter_block_array('edit_files', array(
- 'S_SUCCESS' => $edit_success,
- ), true, 'change');
- }
- if ($change)
- {
- $status = $editor->close_file("{$this->edited_root}$filename");
- if (is_string($status))
- {
- $template->assign_block_vars('error', array(
- 'ERROR' => $status,
- ));
- $mod_installed = false;
- }
- }
- }
- } // end foreach
- if (!$mod_installed)
- {
- $template->assign_var('S_DISPLAY_FILE_EDITS', true);
- }
- // Move included files
- if (isset($actions['NEW_FILES']) && !empty($actions['NEW_FILES']))
- {
- $template->assign_var('S_NEW_FILES', true);
- // Because foreach operates on a copy of the specified array and not the array itself,
- // we cannot rely on the array pointer while using it, so we use a while loop w/ each()
- // We need array pointer to rewind the loop when is_array($target) (See Ticket #62341)
- while (list($source, $target) = each($actions['NEW_FILES']))
- {
- if (is_array($target))
- {
- // If we've shifted off all targets, we're done w/ that element
- if (empty($target))
- {
- continue;
- }
- // Shift off first target, then rewind array pointer to get next target
- $target = array_shift($actions['NEW_FILES'][$source]);
- prev($actions['NEW_FILES']);
- }
- /* if ($change && ($mod_installed || $force_install))
- {
- */ $status = $editor->copy_content($this->mod_root . str_replace('*.*', '', $source), str_replace('*.*', '', $target));
- if ($status !== true && !is_null($status))
- {
- $mod_installed = false;
- $template->assign_var('S_DISPLAY_NEW_FILES', true);
- }
- $template->assign_block_vars('new_files', array(
- 'S_SUCCESS' => ($status === true) ? true : false,
- 'S_NO_COPY_ATTEMPT' => (is_null($status)) ? true : false,
- 'SOURCE' => $source,
- 'TARGET' => $target,
- ));
- /* }
- else if ($display && !$change)
- {
- $template->assign_block_vars('new_files', array(
- 'SOURCE' => $source,
- 'TARGET' => $target,
- ));
- }
- // To avoid "error" on install page when being asked to force install
- else if ($change && $display && !$mod_installed && !$force_install)
- {
- $template->assign_block_vars('new_files', array(
- 'S_NO_COPY_ATTEMPT' => true,
- 'FILENAME' => $target,
- ));
- }
- */ }
- }
- // Delete (or reverse-delete) installed files
- if (!empty($actions['DELETE_FILES']))
- {
- $template->assign_var('S_REMOVING_FILES', true);
- // Dealing with a reverse-delete, must heed to the dangers ahead!
- if ($reverse)
- {
- $directories = array();
- $directories['src'] = array();
- $directories['dst'] = array();
- $directories['del'] = array();
- // Because foreach operates on a copy of the specified array and not the array itself,
- // we cannot rely on the array pointer while using it, so we use a while loop w/ each()
- // We need array pointer to rewind the loop when is_array($target) (See Ticket #62341)
- while (list($source, $target) = each($actions['DELETE_FILES']))
- {
- if (is_array($target))
- {
- // If we've shifted off all targets, we're done w/ that element
- if (empty($target))
- {
- continue;
- }
- // Shift off first target, then rewind array pointer to get next target
- $target = array_shift($actions['DELETE_FILES'][$source]);
- prev($actions['DELETE_FILES']);
- }
- // Some MODs include 'umil/', avoid deleting!
- if (strpos($target, 'umil/') === 0)
- {
- unset($actions['DELETE_FILES'][$source]);
- continue;
- }
- // MODX used '*.*' or 'dir/*.*' (Fun!)
- else if (strpos($source, '*.*') !== false)
- {
- // This could be phpbb_root_path, if "Copy: root/*.* to: *.*" syntax was used
- // or root/custom_dir if "Copy: root/custom/*.* to: custom/*.*", etc.
- $source = $this->mod_root . str_replace('*.*', '', $source);
- $target = str_replace('*.*', '', $target);
- // Get all of the files in the source directory
- $files = find_files($source, '.*');
- // And translate into destination files
- $files = str_replace($source, $target, $files);
- // Get all of the sub-directories in the source directory
- $directories['src'] = find_files($source, '.*', 20, true);
- // And translate it into destination sub-directories
- $directories['dst'] = str_replace($source, $target, $directories['src']);
- // Compare source and destination subdirs, if any, in _reverse_ order! (array_pop)
- for ($i=0, $cnt = count($directories['dst']); $i < $cnt; $i++)
- {
- $dir_source = array_pop($directories['src']);
- $dir_target = array_pop($directories['dst']);
- // Some MODs include 'umil/', avoid deleting!
- if (strpos($dir_target, 'umil/') === 0)
- {
- continue;
- }
- $src_file_cnt = directory_num_files($dir_source, false, true);
- $dst_file_cnt = directory_num_files($phpbb_root_path . $dir_target, false, true);
- $src_dir_cnt = directory_num_files($dir_source, true, true);
- $dst_dir_cnt = directory_num_files($phpbb_root_path . $dir_target, true, true);
- // Do we have a match in recursive file count and match in recursive subdir count?
- // This could be vastly improved..
- if ($src_file_cnt == $dst_file_cnt && $src_dir_cnt == $dst_dir_cnt)
- {
- $directories['del'][] = $dir_target;
- }
- unset($dir_source, $dir_target, $src_file_cnt, $dst_file_cnt, $src_dir_cnt, $dst_dir_cnt); //cleanup
- }
- foreach ($files as $file)
- {
- // Some MODs include 'umil/', avoid deleting!
- if (strpos($file, 'umil/') === 0)
- {
- continue;
- }
- else if (!file_exists($phpbb_root_path . $file) && ($change || $display))
- {
- $template->assign_block_vars('removing_files', array(
- 'S_MISSING_FILE' => true,
- 'S_NO_DELETE_ATTEMPT' => true,
- 'FILENAME' => $file,
- ));
- }
- else if ($change && ($mod_installed || $force_install))
- {
- $status = $editor->remove($file);
- $template->assign_block_vars('removing_files', array(
- 'S_SUCCESS' => ($status === true) ? true : false,
- 'S_NO_DELETE_ATTEMPT' => (is_null($status)) ? true : false,
- 'FILENAME' => $file,
- ));
- }
- else if ($display && !$change)
- {
- $template->assign_block_vars('removing_files', array(
- 'FILENAME' => $file,
- ));
- }
- // To avoid "error" on uninstall page when being asked to force
- else if ($change && $display && !$mod_installed && !$force_install)
- {
- $template->assign_block_vars('removing_files', array(
- 'S_NO_DELETE_ATTEMPT' => true,
- 'FILENAME' => $file,
- ));
- }
- }
- unset($files); //cleanup
- }
- else if (!file_exists($phpbb_root_path . $target) && ($change || $display))
- {
- $template->assign_block_vars('removing_files', array(
- 'S_MISSING_FILE' => true,
- 'S_NO_DELETE_ATTEMPT' => true,
- 'FILENAME' => $target,
- ));
- }
- else if ($change && ($mod_installed || $force_install))
- {
- $status = $editor->remove($target);
- $template->assign_block_vars('removing_files', array(
- 'S_SUCCESS' => ($status === true) ? true : false,
- 'S_NO_DELETE_ATTEMPT' => (is_null($status)) ? true : false,
- 'FILENAME' => $target,
- ));
- }
- else if ($display && !$change)
- {
- $template->assign_block_vars('removing_files', array(
- 'FILENAME' => $target,
- ));
- }
- // To avoid "error" on uninstall page when being asked to force
- else if ($change && $display && !$mod_installed && !$force_install)
- {
- $template->assign_block_vars('removing_files', array(
- 'S_NO_DELETE_ATTEMPT' => true,
- 'FILENAME' => $target,
- ));
- }
- }
- // Delete wildcard directories, if any, which should now be empty anyway (no recursive delete needed)
- if ($cnt = count($directories['del']))
- {
- for ($i=0; $i < $cnt; $i++)
- {
- if ($change && ($mod_installed || $force_install))
- {
- $status = $editor->remove($directories['del'][$i]);
- $template->assign_block_vars('removing_files', array(
- 'S_SUCCESS' => ($status === true) ? true : false,
- 'S_NO_DELETE_ATTEMPT' => (is_null($status)) ? true : false,
- 'FILENAME' => $directories['del'][$i],
- ));
- }
- else if ($display && !$change)
- {
- $template->assign_block_vars('removing_files', array(
- 'FILENAME' => $directories['del'][$i],
- ));
- }
- // To avoid "error" on uninstall page when being asked to force
- else if ($change && $display && !$mod_installed && !$force_install)
- {
- $template->assign_block_vars('removing_files', array(
- 'S_NO_DELETE_ATTEMPT' => true,
- 'FILENAME' => $directories['del'][$i],
- ));
- }
- }
- unset($directories['del']); //cleanup
- }
- }
- // Normal deleting functionality (not in reverse edits mode)
- else if ($mod_installed || $force_install)
- {
- foreach ($actions['DELETE_FILES'] as $file)
- {
- $wildcards = strpos($file, '*.*');
- $file = str_replace('*.*', '', $file);
- if (!file_exists($phpbb_root_path . $file) && ($change || $display))
- {
- $template->assign_block_vars('removing_files', array(
- 'S_MISSING_FILE' => true,
- 'S_NO_DELETE_ATTEMPT' => true,
- 'FILENAME' => $file,
- ));
- }
- // purposely do not use !== false here, because we don't expect wildcards at position 0
- // if there's no wildcard, make sure it's a file to avoid recursively deleting a directory!!!
- else if ($wildcards || is_file($phpbb_root_path . $file))
- {
- if ($change)
- {
- // Delete, recursively if needed
- $status = $editor->remove($file, true);
- $template->assign_block_vars('removing_files', array(
- 'S_SUCCESS' => ($status === true) ? true : false,
- 'S_NO_DELETE_ATTEMPT' => (is_null($status)) ? true : false,
- 'FILENAME' => $file,
- ));
- }
- else if ($display)
- {
- $template->assign_block_vars('removing_files', array(
- 'FILENAME' => $file,
- ));
- }
- }
- }
- }
- }
- // Perform SQL queries last -- Queries usually cannot be done a second
- // time, so do them only if the edits were successful. Still complies
- // with the MODX spec in this location
- if (!empty($actions['SQL']) && ($mod_installed || $force_install || ($display && !$change)))
- {
- $template->assign_var('S_SQL', true);
- // parser::parse_sql($actions['SQL']);
- $db->sql_return_on_error(true);
- foreach ($actions['SQL'] as $query)
- {
- if ($change)
- {
- $query_success = $db->sql_query($query);
- if ($query_success)
- {
- $template->assign_block_vars('sql_queries', array(
- 'S_SUCCESS' => true,
- 'QUERY' => $query,
- ));
- }
- else
- {
- $error = $db->sql_error();
- $template->assign_block_vars('sql_queries', array(
- 'S_SUCCESS' => false,
- 'QUERY' => $query,
- 'ERROR_MSG' => $error['message'],
- 'ERROR_CODE'=> $error['code'],
- ));
- $mod_installed = false;
- }
- }
- else if ($display)
- {
- $template->assign_block_vars('sql_queries', array(
- 'QUERY' => $query,
- ));
- }
- }
- $db->sql_return_on_error(false);
- }
- else
- {
- $template->assign_var('S_SQL', false);
- }
- return $mod_installed;
- }
- /**
- * Search on the file system for other .xml files that belong to this MOD
- * @param string $mod_path - path to the "main" MODX file, relative to phpBB Root
- */
- function find_children($mod_path)
- {
- $children = array();
- if ($this->parser->get_modx_version() == 1.2)
- {
- // TODO: eww, yuck ... processing the XML again?
- $details = $this->mod_details($mod_path, false);
- $children = $details['CHILDREN'];
- if (isset($children['template-lang']))
- {
- if (isset($children['template']))
- {
- $children['template'] = array_merge($children['template'], $children['template-lang']);
- }
- else
- {
- $children['template'] = $children['template-lang'];
- }
- unset($children['template-lang']);
- }
- $child_types = array('contrib', 'template', 'language', 'dependency', 'uninstall');
- foreach ($child_types as $type)
- {
- if (empty($children[$type]))
- {
- continue;
- }
- $child_count = sizeof($children[$type]);
- // Remove duplicate hrefs if they exist (links in multiple languages can cause this)
- for ($i = 1; $i < $child_count; $i++)
- {
- if (isset($children[$type][$i - 1]) && $children[$type][$i - 1]['href'] == $children[$type][$i]['href'])
- {
- unset($children[$type][$i]);
- }
- }
- }
- }
- else if ($this->parser->get_modx_version() == 1.0)
- {
- $search_dir = (strpos($mod_path, $this->mods_dir) === 0) ? dirname($mod_path) : $this->mods_dir . dirname($mod_path);
- $children = $this->find_mods($search_dir, 5);
- }
- return $children;
- }
- function handle_dependency(&$children, $mode, $mod_path)
- {
- if (isset($children['dependency']) && sizeof($children['dependency']))
- {
- // TODO: check for the chance that the MOD has been installed by AutoMOD
- // previously
- if (confirm_box(true))
- {
- // do nothing
- }
- else if (empty($_REQUEST['dependency_confirm']) && empty($_REQUEST['force']))
- {
- global $user, $id;
- $full_url_list = array();
- $message = '';
- $children['dependency'] = array_unique($children['dependency']);
- foreach ($children['dependency'] as $dependency)
- {
- //$full_url_list[] = $dependency_url;
- $message .= sprintf($user->lang['DEPENDENCY_INSTRUCTIONS'], $dependency['href'], $dependency['title']) . '<br /><br />';
- }
- confirm_box(false, $message, build_hidden_fields(array(
- 'dependency_confirm' => true,
- 'mode' => $mode,
- 'action' => 'install',
- 'mod_path' => $mod_path,
- )));
- }
- }
- }
- /**
- * Get all contrib links for the selected language.
- * This functions will be removed.
- */
- function get_contrib_lang($contrib, $lang = 'en')
- {
- $ary = array();
- foreach ($contrib as $element)
- {
- if (match_language($lang, $element['lang']))
- {
- $ary[] = $element;
- }
- }
- return($ary);
- }
- function handle_contrib(&$children)
- {
- global $template, $parent_id, $phpbb_root_path, $user, $db;
- if (isset($children['contrib']) && sizeof($children['contrib']))
- {
- $template->assign_var('S_CONTRIB_AVAILABLE', true);
- // Do we have links in the users selected language.
- // Start with getting the Enlgish links.
- $contrib_en = $this->get_contrib_lang($children['contrib']);
- if ($user->data['user_lang'] == 'en' || sizeof($contrib_en) == sizeof($children['contrib']))
- {
- // Our user has either English or there is only English links.
- $children['contrib'] = $contrib_en;
- }
- else
- {
- // If there are any links in the users language, let's get them.
- $contrib_lang = $this->get_contrib_lang($children['contrib'], $user->data['user_lang']);
- if (!sizeof($contrib_lang))
- {
- // There is no links in the right language, give them the English links.
- $children['contrib'] = $contrib_en;
- }
- else
- {
- $children['contrib'] = $contrib_lang;
- }
- }
- if (!empty($parent_id))
- {
- // get installed contribs from the database
- $sql = 'SELECT mod_contribs FROM ' . MODS_TABLE . '
- WHERE mod_id = ' . $parent_id;
- $result = $db->sql_query($sql);
- $mod_contribs = $db->sql_fetchfield('mod_contribs');
- $db->sql_freeresult();
- }
- $mod_contribs = (!empty($mod_contribs)) ? explode(',', $mod_contribs) : array();
- // there are things like upgrades...we don't care unless the MOD has previously been installed.
- foreach ($children['contrib'] as $xml_file)
- {
- // Another hack for supporting both versions of MODX
- $xml_file = (is_array($xml_file)) ? $xml_file['href'] : str_replace($this->mod_root, '', $xml_file);
- $child_details = $this->mod_details($xml_file, false);
- // don't do the urlencode until after the file is looked up on the
- // filesystem
- $xml_file = urlencode('/' . $xml_file);
- if (in_array(urldecode($xml_file), $mod_contribs))
- {
- $child_details['U_UNINSTALL'] = ($parent_id) ? $this->u_action . "&action=pre_uninstall&parent=$parent_id&mod_path=$xml_file&type=contrib" : '';
- }
- else
- {
- $child_details['U_INSTALL'] = ($parent_id) ? $this->u_action . "&action=pre_install&parent=$parent_id&mod_path=$xml_file&type=contrib" : '';
- }
- $child_details['MOD_NAME'] = localize_title($child_details['MOD_NAME'], $user->data['user_lang']);
- $template->assign_block_vars('contrib', $child_details);
- }
- }
- }
- function handle_merge($type, &$actions, &$children, $process_files)
- {
- if (!isset($children[$type]) || !sizeof($process_files))
- {
- return;
- }
- // add the actions to our $actions array...give praise to array_merge_recursive
- foreach ($process_files as $key => $name)
- {
- foreach ($children[$type] as $child_key => $child_data)
- {
- $root_position = strpos($child_data['href'], $this->mod_root);
- if ($child_data['realname'] == $name && $root_position === false)
- {
- $child_filename = $this->mod_root . ltrim($child_data['href'], './');
- break;
- }
- else if ($child_data['realname'] == $name && $root_position === 0)
- {
- $child_filename = $child_data['href'];
- }
- }
- $actions_ary = $this->mod_actions($child_filename);
- if (!isset($actions_ary['NEW_FILES']))
- {
- $actions = array_merge_recursive($actions, $actions_ary);
- continue;
- }
- // perform some cleanup if the MOD author didn't specify the proper root directory
- foreach ($actions_ary['NEW_FILES'] as $source => $destination)
- {
- // if the source file does not exist, and languages/ is not at the beginning
- // this is probably only applicable with MODX 1.0.
- if (!file_exists($this->mod_root . $source) && strpos("{$type}s/", $source) === false)
- {
- // and it does exist if we force a languages/ into the path
- if (file_exists($this->mod_root . "{$type}s/" . $source))
- {
- // change the array key to include languages
- unset($actions_ary['NEW_FILES'][$source]);
- $actions_ary['NEW_FILES']["{$type}s/$source"] = $destination;
- }
- // else we let the error handling do its thing
- }
- }
- $actions = array_merge_recursive($actions, $actions_ary);
- }
- }
- function handle_language_prompt(&$children, &$elements, $action)
- {
- global $db, $template, $parent_id, $phpbb_root_path;
- if (isset($children['language']) && sizeof($children['language']))
- {
- // additional languages are available...find out which ones we may want to apply
- $sql = 'SELECT lang_id, lang_iso FROM ' . LANG_TABLE;
- $result = $db->sql_query($sql);
- $installed_languages = array();
- while ($row = $db->sql_fetchrow($result))
- {
- $installed_languages[$row['lang_id']] = $row['lang_iso'];
- }
- $db->sql_freeresult($result);
- foreach ($children['language'] as $key => $tag)
- {
- // remove useless title from MODX 1.2.0 tags
- $children['language'][$tag['realname']] = is_array($tag) ? $tag['href'] : $tag;
- }
- $available_languages = array_keys($children['language']);
- $process_languages = $elements['language'] = array_intersect($available_languages, $installed_languages);
- // $unknown_languages are provided for by the MOD, but not installed on the board
- $unknown_languages = array_diff($available_languages, $installed_languages);
- // Inform the user if there are languages provided for by the MOD
- if (sizeof($children['language']))
- {
- if (!empty($parent_id))
- {
- // get installed languages from the database
- $sql = 'SELECT mod_languages FROM ' . MODS_TABLE . '
- WHERE mod_id = ' . $parent_id;
- $result = $db->sql_query($sql);
- $mod_languages = $db->sql_fetchfield('mod_languages');
- $db->sql_freeresult();
- }
- $mod_languages = (!empty($mod_languages)) ? explode(',', $mod_languages) : array();
- foreach ($children['language'] as $row)
- {
- if (is_string($row))
- {
- continue;
- }
- $xml_file = (!empty($row['href'])) ? urlencode($row['href']) : '';
- $template->assign_block_vars('unknown_lang', array(
- 'ENGLISH_NAME' => $row['title'],
- 'LOCAL_NAME' => $row['realname'],
- 'U_INSTALL' => (!empty($xml_file) && !in_array($row['href'], $mod_languages) && $parent_id) ? $this->u_action . "&action=pre_install&parent=$parent_id&mod_path=$xml_file&type=lang" : '',
- 'U_UNINSTALL' => (!empty($xml_file) && $parent_id) ? $this->u_action . "&action=pre_uninstall&parent=$parent_id&mod_path=$xml_file&type=lang" : '',
- ));
- unset($row);
- }
- }
- return $process_languages;
- }
- }
- function handle_template_prompt(&$children, &$elements, $action)
- {
- return;
- /*
- global $db, $template, $phpbb_root_path, $parent_id;
- if (isset($children['template']) && sizeof($children['template']))
- {
- // additional styles are available for this MOD
- $sql = 'SELECT template_id, template_name FROM ' . STYLES_TEMPLATE_TABLE;
- $result = $db->sql_query($sql);
- $installed_templates = array();
- while ($row = $db->sql_fetchrow($result))
- {
- $installed_templates[$row['template_id']] = $row['template_name'];
- }
- $db->sql_freeresult($result);
- foreach ($children['template'] as $key => $tag)
- {
- // remove useless title from MODX 1.2.0 tags
- $children['template'][$tag['realname']] = is_array($tag) ? $tag['href'] : $tag;
- }
- $available_templates = array_keys($children['template']);
- // $process_templates are those that are installed on the board and provided for by the MOD
- $process_templates = $elements['template'] = array_intersect($available_templates, $installed_templates);
- }
- */
- }
- function upload_mod($action)
- {
- global $phpbb_root_path, $phpEx, $template, $user;
- $can_upload = (@ini_get('file_uploads') == '0' || strtolower(@ini_get('file_uploads')) == 'off' || !@extension_loaded('zlib')) ? false : true;
- // get FTP information if we need it
- $hidden_ary = get_connection_info(false);
- if (!isset($_FILES['modupload']) || $action != 'upload_mod')
- {
- $template->assign_vars(array(
- 'S_FRONTEND' => true,
- 'S_MOD_UPLOAD' => ($can_upload) ? true : false,
- 'U_UPLOAD' => $this->u_action . '&action=upload_mod',
- 'S_FORM_ENCTYPE' => ($can_upload) ? ' enctype="multipart/form-data"' : '',
- 'S_HIDDEN_FIELDS' => build_hidden_fields($hidden_ary),
- ));
- add_form_key('acp_mods_upload');
- return false;
- } // end pre_upload_mod
- if (!check_form_key('acp_mods_upload'))
- {
- trigger_error($user->lang['FORM_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING);
- }
- $user->add_lang('posting'); // For error messages
- include($phpbb_root_path . 'includes/functions_upload.' . $phpEx);
- $upload = new fileupload();
- $upload->set_allowed_extensions(array('zip')); // Only allow ZIP files
- $write_method = 'editor_' . determine_write_method(false);
- // For Direct & Manual write methods, make sure store/mods/ directory is writable
- if ($write_method == 'editor_direct' || $write_method == 'editor_manual')
- {
- if (!is_writable($this->mods_dir))
- {
- trigger_error($user->lang['MODS_NOT_WRITABLE'] . adm_back_link($this->u_action), E_USER_WARNING);
- }
- $write_method = 'editor_direct'; // Force Direct method, in the case of manual
- $upload_dir = $this->mods_dir;
- }
- // FTP method: we still need a known world-writable directory (store/) for zip extraction
- else if (is_writable($this->store_dir))
- {
- $upload_dir = $this->store_dir;
- }
- else
- {
- trigger_error($user->lang['STORE_NOT_WRITABLE'] . adm_back_link($this->u_action), E_USER_WARNING);
- }
- $editor = new $write_method();
- // Make sure the store/mods/ directory exists and if it doesn't, create it
- if (!is_dir($this->mods_dir))
- {
- $editor->recursive_mkdir($this->mods_dir);
- }
- // Proceed with the upload
- $file = $upload->form_upload('modupload');
- if (empty($file->filename))
- {
- trigger_error($user->lang['NO_UPLOAD_FILE'] . adm_back_link($this->u_action), E_USER_WARNING);
- }
- else if ($file->init_error || sizeof($file->error))
- {
- $file->remove();
- trigger_error((sizeof($file->error) ? implode('<br />', $file->error) : $user->lang['MOD_UPLOAD_INIT_FAIL']) . adm_back_link($this->u_action), E_USER_WARNING);
- }
- $file->clean_filename('real');
- $file->move_file(str_replace($phpbb_root_path, '', $upload_dir), true, true);
- if (sizeof($file->error))
- {
- $file->remove();
- trigger_error(implode('<br />', $file->error) . adm_back_link($this->u_action), E_USER_WARNING);
- }
- include($phpbb_root_path . 'includes/functions_compress.' . $phpEx);
- $mod_dir = $upload_dir . '/' . str_replace('.zip', '', $file->get('realname'));
- $compress = new compress_zip('r', $file->destination_file);
- $compress->extract($mod_dir . '_tmp/');
- $compress->close();
- $folder_contents = scandir($mod_dir . '_tmp/', 1); // This ensures dir is at index 0
- $folder_contents = array_diff($folder_contents, array('.', '..'));
- // We need to check if there's only one (main) directory inside the temp MOD directory
- if (sizeof($folder_contents) == 1)
- {
- $folder_contents = implode(null, $folder_contents);
- $from_dir = $mod_dir . '_tmp/' . $folder_contents;
- $to_dir = $this->mods_dir . '/' . $folder_contents;
- }
- // Otherwise assume the temp directory is the main directroy, so change the directory
- // name by moving to a directory without the '_tmp' suffix
- else if (!is_dir($mod_dir))
- {
- $from_dir = $mod_dir . '_tmp/';
- $to_dir = $mod_dir . '/';
- }
- // We should never really get here, but you never know!
- else
- {
- trigger_error($user->lang['MOD_UPLOAD_UNRECOGNIZED'] . adm_back_link($this->u_action), E_USER_WARNING);
- }
- // Copy that directory to the new path
- $editor->copy_content($from_dir, $to_dir);
- // Finally remove the main tmp extraction directory, directly, just like we created it
- recursive_unlink($mod_dir . '_tmp/');
- $template->assign_vars(array(
- 'S_MOD_SUCCESSBOX' => true,
- 'MESSAGE' => $user->lang['MOD_UPLOAD_SUCCESS'],
- 'U_RETURN' => $this->u_action,
- ));
- // Remove the uploaded archive file
- $file->remove();
- return true;
- }
- function delete_mod($action, $mod_path = '')
- {
- global $template, $user;
- if (!empty($mod_path))
- {
- $mod_path = explode('/', str_replace('\\', '/', $mod_path));
- $mod_path = (!empty($mod_path[0])) ? $mod_path[0] : $mod_path[1];
- }
- else
- {
- $mod_path = request_var('mod_delete', '');
- }
- if (empty($mod_path) || !is_dir($this->mods_dir . '/' . $mod_path))
- {
- return false; // ERROR
- }
- // get FTP information if we need it
- $hidden_ary = get_connection_info(false);
- $hidden_ary['mod_delete'] = $mod_path;
- if ($action != 'delete_mod')
- {
- $template->assign_vars(array(
- 'S_MOD_DELETE' => true,
- 'U_DELETE' => $this->u_action . '&action=delete_mod',
- 'S_HIDDEN_FIELDS' => build_hidden_fields($hidden_ary),
- ));
- add_form_key('acp_mods_delete');
- return;
- } // end pre_delete_mod
- $write_method = 'editor_' . determine_write_method(false);
- // Force Direct method, in the case of manual - just like above in mod_upload()
- $write_method = ($write_method == 'editor_manual') ? 'editor_direct' : $write_method;
- $editor = new $write_method();
- if (!check_form_key('acp_mods_delete'))
- {
- trigger_error($user->lang['FORM_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING);
- }
- $status = $editor->remove("{$this->mods_dir}/{$mod_path}", true);
- if ($status !== true)
- {
- trigger_error($user->lang['DELETE_ERROR'] . " $status" . adm_back_link($this->u_action), E_USER_WARNING);
- }
- $template->assign_vars(array(
- 'S_MOD_SUCCESSBOX' => true,
- 'MESSAGE' => $user->lang['DELETE_SUCCESS'],
- 'U_RETURN' => $this->u_action,
- ));
- }
- }
- ?>