<?php
/**
 *
 * This file is part of the phpBB Forum Software package.
 *
 * @copyright (c) phpBB Limited <https://www.phpbb.com>
 * @license GNU General Public License, version 2 (GPL-2.0)
 *
 * For full copyright and license information, please see
 * the docs/CREDITS.txt file.
 *
 */

namespace phpbb\install\module\install_data\task;

class add_modules extends \phpbb\install\task_base
{
	/**
	 * @var \phpbb\db\driver\driver_interface
	 */
	protected $db;

	/**
	 * @var \phpbb\extension\manager
	 */
	protected $extension_manager;

	/**
	 * @var \phpbb\install\helper\iohandler\iohandler_interface
	 */
	protected $iohandler;

	/**
	 * @var \phpbb\module\module_manager
	 */
	protected $module_manager;

	/**
	 * Define the module structure so that we can populate the database without
	 * needing to hard-code module_id values
	 *
	 * @var array
	 */
	protected $module_categories;

	/**
	 * @var array
	 */
	protected $module_categories_basenames;

	/**
	 * @var array
	 */
	protected $module_extras;

	/**
	 * Constructor
	 *
	 * @param \phpbb\install\helper\iohandler\iohandler_interface	$iohandler	Installer's input-output handler
	 * @param \phpbb\install\helper\container_factory				$container	Installer's DI container
	 */
	public function __construct(\phpbb\install\helper\iohandler\iohandler_interface $iohandler,
								\phpbb\install\helper\container_factory $container)
	{
		$this->db					= $container->get('dbal.conn');
		$this->extension_manager	= $container->get('ext.manager');
		$this->iohandler			= $iohandler;
		$this->module_manager		= $container->get('module.manager');

		parent::__construct(true);

		$this->module_categories = array(
			'acp' => array(
				'ACP_CAT_GENERAL' => array(
					'ACP_QUICK_ACCESS',
					'ACP_BOARD_CONFIGURATION',
					'ACP_CLIENT_COMMUNICATION',
					'ACP_SERVER_CONFIGURATION',
				),
				'ACP_CAT_FORUMS' => array(
					'ACP_MANAGE_FORUMS',
					'ACP_FORUM_BASED_PERMISSIONS',
				),
				'ACP_CAT_POSTING' => array(
					'ACP_MESSAGES',
					'ACP_ATTACHMENTS',
				),
				'ACP_CAT_USERGROUP' => array(
					'ACP_CAT_USERS',
					'ACP_GROUPS',
					'ACP_USER_SECURITY',
				),
				'ACP_CAT_PERMISSIONS' => array(
					'ACP_GLOBAL_PERMISSIONS',
					'ACP_FORUM_BASED_PERMISSIONS',
					'ACP_PERMISSION_ROLES',
					'ACP_PERMISSION_MASKS',
				),
				'ACP_CAT_CUSTOMISE' => array(
					'ACP_STYLE_MANAGEMENT',
					'ACP_EXTENSION_MANAGEMENT',
					'ACP_LANGUAGE',
				),
				'ACP_CAT_MAINTENANCE' => array(
					'ACP_FORUM_LOGS',
					'ACP_CAT_DATABASE',
				),
				'ACP_CAT_SYSTEM' => array(
					'ACP_AUTOMATION',
					'ACP_GENERAL_TASKS',
					'ACP_MODULE_MANAGEMENT',
				),
				'ACP_CAT_DOT_MODS' => null,
			),
			'mcp' => array(
				'MCP_MAIN'		=> null,
				'MCP_QUEUE'		=> null,
				'MCP_REPORTS'	=> null,
				'MCP_NOTES'		=> null,
				'MCP_WARN'		=> null,
				'MCP_LOGS'		=> null,
				'MCP_BAN'		=> null,
			),
			'ucp' => array(
				'UCP_MAIN'			=> null,
				'UCP_PROFILE'		=> null,
				'UCP_PREFS'			=> null,
				'UCP_PM'			=> null,
				'UCP_USERGROUPS'	=> null,
				'UCP_ZEBRA'			=> null,
			),
		);

		$this->module_categories_basenames = array(
			'UCP_PM' => 'ucp_pm',
		);

		$this->module_extras = array(
			'acp'	=> array(
				'ACP_QUICK_ACCESS' => array(
					'ACP_MANAGE_USERS',
					'ACP_GROUPS_MANAGE',
					'ACP_MANAGE_FORUMS',
					'ACP_MOD_LOGS',
					'ACP_BOTS',
					'ACP_PHP_INFO',
				),
				'ACP_FORUM_BASED_PERMISSIONS' => array(
					'ACP_FORUM_PERMISSIONS',
					'ACP_FORUM_PERMISSIONS_COPY',
					'ACP_FORUM_MODERATORS',
					'ACP_USERS_FORUM_PERMISSIONS',
					'ACP_GROUPS_FORUM_PERMISSIONS',
				),
			),
		);
	}

