diff --git a/pandora_agents/unix/DEBIAN/control b/pandora_agents/unix/DEBIAN/control index 91f315f562..721e0f04a4 100644 --- a/pandora_agents/unix/DEBIAN/control +++ b/pandora_agents/unix/DEBIAN/control @@ -1,5 +1,5 @@ package: pandorafms-agent-unix -Version: 7.0NG.758-211105 +Version: 7.0NG.758-211119 Architecture: all Priority: optional Section: admin diff --git a/pandora_agents/unix/DEBIAN/make_deb_package.sh b/pandora_agents/unix/DEBIAN/make_deb_package.sh index 60540c9910..65c02da12e 100644 --- a/pandora_agents/unix/DEBIAN/make_deb_package.sh +++ b/pandora_agents/unix/DEBIAN/make_deb_package.sh @@ -14,7 +14,7 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -pandora_version="7.0NG.758-211105" +pandora_version="7.0NG.758-211119" echo "Test if you has the tools for to make the packages." whereis dpkg-deb | cut -d":" -f2 | grep dpkg-deb > /dev/null diff --git a/pandora_agents/unix/pandora_agent b/pandora_agents/unix/pandora_agent index 0be5efa174..3f2e7c1b70 100755 --- a/pandora_agents/unix/pandora_agent +++ b/pandora_agents/unix/pandora_agent @@ -1015,7 +1015,7 @@ my $Sem = undef; my $ThreadSem = undef; use constant AGENT_VERSION => '7.0NG.758'; -use constant AGENT_BUILD => '211105'; +use constant AGENT_BUILD => '211119'; # Agent log default file size maximum and instances use constant DEFAULT_MAX_LOG_SIZE => 600000; diff --git a/pandora_agents/unix/pandora_agent.redhat.spec b/pandora_agents/unix/pandora_agent.redhat.spec index d7165163dc..91ad8390de 100644 --- a/pandora_agents/unix/pandora_agent.redhat.spec +++ b/pandora_agents/unix/pandora_agent.redhat.spec @@ -3,7 +3,7 @@ # %define name pandorafms_agent_unix %define version 7.0NG.758 -%define release 211105 +%define release 211119 Summary: Pandora FMS Linux agent, PERL version Name: %{name} diff --git a/pandora_agents/unix/pandora_agent.spec b/pandora_agents/unix/pandora_agent.spec index f8f23dff3b..39a5f4f519 100644 --- a/pandora_agents/unix/pandora_agent.spec +++ b/pandora_agents/unix/pandora_agent.spec @@ -3,7 +3,7 @@ # %define name pandorafms_agent_unix %define version 7.0NG.758 -%define release 211105 +%define release 211119 Summary: Pandora FMS Linux agent, PERL version Name: %{name} diff --git a/pandora_agents/unix/pandora_agent_installer b/pandora_agents/unix/pandora_agent_installer index 6089e2fc55..9ff7b5ff09 100755 --- a/pandora_agents/unix/pandora_agent_installer +++ b/pandora_agents/unix/pandora_agent_installer @@ -10,7 +10,7 @@ # ********************************************************************** PI_VERSION="7.0NG.758" -PI_BUILD="211105" +PI_BUILD="211119" OS_NAME=`uname -s` FORCE=0 diff --git a/pandora_agents/win32/installer/pandora.mpi b/pandora_agents/win32/installer/pandora.mpi index 9e1222577e..aa5741165e 100644 --- a/pandora_agents/win32/installer/pandora.mpi +++ b/pandora_agents/win32/installer/pandora.mpi @@ -186,7 +186,7 @@ UpgradeApplicationID {} Version -{211105} +{211119} ViewReadme {Yes} diff --git a/pandora_agents/win32/pandora.cc b/pandora_agents/win32/pandora.cc index c77f3b4b72..03bcee5982 100644 --- a/pandora_agents/win32/pandora.cc +++ b/pandora_agents/win32/pandora.cc @@ -30,7 +30,7 @@ using namespace Pandora; using namespace Pandora_Strutils; #define PATH_SIZE _MAX_PATH+1 -#define PANDORA_VERSION ("7.0NG.758 Build 211105") +#define PANDORA_VERSION ("7.0NG.758 Build 211119") string pandora_path; string pandora_dir; diff --git a/pandora_agents/win32/versioninfo.rc b/pandora_agents/win32/versioninfo.rc index 381a23ea45..39fada2571 100644 --- a/pandora_agents/win32/versioninfo.rc +++ b/pandora_agents/win32/versioninfo.rc @@ -11,7 +11,7 @@ BEGIN VALUE "LegalCopyright", "Artica ST" VALUE "OriginalFilename", "PandoraAgent.exe" VALUE "ProductName", "Pandora FMS Windows Agent" - VALUE "ProductVersion", "(7.0NG.758(Build 211105))" + VALUE "ProductVersion", "(7.0NG.758(Build 211119))" VALUE "FileVersion", "1.0.0.0" END END diff --git a/pandora_console/.htaccess b/pandora_console/.htaccess index ca572f7df1..0d2cae3781 100644 --- a/pandora_console/.htaccess +++ b/pandora_console/.htaccess @@ -1,6 +1,9 @@ # pandora disable listing Options -Indexes +# Avoid clickjacking +Header always append X-Frame-Options SAMEORIGIN + Order Allow,Deny Deny from All diff --git a/pandora_console/DEBIAN/control b/pandora_console/DEBIAN/control index 805b47d5f0..ce7594a565 100644 --- a/pandora_console/DEBIAN/control +++ b/pandora_console/DEBIAN/control @@ -1,5 +1,5 @@ package: pandorafms-console -Version: 7.0NG.758-211105 +Version: 7.0NG.758-211119 Architecture: all Priority: optional Section: admin diff --git a/pandora_console/DEBIAN/make_deb_package.sh b/pandora_console/DEBIAN/make_deb_package.sh index 49b79dbe40..61276f33cd 100644 --- a/pandora_console/DEBIAN/make_deb_package.sh +++ b/pandora_console/DEBIAN/make_deb_package.sh @@ -14,7 +14,7 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -pandora_version="7.0NG.758-211105" +pandora_version="7.0NG.758-211119" package_pear=0 package_pandora=1 diff --git a/pandora_console/extensions/agents_alerts.php b/pandora_console/extensions/agents_alerts.php index 58ec82c259..d7101d7e9e 100755 --- a/pandora_console/extensions/agents_alerts.php +++ b/pandora_console/extensions/agents_alerts.php @@ -31,7 +31,7 @@ global $config; // Require needed class. require_once $config['homedir'].'/include/class/AgentsAlerts.class.php'; // Get the parameter. -$sec2 = get_parameter_get('sec2'); +$sec2 = get_parameter_get('sec2'); // Add operation menu option. extensions_add_operation_menu_option( __('Agents/Alerts view'), diff --git a/pandora_console/extensions/module_groups.php b/pandora_console/extensions/module_groups.php index 48568f3bd3..affcff93de 100644 --- a/pandora_console/extensions/module_groups.php +++ b/pandora_console/extensions/module_groups.php @@ -351,7 +351,7 @@ function mainModuleGroups() } $data[$i][$j] = "
"; - $data[$i][$j] .= ''; + $data[$i][$j] .= ""; $data[$i][$j] .= $array_data[$key][$k]['total_count']; $data[$i][$j] .= '
'; } else { diff --git a/pandora_console/extensions/users_connected.php b/pandora_console/extensions/users_connected.php index 32be153e69..40b17f4903 100644 --- a/pandora_console/extensions/users_connected.php +++ b/pandora_console/extensions/users_connected.php @@ -206,7 +206,7 @@ function users_extension_main_god($god=true) } -extensions_add_operation_menu_option(__('Users connected'), 'workspace', 'users/icon.png', 'v1r1', '', 'UM'); +extensions_add_operation_menu_option(__('Users connected'), 'workspace', 'users/icon.png', 'v1r1', null, 'UM'); extensions_add_godmode_function('users_extension_main_god'); extensions_add_main_function('users_extension_main'); diff --git a/pandora_console/extras/delete_files/delete_files.txt b/pandora_console/extras/delete_files/delete_files.txt index 8bfef52f3f..888b359278 100644 --- a/pandora_console/extras/delete_files/delete_files.txt +++ b/pandora_console/extras/delete_files/delete_files.txt @@ -111,4 +111,6 @@ enterprise/operation/agentes/pandora_networkmap.view.php enterprise/include/ajax/map_enterprise.ajax.php enterprise/include/javascript/SimpleMapController.js enterprise/include/javascript/tooltipster.bundle.min.js -enterprise/include/styles/tooltipster.bundle.min.css \ No newline at end of file +enterprise/include/styles/tooltipster.bundle.min.css +mobile/include/javascript/jquery.mobile-1.3.1.js +mobile/include/style/jquery.mobile-1.3.1.css \ No newline at end of file diff --git a/pandora_console/extras/mr/51.sql b/pandora_console/extras/mr/51.sql new file mode 100644 index 0000000000..ea9d454839 --- /dev/null +++ b/pandora_console/extras/mr/51.sql @@ -0,0 +1,5 @@ +START TRANSACTION; + +ALTER TABLE `tagent_repository` ADD COLUMN `deployment_timeout` INT UNSIGNED DEFAULT 600 AFTER `path`; + +COMMIT; \ No newline at end of file diff --git a/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql b/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql index 26e946e604..a84cd73032 100644 --- a/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql +++ b/pandora_console/extras/pandoradb_migrate_6.0_to_7.0.mysql.sql @@ -1524,6 +1524,8 @@ ALTER TABLE tevent_filter ADD COLUMN `id_source_event` int(10); ALTER TABLE `tevent_filter` MODIFY COLUMN `user_comment` text NOT NULL; ALTER TABLE `tevent_filter` MODIFY COLUMN `severity` text NOT NULL; ALTER TABLE tevent_filter ADD COLUMN `server_id` int(10) NOT NULL default 0; +ALTER TABLE `tevent_filter` ADD COLUMN `time_from` TIME NULL; +ALTER TABLE `tevent_filter` ADD COLUMN `time_to` TIME NULL; -- --------------------------------------------------------------------- -- Table `tusuario` @@ -2681,6 +2683,7 @@ CREATE TABLE `tagent_repository` ( `arch` ENUM('x64', 'x86') DEFAULT 'x64', `version` VARCHAR(10) DEFAULT '', `path` text, + `deployment_timeout` INT UNSIGNED DEFAULT 600, `uploaded_by` VARCHAR(100) DEFAULT '', `uploaded` bigint(20) NOT NULL DEFAULT 0 COMMENT "When it was uploaded", `last_err` text, diff --git a/pandora_console/general/logon_ok.php b/pandora_console/general/logon_ok.php index 2dbc1ff50c..4d18e0e79d 100644 --- a/pandora_console/general/logon_ok.php +++ b/pandora_console/general/logon_ok.php @@ -64,6 +64,8 @@ $data['monitor_critical'] = (int) $all_data['_monitors_critical_']; $data['monitor_not_normal'] = (int) $all_data['_monitor_not_normal_']; $data['monitor_alerts'] = (int) $all_data['_monitors_alerts_']; $data['monitor_alerts_fired'] = (int) $all_data['_monitors_alerts_fired_']; +$data['monitor_total'] = (int) $all_data['_monitor_total_']; + $data['total_agents'] = (int) $all_data['_total_agents_']; diff --git a/pandora_console/godmode/agentes/agent_manager.php b/pandora_console/godmode/agentes/agent_manager.php index fe39fca203..23a0e401d5 100644 --- a/pandora_console/godmode/agentes/agent_manager.php +++ b/pandora_console/godmode/agentes/agent_manager.php @@ -341,6 +341,7 @@ if (isset($groups[$grupo]) || $new_agent) { 'selected' => $grupo, 'return' => true, 'required' => true, + 'privilege' => 'AW', ] ); } else { diff --git a/pandora_console/godmode/agentes/configurar_agente.php b/pandora_console/godmode/agentes/configurar_agente.php index 0979aa9ccc..7383ba4506 100644 --- a/pandora_console/godmode/agentes/configurar_agente.php +++ b/pandora_console/godmode/agentes/configurar_agente.php @@ -196,6 +196,13 @@ if ($create_agent) { $nombre_agente = hash('sha256', $alias.'|'.$direccion_agente.'|'.time().'|'.sprintf('%04d', rand(0, 10000))); $grupo = (int) get_parameter_post('grupo'); + + if ((bool) check_acl($config['id_user'], $grupo, 'AW') === false) { + db_pandora_audit('ACL Violation', 'Trying to access agent manager'); + include $config['homedir'].'/general/noaccess.php'; + return; + } + $intervalo = (string) get_parameter_post('intervalo', SECONDS_5MINUTES); $comentarios = (string) get_parameter_post('comentarios', ''); $modo = (int) get_parameter_post('modo'); @@ -990,6 +997,8 @@ if ($update_agent) { $cps = get_parameter_switch('cps', -1); $old_values = db_get_row('tagente', 'id_agente', $id_agente); $fields = db_get_all_fields_in_table('tagent_custom_fields'); + $secondary_groups = (string) get_parameter('secondary_hidden', ''); + if ($fields === false) { $fields = []; @@ -1219,7 +1228,16 @@ if ($update_agent) { "Quiet":"'.(int) $quiet.'", "Cps":"'.(int) $cps.'"}'; - enterprise_hook('update_agent', [$id_agente]); + // Create the secondary groups. + enterprise_hook( + 'agents_update_secondary_groups', + [ + $id_agente, + explode(',', $secondary_groups), + [], + ] + ); + ui_print_success_message(__('Successfully updated')); db_pandora_audit( 'Agent management', diff --git a/pandora_console/godmode/alerts/configure_alert_action.php b/pandora_console/godmode/alerts/configure_alert_action.php index e486ccc06b..4d8478c99b 100644 --- a/pandora_console/godmode/alerts/configure_alert_action.php +++ b/pandora_console/godmode/alerts/configure_alert_action.php @@ -237,7 +237,7 @@ $create_ticket_command_id = db_get_value('id', 'talert_commands', 'name', io_saf $sql_exclude_command_id = ''; -if ($config['integria_enabled'] == 0 && $create_ticket_command_id !== false) { +if (!is_metaconsole() && $config['integria_enabled'] == 0 && $create_ticket_command_id !== false) { $sql_exclude_command_id = ' AND id <> '.$create_ticket_command_id; } @@ -597,6 +597,7 @@ $(document).ready (function () { $("#id_command").change (function () { values = Array (); + // No se envia el valor del commando. values.push({ name: "page", value: "godmode/alerts/alert_commands"}); diff --git a/pandora_console/godmode/menu.php b/pandora_console/godmode/menu.php index 6091eabdb2..18237feb17 100644 --- a/pandora_console/godmode/menu.php +++ b/pandora_console/godmode/menu.php @@ -60,7 +60,7 @@ if (check_acl($config['id_user'], 0, 'AR') // Add to menu. $menu_godmode['discovery']['text'] = __('Discovery'); - $menu_godmode['discovery']['sec2'] = 'godmode/servers/discovery'; + $menu_godmode['discovery']['sec2'] = ''; $menu_godmode['discovery']['id'] = 'god-discovery'; $menu_godmode['discovery']['sub'] = $sub; } @@ -254,20 +254,20 @@ if (check_acl($config['id_user'], 0, 'LW') $sub = []; if (check_acl($config['id_user'], 0, 'EW') || check_acl($config['id_user'], 0, 'EM')) { // Custom event fields - $sub['godmode/events/events&section=filter']['text'] = __('Event filters'); - $sub['godmode/events/events&section=filter']['id'] = 'Event filters'; + $sub['godmode/events/events§ion=filter']['text'] = __('Event filters'); + $sub['godmode/events/events§ion=filter']['id'] = 'Event filters'; } if (check_acl($config['id_user'], 0, 'PM')) { - $sub['godmode/events/events&section=fields']['text'] = __('Custom events'); - $sub['godmode/events/events&section=fields']['id'] = 'Custom events'; - $sub['godmode/events/events&section=responses']['text'] = __('Event responses'); - $sub['godmode/events/events&section=responses']['id'] = 'Event responses'; + $sub['godmode/events/events§ion=fields']['text'] = __('Custom events'); + $sub['godmode/events/events§ion=fields']['id'] = 'Custom events'; + $sub['godmode/events/events§ion=responses']['text'] = __('Event responses'); + $sub['godmode/events/events§ion=responses']['id'] = 'Event responses'; } if (!empty($sub)) { $menu_godmode['geventos']['text'] = __('Events'); - $menu_godmode['geventos']['sec2'] = 'godmode/events/events&section=filter'; + $menu_godmode['geventos']['sec2'] = 'godmode/events/events§ion=filter'; $menu_godmode['geventos']['id'] = 'god-events'; $menu_godmode['geventos']['sub'] = $sub; } @@ -307,7 +307,7 @@ if (check_acl($config['id_user'], 0, 'AW') || check_acl($config['id_user'], 0, ' if (check_acl($config['id_user'], 0, 'PM')) { // Setup $menu_godmode['gsetup']['text'] = __('Setup'); - $menu_godmode['gsetup']['sec2'] = 'godmode/setup/setup§ion=general'; + $menu_godmode['gsetup']['sec2'] = 'general'; $menu_godmode['gsetup']['id'] = 'god-setup'; $sub = []; @@ -319,50 +319,50 @@ if (check_acl($config['id_user'], 0, 'PM')) { $sub['general']['subtype'] = 'nolink'; $sub2 = []; - $sub2['godmode/setup/setup&section=general']['text'] = __('General Setup'); - $sub2['godmode/setup/setup&section=general']['id'] = 'General Setup'; - $sub2['godmode/setup/setup&section=general']['refr'] = 0; + $sub2['godmode/setup/setup§ion=general']['text'] = __('General Setup'); + $sub2['godmode/setup/setup§ion=general']['id'] = 'General Setup'; + $sub2['godmode/setup/setup§ion=general']['refr'] = 0; enterprise_hook('password_submenu'); enterprise_hook('enterprise_submenu'); enterprise_hook('historydb_submenu'); enterprise_hook('log_collector_submenu'); - $sub2['godmode/setup/setup&section=auth']['text'] = __('Authentication'); - $sub2['godmode/setup/setup&section=auth']['refr'] = 0; + $sub2['godmode/setup/setup§ion=auth']['text'] = __('Authentication'); + $sub2['godmode/setup/setup§ion=auth']['refr'] = 0; - $sub2['godmode/setup/setup&section=perf']['text'] = __('Performance'); - $sub2['godmode/setup/setup&section=perf']['refr'] = 0; + $sub2['godmode/setup/setup§ion=perf']['text'] = __('Performance'); + $sub2['godmode/setup/setup§ion=perf']['refr'] = 0; - $sub2['godmode/setup/setup&section=vis']['text'] = __('Visual styles'); - $sub2['godmode/setup/setup&section=vis']['refr'] = 0; + $sub2['godmode/setup/setup§ion=vis']['text'] = __('Visual styles'); + $sub2['godmode/setup/setup§ion=vis']['refr'] = 0; if (check_acl($config['id_user'], 0, 'AW')) { if ($config['activate_netflow']) { - $sub2['godmode/setup/setup&section=net']['text'] = __('Netflow'); - $sub2['godmode/setup/setup&section=net']['refr'] = 0; + $sub2['godmode/setup/setup§ion=net']['text'] = __('Netflow'); + $sub2['godmode/setup/setup§ion=net']['refr'] = 0; } } - $sub2['godmode/setup/setup&section=ehorus']['text'] = __('eHorus'); - $sub2['godmode/setup/setup&section=ehorus']['refr'] = 0; + $sub2['godmode/setup/setup§ion=ehorus']['text'] = __('eHorus'); + $sub2['godmode/setup/setup§ion=ehorus']['refr'] = 0; - $sub2['godmode/setup/setup&section=integria']['text'] = __('Integria IMS'); - $sub2['godmode/setup/setup&section=integria']['refr'] = 0; + $sub2['godmode/setup/setup§ion=integria']['text'] = __('Integria IMS'); + $sub2['godmode/setup/setup§ion=integria']['refr'] = 0; enterprise_hook('module_library_submenu'); - $sub2['godmode/setup/setup&section=notifications']['text'] = __('Notifications'); - $sub2['godmode/setup/setup&section=notifications']['refr'] = 0; + $sub2['godmode/setup/setup§ion=notifications']['text'] = __('Notifications'); + $sub2['godmode/setup/setup§ion=notifications']['refr'] = 0; - $sub2['godmode/setup/setup&section=websocket_engine']['text'] = __('Websocket Engine'); - $sub2['godmode/setup/setup&section=websocket_engine']['refr'] = 0; + $sub2['godmode/setup/setup§ion=websocket_engine']['text'] = __('Websocket Engine'); + $sub2['godmode/setup/setup§ion=websocket_engine']['refr'] = 0; - $sub2['godmode/setup/setup&section=external_tools']['text'] = __('External Tools'); - $sub2['godmode/setup/setup&section=external_tools']['refr'] = 0; + $sub2['godmode/setup/setup§ion=external_tools']['text'] = __('External Tools'); + $sub2['godmode/setup/setup§ion=external_tools']['refr'] = 0; if ($config['activate_gis']) { - $sub2['godmode/setup/setup&section=gis']['text'] = __('Map conections GIS'); + $sub2['godmode/setup/setup§ion=gis']['text'] = __('Map conections GIS'); } $sub['general']['sub2'] = $sub2; @@ -462,9 +462,9 @@ if (is_array($config['extensions'])) { $menu_godmode[$extmenu['fatherId']]['sub'][$extmenu['sec2']]['refr'] = 0; $menu_godmode[$extmenu['fatherId']]['sub'][$extmenu['sec2']]['icon'] = $extmenu['icon']; if ($extmenu['name'] == 'Cron jobs') { - $menu_godmode[$extmenu['fatherId']]['sub'][$extmenu['sec2']]['sec'] = 'extensions'; + $menu_godmode[$extmenu['fatherId']]['sub'][$extmenu['sec2']]['sec'] = $extmenu['fatherId']; } else { - $menu_godmode[$extmenu['fatherId']]['sub'][$extmenu['sec2']]['sec'] = 'gextensions'; + $menu_godmode[$extmenu['fatherId']]['sub'][$extmenu['sec2']]['sec'] = $extmenu['fatherId']; } $menu_godmode[$extmenu['fatherId']]['sub'][$extmenu['sec2']]['extension'] = true; @@ -525,8 +525,8 @@ $menu_godmode['links']['sub'] = $sub; // Update Manager if (check_acl($config['id_user'], 0, 'PM') && $config['enable_update_manager']) { $menu_godmode['messages']['text'] = __('Update manager'); - $menu_godmode['messages']['sec2'] = ''; $menu_godmode['messages']['id'] = 'god-um_messages'; + $menu_godmode['messages']['sec2'] = ''; $sub = []; $sub['godmode/update_manager/update_manager&tab=offline']['text'] = __('Update Manager offline'); @@ -543,7 +543,6 @@ if (check_acl($config['id_user'], 0, 'PM') && $config['enable_update_manager']) // Module library. if (check_acl($config['id_user'], 0, 'AR')) { $menu_godmode['gmodule_library']['text'] = __('Module library'); - $menu_godmode['gmodule_library']['sec2'] = 'godmode/module_library/module_library_view'; $menu_godmode['gmodule_library']['id'] = 'god-module_library'; $sub = []; diff --git a/pandora_console/godmode/modules/manage_network_components.php b/pandora_console/godmode/modules/manage_network_components.php index 8089c2799d..4b3fdab1be 100644 --- a/pandora_console/godmode/modules/manage_network_components.php +++ b/pandora_console/godmode/modules/manage_network_components.php @@ -581,7 +581,7 @@ if ($is_management_allowed === true && $multiple_delete) { $id = 0; } -if ($id || $new_component +if ((bool) $id !== false || $new_component || $create_network_from_module || $create_network_from_snmp_browser ) { @@ -598,6 +598,7 @@ $url = ui_get_url_refresh( 'offset' => false, 'search_string' => $search_string, 'search_id_group' => $search_id_group, + 'id' => $id, ], true, false diff --git a/pandora_console/godmode/reporting/reporting_builder.item_editor.php b/pandora_console/godmode/reporting/reporting_builder.item_editor.php index b0caf02af0..5bfdd702fc 100755 --- a/pandora_console/godmode/reporting/reporting_builder.item_editor.php +++ b/pandora_console/godmode/reporting/reporting_builder.item_editor.php @@ -3505,7 +3505,7 @@ function print_SLA_list($width, $action, $idItem=null) @@ -3772,23 +3772,23 @@ function print_SLA_list($width, $action, $idItem=null) ], ] ); - if (!empty($services_tmp) - && $services_tmp != ENTERPRISE_NOT_HOOK + if (!empty($services_tmp) + && $services_tmp != ENTERPRISE_NOT_HOOK + ) { + foreach ($services_tmp as $service) { + $check_module_sla = modules_check_agentmodule_exists( + $service['sla_id_module'] + ); + $check_module_sla_value = modules_check_agentmodule_exists( + $service['sla_value_id_module'] + ); + if ($check_module_sla + && $check_module_sla_value ) { - foreach ($services_tmp as $service) { - $check_module_sla = modules_check_agentmodule_exists( - $service['sla_id_module'] - ); - $check_module_sla_value = modules_check_agentmodule_exists( - $service['sla_value_id_module'] - ); - if ($check_module_sla - && $check_module_sla_value - ) { - $services[$service['id']] = $service['name']; - } - } + $services[$service['id']] = $service['name']; } + } + } echo ''; echo html_print_select( diff --git a/pandora_console/godmode/reporting/reporting_builder.php b/pandora_console/godmode/reporting/reporting_builder.php index 5d1dbb6dff..1f5eba566c 100755 --- a/pandora_console/godmode/reporting/reporting_builder.php +++ b/pandora_console/godmode/reporting/reporting_builder.php @@ -1970,9 +1970,11 @@ switch ($action) { ); $values['id_group'] = get_parameter('combo_group'); - $values['server_name'] = get_parameter( - 'combo_server' - ); + if ($values['server_name'] == '') { + $values['server_name'] = get_parameter( + 'combo_server' + ); + } if ((($values['type'] == 'custom_graph') || ($values['type'] == 'automatic_custom_graph')) diff --git a/pandora_console/godmode/reporting/visual_console_builder.elements.php b/pandora_console/godmode/reporting/visual_console_builder.elements.php index 780ef8b397..295a27e9a3 100755 --- a/pandora_console/godmode/reporting/visual_console_builder.elements.php +++ b/pandora_console/godmode/reporting/visual_console_builder.elements.php @@ -204,6 +204,15 @@ foreach ($layoutDatas as $layoutData) { ); break; + case CIRCULAR_INTERIOR_PROGRESS_BAR: + case CIRCULAR_PROGRESS_BAR: + $table->data[($i + 1)]['icon'] = html_print_image( + 'images/percentile_item.png', + true, + ['title' => __('Percentile')] + ); + break; + case MODULE_GRAPH: $table->data[($i + 1)]['icon'] = html_print_image( 'images/chart_curve.png', @@ -306,6 +315,16 @@ foreach ($layoutDatas as $layoutData) { break; case NETWORK_LINK: + $table->data[($i + 1)]['icon'] = html_print_image( + 'images/network_link_item.png', + true, + [ + 'title' => __('Network link'), + 'class' => 'invert_filter', + ] + ); + break; + case LINE_ITEM: $table->data[($i + 1)]['icon'] = html_print_image( 'images/line_item.png', @@ -325,6 +344,30 @@ foreach ($layoutDatas as $layoutData) { ); break; + case BASIC_CHART: + $table->data[($i + 1)]['icon'] = html_print_image( + 'images/basic_chart.png', + true, + ['title' => __('Basic chart')] + ); + break; + + case ODOMETER: + $table->data[($i + 1)]['icon'] = html_print_image( + 'images/odometer.png', + true, + ['title' => __('Odometer')] + ); + break; + + case CLOCK: + $table->data[($i + 1)]['icon'] = html_print_image( + 'images/clock-tab.png', + true, + ['title' => __('Clock')] + ); + break; + default: if (enterprise_installed()) { $table->data[($i + 1)]['icon'] = enterprise_visual_map_print_list_element('icon', $layoutData); @@ -394,6 +437,13 @@ foreach ($layoutDatas as $layoutData) { $table->data[($i + 1)][2] = html_print_input_text('width_'.$idLayoutData, $layoutData['width'], '', 2, 5, true).' x '.html_print_input_text('height_'.$idLayoutData, $layoutData['width'], '', 2, 5, true); break; + case CIRCULAR_PROGRESS_BAR: + case CIRCULAR_INTERIOR_PROGRESS_BAR: + case PERCENTILE_BUBBLE: + case PERCENTILE_BAR: + $table->data[($i + 1)][2] = html_print_input_text('width_'.$idLayoutData, $layoutData['width'], '', 2, 5, true); + break; + default: $table->data[($i + 1)][2] = html_print_input_text('width_'.$idLayoutData, $layoutData['width'], '', 2, 5, true).' x '.html_print_input_text('height_'.$idLayoutData, $layoutData['height'], '', 2, 5, true); break; @@ -478,6 +528,7 @@ foreach ($layoutDatas as $layoutData) { case LABEL: case NETWORK_LINK: case LINE_ITEM: + case CLOCK: $table->data[($i + 2)][0] = ''; break; @@ -520,7 +571,7 @@ foreach ($layoutDatas as $layoutData) { $params['value'] = db_get_value('alias', 'tagente', 'id_agente', $layoutData['id_agent']); } - if ($layoutData['id_agent'] == 0 and $layoutData['id_custom_graph'] != 0) { + if ($layoutData['id_custom_graph'] != 0) { $table->data[($i + 2)][0] = __('Custom graph'); } else { $table->data[($i + 2)][0] = ui_print_agent_autocomplete_input($params); @@ -540,6 +591,7 @@ foreach ($layoutDatas as $layoutData) { case NETWORK_LINK: case LINE_ITEM: case GROUP_ITEM: + case CLOCK: $table->data[($i + 2)][1] = ''; break; @@ -569,7 +621,7 @@ foreach ($layoutDatas as $layoutData) { $modules = io_safe_output($modules); - if ($layoutData['id_agent'] == 0 and $layoutData['id_custom_graph'] != 0) { + if ($layoutData['id_custom_graph'] != 0) { if (is_metaconsole()) { $graphs = []; $graphs = metaconsole_get_custom_graphs(true); diff --git a/pandora_console/godmode/reporting/visual_console_builder.php b/pandora_console/godmode/reporting/visual_console_builder.php index 444296a290..b8d939516f 100755 --- a/pandora_console/godmode/reporting/visual_console_builder.php +++ b/pandora_console/godmode/reporting/visual_console_builder.php @@ -433,7 +433,10 @@ switch ($activeTab) { $idsElements = db_get_all_rows_filter( 'tlayout_data', ['id_layout' => $idVisualConsole], - ['id'] + [ + 'id', + 'type', + ] ); if ($idsElements === false) { @@ -449,18 +452,33 @@ switch ($activeTab) { $values['height'] = get_parameter('height_'.$id, 0); $values['pos_x'] = get_parameter('left_'.$id, 0); $values['pos_y'] = get_parameter('top_'.$id, 0); - $type = db_get_value('type', 'tlayout_data', 'id', $id); - switch ($type) { - case MODULE_GRAPH: + switch ($idElement['type']) { + case NETWORK_LINK: + case LINE_ITEM: + continue 2; + + break; + case SIMPLE_VALUE_MAX: case SIMPLE_VALUE_MIN: case SIMPLE_VALUE_AVG: $values['period'] = get_parameter('period_'.$id, 0); break; + case MODULE_GRAPH: + $values['period'] = get_parameter('period_'.$id, 0); + unset($values['image']); + break; + case GROUP_ITEM: $values['id_group'] = get_parameter('group_'.$id, 0); - $values['show_statistics'] = get_parameter('show_statistics', 0); + break; + + case CIRCULAR_PROGRESS_BAR: + case CIRCULAR_INTERIOR_PROGRESS_BAR: + case PERCENTILE_BUBBLE: + case PERCENTILE_BAR: + unset($values['height']); break; } diff --git a/pandora_console/godmode/snmpconsole/snmp_alert.php b/pandora_console/godmode/snmpconsole/snmp_alert.php index 674e266f62..671bb310de 100755 --- a/pandora_console/godmode/snmpconsole/snmp_alert.php +++ b/pandora_console/godmode/snmpconsole/snmp_alert.php @@ -1513,7 +1513,7 @@ $(document).ready (function () { $('#table_macros-field' + i) .removeAttr('class'); - $("[name=field" + i + "_value]").val(old_value); + $("[name=field" + i + "_value]").val(old_value).trigger('change'); $('#table_macros-field').show(); } } diff --git a/pandora_console/godmode/users/configure_profile.php b/pandora_console/godmode/users/configure_profile.php index a6bf49dcb1..467bd4703c 100644 --- a/pandora_console/godmode/users/configure_profile.php +++ b/pandora_console/godmode/users/configure_profile.php @@ -1,17 +1,28 @@ [ @@ -79,46 +90,46 @@ if (!is_metaconsole()) { $new_profile = (bool) get_parameter('new_profile'); $id_profile = (int) get_parameter('id'); -// Edit profile +// Edit profile. if ($id_profile || $new_profile) { if ($new_profile) { - // Name + // Name. $name = ''; - // Agents + // Agents. $agent_view = 0; $agent_edit = 0; $agent_disable = 0; - // Alerts + // Alerts. $alert_edit = 0; $alert_management = 0; - // Users + // Users. $user_management = 0; - // DB + // DB. $db_management = 0; - // Pandora + // Pandora. $pandora_management = 0; - // Events + // Events. $event_view = 0; $event_edit = 0; $event_management = 0; - // Reports + // Reports. $report_view = 0; $report_edit = 0; $report_management = 0; - // Network maps + // Network maps. $map_view = 0; $map_edit = 0; $map_management = 0; - // Visual console + // Visual console. $vconsole_view = 0; $vconsole_edit = 0; $vconsole_management = 0; @@ -145,43 +156,43 @@ if ($id_profile || $new_profile) { exit; } - // Name + // Name. $name = $profile['name']; - // Agents + // Agents. $agent_view = (bool) $profile['agent_view']; $agent_edit = (bool) $profile['agent_edit']; $agent_disable = (bool) $profile['agent_disable']; - // Alerts + // Alerts. $alert_edit = (bool) $profile['alert_edit']; $alert_management = (bool) $profile['alert_management']; - // Users + // Users. $user_management = (bool) $profile['user_management']; - // DB + // DB. $db_management = (bool) $profile['db_management']; - // Pandora + // Pandora. $pandora_management = (bool) $profile['pandora_management']; - // Events + // Events. $event_view = (bool) $profile['event_view']; $event_edit = (bool) $profile['event_edit']; $event_management = (bool) $profile['event_management']; - // Reports + // Reports. $report_view = (bool) $profile['report_view']; $report_edit = (bool) $profile['report_edit']; $report_management = (bool) $profile['report_management']; - // Network maps + // Network maps. $map_view = (bool) $profile['map_view']; $map_edit = (bool) $profile['map_edit']; $map_management = (bool) $profile['map_management']; - // Visual console + // Visual console. $vconsole_view = (bool) $profile['vconsole_view']; $vconsole_edit = (bool) $profile['vconsole_edit']; $vconsole_management = (bool) $profile['vconsole_management']; @@ -249,14 +260,14 @@ if ($id_profile || $new_profile) { $table->style[0] = 'font-weight: bold'; $table->data = []; - // Name + // Name. $row = []; $row['name'] = __('Profile name'); $row['input'] = html_print_input_text('name', $name, '', 30, 60, true); $table->data['name'] = $row; $table->data[] = '
'; - // Agents + // Agents. $row = []; $row['name'] = __('View agents'); $row['input'] = html_print_checkbox('agent_view', 1, $agent_view, true); @@ -271,7 +282,7 @@ if ($id_profile || $new_profile) { $table->data['AW'] = $row; $table->data[] = '
'; - // Alerts + // Alerts. $row = []; $row['name'] = __('Edit alerts'); $row['input'] = html_print_checkbox('alert_edit', 1, $alert_edit, true); @@ -282,7 +293,7 @@ if ($id_profile || $new_profile) { $table->data['LM'] = $row; $table->data[] = '
'; - // Events + // Events. $row = []; $row['name'] = __('View events'); $row['input'] = html_print_checkbox('event_view', 1, $event_view, true); @@ -297,7 +308,7 @@ if ($id_profile || $new_profile) { $table->data['EM'] = $row; $table->data[] = '
'; - // Reports + // Reports. $row = []; $row['name'] = __('View reports'); $row['input'] = html_print_checkbox('report_view', 1, $report_view, true); @@ -312,7 +323,7 @@ if ($id_profile || $new_profile) { $table->data['RM'] = $row; $table->data[] = '
'; - // Network maps + // Network maps. $row = []; $row['name'] = __('View network maps'); $row['input'] = html_print_checkbox('map_view', 1, $map_view, true); @@ -327,7 +338,7 @@ if ($id_profile || $new_profile) { $table->data['MM'] = $row; $table->data[] = '
'; - // Visual console + // Visual console. $row = []; $row['name'] = __('View visual console'); $row['input'] = html_print_checkbox('vconsole_view', 1, $vconsole_view, true); @@ -347,36 +358,36 @@ if ($id_profile || $new_profile) { $disable_option = ''; } - // NCM + // NCM. $row = []; $row['name'] = __('View NCM data'); $row['input'] = html_print_checkbox('network_config_view', 1, $network_config_view, true); - $table->data['VR'] = $row; + $table->data['NR'] = $row; $row = []; $row['name'] = __('Operate NCM'); $row['input'] = html_print_checkbox('network_config_edit', 1, $network_config_edit, true, false, 'autoclick_profile_users(\'network_config_edit\', \'network_config_view\', \'false\')'); - $table->data['VW'] = $row; + $table->data['NW'] = $row; $row = []; $row['name'] = __('Manage NCM'); $row['input'] = html_print_checkbox('network_config_management', 1, $network_config_management, true, false, 'autoclick_profile_users(\'network_config_management\', \'network_config_view\', \'network_config_edit\')'); - $table->data['VM'] = $row; + $table->data['NM'] = $row; $table->data[] = '
'; - // Users + // Users. $row = []; $row['name'] = __('Manage users'); $row['input'] = html_print_checkbox('user_management', 1, $user_management, true, false, $disable_option); $table->data['UM'] = $row; $table->data[] = '
'; - // DB + // DB. $row = []; $row['name'] = __('Manage database'); $row['input'] = html_print_checkbox('db_management', 1, $db_management, true, false, $disable_option); $table->data['DM'] = $row; $table->data[] = '
'; - // Pandora + // Pandora. $row = []; $row['name'] = __('%s management', get_product_name()); $row['input'] = html_print_checkbox('pandora_management', 1, $pandora_management, true, false, $disable_option); diff --git a/pandora_console/godmode/users/configure_user.php b/pandora_console/godmode/users/configure_user.php index 14e39ccb76..b91609e257 100644 --- a/pandora_console/godmode/users/configure_user.php +++ b/pandora_console/godmode/users/configure_user.php @@ -140,7 +140,9 @@ if (is_ajax()) { $has_profile = db_get_row('tusuario_perfil', 'id_usuario', $id2); - if ($has_profile == false) { + $user_is_global_admin = users_is_admin($id2); + + if ($has_profile === false && $user_is_global_admin === false) { $result = delete_user($id2); if ($result) { @@ -1544,6 +1546,7 @@ $(document).ready (function () { var img_delete = ''; var id_user = ''; var is_metaconsole = ''; + var user_is_global_admin = ''; var data = []; $('input:image[name="add"]').click(function (e) { @@ -1588,7 +1591,7 @@ $(document).ready (function () { $('input:image[name="del"]').click(function (e) { e.preventDefault(); var rows = $("#table_profiles tr").length; - if ((is_metaconsole === '1' && rows <= 4) || (is_metaconsole === '' && rows <= 3)) { + if (((is_metaconsole === '1' && rows <= 4) || (is_metaconsole === '' && rows <= 3)) && user_is_global_admin !== '1') { if (!confirm('' + '. ' + '')) { return; } @@ -1610,8 +1613,11 @@ $(document).ready (function () { success: function (data) { row.remove(); var rows = $("#table_profiles tr").length; - if ((is_metaconsole === '1' && rows <= 3) || (is_metaconsole === '' && rows <= 2)) { + + if (is_metaconsole === '' && rows <= 2 && user_is_global_admin !== '1') { window.location.replace(""); + } else if (is_metaconsole === '1' && rows <= 3 && user_is_global_admin !== '1') { + window.location.replace(""); } } }); @@ -1750,7 +1756,6 @@ function show_double_auth_info () { var $dialogContainer = $("div#dialog-double_auth-container"); $dialogContainer.html($loadingSpinner); -console.log(userID); // Load the info page var request = $.ajax({ url: "", @@ -1915,7 +1920,6 @@ function show_double_auth_deactivation () { }, success: function(data, textStatus, xhr) { - console.log(data); if (data === -1) { $dialogContainer.html("
'.__('Authentication error').'
'; ?>"); } diff --git a/pandora_console/include/ajax/events.php b/pandora_console/include/ajax/events.php index 6fc7401f22..310ee8ffb0 100644 --- a/pandora_console/include/ajax/events.php +++ b/pandora_console/include/ajax/events.php @@ -1612,6 +1612,7 @@ if ($get_extended_event) { data : { page: "include/ajax/events", get_comments: 1, + meta: '.(int) is_metaconsole().', event: '.json_encode($event).', }, dataType : "html", diff --git a/pandora_console/include/ajax/menu.ajax.php b/pandora_console/include/ajax/menu.ajax.php index 7b13f626f9..2ef22cd405 100644 --- a/pandora_console/include/ajax/menu.ajax.php +++ b/pandora_console/include/ajax/menu.ajax.php @@ -41,6 +41,8 @@ if ($get_sec_pages) { $pages = menu_get_sec_pages($sec, $menu_hash); } + $pages = menu_pepare_acl_select_data($pages, $sec); + echo json_encode($pages); return; } diff --git a/pandora_console/include/auth/mysql.php b/pandora_console/include/auth/mysql.php index 2ee9272772..dd223fecc2 100644 --- a/pandora_console/include/auth/mysql.php +++ b/pandora_console/include/auth/mysql.php @@ -218,6 +218,11 @@ function process_user_login_remote($login, $pass, $api=false) switch ($config['auth']) { // LDAP case 'ldap': + // Use local authentication if user is global admin. + if (is_user_admin($login) === true) { + return false; + } + $sr = ldap_process_user_login($login, $pass); if (!$sr) { @@ -227,6 +232,11 @@ function process_user_login_remote($login, $pass, $api=false) // Active Directory case 'ad': + // Use local authentication if user is global admin. + if (is_user_admin($login) === true) { + return false; + } + if (enterprise_hook('ad_process_user_login', [$login, $pass]) === false) { $config['auth_error'] = 'User not found in database or incorrect password'; return false; @@ -780,7 +790,7 @@ function ldap_process_user_login($login, $password) io_safe_output($config['ldap_base_dn']), $config['ldap_login_attr'], io_safe_output($config['ldap_admin_login']), - io_safe_output($config['ldap_admin_pass']), + io_output_password($config['ldap_admin_pass']), io_safe_output($login) ); @@ -804,7 +814,7 @@ function ldap_process_user_login($login, $password) } else { // PHP LDAP function if ($config['ldap_admin_login'] != '' && $config['ldap_admin_pass'] != '') { - if (!@ldap_bind($ds, io_safe_output($config['ldap_admin_login']), $config['ldap_admin_pass'])) { + if (!@ldap_bind($ds, io_safe_output($config['ldap_admin_login']), io_output_password($config['ldap_admin_pass']))) { $config['auth_error'] = 'Admin ldap connection fail'; @ldap_close($ds); return false; diff --git a/pandora_console/include/class/AgentWizard.class.php b/pandora_console/include/class/AgentWizard.class.php index 244126d067..13e4264e23 100644 --- a/pandora_console/include/class/AgentWizard.class.php +++ b/pandora_console/include/class/AgentWizard.class.php @@ -2423,17 +2423,20 @@ class AgentWizard extends HTML } // Get current value. - if (in_array( - $moduleData['module_type'], - [ - MODULE_TYPE_REMOTE_SNMP, - MODULE_TYPE_REMOTE_SNMP_INC, - MODULE_TYPE_REMOTE_SNMP_STRING, - MODULE_TYPE_REMOTE_SNMP_PROC, - ] - ) === true + if ($this->serverType === SERVER_TYPE_ENTERPRISE_SATELLITE + || in_array( + $moduleData['module_type'], + [ + MODULE_TYPE_REMOTE_SNMP, + MODULE_TYPE_REMOTE_SNMP_INC, + MODULE_TYPE_REMOTE_SNMP_STRING, + MODULE_TYPE_REMOTE_SNMP_PROC, + ] + ) === true ) { - $currentValue = $this->snmpGetValue($moduleData['value']); + if (isset($moduleData['value']) === true) { + $currentValue = $this->snmpGetValue($moduleData['value']); + } } // It unit of measure have data, attach to current value. @@ -2593,17 +2596,20 @@ class AgentWizard extends HTML // Get current value. $currentValue = ''; - if (in_array( - $moduleData['module_type'], - [ - MODULE_TYPE_REMOTE_SNMP, - MODULE_TYPE_REMOTE_SNMP_INC, - MODULE_TYPE_REMOTE_SNMP_STRING, - MODULE_TYPE_REMOTE_SNMP_PROC, - ] - ) === true + if ($this->serverType === SERVER_TYPE_ENTERPRISE_SATELLITE + || in_array( + $moduleData['module_type'], + [ + MODULE_TYPE_REMOTE_SNMP, + MODULE_TYPE_REMOTE_SNMP_INC, + MODULE_TYPE_REMOTE_SNMP_STRING, + MODULE_TYPE_REMOTE_SNMP_PROC, + ] + ) === true ) { - $currentValue = $this->snmpGetValue($moduleData['value']); + if (isset($moduleData['value']) === true) { + $currentValue = $this->snmpGetValue($moduleData['value']); + } } // Format current value with thousands and decimals. @@ -3461,7 +3467,7 @@ class AgentWizard extends HTML } else { preg_match('/\.\d+$/', $key, $index); $tmp = explode(': ', $oid_unit); - $output[$index[0]] = ($tmp[1] ?? ''); + $output[$index[0]] = str_replace('"', '', ($tmp[1] ?? '')); } } } diff --git a/pandora_console/include/class/AgentsAlerts.class.php b/pandora_console/include/class/AgentsAlerts.class.php index 5e0861ed6c..b7e39af4b7 100644 --- a/pandora_console/include/class/AgentsAlerts.class.php +++ b/pandora_console/include/class/AgentsAlerts.class.php @@ -133,7 +133,9 @@ class AgentsAlerts extends HTML // Refresh rate. $this->refreshSelectedRate = (string) get_parameter('refresh-rate', '30'); // Show Modules without alerts table. - $this->showWithoutAlertModules = isset($_POST['show-modules-without-alerts']); + $this->showWithoutAlertModules = (isset($_POST['show-modules-without-alerts'])) + ? true + : isset($_GET['show-modules-without-alerts']); // Selected group. $this->groupId = (int) get_parameter('group-id', 0); // Create alert token. @@ -200,6 +202,8 @@ class AgentsAlerts extends HTML */ private function createAlertTable() { + global $config; + $table = new stdClass(); if ($this->groupId > 0) { @@ -219,7 +223,7 @@ class AgentsAlerts extends HTML $sql = 'SELECT tagente.alias, tagente_modulo.nombre, tagente_modulo.id_agente_modulo FROM tagente_modulo INNER JOIN tagente ON tagente.id_agente = tagente_modulo.id_agente - WHERE id_agente_modulo NOT IN (SELECT id_agent_module FROM talert_template_modules) '.$grupo.' LIMIT 20 OFFSET '.$offset_modules; + WHERE id_agente_modulo NOT IN (SELECT id_agent_module FROM talert_template_modules) '.$grupo.' LIMIT '.$config['block_size'].' OFFSET '.$offset_modules; $agent_modules = db_get_all_rows_sql($sql); @@ -227,7 +231,7 @@ class AgentsAlerts extends HTML $count_agent_module[0]['COUNT(tagente_modulo.nombre)'], ui_get_url_refresh(), 0, - 0, + false, false, 'offset', true, @@ -831,6 +835,7 @@ class AgentsAlerts extends HTML 'arguments' => [ 'type' => 'button', 'return' => true, + 'label' => '', 'name' => 'pure', 'attributes' => 'class="full_screen_button '.$screenSwitchClass.'" title="'.$screenSwitchTitle.'"', ], diff --git a/pandora_console/include/class/ConsoleSupervisor.php b/pandora_console/include/class/ConsoleSupervisor.php index f6e6da12d7..ea0ac8479d 100644 --- a/pandora_console/include/class/ConsoleSupervisor.php +++ b/pandora_console/include/class/ConsoleSupervisor.php @@ -52,6 +52,12 @@ class ConsoleSupervisor */ public const MIN_PERFORMANCE_MODULES = 100; + + /** + * Minimum queued elements in synchronization queue to be warned.. + */ + public const MIN_SYNC_QUEUE_LENGTH = 200; + /** * Show if console supervisor is enabled or not. * @@ -242,6 +248,16 @@ class ConsoleSupervisor $this->checkAuditLogOldLocation(); + /* + * Checks if sync queue is longer than limits. + * NOTIF.SYNCQUEUE.LENGTH + */ + + if (is_metaconsole() === true) { + $this->checkSyncQueueLength(); + $this->checkSyncQueueStatus(); + } + } @@ -492,6 +508,16 @@ class ConsoleSupervisor */ $this->checkAuditLogOldLocation(); + + /* + * Checks if sync queue is longer than limits. + * NOTIF.SYNCQUEUE.LENGTH + */ + + if (is_metaconsole() === true) { + $this->checkSyncQueueLength(); + $this->checkSyncQueueStatus(); + } } @@ -2684,4 +2710,117 @@ class ConsoleSupervisor } + /** + * Verifies the status of synchronization queue and warns if something is + * not working as expected. + * + * @return void + */ + public function checkSyncQueueLength() + { + global $config; + + if (is_metaconsole() !== true) { + return; + } + + $sync = new PandoraFMS\Enterprise\Metaconsole\Synchronizer(); + $counts = $sync->getQueues(true); + + if (count($counts) === 0) { + // Clean all. + $this->cleanNotifications('NOTIF.SYNCQUEUE.LENGTH.%'); + } + + $items_min = $config['sync_queue_items_max']; + if (is_numeric($items_min) !== true && $items_min <= 0) { + $items_min = self::MIN_SYNC_QUEUE_LENGTH; + } + + foreach ($counts as $node_id => $count) { + if ($count < $items_min) { + $this->cleanNotifications('NOTIF.SYNCQUEUE.LENGTH.'.$node_id); + } else { + try { + $node = new PandoraFMS\Enterprise\Metaconsole\Node($node_id); + + $url = '__url__/index.php?sec=advanced&sec2=advanced/metasetup&tab=consoles'; + + $this->notify( + [ + 'type' => 'NOTIF.SYNCQUEUE.LENGTH.'.$node_id, + 'title' => __('Node %s sync queue length exceeded, ', $node->server_name()), + 'message' => __( + 'Synchronization queue lenght for node %s is %d items, this value should be 0 or lower than %d, please check the queue status.', + $node->server_name(), + $count, + $items_min + ), + 'url' => $url, + ] + ); + } catch (\Exception $e) { + // Clean, exception in node finding. + $this->cleanNotifications('NOTIF.SYNCQUEUE.LENGTH.'.$node_id); + } + } + } + + } + + + /** + * Verifies the status of synchronization queue and warns if something is + * not working as expected. + * + * @return void + */ + public function checkSyncQueueStatus() + { + if (is_metaconsole() !== true) { + return; + } + + $sync = new PandoraFMS\Enterprise\Metaconsole\Synchronizer(); + $queues = $sync->getQueues(); + if (count($queues) === 0) { + // Clean all. + $this->cleanNotifications('NOTIF.SYNCQUEUE.STATUS.%'); + } + + foreach ($queues as $node_id => $queue) { + if (count($queue) === 0) { + $this->cleanNotifications('NOTIF.SYNCQUEUE.STATUS.'.$node_id); + continue; + } + + $item = $queue[0]; + + if (empty($item->error()) === false) { + try { + $node = new PandoraFMS\Enterprise\Metaconsole\Node($node_id); + $url = '__url__/index.php?sec=advanced&sec2=advanced/metasetup&tab=consoles'; + + $this->notify( + [ + 'type' => 'NOTIF.SYNCQUEUE.STATUS.'.$node_id, + 'title' => __('Node %s sync queue failed, ', $node->server_name()), + 'message' => __( + 'Node %s cannot process synchronization queue due %s, please check the queue status.', + $node->server_name(), + $item->error() + ), + 'url' => $url, + ] + ); + } catch (\Exception $e) { + // Clean, exception in node finding. + $this->cleanNotifications('NOTIF.SYNCQUEUE.STATUS.'.$node_id); + } + } + } + + } + + } diff --git a/pandora_console/include/class/Tree.class.php b/pandora_console/include/class/Tree.class.php index aec55d97c7..73e19c0d38 100644 --- a/pandora_console/include/class/Tree.class.php +++ b/pandora_console/include/class/Tree.class.php @@ -600,7 +600,7 @@ class Tree if (is_metaconsole()) { $module['serverID'] = $this->serverID; - $module['serverName'] = $this->serverName; + $module['serverName'] = empty($this->serverName) === false ? $this->serverName : servers_get_name($this->serverID); } else { $module['serverName'] = false; $module['serverID'] = false; @@ -895,9 +895,15 @@ class Tree protected function processAgents(&$agents, $server=false) { if (!empty($agents)) { + $agents_aux = []; foreach ($agents as $iterator => $agent) { $this->processAgent($agents[$iterator], $server); + if ($agents[$iterator]['counters']['total'] !== '0') { + $agents_aux[] = $agents[$iterator]; + } } + + $agents = $agents_aux; } } @@ -950,10 +956,9 @@ class Tree $module_status_inner = ''; $module_search_inner = ''; $module_search_filter = ''; + if (!empty($this->filter['searchModule'])) { $module_search_inner = ' - INNER JOIN tagente_modulo tam - ON ta.id_agente = tam.id_agente INNER JOIN tagente_estado tae ON tae.id_agente_modulo = tam.id_agente_modulo'; $module_search_filter = "AND tam.disabled = 0 diff --git a/pandora_console/include/class/TreeOS.class.php b/pandora_console/include/class/TreeOS.class.php index ed4f61718b..4820047562 100644 --- a/pandora_console/include/class/TreeOS.class.php +++ b/pandora_console/include/class/TreeOS.class.php @@ -32,6 +32,8 @@ class TreeOS extends Tree 'tco.icon_name AS iconHTML', ]; $this->L1inner = 'INNER JOIN tconfig_os tco ON tco.id_os = x2.g'; + $this->L1innerInside = 'INNER JOIN tagente_modulo tam + ON ta.id_agente = tam.id_agente'; $this->L1orderByFinal = 'tco.name'; $this->L2condition = 'AND ta.id_os = '.$this->rootID; diff --git a/pandora_console/include/config_process.php b/pandora_console/include/config_process.php index 665550df1a..04cc7812d3 100644 --- a/pandora_console/include/config_process.php +++ b/pandora_console/include/config_process.php @@ -20,7 +20,7 @@ /** * Pandora build version and version */ -$build_version = 'PC211105'; +$build_version = 'PC211119'; $pandora_version = 'v7.0NG.758'; // Do not overwrite default timezone set if defined. diff --git a/pandora_console/include/functions_agents.php b/pandora_console/include/functions_agents.php index 26ad1c59dc..4e3765a003 100644 --- a/pandora_console/include/functions_agents.php +++ b/pandora_console/include/functions_agents.php @@ -1181,6 +1181,10 @@ function agents_get_group_agents( } } + if (isset($search['all_agents'])) { + unset($search['all_agents']); + } + if (isset($search['string']) === true) { $string = io_safe_input($search['string']); $filter[] = "(nombre COLLATE utf8_general_ci LIKE '%$string%' OR direccion LIKE '%$string%')"; @@ -1283,7 +1287,7 @@ function agents_get_group_agents( if (!$add_alert_bulk_op) { // Add the rest of the filter from the search array. foreach ($search as $key => $value) { - $filter[] = $value; + $filter[$key] = $value; } } } else if ($filter !== true) { diff --git a/pandora_console/include/functions_config.php b/pandora_console/include/functions_config.php index dcf9e09766..27e976d486 100644 --- a/pandora_console/include/functions_config.php +++ b/pandora_console/include/functions_config.php @@ -620,7 +620,7 @@ function config_update_config() $error_update[] = __('Admin LDAP login'); } - if (!config_update_value('ldap_admin_pass', get_parameter('ldap_admin_pass'))) { + if (!config_update_value('ldap_admin_pass', io_input_password(io_safe_output(get_parameter('ldap_admin_pass'))))) { $error_update[] = __('Admin LDAP password'); } diff --git a/pandora_console/include/functions_events.php b/pandora_console/include/functions_events.php index 5e1888f1f3..8ca918314f 100644 --- a/pandora_console/include/functions_events.php +++ b/pandora_console/include/functions_events.php @@ -4891,7 +4891,8 @@ function events_page_general($event) $data = []; $data[0] = __('Event ID'); - $data[1] = '#'.$event['id_evento']; + $table_event_id = (isset($event['max_id_evento']) === true) ? $event['max_id_evento'] : $event['id_evento']; + $data[1] = '#'.$table_event_id; $table_general->data[] = $data; $data = []; diff --git a/pandora_console/include/functions_graph.php b/pandora_console/include/functions_graph.php index 44cc3380be..70aceab133 100644 --- a/pandora_console/include/functions_graph.php +++ b/pandora_console/include/functions_graph.php @@ -2050,7 +2050,7 @@ function graphic_combined_module( $width, $height, $color, - $module_name_list, + [], $long_index, ui_get_full_url( 'images/image_problem_area_small.png', diff --git a/pandora_console/include/functions_html.php b/pandora_console/include/functions_html.php index d7136bb7ce..804bf56024 100644 --- a/pandora_console/include/functions_html.php +++ b/pandora_console/include/functions_html.php @@ -728,7 +728,8 @@ function html_print_select( $simple_multiple_options=false, $required=false, $truncate_size=false, - $select2_enable=true + $select2_enable=true, + $multiple_select2=false ) { $output = "\n"; @@ -900,7 +901,8 @@ function html_print_select( $select2 = 'select2_dark.min'; } - if ($multiple === false && $select2_enable === true) { + // Note that multiple_select2 is introduced as a workaround to overcome the pointless limitation of preventing "multiple" select inputs from using select2 library without affecting the existing calls to this function. + if ($multiple === false && $select2_enable === true || $multiple_select2 === true) { if (is_ajax()) { $output .= ' - '; + '; } // NOTE: jquery.flot.threshold is not te original file. Is patched to allow multiple thresholds and filled area diff --git a/pandora_console/include/javascript/pandora.js b/pandora_console/include/javascript/pandora.js index 0e545e9f4e..5b96a8fcd6 100644 --- a/pandora_console/include/javascript/pandora.js +++ b/pandora_console/include/javascript/pandora.js @@ -134,7 +134,8 @@ function agent_changed_by_multiple_agents(event, id_agent, selected) { $("input.module_types_excluded").each(function(index, el) { var module_type = parseInt($(el).val()); - if (module_type !== NaN) module_types_excluded.push(module_type); + if (isNaN(module_type) == false) + module_types_excluded.push(module_type); }); } catch (error) {} } diff --git a/pandora_console/include/javascript/tree/TreeController.js b/pandora_console/include/javascript/tree/TreeController.js index 69552d7b6c..461066abbc 100644 --- a/pandora_console/include/javascript/tree/TreeController.js +++ b/pandora_console/include/javascript/tree/TreeController.js @@ -1242,9 +1242,8 @@ var TreeController = { } if ( - (typeof element.searchChildren != "undefined" && - element.searchChildren) || - element.disabled == true + typeof element.searchChildren != "undefined" && + element.searchChildren ) { if ( element.rootType == "group_edition" && diff --git a/pandora_console/include/lib/Dashboard/Widgets/maps_made_by_user.php b/pandora_console/include/lib/Dashboard/Widgets/maps_made_by_user.php index 9bbd69d470..80f6183bde 100644 --- a/pandora_console/include/lib/Dashboard/Widgets/maps_made_by_user.php +++ b/pandora_console/include/lib/Dashboard/Widgets/maps_made_by_user.php @@ -304,8 +304,21 @@ class MapsMadeByUser extends Widget // Retrieve global - common inputs. $inputs = parent::getFormInputs(); + $node_id = $this->nodeId; + if (\is_metaconsole() === true && $node_id > 0) { + if (\metaconsole_connect(null, $node_id) !== NOERR) { + echo json_encode( + ['error' => __('Failed to connect to node %d', $node_id) ] + ); + } + } + $fields = $this->getVisualConsoles(); + if (\is_metaconsole() === true && $node_id > 0) { + \metaconsole_restore_db(); + } + // Visual console. $inputs[] = [ 'label' => __('Visual console'), diff --git a/pandora_console/include/lib/Dashboard/Widgets/tactical.php b/pandora_console/include/lib/Dashboard/Widgets/tactical.php index 06f14cf557..74d72d0c5c 100755 --- a/pandora_console/include/lib/Dashboard/Widgets/tactical.php +++ b/pandora_console/include/lib/Dashboard/Widgets/tactical.php @@ -339,6 +339,7 @@ class TacticalWidget extends Widget $data['monitor_not_normal'] = (int) $all_data['_monitor_not_normal_']; $data['monitor_alerts'] = (int) $all_data['_monitors_alerts_']; $data['monitor_alerts_fired'] = (int) $all_data['_monitors_alerts_fired_']; + $data['monitor_total'] = (int) $all_data['_monitor_total_']; $data['total_agents'] = (int) $all_data['_total_agents_']; diff --git a/pandora_console/include/rest-api/models/VisualConsole/Items/ModuleGraph.php b/pandora_console/include/rest-api/models/VisualConsole/Items/ModuleGraph.php index 6f5a248a78..77a62b4a45 100644 --- a/pandora_console/include/rest-api/models/VisualConsole/Items/ModuleGraph.php +++ b/pandora_console/include/rest-api/models/VisualConsole/Items/ModuleGraph.php @@ -327,6 +327,10 @@ final class ModuleGraph extends Item $width = (int) $data['width']; $height = (int) $data['height']; + if ($height == 0) { + $height = 15; + } + // Custom graph. if (empty($customGraphId) === false) { $customGraph = \db_get_row('tgraph', 'id_graph', $customGraphId); diff --git a/pandora_console/include/styles/module_library.css b/pandora_console/include/styles/module_library.css index 2dac88304e..31dc57d9e3 100644 --- a/pandora_console/include/styles/module_library.css +++ b/pandora_console/include/styles/module_library.css @@ -110,7 +110,7 @@ /* --- Sidebar --- */ .sidebar_library { - min-width: 360px; + width: 240px; margin-left: 50px; border-left: 1px solid #d0d0d0; padding: 0px 0px 0px 30px; @@ -151,7 +151,7 @@ #category_result, #search_result { display: grid; - grid-template-columns: repeat(3, minmax(300px, 1fr)); + grid-template-columns: repeat(3, minmax(175px, 1fr)); grid-gap: 20px; } diff --git a/pandora_console/include/styles/pandora.css b/pandora_console/include/styles/pandora.css index e541c8ff97..496f57ea1c 100644 --- a/pandora_console/include/styles/pandora.css +++ b/pandora_console/include/styles/pandora.css @@ -3000,7 +3000,7 @@ input.search_input { background-color: #f2f6f7; padding: 0px; margin: 0; - width: 300px; + width: 150px; height: 30px; margin-left: 2px; padding-left: 15px; @@ -6162,9 +6162,8 @@ div.graph div.legend table { .sound_events { background-color: #494949; - max-width: 550px; - max-height: 400px; - margin-top: 40px; + margin: 40px 2em 0; + min-height: auto; } .w16px { diff --git a/pandora_console/include/styles/pandora_black.css b/pandora_console/include/styles/pandora_black.css index f8848da4c4..42af9e36c6 100644 --- a/pandora_console/include/styles/pandora_black.css +++ b/pandora_console/include/styles/pandora_black.css @@ -58,7 +58,8 @@ input.sub, button.sub, .bg_general, .show_result_interpreter, -div#rules::after table.agent_info_table thead > tr:first-child th, +div#rules::after, +table.agent_info_table thead > tr:first-child th, table.agent_info_table tr { background-color: #222 !important; color: #fff !important; diff --git a/pandora_console/index.php b/pandora_console/index.php index 548991159e..d6d3be20e0 100755 --- a/pandora_console/index.php +++ b/pandora_console/index.php @@ -220,8 +220,6 @@ echo ''."\n"; // This starts the page head. In the callback function, // $page['head'] array content will be processed into the head. ob_start('ui_process_page_head'); -// Avoid clickjacking. -header('X-Frame-Options: SAMEORIGIN'); // Enterprise main. enterprise_include_once('index.php'); diff --git a/pandora_console/install.php b/pandora_console/install.php index 63d752755d..8e25295f25 100644 --- a/pandora_console/install.php +++ b/pandora_console/install.php @@ -129,7 +129,7 @@
Date: Wed Apr 10 2013 21:57:23 UTC -* http://jquerymobile.com -* -* Copyright 2010, 2013 jQuery Foundation, Inc. and other contributors -* Released under the MIT license. -* http://jquery.org/license -* -*/ - - -(function ( root, doc, factory ) { - if ( typeof define === "function" && define.amd ) { - // AMD. Register as an anonymous module. - define( [ "jquery" ], function ( $ ) { - factory( $, root, doc ); - return $.mobile; - }); - } else { - // Browser globals - factory( root.jQuery, root, doc ); - } -}( this, document, function ( jQuery, window, document, undefined ) { -(function( $ ) { - $.mobile = {}; -}( jQuery )); -(function( $, window, undefined ) { - var nsNormalizeDict = {}; - - // jQuery.mobile configurable options - $.mobile = $.extend($.mobile, { - - // Version of the jQuery Mobile Framework - version: "1.3.1", - - // Namespace used framework-wide for data-attrs. Default is no namespace - ns: "", - - // Define the url parameter used for referencing widget-generated sub-pages. - // Translates to to example.html&ui-page=subpageIdentifier - // hash segment before &ui-page= is used to make Ajax request - subPageUrlKey: "ui-page", - - // Class assigned to page currently in view, and during transitions - activePageClass: "ui-page-active", - - // Class used for "active" button state, from CSS framework - activeBtnClass: "ui-btn-active", - - // Class used for "focus" form element state, from CSS framework - focusClass: "ui-focus", - - // Automatically handle clicks and form submissions through Ajax, when same-domain - ajaxEnabled: true, - - // Automatically load and show pages based on location.hash - hashListeningEnabled: true, - - // disable to prevent jquery from bothering with links - linkBindingEnabled: true, - - // Set default page transition - 'none' for no transitions - defaultPageTransition: "fade", - - // Set maximum window width for transitions to apply - 'false' for no limit - maxTransitionWidth: false, - - // Minimum scroll distance that will be remembered when returning to a page - minScrollBack: 250, - - // DEPRECATED: the following property is no longer in use, but defined until 2.0 to prevent conflicts - touchOverflowEnabled: false, - - // Set default dialog transition - 'none' for no transitions - defaultDialogTransition: "pop", - - // Error response message - appears when an Ajax page request fails - pageLoadErrorMessage: "Error Loading Page", - - // For error messages, which theme does the box uses? - pageLoadErrorMessageTheme: "e", - - // replace calls to window.history.back with phonegaps navigation helper - // where it is provided on the window object - phonegapNavigationEnabled: false, - - //automatically initialize the DOM when it's ready - autoInitializePage: true, - - pushStateEnabled: true, - - // allows users to opt in to ignoring content by marking a parent element as - // data-ignored - ignoreContentEnabled: false, - - // turn of binding to the native orientationchange due to android orientation behavior - orientationChangeEnabled: true, - - buttonMarkup: { - hoverDelay: 200 - }, - - // define the window and the document objects - window: $( window ), - document: $( document ), - - // TODO might be useful upstream in jquery itself ? - keyCode: { - ALT: 18, - BACKSPACE: 8, - CAPS_LOCK: 20, - COMMA: 188, - COMMAND: 91, - COMMAND_LEFT: 91, // COMMAND - COMMAND_RIGHT: 93, - CONTROL: 17, - DELETE: 46, - DOWN: 40, - END: 35, - ENTER: 13, - ESCAPE: 27, - HOME: 36, - INSERT: 45, - LEFT: 37, - MENU: 93, // COMMAND_RIGHT - NUMPAD_ADD: 107, - NUMPAD_DECIMAL: 110, - NUMPAD_DIVIDE: 111, - NUMPAD_ENTER: 108, - NUMPAD_MULTIPLY: 106, - NUMPAD_SUBTRACT: 109, - PAGE_DOWN: 34, - PAGE_UP: 33, - PERIOD: 190, - RIGHT: 39, - SHIFT: 16, - SPACE: 32, - TAB: 9, - UP: 38, - WINDOWS: 91 // COMMAND - }, - - // Place to store various widget extensions - behaviors: {}, - - // Scroll page vertically: scroll to 0 to hide iOS address bar, or pass a Y value - silentScroll: function( ypos ) { - if ( $.type( ypos ) !== "number" ) { - ypos = $.mobile.defaultHomeScroll; - } - - // prevent scrollstart and scrollstop events - $.event.special.scrollstart.enabled = false; - - setTimeout( function() { - window.scrollTo( 0, ypos ); - $.mobile.document.trigger( "silentscroll", { x: 0, y: ypos }); - }, 20 ); - - setTimeout( function() { - $.event.special.scrollstart.enabled = true; - }, 150 ); - }, - - // Expose our cache for testing purposes. - nsNormalizeDict: nsNormalizeDict, - - // Take a data attribute property, prepend the namespace - // and then camel case the attribute string. Add the result - // to our nsNormalizeDict so we don't have to do this again. - nsNormalize: function( prop ) { - if ( !prop ) { - return; - } - - return nsNormalizeDict[ prop ] || ( nsNormalizeDict[ prop ] = $.camelCase( $.mobile.ns + prop ) ); - }, - - // Find the closest parent with a theme class on it. Note that - // we are not using $.fn.closest() on purpose here because this - // method gets called quite a bit and we need it to be as fast - // as possible. - getInheritedTheme: function( el, defaultTheme ) { - var e = el[ 0 ], - ltr = "", - re = /ui-(bar|body|overlay)-([a-z])\b/, - c, m; - - while ( e ) { - c = e.className || ""; - if ( c && ( m = re.exec( c ) ) && ( ltr = m[ 2 ] ) ) { - // We found a parent with a theme class - // on it so bail from this loop. - break; - } - - e = e.parentNode; - } - - // Return the theme letter we found, if none, return the - // specified default. - - return ltr || defaultTheme || "a"; - }, - - // TODO the following $ and $.fn extensions can/probably should be moved into jquery.mobile.core.helpers - // - // Find the closest javascript page element to gather settings data jsperf test - // http://jsperf.com/single-complex-selector-vs-many-complex-selectors/edit - // possibly naive, but it shows that the parsing overhead for *just* the page selector vs - // the page and dialog selector is negligable. This could probably be speed up by - // doing a similar parent node traversal to the one found in the inherited theme code above - closestPageData: function( $target ) { - return $target - .closest( ':jqmData(role="page"), :jqmData(role="dialog")' ) - .data( "mobile-page" ); - }, - - enhanceable: function( $set ) { - return this.haveParents( $set, "enhance" ); - }, - - hijackable: function( $set ) { - return this.haveParents( $set, "ajax" ); - }, - - haveParents: function( $set, attr ) { - if ( !$.mobile.ignoreContentEnabled ) { - return $set; - } - - var count = $set.length, - $newSet = $(), - e, $element, excluded; - - for ( var i = 0; i < count; i++ ) { - $element = $set.eq( i ); - excluded = false; - e = $set[ i ]; - - while ( e ) { - var c = e.getAttribute ? e.getAttribute( "data-" + $.mobile.ns + attr ) : ""; - - if ( c === "false" ) { - excluded = true; - break; - } - - e = e.parentNode; - } - - if ( !excluded ) { - $newSet = $newSet.add( $element ); - } - } - - return $newSet; - }, - - getScreenHeight: function() { - // Native innerHeight returns more accurate value for this across platforms, - // jQuery version is here as a normalized fallback for platforms like Symbian - return window.innerHeight || $.mobile.window.height(); - } - }, $.mobile ); - - // Mobile version of data and removeData and hasData methods - // ensures all data is set and retrieved using jQuery Mobile's data namespace - $.fn.jqmData = function( prop, value ) { - var result; - if ( typeof prop !== "undefined" ) { - if ( prop ) { - prop = $.mobile.nsNormalize( prop ); - } - - // undefined is permitted as an explicit input for the second param - // in this case it returns the value and does not set it to undefined - if( arguments.length < 2 || value === undefined ){ - result = this.data( prop ); - } else { - result = this.data( prop, value ); - } - } - return result; - }; - - $.jqmData = function( elem, prop, value ) { - var result; - if ( typeof prop !== "undefined" ) { - result = $.data( elem, prop ? $.mobile.nsNormalize( prop ) : prop, value ); - } - return result; - }; - - $.fn.jqmRemoveData = function( prop ) { - return this.removeData( $.mobile.nsNormalize( prop ) ); - }; - - $.jqmRemoveData = function( elem, prop ) { - return $.removeData( elem, $.mobile.nsNormalize( prop ) ); - }; - - $.fn.removeWithDependents = function() { - $.removeWithDependents( this ); - }; - - $.removeWithDependents = function( elem ) { - var $elem = $( elem ); - - ( $elem.jqmData( 'dependents' ) || $() ).remove(); - $elem.remove(); - }; - - $.fn.addDependents = function( newDependents ) { - $.addDependents( $( this ), newDependents ); - }; - - $.addDependents = function( elem, newDependents ) { - var dependents = $( elem ).jqmData( 'dependents' ) || $(); - - $( elem ).jqmData( 'dependents', $.merge( dependents, newDependents ) ); - }; - - // note that this helper doesn't attempt to handle the callback - // or setting of an html element's text, its only purpose is - // to return the html encoded version of the text in all cases. (thus the name) - $.fn.getEncodedText = function() { - return $( "
" ).text( $( this ).text() ).html(); - }; - - // fluent helper function for the mobile namespaced equivalent - $.fn.jqmEnhanceable = function() { - return $.mobile.enhanceable( this ); - }; - - $.fn.jqmHijackable = function() { - return $.mobile.hijackable( this ); - }; - - // Monkey-patching Sizzle to filter the :jqmData selector - var oldFind = $.find, - jqmDataRE = /:jqmData\(([^)]*)\)/g; - - $.find = function( selector, context, ret, extra ) { - selector = selector.replace( jqmDataRE, "[data-" + ( $.mobile.ns || "" ) + "$1]" ); - - return oldFind.call( this, selector, context, ret, extra ); - }; - - $.extend( $.find, oldFind ); - - $.find.matches = function( expr, set ) { - return $.find( expr, null, null, set ); - }; - - $.find.matchesSelector = function( node, expr ) { - return $.find( expr, null, null, [ node ] ).length > 0; - }; -})( jQuery, this ); - - -/*! - * jQuery UI Widget v1.10.0pre - 2012-11-13 (ff055a0c353c3c8ce6e5bfa07ad7cb03e8885bc5) - * http://jqueryui.com - * - * Copyright 2010, 2013 jQuery Foundation and other contributors - * Released under the MIT license. - * http://jquery.org/license - * - * http://api.jqueryui.com/jQuery.widget/ - */ -(function( $, undefined ) { - -var uuid = 0, - slice = Array.prototype.slice, - _cleanData = $.cleanData; -$.cleanData = function( elems ) { - for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { - try { - $( elem ).triggerHandler( "remove" ); - // http://bugs.jquery.com/ticket/8235 - } catch( e ) {} - } - _cleanData( elems ); -}; - -$.widget = function( name, base, prototype ) { - var fullName, existingConstructor, constructor, basePrototype, - namespace = name.split( "." )[ 0 ]; - - name = name.split( "." )[ 1 ]; - fullName = namespace + "-" + name; - - if ( !prototype ) { - prototype = base; - base = $.Widget; - } - - // create selector for plugin - $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { - return !!$.data( elem, fullName ); - }; - - $[ namespace ] = $[ namespace ] || {}; - existingConstructor = $[ namespace ][ name ]; - constructor = $[ namespace ][ name ] = function( options, element ) { - // allow instantiation without "new" keyword - if ( !this._createWidget ) { - return new constructor( options, element ); - } - - // allow instantiation without initializing for simple inheritance - // must use "new" keyword (the code above always passes args) - if ( arguments.length ) { - this._createWidget( options, element ); - } - }; - // extend with the existing constructor to carry over any static properties - $.extend( constructor, existingConstructor, { - version: prototype.version, - // copy the object used to create the prototype in case we need to - // redefine the widget later - _proto: $.extend( {}, prototype ), - // track widgets that inherit from this widget in case this widget is - // redefined after a widget inherits from it - _childConstructors: [] - }); - - basePrototype = new base(); - // we need to make the options hash a property directly on the new instance - // otherwise we'll modify the options hash on the prototype that we're - // inheriting from - basePrototype.options = $.widget.extend( {}, basePrototype.options ); - $.each( prototype, function( prop, value ) { - if ( $.isFunction( value ) ) { - prototype[ prop ] = (function() { - var _super = function() { - return base.prototype[ prop ].apply( this, arguments ); - }, - _superApply = function( args ) { - return base.prototype[ prop ].apply( this, args ); - }; - return function() { - var __super = this._super, - __superApply = this._superApply, - returnValue; - - this._super = _super; - this._superApply = _superApply; - - returnValue = value.apply( this, arguments ); - - this._super = __super; - this._superApply = __superApply; - - return returnValue; - }; - })(); - } - }); - constructor.prototype = $.widget.extend( basePrototype, { - // TODO: remove support for widgetEventPrefix - // always use the name + a colon as the prefix, e.g., draggable:start - // don't prefix for widgets that aren't DOM-based - widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name - }, prototype, { - constructor: constructor, - namespace: namespace, - widgetName: name, - widgetFullName: fullName - }); - - // If this widget is being redefined then we need to find all widgets that - // are inheriting from it and redefine all of them so that they inherit from - // the new version of this widget. We're essentially trying to replace one - // level in the prototype chain. - if ( existingConstructor ) { - $.each( existingConstructor._childConstructors, function( i, child ) { - var childPrototype = child.prototype; - - // redefine the child widget using the same prototype that was - // originally used, but inherit from the new version of the base - $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto ); - }); - // remove the list of existing child constructors from the old constructor - // so the old child constructors can be garbage collected - delete existingConstructor._childConstructors; - } else { - base._childConstructors.push( constructor ); - } - - $.widget.bridge( name, constructor ); -}; - -$.widget.extend = function( target ) { - var input = slice.call( arguments, 1 ), - inputIndex = 0, - inputLength = input.length, - key, - value; - for ( ; inputIndex < inputLength; inputIndex++ ) { - for ( key in input[ inputIndex ] ) { - value = input[ inputIndex ][ key ]; - if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { - // Clone objects - if ( $.isPlainObject( value ) ) { - target[ key ] = $.isPlainObject( target[ key ] ) ? - $.widget.extend( {}, target[ key ], value ) : - // Don't extend strings, arrays, etc. with objects - $.widget.extend( {}, value ); - // Copy everything else by reference - } else { - target[ key ] = value; - } - } - } - } - return target; -}; - -$.widget.bridge = function( name, object ) { - var fullName = object.prototype.widgetFullName || name; - $.fn[ name ] = function( options ) { - var isMethodCall = typeof options === "string", - args = slice.call( arguments, 1 ), - returnValue = this; - - // allow multiple hashes to be passed on init - options = !isMethodCall && args.length ? - $.widget.extend.apply( null, [ options ].concat(args) ) : - options; - - if ( isMethodCall ) { - this.each(function() { - var methodValue, - instance = $.data( this, fullName ); - if ( !instance ) { - return $.error( "cannot call methods on " + name + " prior to initialization; " + - "attempted to call method '" + options + "'" ); - } - if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) { - return $.error( "no such method '" + options + "' for " + name + " widget instance" ); - } - methodValue = instance[ options ].apply( instance, args ); - if ( methodValue !== instance && methodValue !== undefined ) { - returnValue = methodValue && methodValue.jquery ? - returnValue.pushStack( methodValue.get() ) : - methodValue; - return false; - } - }); - } else { - this.each(function() { - var instance = $.data( this, fullName ); - if ( instance ) { - instance.option( options || {} )._init(); - } else { - $.data( this, fullName, new object( options, this ) ); - } - }); - } - - return returnValue; - }; -}; - -$.Widget = function( /* options, element */ ) {}; -$.Widget._childConstructors = []; - -$.Widget.prototype = { - widgetName: "widget", - widgetEventPrefix: "", - defaultElement: "
", - options: { - disabled: false, - - // callbacks - create: null - }, - _createWidget: function( options, element ) { - element = $( element || this.defaultElement || this )[ 0 ]; - this.element = $( element ); - this.uuid = uuid++; - this.eventNamespace = "." + this.widgetName + this.uuid; - this.options = $.widget.extend( {}, - this.options, - this._getCreateOptions(), - options ); - - this.bindings = $(); - this.hoverable = $(); - this.focusable = $(); - - if ( element !== this ) { - $.data( element, this.widgetFullName, this ); - this._on( true, this.element, { - remove: function( event ) { - if ( event.target === element ) { - this.destroy(); - } - } - }); - this.document = $( element.style ? - // element within the document - element.ownerDocument : - // element is window or document - element.document || element ); - this.window = $( this.document[0].defaultView || this.document[0].parentWindow ); - } - - this._create(); - this._trigger( "create", null, this._getCreateEventData() ); - this._init(); - }, - _getCreateOptions: $.noop, - _getCreateEventData: $.noop, - _create: $.noop, - _init: $.noop, - - destroy: function() { - this._destroy(); - // we can probably remove the unbind calls in 2.0 - // all event bindings should go through this._on() - this.element - .unbind( this.eventNamespace ) - // 1.9 BC for #7810 - // TODO remove dual storage - .removeData( this.widgetName ) - .removeData( this.widgetFullName ) - // support: jquery <1.6.3 - // http://bugs.jquery.com/ticket/9413 - .removeData( $.camelCase( this.widgetFullName ) ); - this.widget() - .unbind( this.eventNamespace ) - .removeAttr( "aria-disabled" ) - .removeClass( - this.widgetFullName + "-disabled " + - "ui-state-disabled" ); - - // clean up events and states - this.bindings.unbind( this.eventNamespace ); - this.hoverable.removeClass( "ui-state-hover" ); - this.focusable.removeClass( "ui-state-focus" ); - }, - _destroy: $.noop, - - widget: function() { - return this.element; - }, - - option: function( key, value ) { - var options = key, - parts, - curOption, - i; - - if ( arguments.length === 0 ) { - // don't return a reference to the internal hash - return $.widget.extend( {}, this.options ); - } - - if ( typeof key === "string" ) { - // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } - options = {}; - parts = key.split( "." ); - key = parts.shift(); - if ( parts.length ) { - curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); - for ( i = 0; i < parts.length - 1; i++ ) { - curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; - curOption = curOption[ parts[ i ] ]; - } - key = parts.pop(); - if ( value === undefined ) { - return curOption[ key ] === undefined ? null : curOption[ key ]; - } - curOption[ key ] = value; - } else { - if ( value === undefined ) { - return this.options[ key ] === undefined ? null : this.options[ key ]; - } - options[ key ] = value; - } - } - - this._setOptions( options ); - - return this; - }, - _setOptions: function( options ) { - var key; - - for ( key in options ) { - this._setOption( key, options[ key ] ); - } - - return this; - }, - _setOption: function( key, value ) { - this.options[ key ] = value; - - if ( key === "disabled" ) { - this.widget() - .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value ) - .attr( "aria-disabled", value ); - this.hoverable.removeClass( "ui-state-hover" ); - this.focusable.removeClass( "ui-state-focus" ); - } - - return this; - }, - - enable: function() { - return this._setOption( "disabled", false ); - }, - disable: function() { - return this._setOption( "disabled", true ); - }, - - _on: function( suppressDisabledCheck, element, handlers ) { - var delegateElement, - instance = this; - - // no suppressDisabledCheck flag, shuffle arguments - if ( typeof suppressDisabledCheck !== "boolean" ) { - handlers = element; - element = suppressDisabledCheck; - suppressDisabledCheck = false; - } - - // no element argument, shuffle and use this.element - if ( !handlers ) { - handlers = element; - element = this.element; - delegateElement = this.widget(); - } else { - // accept selectors, DOM elements - element = delegateElement = $( element ); - this.bindings = this.bindings.add( element ); - } - - $.each( handlers, function( event, handler ) { - function handlerProxy() { - // allow widgets to customize the disabled handling - // - disabled as an array instead of boolean - // - disabled class as method for disabling individual parts - if ( !suppressDisabledCheck && - ( instance.options.disabled === true || - $( this ).hasClass( "ui-state-disabled" ) ) ) { - return; - } - return ( typeof handler === "string" ? instance[ handler ] : handler ) - .apply( instance, arguments ); - } - - // copy the guid so direct unbinding works - if ( typeof handler !== "string" ) { - handlerProxy.guid = handler.guid = - handler.guid || handlerProxy.guid || $.guid++; - } - - var match = event.match( /^(\w+)\s*(.*)$/ ), - eventName = match[1] + instance.eventNamespace, - selector = match[2]; - if ( selector ) { - delegateElement.delegate( selector, eventName, handlerProxy ); - } else { - element.bind( eventName, handlerProxy ); - } - }); - }, - - _off: function( element, eventName ) { - eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace; - element.unbind( eventName ).undelegate( eventName ); - }, - - _delay: function( handler, delay ) { - function handlerProxy() { - return ( typeof handler === "string" ? instance[ handler ] : handler ) - .apply( instance, arguments ); - } - var instance = this; - return setTimeout( handlerProxy, delay || 0 ); - }, - - _hoverable: function( element ) { - this.hoverable = this.hoverable.add( element ); - this._on( element, { - mouseenter: function( event ) { - $( event.currentTarget ).addClass( "ui-state-hover" ); - }, - mouseleave: function( event ) { - $( event.currentTarget ).removeClass( "ui-state-hover" ); - } - }); - }, - - _focusable: function( element ) { - this.focusable = this.focusable.add( element ); - this._on( element, { - focusin: function( event ) { - $( event.currentTarget ).addClass( "ui-state-focus" ); - }, - focusout: function( event ) { - $( event.currentTarget ).removeClass( "ui-state-focus" ); - } - }); - }, - - _trigger: function( type, event, data ) { - var prop, orig, - callback = this.options[ type ]; - - data = data || {}; - event = $.Event( event ); - event.type = ( type === this.widgetEventPrefix ? - type : - this.widgetEventPrefix + type ).toLowerCase(); - // the original event may come from any element - // so we need to reset the target on the new event - event.target = this.element[ 0 ]; - - // copy original event properties over to the new event - orig = event.originalEvent; - if ( orig ) { - for ( prop in orig ) { - if ( !( prop in event ) ) { - event[ prop ] = orig[ prop ]; - } - } - } - - this.element.trigger( event, data ); - return !( $.isFunction( callback ) && - callback.apply( this.element[0], [ event ].concat( data ) ) === false || - event.isDefaultPrevented() ); - } -}; - -$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { - $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { - if ( typeof options === "string" ) { - options = { effect: options }; - } - var hasOptions, - effectName = !options ? - method : - options === true || typeof options === "number" ? - defaultEffect : - options.effect || defaultEffect; - options = options || {}; - if ( typeof options === "number" ) { - options = { duration: options }; - } - hasOptions = !$.isEmptyObject( options ); - options.complete = callback; - if ( options.delay ) { - element.delay( options.delay ); - } - if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { - element[ method ]( options ); - } else if ( effectName !== method && element[ effectName ] ) { - element[ effectName ]( options.duration, options.easing, callback ); - } else { - element.queue(function( next ) { - $( this )[ method ](); - if ( callback ) { - callback.call( element[ 0 ] ); - } - next(); - }); - } - }; -}); - -})( jQuery ); - -(function( $, undefined ) { - -$.widget( "mobile.widget", { - // decorate the parent _createWidget to trigger `widgetinit` for users - // who wish to do post post `widgetcreate` alterations/additions - // - // TODO create a pull request for jquery ui to trigger this event - // in the original _createWidget - _createWidget: function() { - $.Widget.prototype._createWidget.apply( this, arguments ); - this._trigger( 'init' ); - }, - - _getCreateOptions: function() { - - var elem = this.element, - options = {}; - - $.each( this.options, function( option ) { - - var value = elem.jqmData( option.replace( /[A-Z]/g, function( c ) { - return "-" + c.toLowerCase(); - }) - ); - - if ( value !== undefined ) { - options[ option ] = value; - } - }); - - return options; - }, - - enhanceWithin: function( target, useKeepNative ) { - this.enhance( $( this.options.initSelector, $( target )), useKeepNative ); - }, - - enhance: function( targets, useKeepNative ) { - var page, keepNative, $widgetElements = $( targets ), self = this; - - // if ignoreContentEnabled is set to true the framework should - // only enhance the selected elements when they do NOT have a - // parent with the data-namespace-ignore attribute - $widgetElements = $.mobile.enhanceable( $widgetElements ); - - if ( useKeepNative && $widgetElements.length ) { - // TODO remove dependency on the page widget for the keepNative. - // Currently the keepNative value is defined on the page prototype so - // the method is as well - page = $.mobile.closestPageData( $widgetElements ); - keepNative = ( page && page.keepNativeSelector()) || ""; - - $widgetElements = $widgetElements.not( keepNative ); - } - - $widgetElements[ this.widgetName ](); - }, - - raise: function( msg ) { - throw "Widget [" + this.widgetName + "]: " + msg; - } -}); - -})( jQuery ); - - -(function( $, window ) { - // DEPRECATED - // NOTE global mobile object settings - $.extend( $.mobile, { - // DEPRECATED Should the text be visble in the loading message? - loadingMessageTextVisible: undefined, - - // DEPRECATED When the text is visible, what theme does the loading box use? - loadingMessageTheme: undefined, - - // DEPRECATED default message setting - loadingMessage: undefined, - - // DEPRECATED - // Turn on/off page loading message. Theme doubles as an object argument - // with the following shape: { theme: '', text: '', html: '', textVisible: '' } - // NOTE that the $.mobile.loading* settings and params past the first are deprecated - showPageLoadingMsg: function( theme, msgText, textonly ) { - $.mobile.loading( 'show', theme, msgText, textonly ); - }, - - // DEPRECATED - hidePageLoadingMsg: function() { - $.mobile.loading( 'hide' ); - }, - - loading: function() { - this.loaderWidget.loader.apply( this.loaderWidget, arguments ); - } - }); - - // TODO move loader class down into the widget settings - var loaderClass = "ui-loader", $html = $( "html" ), $window = $.mobile.window; - - $.widget( "mobile.loader", { - // NOTE if the global config settings are defined they will override these - // options - options: { - // the theme for the loading message - theme: "a", - - // whether the text in the loading message is shown - textVisible: false, - - // custom html for the inner content of the loading message - html: "", - - // the text to be displayed when the popup is shown - text: "loading" - }, - - defaultHtml: "
" + - "" + - "

