__('Loaded Plugins'), 'avail' => __('Available Plugins'), // classic calls 'install' => __('Install'), 'enable' => __('Enable'), 'disable' => __('Disable'), 'uninstall' => __('Uninstall'), 'check' => __('Check Configuration'), 'confirm' => __('Install Prompt Confirmation'), // removed plugin data handling 'remove_data' => __('Remove Plugin Data'), // load order switching 'moveup' => __('Move Up'), 'movedown' => __('Move Down'), // plugin archiving 'archive' => __('Archive'), 'restore' => __('Archive Restore'), 'delete' => __('Archive Delete'), // manage downloaded content 'load' => __('Install from Downloaded Plugins'), 'readme' => __('View the Plugins Readme File'), 'changelog' => __('View the Plugins ChangeLog File'), 'latest' => __('Fetch Latest Plugin Archives'), // remote poller plugin functions 'remote_enable' => __('Remote Enable'), 'remote_disable' => __('Remote Disable'), // drag and drop 'ajax_dnd' => __('Drag and Drop'), ]; $status_names = [ -1 => __('Not Compatible'), -2 => __('Disabled Naming Errors'), -3 => __('Disabled Invalid Directory'), -4 => __('Disabled No INFO File'), -5 => __('Disabled Directory Missing'), 0 => __('Loaded, Not Installed'), 1 => __('Installed/Active'), 2 => __('Configuration Issues'), 3 => __('Awaiting Upgrade'), 4 => __('Installed/Inactive'), 5 => __('Installed or Active'), 6 => __('Installable'), 7 => __('Disabled by Error'), 8 => __('Archived'), ]; // temporary workaround till project finished db_execute("CREATE TABLE IF NOT EXISTS `plugin_available` ( `plugin` varchar(32) NOT NULL DEFAULT '', `description` varchar(128) NOT NULL DEFAULT '', `author` varchar(40) NOT NULL DEFAULT '', `webpage` varchar(128) NOT NULL DEFAULT '', `tag_name` varchar(20) NOT NULL DEFAULT '', `published_at` timestamp NULL DEFAULT NULL, `compat` varchar(20) NOT NULL DEFAULT '', `requires` varchar(128) NOT NULL DEFAULT '', `body` blob DEFAULT NULL, `info` blob DEFAULT NULL, `readme` blob DEFAULT NULL, `changelog` blob DEFAULT NULL, `archive` longblob DEFAULT NULL, `last_updated` timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), PRIMARY KEY (`plugin`,`tag_name`)) ENGINE=InnoDB ROW_FORMAT=DYNAMIC"); db_execute("CREATE TABLE IF NOT EXISTS `plugin_archive` ( `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, `plugin` varchar(32) NOT NULL DEFAULT '', `description` varchar(64) NOT NULL DEFAULT '', `author` varchar(64) NOT NULL DEFAULT '', `webpage` varchar(255) NOT NULL DEFAULT '', `user_id` int(10) unsigned NOT NULL DEFAULT 0, `version` varchar(10) NOT NULL DEFAULT '', `requires` varchar(128) DEFAULT '', `compat` varchar(20) NOT NULL DEFAULT '', `dir_md5sum` varchar(32) NOT NULL DEFAULT '', `last_updated` timestamp NULL DEFAULT NULL, `archive` longblob DEFAULT NULL, PRIMARY KEY (`id`), KEY `directory` (`plugin`)) ENGINE=InnoDB ROW_FORMAT=DYNAMIC"); // get the list of installed plugins $pluginslist = plugins_retrieve_plugin_list(); set_default_action('list'); global $plugins_integrated; /** * this is for legacy support for plugins like syslog * that are dependent on the mode request variable * to be set. */ if (isrv('mode')) { srv('action', gnrv('mode')); if (isrv('id')) { srv('plugin', gnrv('id')); } } $action = gnrv('action'); // pre-check for actions that will fail by default if (isrv('plugin')) { gfrv('plugin', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^([a-zA-Z0-9 _]+)$/']]); $plugin = sanitize_search_string(grv('plugin')); $safe_actions = [ 'changelog', 'readme', 'load', 'install', 'confirm', 'delete', 'ajax_dnd', 'remove_data' ]; $display_action = ucwords(str_replace('_', ' ', $action)); if (!in_array($plugin, $pluginslist, true) && !in_array($action, $safe_actions, true)) { raise_message('invalid_plugin', __('The action \'%s\' on Plugin \'%s\' can not be performed due to the Plugin in it\'s current state.', $display_action, $plugin), MESSAGE_LEVEL_ERROR); header('Location: plugins.php'); exit; } if (in_array($plugin, $plugins_integrated, true)) { raise_message('invalid_plugin_action', __('The action \'%s\' \'%s\' on Plugin \'%s\' can not be taken as the Plugin is integrated.', $display_action, $plugin), MESSAGE_LEVEL_ERROR); header('Location: plugins.php'); exit; } } else { $plugin = ''; } switch($action) { case 'list': case 'avail': top_header(); update_show_current(); bottom_footer(); break; case 'load': $tag = gnrv('tag'); api_plugin_archive_restore($plugin, $tag, 'available'); header('Location: plugins.php'); exit; case 'readme': $tag = gnrv('tag'); api_plugin_get_available_file_contents($plugin, $tag, 'readme'); break; case 'changelog': $tag = gnrv('tag'); api_plugin_get_available_file_contents($plugin, $tag, 'changelog'); break; case 'latest': if (read_config_option('github_access_token') != '') { $running = is_process_running('pfetch', 'master', 0); if ($running === false) { $php_binary = read_config_option('path_php_binary'); exec_background($php_binary, CACTI_PATH_CLI . '/fetch_plugins.php'); usleep(300000); raise_message('fetch_background', __('The fetch latest plugins process has been launched into background.'), MESSAGE_LEVEL_INFO); } elseif ($running === true) { raise_message('fetch_background', __('The fetch latest plugins process has already been started.'), MESSAGE_LEVEL_WARN); } header('Location: plugins.php'); } else { raise_message('get_latest1', __('You must enter your GitHub user, repo and personal access token before you can refresh the plugins. You can set the GitHub defaults under Console > Configuration > Settings > General.'), MESSAGE_LEVEL_ERROR); header('Location: plugins.php'); } break; case 'install': api_plugin_install($plugin); define('IN_PLUGIN_INSTALL', 1); if ($_SESSION['sess_plugins_state'] >= 0) { header('Location: plugins.php?state=5'); } else { header('Location: plugins.php'); } break; case 'uninstall': define('IN_PLUGIN_INSTALL', 1); api_plugin_uninstall($plugin); header('Location: plugins.php'); break; case 'remove_data': api_plugin_remove_data($plugin); header('Location: plugins.php'); break; case 'disable': api_plugin_disable($plugin); header('Location: plugins.php'); break; case 'enable': api_plugin_enable($plugin); header('Location: plugins.php'); break; case 'check': $response = api_plugin_check_config($plugin); if ($response === true) { // set the status as installable again if check passes db_execute_prepared('UPDATE plugin_config SET status = 0 WHERE directory = ?', [$plugin]); raise_message('plugin_good', __('Plugin \'%s\' has passed it\'s Configuration Check test and can not be Installed', $plugin), MESSAGE_LEVEL_INFO); } elseif ($response === null) { raise_message('plugin_good', __('Plugin \'%s\' Check Configuration function returned a null response which is invalid. Please check with Plugin Developer for an update.', $plugin), MESSAGE_LEVEL_WARN); } header('Location: plugins.php'); break; case 'moveup': api_plugin_moveup($plugin); header('Location: plugins.php'); break; case 'movedown': api_plugin_movedown($plugin); header('Location: plugins.php'); break; case 'remote_enable': if (POLLER_ID > 1) { db_execute_prepared('UPDATE plugin_config SET status = 1 WHERE directory = ?', [$plugin], false, $local_db_cnn_id); } header('Location: plugins.php'); break; case 'remote_disable': if (POLLER_ID > 1) { db_execute_prepared('UPDATE plugin_config SET status = 4 WHERE directory = ?', [$plugin], false, $local_db_cnn_id); } header('Location: plugins.php'); break; case 'restore': $id = gfrv('id'); api_plugin_archive_restore($plugin, $id, 'archive'); header('Location: plugins.php'); break; case 'delete': $id = gfrv('id'); api_plugin_archive_remove($plugin, $id); header('Location: plugins.php'); break; case 'archive': $archive_note = gnrv('archive_note'); api_plugin_archive($plugin, $archive_note); header('Location: plugins.php'); break; case 'ajax_dnd': $new_order = gnrv('dnd'); api_plugin_reorder($new_order); header('Location: plugins.php'); break; } exit; function plugins_retrieve_plugin_list() : array { $pluginslist = []; $temp = db_fetch_assoc('SELECT directory AS plugin FROM plugin_config ORDER BY name'); foreach ($temp as $t) { $pluginslist[] = $t['plugin']; } return $pluginslist; } function plugins_temp_table_exists(string $table) : mixed { return cacti_sizeof(db_fetch_row("SHOW TABLES LIKE '$table'")); } function plugins_load_temp_table() : string { global $plugins, $plugins_integrated, $local_db_cnn_id; $table = 'plugin_temp_table_' . rand(); $x = 0; while ($x < 30) { if (!plugins_temp_table_exists($table)) { $_SESSION['plugin_temp_table'] = $table; db_execute("CREATE TEMPORARY TABLE IF NOT EXISTS $table LIKE plugin_config"); db_execute("TRUNCATE $table"); db_execute("INSERT INTO $table SELECT * FROM plugin_config"); break; } else { $table = 'plugin_temp_table_' . rand(); } $x++; } if (!db_column_exists($table, 'requires')) { db_execute("ALTER TABLE $table CHANGE COLUMN directory plugin varchar(32) NOT NULL default '', CHANGE COLUMN name description varchar(64) NOT NULL default '', ADD COLUMN compat varchar(64) NOT NULL default '', ADD COLUMN remote_status tinyint(2) DEFAULT '0' AFTER status, ADD COLUMN capabilities varchar(128) DEFAULT NULL, ADD COLUMN requires varchar(80) DEFAULT NULL, ADD COLUMN dir_md5sum varchar(32) DEFAULT NULL"); } if (POLLER_ID > 1) { $status = db_fetch_assoc('SELECT directory AS plugin, status FROM plugin_config', false, $local_db_cnn_id); if (cacti_sizeof($status)) { foreach ($status as $r) { $exists = db_fetch_cell_prepared("SELECT id FROM $table WHERE plugin = ?", [$r['plugin']]); if ($exists) { $capabilities = api_plugin_remote_capabilities($r['plugin']); db_execute_prepared("UPDATE $table SET capabilities = ? WHERE plugin = ?", [$capabilities, $r['plugin']]); db_execute_prepared("UPDATE $table SET remote_status = ? WHERE plugin = ?", [$r['status'], $r['plugin']]); } else { db_execute_prepared("UPDATE $table SET status = -2, remote_status = ? WHERE plugin = ?", [$r['status'], $r['plugin']]); } } } } $path = CACTI_PATH_PLUGINS . '/'; $dh = opendir($path); $cinfo = []; if ($dh !== false) { while (($file = readdir($dh)) !== false) { if (is_dir("$path$file") && file_exists("$path$file/setup.php") && !in_array($file, $plugins_integrated, true)) { $info_file = "$path$file/INFO"; $md5sum = md5sum_path("$path$file"); if (file_exists($info_file)) { $cinfo[$file] = plugin_load_info_file($info_file); $pluginslist[] = $file; } else { $cinfo[$file] = plugin_load_info_defaults($info_file, false); } $exists = db_fetch_cell_prepared("SELECT COUNT(*) FROM $table WHERE plugin = ?", [$file]); $plugin_name = $cinfo[$file]['name']; if (!$exists) { db_execute_prepared("INSERT INTO $table (plugin, description, status, author, webpage, version, requires, compat, dir_md5sum) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", [ $plugin_name, $cinfo[$file]['longname'], $cinfo[$file]['status'], $cinfo[$file]['author'], $cinfo[$file]['homepage'], $cinfo[$file]['version'], $cinfo[$file]['requires'], $cinfo[$file]['compat'], $md5sum ] ); } else { db_execute_prepared("UPDATE $table SET requires = ?, dir_md5sum = ?, compat = ? WHERE plugin = ?", [$cinfo[$file]['requires'], $md5sum, $cinfo[$file]['compat'], $plugin_name]); } } } closedir($dh); } $found_plugins = array_keys($cinfo); $plugins = db_fetch_assoc('SELECT id, directory AS plugin, status FROM plugin_config'); if (cacti_sizeof($plugins)) { foreach ($plugins as $plugin) { if (!in_array($plugin['plugin'], $found_plugins, true)) { $plugin['status'] = '-5'; $exists = db_fetch_cell_prepared("SELECT COUNT(*) FROM $table WHERE plugin = ?", [$plugin['plugin']]); if (!$exists) { $md5sum = md5sum_path($path); db_execute_prepared("INSERT INTO $table (plugin, description, status, author, webpage, version, requires, dir_md5sum) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", [ $plugin['plugin'], $plugin['longname'], $plugin['status'], $plugin['author'], $plugin['homepage'], $plugin['version'], $plugin['requires'], $md5sum ] ); } else { $md5sum = md5sum_path($path); db_execute_prepared("UPDATE $table SET status = ?, dir_md5sum = ? WHERE plugin = ?", [$plugin['status'], $md5sum, $plugin['plugin']]); } } } } return $table; } function update_show_current() : void { global $plugins, $pluginslist, $status_names, $actions, $item_rows; // ================= input validation and session storage ================= $filters = [ 'rows' => [ 'filter' => FILTER_VALIDATE_INT, 'pageset' => true, 'default' => '-1' ], 'page' => [ 'filter' => FILTER_VALIDATE_INT, 'default' => '1' ], 'type' => [ 'filter' => FILTER_VALIDATE_INT, 'pageset' => true, 'default' => '-1' ], 'filter' => [ 'filter' => FILTER_DEFAULT, 'pageset' => true, 'default' => '' ], 'sort_column' => [ 'filter' => FILTER_CALLBACK, 'default' => 'pi.plugin', 'options' => ['options' => 'sanitize_search_string'] ], 'sort_direction' => [ 'filter' => FILTER_CALLBACK, 'default' => 'ASC', 'options' => ['options' => 'sanitize_search_string'] ], 'state' => [ 'filter' => FILTER_VALIDATE_INT, 'pageset' => true, 'default' => '-99' ] ]; validate_store_request_vars($filters, 'sess_plugins'); // ================= input validation ================= $table = plugins_load_temp_table(); $uninstall_msg = __esc('Uninstalling this Plugin and may remove all Plugin Data and Settings. If you really want to Uninstall the Plugin, click \'Uninstall\' below. Otherwise click \'Cancel\'.'); $uninstall_title = __esc('Are you sure you want to Uninstall?'); $rmdata_msg = __esc('Removing Plugin Data and Settings for will remove all Plugin Data and Settings. If you really want to Remove Data and Settings for this Plugin, click \'Remove Data\' below. Otherwise click \'Cancel\'.'); $rmdata_title = __esc('Are you sure you want to Remove all Plugin Data and Settings?'); $resarchive_msg = __esc('Restoring this Plugin Archive will overwrite the current Plugin directory. If you really want to Restore this Plugin Archive, click \'Restore\' below. Otherwise click \'Cancel\'.'); $resarchive_title = __esc('Are you sure you want to Restore this Archive?'); $rmarchive_msg = __esc('Deleting this Plugin Archive is not reversible without a table restore. If you really want to Delete the Plugin Archive, click \'Delete\' below. Otherwise click \'Cancel\'.'); $rmarchive_title = __esc('Are you sure you want to Delete this Archive?'); $archive_msg = __esc('Archiving makes a backup of the plugin that you may restore at a later date. Before archiving, you may enter an archive note that will be stored with the Archive for later reference. If you really want to Archive the Plugin, click \'Delete\' below. Otherwise click \'Cancel\'.'); $archive_form = '

 