	/**
	 * {@inheritdoc}
	 */
	public function run()
	{
		$this->db->sql_return_on_error(true);

		$module_classes = array('acp', 'mcp', 'ucp');
		foreach ($module_classes as $module_class)
		{
			$categories = array();

			foreach ($this->module_categories[$module_class] as $cat_name => $subs)
			{
				// Check if this sub-category has a basename. If it has, use it.
				$basename = (isset($this->module_categories_basenames[$cat_name])) ? $this->module_categories_basenames[$cat_name] : '';

				$module_data = array(
					'module_basename'	=> $basename,
					'module_enabled'	=> 1,
					'module_display'	=> 1,
					'parent_id'			=> 0,
					'module_class'		=> $module_class,
					'module_langname'	=> $cat_name,
					'module_mode'		=> '',
					'module_auth'		=> '',
				);

				$this->module_manager->update_module_data($module_data);

				// Check for last sql error happened
				if ($this->db->get_sql_error_triggered())
				{
					$error = $this->db->sql_error($this->db->get_sql_error_sql());
					$this->iohandler->add_error_message('INST_ERR_DB', $error['message']);
				}

				$categories[$cat_name]['id'] = (int)$module_data['module_id'];
				$categories[$cat_name]['parent_id'] = 0;

				if (is_array($subs))
				{
					foreach ($subs as $level2_name)
					{
						// Check if this sub-category has a basename. If it has, use it.
						$basename = (isset($this->module_categories_basenames[$level2_name])) ? $this->module_categories_basenames[$level2_name] : '';

						$module_data = array(
							'module_basename'	=> $basename,
							'module_enabled'	=> 1,
							'module_display'	=> 1,
							'parent_id'			=> (int)$categories[$cat_name]['id'],
							'module_class'		=> $module_class,
							'module_langname'	=> $level2_name,
							'module_mode'		=> '',
							'module_auth'		=> '',
						);

						$this->module_manager->update_module_data($module_data);

						// Check for last sql error happened
						if ($this->db->get_sql_error_triggered())
						{
							$error = $this->db->sql_error($this->db->get_sql_error_sql());
							$this->iohandler->add_error_message('INST_ERR_DB', $error['message']);
						}

						$categories[$level2_name]['id'] = (int)$module_data['module_id'];
						$categories[$level2_name]['parent_id'] = (int)$categories[$cat_name]['id'];
					}
				}
			}

			// Get the modules we want to add... returned sorted by name
			$module_info = $this->module_manager->get_module_infos($module_class);

			foreach ($module_info as $module_basename => $fileinfo)
			{
				foreach ($fileinfo['modes'] as $module_mode => $row)
				{
					foreach ($row['cat'] as $cat_name)
					{
						if (!isset($categories[$cat_name]))
						{
							continue;
						}

						$module_data = array(
							'module_basename'	=> $module_basename,
							'module_enabled'	=> 1,
							'module_display'	=> (isset($row['display'])) ? (int) $row['display'] : 1,
							'parent_id'			=> (int) $categories[$cat_name]['id'],
							'module_class'		=> $module_class,
							'module_langname'	=> $row['title'],
							'module_mode'		=> $module_mode,
							'module_auth'		=> $row['auth'],
						);

						$this->module_manager->update_module_data($module_data);

						// Check for last sql error happened
						if ($this->db->get_sql_error_triggered())
						{
							$error = $this->db->sql_error($this->db->get_sql_error_sql());
							$this->iohandler->add_error_message('INST_ERR_DB', $error['message']);
						}
					}
				}
			}

			// Move some of the modules around since the code above will put them in the wrong place
			if ($module_class === 'acp')
			{
				// Move main module 4 up...
				$sql = 'SELECT *
					FROM ' . MODULES_TABLE . "
					WHERE module_basename = 'acp_main'
						AND module_class = 'acp'
						AND module_mode = 'main'";
				$result = $this->db->sql_query($sql);
				$row = $this->db->sql_fetchrow($result);
				$this->db->sql_freeresult($result);

				$this->module_manager->move_module_by($row, 'acp', 'move_up', 4);

				// Move permissions intro screen module 4 up...
				$sql = 'SELECT *
					FROM ' . MODULES_TABLE . "
					WHERE module_basename = 'acp_permissions'
						AND module_class = 'acp'
						AND module_mode = 'intro'";
				$result = $this->db->sql_query($sql);
				$row = $this->db->sql_fetchrow($result);
				$this->db->sql_freeresult($result);

				$this->module_manager->move_module_by($row, 'acp', 'move_up', 4);

				// Move manage users screen module 5 up...
				$sql = 'SELECT *
					FROM ' . MODULES_TABLE . "
					WHERE module_basename = 'acp_users'
						AND module_class = 'acp'
						AND module_mode = 'overview'";
				$result = $this->db->sql_query($sql);
				$row = $this->db->sql_fetchrow($result);
				$this->db->sql_freeresult($result);

				$this->module_manager->move_module_by($row, 'acp', 'move_up', 5);

				// Move extension management module 1 up...
				$sql = 'SELECT *
					FROM ' . MODULES_TABLE . "
					WHERE module_langname = 'ACP_EXTENSION_MANAGEMENT'
						AND module_class = 'acp'
						AND module_mode = ''
						AND module_basename = ''";
				$result = $this->db->sql_query($sql);
				$row = $this->db->sql_fetchrow($result);
				$this->db->sql_freeresult($result);

				$this->module_manager->move_module_by($row, 'acp', 'move_up', 1);
			}

			if ($module_class == 'mcp')
			{
				// Move pm report details module 3 down...
				$sql = 'SELECT *
					FROM ' . MODULES_TABLE . "
					WHERE module_basename = 'mcp_pm_reports'
						AND module_class = 'mcp'
						AND module_mode = 'pm_report_details'";
				$result = $this->db->sql_query($sql);
				$row = $this->db->sql_fetchrow($result);
				$this->db->sql_freeresult($result);

				$this->module_manager->move_module_by($row, 'mcp', 'move_down', 3);

				// Move closed pm reports module 3 down...
				$sql = 'SELECT *
					FROM ' . MODULES_TABLE . "
					WHERE module_basename = 'mcp_pm_reports'
						AND module_class = 'mcp'
						AND module_mode = 'pm_reports_closed'";
				$result = $this->db->sql_query($sql);
				$row = $this->db->sql_fetchrow($result);
				$this->db->sql_freeresult($result);

				$this->module_manager->move_module_by($row, 'mcp', 'move_down', 3);

				// Move open pm reports module 3 down...
				$sql = 'SELECT *
					FROM ' . MODULES_TABLE . "
					WHERE module_basename = 'mcp_pm_reports'
						AND module_class = 'mcp'
						AND module_mode = 'pm_reports'";
				$result = $this->db->sql_query($sql);
				$row = $this->db->sql_fetchrow($result);
				$this->db->sql_freeresult($result);

				$this->module_manager->move_module_by($row, 'mcp', 'move_down', 3);
			}

			if ($module_class == 'ucp')
			{
				// Move attachment module 4 down...
				$sql = 'SELECT *
					FROM ' . MODULES_TABLE . "
					WHERE module_basename = 'ucp_attachments'
						AND module_class = 'ucp'
						AND module_mode = 'attachments'";
				$result = $this->db->sql_query($sql);
				$row = $this->db->sql_fetchrow($result);
				$this->db->sql_freeresult($result);

				$this->module_manager->move_module_by($row, 'ucp', 'move_down', 4);

				// Move notification options module 4 down...
				$sql = 'SELECT *
					FROM ' . MODULES_TABLE . "
					WHERE module_basename = 'ucp_notifications'
						AND module_class = 'ucp'
						AND module_mode = 'notification_options'";
				$result = $this->db->sql_query($sql);
				$row = $this->db->sql_fetchrow($result);
				$this->db->sql_freeresult($result);

				$this->module_manager->move_module_by($row, 'ucp', 'move_down', 4);

				// Move OAuth module 5 down...
				$sql = 'SELECT *
					FROM ' . MODULES_TABLE . "
					WHERE module_basename = 'ucp_auth_link'
						AND module_class = 'ucp'
						AND module_mode = 'auth_link'";
				$result = $this->db->sql_query($sql);
				$row = $this->db->sql_fetchrow($result);
				$this->db->sql_freeresult($result);

				$this->module_manager->move_module_by($row, 'ucp', 'move_down', 5);
			}

			// And now for the special ones
			// (these are modules which appear in multiple categories and thus get added manually
			// to some for more control)
			if (isset($this->module_extras[$module_class]))
			{
				foreach ($this->module_extras[$module_class] as $cat_name => $mods)
				{
					$sql = 'SELECT module_id, left_id, right_id
						FROM ' . MODULES_TABLE . "
						WHERE module_langname = '" . $this->db->sql_escape($cat_name) . "'
							AND module_class = '" . $this->db->sql_escape($module_class) . "'";
					$result = $this->db->sql_query_limit($sql, 1);
					$row2 = $this->db->sql_fetchrow($result);
					$this->db->sql_freeresult($result);

					foreach ($mods as $mod_name)
					{
						$sql = 'SELECT *
							FROM ' . MODULES_TABLE . "
							WHERE module_langname = '" . $this->db->sql_escape($mod_name) . "'
								AND module_class = '" . $this->db->sql_escape($module_class) . "'
								AND module_basename <> ''";
						$result = $this->db->sql_query_limit($sql, 1);
						$row = $this->db->sql_fetchrow($result);
						$this->db->sql_freeresult($result);

						$module_data = array(
							'module_basename'	=> $row['module_basename'],
							'module_enabled'	=> (int) $row['module_enabled'],
							'module_display'	=> (int) $row['module_display'],
							'parent_id'			=> (int) $row2['module_id'],
							'module_class'		=> $row['module_class'],
							'module_langname'	=> $row['module_langname'],
							'module_mode'		=> $row['module_mode'],
							'module_auth'		=> $row['module_auth'],
						);

						$this->module_manager->update_module_data($module_data);

						// Check for last sql error happened
						if ($this->db->get_sql_error_triggered())
						{
							$error = $this->db->sql_error($this->db->get_sql_error_sql());
							$this->iohandler->add_error_message('INST_ERR_DB', $error['message']);
						}
					}
				}
			}

			$this->module_manager->remove_cache_file($module_class);
		}
	}

	/**
	 * {@inheritdoc}
	 */
	static public function get_step_count()
	{
		return 1;
	}

	/**
	 * {@inheritdoc}
	 */
	public function get_task_lang_name()
	{
		return 'TASK_ADD_MODULES';
	}
}