Browse Source

+ Added the full Integration Hooks report with removal and disabling of hooks

emanuele 13 years ago
parent
commit
3813c291e6

+ 1 - 0
Sources/Admin.php

@@ -181,6 +181,7 @@ function AdminMain()
 					'function' => 'ModifyModSettings',
 					'icon' => 'modifications.png',
 					'subsections' => array(
+						'hooks' => array($txt['hooks_title_list']),
 						'general' => array($txt['mods_cat_modifications_misc']),
 						// Mod Authors for a "ADD AFTER" on this line. Ensure you end your change with a comma. For example:
 						// 'shout' => array($txt['shout']),

+ 366 - 0
Sources/ManageSettings.php

@@ -151,6 +151,7 @@ function ModifyModSettings()
 	$context['page_title'] = $txt['admin_modifications'];
 
 	$subActions = array(
+		'hooks' => 'list_integration_hooks',
 		'general' => 'ModifyGeneralModSettings',
 		// Mod authors, once again, if you have a whole section to add do it AFTER this line, and keep a comma at the end.
 	);
@@ -2160,4 +2161,369 @@ function ModifyGeneralModSettings($return_config = false)
 	prepareDBSettingContext($config_vars);
 }
 
+function list_integration_hooks()
+{
+	global $sourcedir, $scripturl, $context, $txt, $modSettings, $settings;
+
+	if (!empty($_POST['remove_hooks']) && !empty($_POST['remove']) && is_array($_POST['remove']))
+	{
+		checkSession();
+
+		foreach ($_POST['remove'] as $hook => $functions)
+		{
+			if (!is_array($functions))
+				continue;
+
+			foreach ($functions as $function)
+				remove_integration_function($hook, $function);
+		}
+	}
+
+	if (!empty($_POST['disable']))
+	{
+		checkSession();
+
+		foreach ($_POST['disable'] as $hook => $functions)
+		{
+			if (!is_array($functions))
+				continue;
+
+			$active_hooks = explode(',', $modSettings[$hook]);
+			foreach ($active_hooks as &$active_hook)
+				$active_hook = trim($active_hook);
+
+			foreach ($functions as $function => $state)
+			{
+				$function = trim($function);
+				if ($state == 'disable' && in_array($function, $active_hooks))
+				{
+					remove_integration_function($hook, $function);
+					// It's a hack I know...but I'm way too lazy!!!
+					add_integration_function($hook, ']' . $function);
+				}
+				elseif ($state == 'enable' && in_array(']' . $function, $active_hooks))
+				{
+					remove_integration_function($hook, ']' . $function);
+					// It's a hack I know...but I'm way too lazy!!!
+					add_integration_function($hook, $function);
+				}
+			}
+		}
+	}
+
+	$context['filter'] = false;
+	$presentHooks = get_integration_hooks();
+	if (isset($_GET['filter']) && in_array($_GET['filter'], array_keys($presentHooks)))
+		$context['filter'] = $_GET['filter'];
+
+	$list_options = array(
+		'id' => 'list_integration_hooks',
+		'title' => $txt['hooks_title_list'],
+		'items_per_page' => 20,
+		'base_href' => $scripturl . '?action=admin;area=modsettings;sa=hooks;' . (!empty($context['filter']) ? ('filter=' . $context['filter'] . ';') : '') . $context['session_var'] . '=' . $context['session_id'],
+		'default_sort_col' => 'hook_name',
+		'get_items' => array(
+			'function' => 'get_integration_hooks_data',
+		),
+		'get_count' => array(
+			'function' => 'get_integration_hooks_count',
+		),
+		'no_items_label' => $txt['hooks_no_hooks'],
+		'columns' => array(
+			'hook_name' => array(
+				'header' => array(
+					'value' => $txt['hooks_field_hook_name'],
+				),
+				'data' => array(
+					'db' => 'hook_name',
+				),
+				'sort' =>  array(
+					'default' => 'hook_name',
+					'reverse' => 'hook_name DESC',
+				),
+			),
+			'function_name' => array(
+				'header' => array(
+					'value' => $txt['hooks_field_function_name'],
+				),
+				'data' => array(
+					'db' => 'function_name',
+				),
+				'sort' =>  array(
+					'default' => 'function_name',
+					'reverse' => 'function_name DESC',
+				),
+			),
+			'file_name' => array(
+				'header' => array(
+					'value' => $txt['hooks_field_file_name'],
+				),
+				'data' => array(
+					'db' => 'file_name',
+				),
+				'sort' =>  array(
+					'default' => 'file_name',
+					'reverse' => 'file_name DESC',
+				),
+			),
+			'status' => array(
+				'header' => array(
+					'value' => $txt['hooks_field_hook_exists'],
+					'style' => 'width:3%',
+				),
+				'data' => array(
+					'function' => create_function('$data', '
+						global $settings;
+
+						$change_status = array(\'before\' => \'\', \'after\' => \'\');
+						if ($data[\'can_be_disabled\'] && $data[\'status\'] != \'deny\')
+						{
+							$change_status[\'before\'] = \'<a href="" onclick="integrationHooks_switchstatus(this.id); return false;" id="\' . $data[\'id\'] . \'">\';
+							$change_status[\'after\'] = \'</a><input id="input_\' . $data[\'id\'] . \'" type="hidden" name="disable[\' . $data[\'hook_name\'] . \'][\' . $data[\'function_name\'] . \']" value="\' . ($data[\'enabled\'] ? \'enable\' : \'disable\') . \'" />\';
+						}
+						return $change_status[\'before\'] . \'<img src="\' . $settings[\'images_url\'] . \'/admin/post_moderation_\' . $data[\'status\'] . \'.png" alt="\' . $data[\'img_text\'] . \'" title="\' . $data[\'img_text\'] . \'" />\' . $change_status[\'after\'];
+					'),
+					'class' => 'centertext',
+				),
+				'sort' =>  array(
+					'default' => 'status',
+					'reverse' => 'status DESC',
+				),
+			),
+			'check' => array(
+				'header' => array(
+					'value' => $txt['hooks_button_remove'],
+					'style' => 'width:3%',
+				),
+				'data' => array(
+					'function' => create_function('$data', '
+						global $settings;
+
+						if (!$data[\'hook_exists\'])
+							return \'
+							<a href="" onclick="integrationHooks_remove(this.id); return false;" id="remove_\' . $data[\'id\'] . \'">
+								<img src="\' . $settings[\'images_url\'] . \'/icons/quick_remove.png" alt="*" title="*" />
+							</a>
+							<input id="input_remove_\' . $data[\'id\'] . \'" type="hidden" name="remove[\' . $data[\'hook_name\'] . \'][\' . $data[\'function_name\'] . \']" value="\' . ($data[\'enabled\'] ? \'enable\' : \'disable\') . \'" />\';
+					'),
+					'class' => 'centertext',
+				),
+			),
+		),
+		'form' => array(
+			'href' => $scripturl . '?action=admin;area=modsettings;sa=hooks;' . (!empty($context['filter']) ? ('filter=' . $context['filter'] . ';') : '') . $context['session_var'] . '=' . $context['session_id'],
+			'name' => 'list_integration_hooks',
+		),
+		'additional_rows' => array(
+			array(
+				'position' => 'after_title',
+				'value' => $txt['hooks_disable_instructions'] . '<br />
+					' . $txt['hooks_disable_legend'] . ':
+									<ul>
+					<li><img src="' . $settings['images_url'] . '/admin/post_moderation_allow.png" alt="' . $txt['hooks_active'] . '" title="' . $txt['hooks_active'] . '" /> ' . $txt['hooks_disable_legend_exists'] . '</li>
+					<li><img src="' . $settings['images_url'] . '/admin/post_moderation_moderate.png" alt="' . $txt['hooks_disabled'] . '" title="' . $txt['hooks_disabled'] . '" /> ' . $txt['hooks_disable_legend_disabled'] . '</li>
+					<li><img src="' . $settings['images_url'] . '/admin/post_moderation_deny.png" alt="' . $txt['hooks_missing'] . '" title="' . $txt['hooks_missing'] . '" /> ' . $txt['hooks_disable_legend_missing'] . '</li>
+				</ul>'
+			),
+		),
+	);
+
+	require_once($sourcedir . '/Subs-List.php');
+
+	createList($list_options);
+
+	$context['page_title'] = $txt['hooks_title_list'];
+	$context['sub_template'] = 'show_list';
+	$context['template_layers'][] = 'integrationHooks';
+	$context['default_list'] = 'list_integration_hooks';
+}
+
+function get_files_recursive($dir_path)
+{
+	$files = array();
+
+	if ($dh = opendir($dir_path))
+	{
+		while (($file = readdir($dh)) !== false)
+			if ($file != '.' && $file != '..')
+			{
+				if (is_dir($dir_path . '/' . $file))
+					$files = array_merge($files, get_files_recursive($dir_path . '/' . $file));
+				else
+					$files[] = array('dir' => $dir_path, 'name' => $file);
+			}
+	}
+	closedir($dh);
+
+	return $files;
+}
+
+function get_integration_hooks_data($start, $per_page, $sort)
+{
+	global $boarddir, $sourcedir, $settings, $txt, $context, $scripturl;
+
+	$hooks = $temp_hooks = get_integration_hooks();
+	$hooks_data = $temp_data = $hook_status = array();
+
+	$files = get_files_recursive($sourcedir);
+	if (!empty($files))
+		foreach ($files as $file)
+		{
+			if (is_file($file['dir'] . '/' . $file['name']) && substr($file['name'], -4) === '.php')
+			{
+				$fp = fopen($file['dir'] . '/' . $file['name'], 'rb');
+				$fc = fread($fp, filesize($file['dir'] . '/' . $file['name']));
+				fclose($fp);
+
+				foreach ($temp_hooks as $hook => $functions)
+				{
+					foreach ($functions as $function_o)
+					{
+						$function = str_replace(']', '', $function_o);
+						if (substr($hook, -8) === '_include')
+						{
+							$hook_status[$hook][$function]['exists'] = file_exists(strtr(trim($function), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir, '$themedir' => $settings['theme_dir'])));
+							// I need to know if there is at least one function called in this file.
+							$temp_data['include'][basename($function)] = array('hook' => $hook, 'function' => $function);
+							unset($temp_hooks[$hook][$function_o]);
+						}
+						// @TODO replace with a preg_match? (the difference is the space before the open parentheses
+						elseif (strpos($fc, 'function ' . trim($function) . '(') !== false || strpos($fc, 'function ' . trim($function) . ' (') !== false)
+						{
+							$hook_status[$hook][$function]['exists'] = true;
+							$hook_status[$hook][$function]['in_file'] = $file['name'];
+							// I want to remember all the functions called within this file (to check later if they are enabled or disabled and decide if the integrare_*_include of that file can be disabled too)
+							$temp_data['function'][$file['name']][] = $function_o;
+							unset($temp_hooks[$hook][$function_o]);
+						}
+					}
+				}
+			}
+		}
+
+	$sort_types = array(
+		'hook_name' => array('hook', SORT_ASC),
+		'hook_name DESC' => array('hook', SORT_DESC),
+		'function_name' => array('function', SORT_ASC),
+		'function_name DESC' => array('function', SORT_DESC),
+		'file_name' => array('file_name', SORT_ASC),
+		'file_name DESC' => array('file_name', SORT_DESC),
+		'status' => array('status', SORT_ASC),
+		'status DESC' => array('status', SORT_DESC),
+	);
+
+	$sort_options = $sort_types[$sort];
+	$sort = array();
+	$context['hooks_filters'] = '';
+	$hooks_filters = array();
+
+	foreach ($hooks as $hook => $functions)
+	{
+		$hooks_filters[] = '<option onclick="window.location = \'' . $scripturl . '?action=admin;area=modsettings;sa=hooks;filter=' . $hook . '\';">' . $hook . '</option>';
+		foreach ($functions as $function)
+		{
+			$enabled = strstr($function, ']') === false;
+			$function = str_replace(']', '', $function);
+			// This is a not an include and the function is included in a certain file (if not it doesn't exists so don't care)
+			if (substr($hook, -8) !== '_include' && isset($hook_status[$hook][$function]['in_file']))
+			{
+				$current_hook = isset($temp_data['include'][$hook_status[$hook][$function]['in_file']]) ? $temp_data['include'][$hook_status[$hook][$function]['in_file']] : '';
+				$enabled = false;
+
+				// Checking all the functions within this particular file
+				// if any of them is enable then the file *must* be included and the integrate_*_include hook cannot be disabled
+				foreach ($temp_data['function'][$hook_status[$hook][$function]['in_file']] as $func)
+					$enabled = $enabled || strstr($func, ']') !== false;
+
+				if (!$enabled &&  !empty($current_hook))
+					$hook_status[$current_hook['hook']][$current_hook['function']]['enabled'] = true;
+			}
+		}
+	}
+
+	if (!empty($hooks_filters))
+		$context['hooks_filters'] = '<select style="margin-left:15px;">' . '<option>---</option><option onclick="window.location = \'' . $scripturl . '?action=admin;area=modsettings;sa=hooks\';">' . $txt['hooks_reset_filter'] . '</option>' . implode('', $hooks_filters) . '</select>';
+
+	$temp_data = array();
+	$id = 0;
+
+	foreach ($hooks as $hook => $functions)
+	{
+		if (empty($context['filter']) || (!empty($context['filter']) && $context['filter'] == $hook))
+			foreach ($functions as $function)
+			{
+				$enabled = strstr($function, ']') === false;
+				$function = str_replace(']', '', $function);
+				$hook_exists = !empty($hook_status[$hook][$function]['exists']);
+				$file_name = isset($hook_status[$hook][$function]['in_file']) ? $hook_status[$hook][$function]['in_file'] : ((substr($hook, -8) === '_include') ? 'zzzzzzzzz' : 'zzzzzzzza');
+				$status = $hook_exists ? ($enabled ? 'a' : 'b') : 'c';
+				$sort[] = $$sort_options[0];
+				$temp_data[] = array(
+					'id' => 'hookid_' . $id++,
+					'hook_name' => $hook,
+					'function_name' => $function,
+					'file_name' => (isset($hook_status[$hook][$function]['in_file']) ? $hook_status[$hook][$function]['in_file'] : ''),
+					'hook_exists' => $hook_exists,
+					'status' => $hook_exists ? ($enabled ? 'allow' : 'moderate') : 'deny',
+					'img_text' => $txt['hooks_' . ($hook_exists ? ($enabled ? 'active' : 'disabled') : 'missing')],
+					'enabled' => $enabled,
+					'can_be_disabled' => (isset($hook_status[$hook][$function]['enabled']) || $thisMod ? false : true),
+				);
+			}
+	}
+
+	array_multisort($sort, $sort_options[1], $temp_data);
+
+	$counter = 0;
+	$start++;
+
+	foreach ($temp_data as $data)
+	{
+		if (++$counter < $start)
+			continue;
+		elseif ($counter == $start + $per_page)
+			break;
+
+		$hooks_data[] = $data;
+	}
+
+	return $hooks_data;
+}
+
+function get_integration_hooks_count()
+{
+	global $context;
+
+	$hooks = get_integration_hooks();
+	$hooks_count = 0;
+
+	$context['filter'] = false;
+	if (isset($_GET['filter']))
+		$context['filter'] = $_GET['filter'];
+
+	foreach ($hooks as $hook => $functions)
+		if (empty($context['filter']) || (!empty($context['filter']) && $context['filter'] == $hook))
+			$hooks_count += count($functions);
+
+	return $hooks_count;
+}
+
+function get_integration_hooks()
+{
+	global $modSettings;
+	static $integration_hooks;
+
+	if (!isset($integration_hooks))
+	{
+		$integration_hooks = array();
+		foreach ($modSettings as $key => $value)
+		{
+			if (!empty($value) && substr($key, 0, 10) === 'integrate_')
+				$integration_hooks[$key] = explode(',', $value);
+		}
+	}
+
+	return $integration_hooks;
+}
+
 ?>

+ 40 - 0
Themes/default/Admin.template.php

@@ -1713,4 +1713,44 @@ function template_clean_cache_button_below()
 	</div>';
 }
 
+
+function template_integrationHooks_above()
+{
+}
+function template_integrationHooks_below()
+{
+	global $context;
+
+	if (empty($context['hooks_filters']))
+		return;
+
+	echo '
+	<script type="text/javascript"><!-- // --><![CDATA[
+		var tblHeader = document.getElementById(\'' . $context['default_list'] . '\').getElementsByTagName(\'th\')[0];
+		tblHeader.innerHTML += ' . JavaScriptEscape($context['hooks_filters']) . ';
+
+		function integrationHooks_switchstatus(id)
+		{
+			var elem = document.getElementById(\'input_\'+id);
+			if (elem.value == \'enable\')
+				elem.value = \'disable\';
+			else if (elem.value == \'disable\')
+				elem.value = \'enable\';
+
+			document.forms["' . $context['default_list'] . '"].submit();
+		}
+
+		function integrationHooks_remove(id)
+		{
+			var elem = document.getElementById(\'input_remove_\'+id);
+			if (elem.value == \'enable\')
+				elem.value = \'disable\';
+			else if (elem.value == \'disable\')
+				elem.value = \'enable\';
+
+			document.forms["' . $context['default_list'] . '"].submit();
+		}
+	// ]]></script>';
+}
+
 ?>

+ 17 - 0
Themes/default/languages/Admin.english.php

@@ -641,4 +641,21 @@ $txt['spider_stats'] = 'Stats';
 $txt['paid_subscriptions'] = 'Paid Subscriptions';
 $txt['paid_subs_view'] = 'View Subscriptions';
 
+$txt['hooks_title_list'] = 'Integration Hooks';
+$txt['hooks_field_hook_name'] = 'Hook Name';
+$txt['hooks_field_function_name'] = 'Function Name';
+$txt['hooks_field_file_name'] = 'File Name';
+$txt['hooks_field_hook_exists'] = 'Status';
+$txt['hooks_active'] = 'Exists';
+$txt['hooks_disabled'] = 'Disabled';
+$txt['hooks_missing'] = 'Not found';
+$txt['hooks_no_hooks'] = 'There are not any hooks setup.';
+$txt['hooks_button_remove'] = 'Remove';
+$txt['hooks_disable_instructions'] = 'Click on the status icon to enable or disable the hook';
+$txt['hooks_disable_legend'] = 'Legend';
+$txt['hooks_disable_legend_exists'] = 'the hook exists and is active';
+$txt['hooks_disable_legend_disabled'] = 'the hook exists but has been disabled';
+$txt['hooks_disable_legend_missing'] = 'the hook has not been found';
+$txt['hooks_reset_filter'] = 'Reset filter';
+
 ?>