'; $archive_title = __esc('Are you sure you want to Archive this Plugin?'); html_filter_start_box(__('Plugin Management'), '100%', false, true, 'center', ''); ?>
'>
[ 'display' => __('Actions'), 'align' => 'left', 'sort' => '', 'tip' => __('Actions available include \'Restore\', \'Delete\'.') ], 'pa.plugin' => [ 'display' => __('Plugin Name'), 'align' => 'left', 'sort' => 'ASC', 'tip' => __('The name for this Plugin. The name is controlled by the directory it resides in.') ], 'pi.description' => [ 'display' => __('Plugin Description'), 'align' => 'left', 'sort' => 'ASC', 'tip' => __('A description that the Plugins author has given to the Plugin.') ], 'nosort01' => [ 'display' => __('Archive Notes'), 'align' => 'left', 'tip' => __('Hover over the Notes column to see the Archive notes.') ], 'pi.status' => [ 'display' => POLLER_ID == 1 ? __('Status') : __('Main / Remote Status'), 'align' => 'left', 'sort' => 'ASC', 'tip' => __('The Status of this available Plugin. Loadable means it is currently not installed and can be loaded.') ], 'pi.author' => [ 'display' => __('Author'), 'align' => 'left', 'sort' => 'ASC', 'tip' => __('The author of this Plugin.') ], 'pa.compat' => [ 'display' => __('Cacti'), 'align' => 'left', 'sort' => 'ASC', 'tip' => __('The Cacti version ranges required to use this Plugin.') ], 'nosort02' => [ 'display' => __('Versions'), 'align' => 'right', 'sort' => 'ASC', 'tip' => __('The Installed version over the Archived version of the Plugin.') ], 'pa.archive_length' => [ 'display' => __('Size'), 'align' => 'right', 'sort' => 'ASC', 'tip' => __('The compressed size of this Plugin in bytes.') ], 'requires' => [ 'display' => __('Requires'), 'align' => 'right', 'sort' => 'ASC', 'tip' => __('This Plugin requires the following Plugins be installed first.') ], 'pa.last_updated' => [ 'display' => __('Archive Date'), 'align' => 'right', 'sort' => 'DESC', 'tip' => __('The date that this Plugin was Archived.') ], 'pi.last_updated' => [ 'display' => __('Installed/Upgraded'), 'align' => 'right', 'sort' => 'DESC', 'tip' => __('The date that this Plugin was last Installed or Upgraded.') ], ]; break; case 6: $display_text = [ 'nosort0' => [ 'display' => __('Actions'), 'align' => 'left', 'sort' => '', 'tip' => __('Actions available include \'Install\', \'Activate\', \'Disable\', \'Enable\', \'Uninstall\'.') ], 'pi.plugin' => [ 'display' => __('Plugin Name'), 'align' => 'left', 'sort' => 'ASC', 'tip' => __('The name for this Plugin. The name is controlled by the directory it resides in.') ], 'pi.description' => [ 'display' => __('Plugin Description'), 'align' => 'left', 'sort' => 'ASC', 'tip' => __('A description that the Plugins author has given to the Plugin.') ], 'status' => [ 'display' => POLLER_ID == 1 ? __('Status') : __('Main / Remote Status'), 'align' => 'left', 'sort' => 'ASC', 'tip' => __('The status of this Plugin.') ], 'author' => [ 'display' => __('Author'), 'align' => 'left', 'sort' => 'ASC', 'tip' => __('The author of this Plugin.') ], 'nosort1' => [ 'display' => __('Cacti Releases'), 'align' => 'left', 'sort' => 'ASC', 'tip' => __('The Cacti Releases that are eligible to use this Plugin. The format of the allowed versions follows common naming.') ], 'pi.version' => [ 'display' => __('Installed Version'), 'align' => 'right', 'sort' => 'ASC', 'tip' => __('The currently installed version of this Plugin.') ], 'nosort2' => [ 'display' => __('Version'), 'align' => 'right', 'sort' => 'ASC', 'tip' => __('The Available version for install for this Plugin.') ], 'pa.archive_length' => [ 'display' => __('Size'), 'align' => 'right', 'sort' => 'ASC', 'tip' => __('The compressed size of this Plugin in bytes.') ], 'nosort3' => [ 'display' => __('Requires'), 'align' => 'right', 'sort' => 'ASC', 'tip' => __('This Plugin requires the following Plugins be installed first.') ], 'pa.published_at' => [ 'display' => __('Last Published'), 'align' => 'right', 'sort' => 'DESC', 'tip' => __('The date the release was published or develop was last pushed.') ], 'pi.last_updated' => [ 'display' => __('Installed/Upgraded'), 'align' => 'right', 'sort' => 'DESC', 'tip' => __('The date that this Plugin was last installed or upgraded.') ], ]; break; default: $display_text = [ 'nosort' => [ 'display' => __('Actions'), 'align' => 'left', 'sort' => '', 'tip' => __('Actions available include \'Install\', \'Activate\', \'Disable\', \'Enable\', \'Uninstall\'.') ], 'pi.plugin' => [ 'display' => __('Plugin Name'), 'align' => 'left', 'sort' => 'ASC', 'tip' => __('The name for this Plugin. The name is controlled by the directory it resides in.') ], 'pi.description' => [ 'display' => __('Plugin Description'), 'align' => 'left', 'sort' => 'ASC', 'tip' => __('A description that the Plugins author has given to the Plugin.') ], 'pi.status' => [ 'display' => POLLER_ID == 1 ? __('Status') : __('Main / Remote Status'), 'align' => 'left', 'sort' => 'ASC', 'tip' => __('The Status of this available Plugin. Loadable means it is currently not installed and can be loaded.') ], 'pi.author' => [ 'display' => __('Author'), 'align' => 'left', 'sort' => 'ASC', 'tip' => __('The author of this Plugin.') ], 'pa.compat' => [ 'display' => __('Cacti'), 'align' => 'left', 'sort' => 'ASC', 'tip' => __('The Cacti version ranges required to use this Plugin.') ], 'pi.requires' => [ 'display' => __('Plugin Requires'), 'align' => 'left', 'sort' => 'ASC', 'tip' => __('This Plugin requires the following Plugins be installed first.') ], 'pi.version' => [ 'display' => __('Version'), 'align' => 'right', 'sort' => 'ASC', 'tip' => __('The version of this Plugin.') ], 'pi.last_updated' => [ 'display' => __('Installed/Upgraded'), 'align' => 'right', 'sort' => 'ASC', 'tip' => __('The date that this Plugin was last installed or upgraded.') ], 'pi.id' => [ 'display' => __('Load Order'), 'align' => 'right', 'sort' => 'ASC', 'tip' => __('The load order of the Plugin. You can change the load order by first sorting by it, then moving a Plugin either up or down.') ] ]; break; } html_header_sort($display_text, grv('sort_column'), grv('sort_direction'), 1); $i = 0; if (cacti_sizeof($plugins)) { $j = 0; foreach ($plugins as $plugin) { if ((isset($plugins[$j + 1]) && $plugins[$j + 1]['status'] < 0) || (!isset($plugins[$j + 1]))) { $last_plugin = true; } else { $last_plugin = false; } if ($plugin['status'] <= 0 || (grv('sort_column') != 'pi.id')) { $load_ordering = false; } else { $load_ordering = true; } print ""; switch(grv('state')) { case 8: print format_archive_plugin_row($plugin, $table); break; case 6: print format_available_plugin_row($plugin, $table); break; default: print format_plugin_row($plugin, $last_plugin, $load_ordering, $table); break; } form_end_row(); $i++; $j++; } } else { print '' . __('No Plugins Found') . ''; } html_end_box(false); if (cacti_sizeof($plugins)) { print $nav; } form_end(); db_execute("DROP TABLE $table"); } function format_plugin_row(array $plugin, bool $last_plugin, bool $include_ordering, string $table) : string { global $status_names; static $first_plugin = true; static $row_id = 1; $row = plugin_actions($plugin, $table); $uname = cacti_strtoupper($plugin['plugin']); if ($uname == $plugin['plugin']) { $plugin_name = $uname; } else { $plugin_name = ucfirst($plugin['plugin']); } $row .= "" . filter_value($plugin_name, grv('filter')) . ''; $row .= "" . filter_value($plugin['description'], grv('filter')) . ''; if ($plugin['status'] == '-1') { $status = plugin_is_compatible($plugin['plugin']); $row .= "" . __('Not Compatible, \'%s\'', $status['requires']); } elseif ($plugin['status'] < -1) { $row .= "" . __('Plugin Error'); } else { $row .= "" . $status_names[$plugin['status']]; } if ($plugin['last_updated'] != '0000-00-00 00:00:00') { if (read_config_option('github_allow_unsafe') == 'on') { $newer = db_fetch_cell_prepared('SELECT COUNT(*) FROM plugin_available WHERE plugin = ? AND published_at > ?', [$plugin['plugin'], $plugin['last_updated']]); } else { $newer = db_fetch_cell_prepared('SELECT COUNT(*) FROM plugin_available WHERE plugin = ? AND last_updated > ? AND tag_name != "develop"', [$plugin['plugin'], $plugin['last_updated']]); } } else { $newer = 0; } if ($newer > 0) { $row .= ", " . __('New Version') . ''; } if (POLLER_ID > 1) { if (isset($plugin['capabilities']) && (str_contains($plugin['capabilities'], 'remote_collect:1') || str_contains($plugin['capabilities'], 'remote_poller:1'))) { if ($plugin['remote_status'] == '-1') { $status = plugin_is_compatible($plugin['plugin']); $row .= ' / ' . __('Not Compatible, \'%s\'', $status['requires']); } elseif ($plugin['remote_status'] < -1) { $row .= ' / ' . __('Plugin Error'); } else { $row .= ' / ' . $status_names[$plugin['remote_status']]; } } else { $row .= ' / ' . __('N/A'); } } $row .= ''; if ($plugin['requires'] != '') { $requires = explode(' ', $plugin['requires']); foreach ($requires as $r) { $nr[] = ucfirst($r); } $requires = implode(', ', $nr); } else { $requires = $plugin['requires']; } if ($plugin['last_updated'] == '0000-00-00 00:00:00') { $last_updated = __('N/A'); } elseif (isset($plugin['last_updated'])) { $last_updated = substr($plugin['last_updated'], 0, 16); } else { $last_updated = __('N/A'); } $plugin['compat'] = plugin_display_compat($plugin['compat']); $row .= "" . filter_value($plugin['author'], grv('filter')) . ''; $row .= "" . htmle($plugin['compat']) . ''; $row .= "" . htmle($requires) . ''; $row .= "" . htmle($plugin['version']) . ''; $row .= "" . $last_updated . ''; if ($include_ordering) { $row .= ""; if (!$first_plugin) { $row .= ""; } else { $row .= ''; } if ($last_plugin === false) { $row .= ""; } else { $row .= ''; } $row .= ''; } else { $row .= ''; } if ($include_ordering) { $first_plugin = false; } $row_id++; return $row; } function plugin_check_available_status(array $plugin, string &$row) : void { if (cacti_version_compare(CACTI_VERSION, $plugin['avail_compat'], '<')) { $row .= "" . __('Cacti Upgrade Required') . ''; } else { $row .= "" . __('Compatible') . ''; } } function format_available_plugin_row(array $plugin, string $table) : string { global $status_names; // action icons $row = ""; // remove leading 'v' off tag names for compares $avail_version = ltrim($plugin['avail_tag_name'], 'v'); if (plugin_valid_version_range($plugin['avail_compat'])) { if (plugin_valid_dependencies($plugin['avail_requires'])) { if ($plugin['version'] == '') { if ($avail_version != 'develop') { $row .= ""; } else { $row .= ""; } $status = __('Compatible, Loadable'); } elseif ($avail_version == 'develop') { $row .= ""; $status = __('Compatible, Upgrade'); } elseif (cacti_version_compare($avail_version, $plugin['version'], '<')) { $row .= ""; $status = __('Compatible, Downgrade'); } elseif (cacti_version_compare($avail_version, $plugin['version'], '=')) { $row .= ""; $status = __('Compatible, Same Version'); } else { $row .= ""; $status = __('Compatible, Upgrade'); } } else { $row .= ""; $status = __('Not Compatible, Dependencies'); } } else { $row .= ""; $status = __('Not Compatible, Cacti Version'); } $row .= ""; // no link to the changelog unless it exists if ($plugin['changelog'] > 0) { $row .= ""; } $row .= ''; $uname = cacti_strtoupper($plugin['plugin']); if ($uname == $plugin['plugin']) { $plugin_name = $uname; } else { $plugin_name = ucfirst($plugin['plugin']); } $row .= "" . filter_value($plugin_name, grv('filter')) . ''; $row .= "" . filter_value($plugin['avail_description'], grv('filter')) . ''; $row .= "" . $status . ''; if ($plugin['avail_requires'] != '') { $requires = explode(' ', $plugin['avail_requires']); foreach ($requires as $r) { $nr[] = ucfirst($r); } $requires = implode(', ', $nr); } else { $requires = $plugin['avail_requires']; } $plugin['avail_compat'] = plugin_display_compat($plugin['avail_compat']); $row .= "" . filter_value($plugin['avail_author'], grv('filter')) . ''; $row .= "" . htmle($plugin['avail_compat']) . ''; if ($plugin['version'] == '') { $row .= "" . __esc('Not Loaded') . ''; } else { $row .= "" . htmle($plugin['version']) . ''; } if ($plugin['last_updated'] == '0000-00-00 00:00:00' || $plugin['last_updated'] == '') { $last_updated = __('N/A'); } else { $last_updated = substr($plugin['last_updated'], 0, 16); } if ($plugin['avail_tag_name'] !== 'develop') { $tag_version = str_replace('v', '', $plugin['avail_tag_name']); } else { $tag_version = $plugin['avail_tag_name']; } $row .= "" . htmle($tag_version) . ''; $size = $plugin['archive_length']; $suffix = ''; if ($size > 1024) { $suffix = ' KB'; $size /= 1024; } if ($size > 1024) { $suffix = ' MB'; $size /= 1024; } $row .= "" . number_format_i18n($size, 1) . $suffix . ''; $row .= "" . htmle($requires) . ''; $row .= "" . substr($plugin['avail_published'], 0, 16) . ''; $row .= "" . $last_updated . ''; return $row; } function format_archive_plugin_row(array $plugin, string $table) : string { global $status_names; static $first_plugin = true; // action icons $row = ""; if (plugin_valid_version_range($plugin['archive_compat'])) { if (plugin_valid_dependencies($plugin['archive_requires'])) { if ($plugin['version'] == '') { $row .= ""; $status = __('Compatible, Loadable'); } elseif (cacti_version_compare($plugin['archive_version'], $plugin['version'], '<')) { $row .= ""; $status = __('Compatible, Downgrade'); } elseif (cacti_version_compare($plugin['archive_version'], $plugin['version'], '=')) { $row .= ""; $status = __('Compatible, Same Version'); } else { $row .= ""; $status = __('Compatible, Upgradable'); } } else { $row .= ""; $status = __('Not Compatible, Dependencies'); } } else { $row .= ""; $status = __('Not Compatible, Cacti Version'); } $row .= ""; $row .= ''; $uname = cacti_strtoupper($plugin['plugin']); if ($uname == $plugin['plugin']) { $plugin_name = $uname; } else { $plugin_name = ucfirst($plugin['plugin']); } $row .= "" . filter_value($plugin_name, grv('filter')) . ''; $row .= "" . filter_value($plugin['description'], grv('filter')) . ''; if ($plugin['archive_note'] != '') { $row .= "" . __esc('Notes') . ''; } else { $row .= "-"; } $row .= "" . $status . ''; if ($plugin['archive_requires'] != '') { $requires = explode(' ', $plugin['archive_requires']); foreach ($requires as $r) { $nr[] = ucfirst($r); } $requires = implode(', ', $nr); } else { $requires = $plugin['archive_requires']; } $plugin['archive_compat'] = plugin_display_compat($plugin['archive_compat']); $row .= "" . filter_value($plugin['author'], grv('filter')) . ''; $row .= "" . htmle($plugin['archive_compat']) . ''; if ($plugin['version'] == '') { $row .= "" . __esc('Not Installed') . ' / ' . htmle($plugin['archive_version']) . ''; } else { $row .= "" . htmle($plugin['version']) . ' / ' . htmle($plugin['archive_version']) . ''; } $size = $plugin['archive_length']; $suffix = ''; if ($size > 1024) { $suffix = ' KB'; $size /= 1024; } if ($size > 1024) { $suffix = ' MB'; $size /= 1024; } $row .= "" . number_format_i18n($size, 1) . $suffix . ''; $row .= "" . htmle($plugin['archive_requires']) . ''; if ($plugin['last_updated'] == '0000-00-00 00:00:00') { $last_updated = __('N/A'); } elseif (!empty($plugin['last_updated'])) { $last_updated = substr($plugin['last_updated'], 0, 16); } else { $last_updated = __('N/A'); } $archive_date = substr($plugin['archive_date'], 0, 16); $row .= "" . $archive_date . ''; $row .= "" . $last_updated . ''; return $row; } function plugin_required_for_others(array $plugin, string $table) : mixed { $required_for_others = db_fetch_cell("SELECT GROUP_CONCAT(plugin) FROM $table WHERE requires LIKE '%" . $plugin['plugin'] . "%' AND status IN (1,4,7)"); if ($required_for_others) { $parts = explode(',', $required_for_others); foreach ($parts as $p) { $np[] = ucfirst($p); } return implode(', ', $np); } else { return false; } } function plugin_required_installed(array $plugin, string $table) : string { $not_installed = ''; api_plugin_can_install($plugin['plugin'], $not_installed); return $not_installed; } function plugin_display_compat(string $compat) : string { $compat = explode(' ', $compat); foreach ($compat as $index => $c) { if (str_contains($c, '>=')) { $compat[$index] = str_replace('>=', '>= ', $c); } elseif (str_contains($c, '<=')) { $compat[$index] = str_replace('<=', '<= ', $c); } elseif (str_contains($c, '>')) { $compat[$index] = str_replace('>', '> ', $c); } elseif (str_contains($c, '<')) { $compat[$index] = str_replace('<', '< ', $c); } else { $compat[$index] = '>= ' . $c; } } return implode(' ', $compat); } function plugin_get_install_links(array $plugin, string $table) : string { $path = CACTI_PATH_PLUGINS . '/' . $plugin['plugin']; $link = ''; if ($plugin['status'] == 0) { if (!file_exists("$path/setup.php")) { $link .= ""; } elseif (!file_exists("$path/INFO")) { $link .= ""; } else { $not_installed = plugin_required_installed($plugin, $table); if ($not_installed != '') { $link .= ""; } else { $link .= ""; } $link .= ""; } $link .= ""; $setup_file = CACTI_PATH_BASE . '/plugins/' . $plugin['plugin'] . '/setup.php'; if (file_exists($setup_file)) { require_once($setup_file); $has_data_function = "plugin_{$plugin['plugin']}_has_data"; $rm_data_function = "plugin_{$plugin['plugin']}_remove_data"; if (function_exists($has_data_function) && function_exists($rm_data_function) && $has_data_function()) { $link .= ""; } } } return $link; } function plugin_actions(array $plugin, string $table) : string { global $pluginslist, $plugins_integrated; $link = ''; $archived = db_fetch_cell_prepared('SELECT COUNT(*) FROM plugin_archive WHERE plugin = ? AND dir_md5sum = ?', [$plugin['plugin'], $plugin['dir_md5sum']]); switch ($plugin['status']) { case '0': // Not Installed $link .= plugin_get_install_links($plugin, $table); break; case '1': // Currently Active $required = plugin_required_for_others($plugin, $table); if ($required != '') { $link .= ""; } else { $link .= ""; } $link .= ""; if ($archived) { $link .= ""; } else { $link .= ""; } break; case '2': // Configuration issues $link .= ""; $link .= ""; $link .= ""; break; case '4': // Installed but not active $required = plugin_required_for_others($plugin, $table); if ($required != '') { $link .= ""; } else { $link .= ""; } $link .= ""; if ($archived) { $link .= ""; } else { $link .= ""; } break; case '7': // Installed but errored $required = plugin_required_for_others($plugin, $table); if ($required != '') { $link .= ""; } else { $link .= ""; } $link .= ""; if ($archived) { $link .= ""; } else { $link .= ""; } break; case '-5': // Plugin directory missing $link .= ""; break; case '-4': // Plugins should have INFO file since 1.0.0 $link .= ""; break; case '-3': // Plugins can have spaces in their names $link .= ""; break; case '-2': // Naming issues $link .= ""; break; default: // Old PIA $path = CACTI_PATH_PLUGINS . '/' . $plugin['plugin']; if (!file_exists("$path/setup.php")) { $link .= ""; } elseif (!file_exists("$path/INFO")) { $link .= ""; } elseif (in_array($plugin['plugin'], $plugins_integrated, true)) { $link .= ""; } else { $link .= ""; } break; } if (POLLER_ID > 1) { if (isset($plugin['capabilities']) && (str_contains($plugin['capabilities'], 'remote_collect:1') || str_contains($plugin['capabilities'], 'remote_poller:1'))) { if ($plugin['remote_status'] == 1) { // Installed and Active // ToDo: Disabling here does not make much sense as the main will be replicated // with any change of any other plugin thus undoing. Fix that moving forward // $link .= ""; } elseif ($plugin['remote_status'] == 4) { // Installed but inactive if ($plugin['status'] == 1) { $link .= ""; } } } } $link .= ''; return $link; }