" + - "
", - - // For non-fixed supportin browsers. Position at y center (if scrollTop supported), above the activeBtn (if defined), or just 100px from top - fakeFixLoader: function() { - var activeBtn = $( "." + $.mobile.activeBtnClass ).first(); - - this.element - .css({ - top: $.support.scrollTop && $window.scrollTop() + $window.height() / 2 || - activeBtn.length && activeBtn.offset().top || 100 - }); - }, - - // check position of loader to see if it appears to be "fixed" to center - // if not, use abs positioning - checkLoaderPosition: function() { - var offset = this.element.offset(), - scrollTop = $window.scrollTop(), - screenHeight = $.mobile.getScreenHeight(); - - if ( offset.top < scrollTop || ( offset.top - scrollTop ) > screenHeight ) { - this.element.addClass( "ui-loader-fakefix" ); - this.fakeFixLoader(); - $window - .unbind( "scroll", this.checkLoaderPosition ) - .bind( "scroll", $.proxy( this.fakeFixLoader, this ) ); - } - }, - - resetHtml: function() { - this.element.html( $( this.defaultHtml ).html() ); - }, - - // Turn on/off page loading message. Theme doubles as an object argument - // with the following shape: { theme: '', text: '', html: '', textVisible: '' } - // NOTE that the $.mobile.loading* settings and params past the first are deprecated - // TODO sweet jesus we need to break some of this out - show: function( theme, msgText, textonly ) { - var textVisible, message, $header, loadSettings; - - this.resetHtml(); - - // use the prototype options so that people can set them globally at - // mobile init. Consistency, it's what's for dinner - if ( $.type(theme) === "object" ) { - loadSettings = $.extend( {}, this.options, theme ); - - // prefer object property from the param then the old theme setting - theme = loadSettings.theme || $.mobile.loadingMessageTheme; - } else { - loadSettings = this.options; - - // here we prefer the them value passed as a string argument, then - // we prefer the global option because we can't use undefined default - // prototype options, then the prototype option - theme = theme || $.mobile.loadingMessageTheme || loadSettings.theme; - } - - // set the message text, prefer the param, then the settings object - // then loading message - message = msgText || $.mobile.loadingMessage || loadSettings.text; - - // prepare the dom - $html.addClass( "ui-loading" ); - - if ( $.mobile.loadingMessage !== false || loadSettings.html ) { - // boolean values require a bit more work :P, supports object properties - // and old settings - if ( $.mobile.loadingMessageTextVisible !== undefined ) { - textVisible = $.mobile.loadingMessageTextVisible; - } else { - textVisible = loadSettings.textVisible; - } - - // add the proper css given the options (theme, text, etc) - // Force text visibility if the second argument was supplied, or - // if the text was explicitly set in the object args - this.element.attr("class", loaderClass + - " ui-corner-all ui-body-" + theme + - " ui-loader-" + ( textVisible || msgText || theme.text ? "verbose" : "default" ) + - ( loadSettings.textonly || textonly ? " ui-loader-textonly" : "" ) ); - - // TODO verify that jquery.fn.html is ok to use in both cases here - // this might be overly defensive in preventing unknowing xss - // if the html attribute is defined on the loading settings, use that - // otherwise use the fallbacks from above - if ( loadSettings.html ) { - this.element.html( loadSettings.html ); - } else { - this.element.find( "h1" ).text( message ); - } - - // attach the loader to the DOM - this.element.appendTo( $.mobile.pageContainer ); - - // check that the loader is visible - this.checkLoaderPosition(); - - // on scroll check the loader position - $window.bind( "scroll", $.proxy( this.checkLoaderPosition, this ) ); - } - }, - - hide: function() { - $html.removeClass( "ui-loading" ); - - if ( $.mobile.loadingMessage ) { - this.element.removeClass( "ui-loader-fakefix" ); - } - - $.mobile.window.unbind( "scroll", this.fakeFixLoader ); - $.mobile.window.unbind( "scroll", this.checkLoaderPosition ); - } - }); - - $window.bind( 'pagecontainercreate', function() { - $.mobile.loaderWidget = $.mobile.loaderWidget || $( $.mobile.loader.prototype.defaultHtml ).loader(); - }); -})(jQuery, this); - - -// Script: jQuery hashchange event -// -// *Version: 1.3, Last updated: 7/21/2010* -// -// Project Home - http://benalman.com/projects/jquery-hashchange-plugin/ -// GitHub - http://github.com/cowboy/jquery-hashchange/ -// Source - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js -// (Minified) - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (0.8kb gzipped) -// -// About: License -// -// Copyright (c) 2010 "Cowboy" Ben Alman, -// Dual licensed under the MIT and GPL licenses. -// http://benalman.com/about/license/ -// -// About: Examples -// -// These working examples, complete with fully commented code, illustrate a few -// ways in which this plugin can be used. -// -// hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/ -// document.domain - http://benalman.com/code/projects/jquery-hashchange/examples/document_domain/ -// -// About: Support and Testing -// -// Information about what version or versions of jQuery this plugin has been -// tested with, what browsers it has been tested in, and where the unit tests -// reside (so you can test it yourself). -// -// jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2 -// Browsers Tested - Internet Explorer 6-8, Firefox 2-4, Chrome 5-6, Safari 3.2-5, -// Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5. -// Unit Tests - http://benalman.com/code/projects/jquery-hashchange/unit/ -// -// About: Known issues -// -// While this jQuery hashchange event implementation is quite stable and -// robust, there are a few unfortunate browser bugs surrounding expected -// hashchange event-based behaviors, independent of any JavaScript -// window.onhashchange abstraction. See the following examples for more -// information: -// -// Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/ -// Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/ -// WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/ -// Safari: Back Button from a different domain - http://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/ -// -// Also note that should a browser natively support the window.onhashchange -// event, but not report that it does, the fallback polling loop will be used. -// -// About: Release History -// -// 1.3 - (7/21/2010) Reorganized IE6/7 Iframe code to make it more -// "removable" for mobile-only development. Added IE6/7 document.title -// support. Attempted to make Iframe as hidden as possible by using -// techniques from http://www.paciellogroup.com/blog/?p=604. Added -// support for the "shortcut" format $(window).hashchange( fn ) and -// $(window).hashchange() like jQuery provides for built-in events. -// Renamed jQuery.hashchangeDelay to and -// lowered its default value to 50. Added -// and properties plus document-domain.html -// file to address access denied issues when setting document.domain in -// IE6/7. -// 1.2 - (2/11/2010) Fixed a bug where coming back to a page using this plugin -// from a page on another domain would cause an error in Safari 4. Also, -// IE6/7 Iframe is now inserted after the body (this actually works), -// which prevents the page from scrolling when the event is first bound. -// Event can also now be bound before DOM ready, but it won't be usable -// before then in IE6/7. -// 1.1 - (1/21/2010) Incorporated document.documentMode test to fix IE8 bug -// where browser version is incorrectly reported as 8.0, despite -// inclusion of the X-UA-Compatible IE=EmulateIE7 meta tag. -// 1.0 - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special -// window.onhashchange functionality into a separate plugin for users -// who want just the basic event & back button support, without all the -// extra awesomeness that BBQ provides. This plugin will be included as -// part of jQuery BBQ, but also be available separately. - -(function( $, window, undefined ) { - // Reused string. - var str_hashchange = 'hashchange', - - // Method / object references. - doc = document, - fake_onhashchange, - special = $.event.special, - - // Does the browser support window.onhashchange? Note that IE8 running in - // IE7 compatibility mode reports true for 'onhashchange' in window, even - // though the event isn't supported, so also test document.documentMode. - doc_mode = doc.documentMode, - supports_onhashchange = 'on' + str_hashchange in window && ( doc_mode === undefined || doc_mode > 7 ); - - // Get location.hash (or what you'd expect location.hash to be) sans any - // leading #. Thanks for making this necessary, Firefox! - function get_fragment( url ) { - url = url || location.href; - return '#' + url.replace( /^[^#]*#?(.*)$/, '$1' ); - }; - - // Method: jQuery.fn.hashchange - // - // Bind a handler to the window.onhashchange event or trigger all bound - // window.onhashchange event handlers. This behavior is consistent with - // jQuery's built-in event handlers. - // - // Usage: - // - // > jQuery(window).hashchange( [ handler ] ); - // - // Arguments: - // - // handler - (Function) Optional handler to be bound to the hashchange - // event. This is a "shortcut" for the more verbose form: - // jQuery(window).bind( 'hashchange', handler ). If handler is omitted, - // all bound window.onhashchange event handlers will be triggered. This - // is a shortcut for the more verbose - // jQuery(window).trigger( 'hashchange' ). These forms are described in - // the section. - // - // Returns: - // - // (jQuery) The initial jQuery collection of elements. - - // Allow the "shortcut" format $(elem).hashchange( fn ) for binding and - // $(elem).hashchange() for triggering, like jQuery does for built-in events. - $.fn[ str_hashchange ] = function( fn ) { - return fn ? this.bind( str_hashchange, fn ) : this.trigger( str_hashchange ); - }; - - // Property: jQuery.fn.hashchange.delay - // - // The numeric interval (in milliseconds) at which the - // polling loop executes. Defaults to 50. - - // Property: jQuery.fn.hashchange.domain - // - // If you're setting document.domain in your JavaScript, and you want hash - // history to work in IE6/7, not only must this property be set, but you must - // also set document.domain BEFORE jQuery is loaded into the page. This - // property is only applicable if you are supporting IE6/7 (or IE8 operating - // in "IE7 compatibility" mode). - // - // In addition, the property must be set to the - // path of the included "document-domain.html" file, which can be renamed or - // modified if necessary (note that the document.domain specified must be the - // same in both your main JavaScript as well as in this file). - // - // Usage: - // - // jQuery.fn.hashchange.domain = document.domain; - - // Property: jQuery.fn.hashchange.src - // - // If, for some reason, you need to specify an Iframe src file (for example, - // when setting document.domain as in ), you can - // do so using this property. Note that when using this property, history - // won't be recorded in IE6/7 until the Iframe src file loads. This property - // is only applicable if you are supporting IE6/7 (or IE8 operating in "IE7 - // compatibility" mode). - // - // Usage: - // - // jQuery.fn.hashchange.src = 'path/to/file.html'; - - $.fn[ str_hashchange ].delay = 50; - /* - $.fn[ str_hashchange ].domain = null; - $.fn[ str_hashchange ].src = null; - */ - - // Event: hashchange event - // - // Fired when location.hash changes. In browsers that support it, the native - // HTML5 window.onhashchange event is used, otherwise a polling loop is - // initialized, running every milliseconds to - // see if the hash has changed. In IE6/7 (and IE8 operating in "IE7 - // compatibility" mode), a hidden Iframe is created to allow the back button - // and hash-based history to work. - // - // Usage as described in : - // - // > // Bind an event handler. - // > jQuery(window).hashchange( function(e) { - // > var hash = location.hash; - // > ... - // > }); - // > - // > // Manually trigger the event handler. - // > jQuery(window).hashchange(); - // - // A more verbose usage that allows for event namespacing: - // - // > // Bind an event handler. - // > jQuery(window).bind( 'hashchange', function(e) { - // > var hash = location.hash; - // > ... - // > }); - // > - // > // Manually trigger the event handler. - // > jQuery(window).trigger( 'hashchange' ); - // - // Additional Notes: - // - // * The polling loop and Iframe are not created until at least one handler - // is actually bound to the 'hashchange' event. - // * If you need the bound handler(s) to execute immediately, in cases where - // a location.hash exists on page load, via bookmark or page refresh for - // example, use jQuery(window).hashchange() or the more verbose - // jQuery(window).trigger( 'hashchange' ). - // * The event can be bound before DOM ready, but since it won't be usable - // before then in IE6/7 (due to the necessary Iframe), recommended usage is - // to bind it inside a DOM ready handler. - - // Override existing $.event.special.hashchange methods (allowing this plugin - // to be defined after jQuery BBQ in BBQ's source code). - special[ str_hashchange ] = $.extend( special[ str_hashchange ], { - - // Called only when the first 'hashchange' event is bound to window. - setup: function() { - // If window.onhashchange is supported natively, there's nothing to do.. - if ( supports_onhashchange ) { return false; } - - // Otherwise, we need to create our own. And we don't want to call this - // until the user binds to the event, just in case they never do, since it - // will create a polling loop and possibly even a hidden Iframe. - $( fake_onhashchange.start ); - }, - - // Called only when the last 'hashchange' event is unbound from window. - teardown: function() { - // If window.onhashchange is supported natively, there's nothing to do.. - if ( supports_onhashchange ) { return false; } - - // Otherwise, we need to stop ours (if possible). - $( fake_onhashchange.stop ); - } - - }); - - // fake_onhashchange does all the work of triggering the window.onhashchange - // event for browsers that don't natively support it, including creating a - // polling loop to watch for hash changes and in IE 6/7 creating a hidden - // Iframe to enable back and forward. - fake_onhashchange = (function() { - var self = {}, - timeout_id, - - // Remember the initial hash so it doesn't get triggered immediately. - last_hash = get_fragment(), - - fn_retval = function( val ) { return val; }, - history_set = fn_retval, - history_get = fn_retval; - - // Start the polling loop. - self.start = function() { - timeout_id || poll(); - }; - - // Stop the polling loop. - self.stop = function() { - timeout_id && clearTimeout( timeout_id ); - timeout_id = undefined; - }; - - // This polling loop checks every $.fn.hashchange.delay milliseconds to see - // if location.hash has changed, and triggers the 'hashchange' event on - // window when necessary. - function poll() { - var hash = get_fragment(), - history_hash = history_get( last_hash ); - - if ( hash !== last_hash ) { - history_set( last_hash = hash, history_hash ); - - $(window).trigger( str_hashchange ); - - } else if ( history_hash !== last_hash ) { - location.href = location.href.replace( /#.*/, '' ) + history_hash; - } - - timeout_id = setTimeout( poll, $.fn[ str_hashchange ].delay ); - }; - - // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - // vvvvvvvvvvvvvvvvvvv REMOVE IF NOT SUPPORTING IE6/7/8 vvvvvvvvvvvvvvvvvvv - // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - window.attachEvent && !window.addEventListener && !supports_onhashchange && (function() { - // Not only do IE6/7 need the "magical" Iframe treatment, but so does IE8 - // when running in "IE7 compatibility" mode. - - var iframe, - iframe_src; - - // When the event is bound and polling starts in IE 6/7, create a hidden - // Iframe for history handling. - self.start = function() { - if ( !iframe ) { - iframe_src = $.fn[ str_hashchange ].src; - iframe_src = iframe_src && iframe_src + get_fragment(); - - // Create hidden Iframe. Attempt to make Iframe as hidden as possible - // by using techniques from http://www.paciellogroup.com/blog/?p=604. - iframe = $('