Consolidate configuration settings and implement dynamic webui settings (#9809)

* initial db/defaults dump

* numeric values

* Remove $config['time']

* Use config_definitions.json

* try setting definition as a complete array

* a little more

* fix format

* WIP converting dynamic setting pages

* rewriting the webui

* remove legacy and add translations

* finish email section

* improve navigation
update js a bit

* Import the remaining existing settings

* Update backend still some wip
migration

* fix config loading (db not overriding defaults)

* some cleanup

* more array collapsing

* update settings

* Settings search

* add purge settings to ui, order groups and sections

* collapse more arrays

* Auth definitions WIP

* remove needless slash escapes

* add warning to json

* moving settings from defaults.inc

* remove slow_statistics

* Move rrdtool settings to the poller section

* Use translations more consistently

* removing more settings from defaults.inc.php

* show setting name in tooltip

* allow help text to be shown on mobile

* disable settings that are set in config.php

* Implement undo and reset to default.

* Vue.js functional

* Vue.js WIP

* Implement tabs component

* accordion WIP

* lodash ver update

* two items

* Accordion somewhat working

* hash navigation

* Refine Accordion

* Fix up tab styling a bit

* Cleaner tab selected property
Hide html while loading

* spinner?

* Icon support
property for accordion to active

* WIP

* Tabs/Sections from ajax

* Setting Component skeletons

* Dynamic Component resolution

* Basic functionality

* toggle class

* Refactor components

* translate tabs and accordions

* simple array attempt

* improve readonly tooltip

* array styling

* array value editing

* organize snmp info

* Handle initial tab/section in url

* Use Laravel to parse tab/section, dump old

* Draggable array entries

* v-tooltip, for clickable (and touch) help tooltips
disable draggable

* Navigation WIP

* Navigation WIP

* groups computed

* filter settings

* fix event reference

* vue.js i18n initial

* missing description = just setting name

* en fallback

* tidy up the language support and js generation

* persist value to db

* fix issue with 0

* Delete settings from DB instead of setting them to default

* ldap-groups
fixup style

* Default dashboard selection

* fix array of options instead of object

* allow custom validation for settings

* translate options in SettingSelect

* SNMP v3 WIP

* fix setting arrays

* Split persist out of set

* Hook up events for SNMP v3 Auth
correct Config::persist behaviour with arrays

* dependent settings (primitive for now)
actually update the settings values in the LibrenmsSettings component

* more complex "when" behaviour

* remove un-needed seeder

* add poller_modules.cisco-qfp

* remove en.json (disable warning)

* don't set default for log_dir or log_file, otherwise it won't be processed correctly

* Fix module order
add some missing settings

* more config corrections

* correct graphs
correct loading values set to null (although it should have no difference)
remove project_name_version

* Add nfsen settings.  Docs are very confusing, so might have flubbed something
remove option for array definition of select option as numeric indexes messes it up

* Correct more upstream config differences

* Config cleanup after a bunch of merges.

* Fixes

* add version tags to js/css files
remove old js

* Print out full settings list read-only

* Add http_proxy setting
fix indents in config_definitions.json

* repeaters default is 0 (aka 20)

* cleanups

* rewrite the dynamic config docs

* add language docs

* Don't show snmp v3 auth add/remove if disabled by config.php
This commit is contained in:
Tony Murray 2019-10-16 21:22:05 +00:00 committed by GitHub
parent 750b19f3e8
commit 699aa8a042
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
80 changed files with 77045 additions and 2422 deletions

2
.gitignore vendored
View File

@ -35,6 +35,8 @@ composer.phar
_ide_helper.php
_ide_helper_models.php
resources/views/menu/custom.blade.php
resources/js/vue-i18n-locales.generated.js
resources/js/lang/*
# Docs #
########

View File

@ -29,6 +29,7 @@ use App\Models\GraphType;
use Illuminate\Database\QueryException;
use Illuminate\Support\Arr;
use LibreNMS\DB\Eloquent;
use Log;
class Config
{
@ -41,28 +42,23 @@ class Config
*/
public static function load()
{
// don't reload the config if it is already loaded, reload() should be used for that
if (!is_null(self::$config)) {
return self::$config;
}
self::loadFiles();
// merge all config sources together config.php > db config > config_definitions.json
self::$config = array_replace_recursive(
self::loadDefaults(),
Eloquent::isConnected() ? self::loadDB() : [],
self::loadUserConfigFile()
);
// Make sure the database is connected
if (Eloquent::isConnected()) {
// pull in the database config settings
self::mergeDb();
// final cleanups and validations
self::processConfig();
self::populateTime();
// load graph types from the database
self::loadGraphsFromDb();
// process $config to tidy up
self::processConfig(true);
} else {
// just process $config
self::processConfig(false);
}
// set to global for legacy/external things
// set to global for legacy/external things (is this needed?)
global $config;
$config = self::$config;
@ -80,33 +76,50 @@ class Config
}
/**
* Load the user config from config.php, defaults.inc.php and definitions.inc.php, etc.
* Erases existing config.
* Get the config setting definitions
*
* @return array
*/
private static function &loadFiles()
public static function getDefinitions()
{
$config = []; // start fresh
return json_decode(file_get_contents(base_path('misc/config_definitions.json')), true)['config'];
}
$install_dir = realpath(__DIR__ . '/../');
$config['install_dir'] = $install_dir;
private static function loadDefaults()
{
$def_config = [];
$definitions = self::getDefinitions();
// load defaults
require $install_dir . '/includes/defaults.inc.php';
require $install_dir . '/includes/definitions.inc.php';
foreach ($definitions as $path => $def) {
if (array_key_exists('default', $def)) {
Arr::set($def_config, $path, $def['default']);
}
}
// import standard settings
$macros = json_decode(file_get_contents($install_dir . '/misc/macros.json'), true);
$config['alert']['macros']['rule'] = $macros;
// load macros from json
$macros = json_decode(file_get_contents(base_path('misc/macros.json')), true);
Arr::set($def_config, 'alert.macros.rule', $macros);
// Load user config
@include $install_dir . '/config.php';
self::processDefaults($def_config);
// set it
self::$config = $config;
return $def_config;
}
return self::$config;
/**
* Load the user config from config.php
*
* @return array
*/
private static function loadUserConfigFile()
{
$config = [];
$config['install_dir'] = base_path();
// Load user config file
@include base_path('config.php');
return $config;
}
@ -127,22 +140,7 @@ class Config
return $default;
}
$keys = explode('.', $key);
$curr = &self::$config;
foreach ($keys as $k) {
// do not add keys that don't exist
if (!isset($curr[$k])) {
return $default;
}
$curr = &$curr[$k];
}
if (is_null($curr)) {
return $default;
}
return $curr;
return Arr::get(self::$config, $key, $default);
}
/**
@ -245,44 +243,58 @@ class Config
*
* @param mixed $key period separated config variable name
* @param mixed $value
* @param bool $persist set the setting in the database so it persists across runs
* @param string $default default (only set when initially created)
* @param string $descr webui description (only set when initially created)
* @param string $group webui group (only set when initially created)
* @param string $sub_group webui subgroup (only set when initially created)
*/
public static function set($key, $value, $persist = false, $default = null, $descr = null, $group = null, $sub_group = null)
public static function set($key, $value)
{
if ($persist) {
try {
\App\Models\Config::updateOrCreate(['config_name' => $key], collect([
Arr::set(self::$config, $key, $value);
}
/**
* Save setting to persistent storage.
*
* @param mixed $key period separated config variable name
* @param mixed $value
* @return bool if the save was successful
*/
public static function persist($key, $value)
{
try {
if (is_array($value)) {
// get all children
$children = \App\Models\Config::query()->where('config_name', 'like', "$key.%")->pluck('config_value', 'config_name');
// flatten an array and set each
foreach (Arr::dot($value, "$key.") as $key => $value) {
if ($children->get($key) !== $value) {
\App\Models\Config::updateOrCreate(['config_name' => $key], [
'config_name' => $key,
'config_value' => $value,
]);
}
Arr::set(self::$config, $key, $value);
$children->forget($key);
}
// delete any non-existent children
\App\Models\Config::query()->whereIn('config_name', $children)->delete();
} else {
\App\Models\Config::updateOrCreate(['config_name' => $key], [
'config_name' => $key,
'config_default' => $default,
'config_descr' => $descr,
'config_group' => $group,
'config_sub_group' => $sub_group,
])->filter(function ($value, $field) {
return !is_null($value);
})->put('config_value', $value)->toArray());
} catch (QueryException $e) {
if (class_exists(\Log::class)) {
\Log::error($e);
}
global $debug;
if ($debug) {
echo $e;
}
'config_value' => $value,
]);
Arr::set(self::$config, $key, $value);
}
return true;
} catch (QueryException $e) {
if (class_exists(Log::class)) {
Log::error($e);
}
global $debug;
if ($debug) {
echo $e;
}
return false;
}
$keys = explode('.', $key);
$curr = &self::$config;
foreach ($keys as $k) {
$curr = &$curr[$k];
}
$curr = $value;
}
/**
@ -301,19 +313,7 @@ class Config
return false;
}
$keys = explode('.', $key);
$last = array_pop($keys);
$curr = &self::$config;
foreach ($keys as $k) {
// do not add keys that don't exist
if (!isset($curr[$k])) {
return false;
}
$curr = &$curr[$k];
}
return is_array($curr) && isset($curr[$last]);
return Arr::has(self::$config, $key);
}
/**
@ -321,7 +321,7 @@ class Config
*
* @return string
*/
public static function json_encode()
public static function toJson()
{
return json_encode(self::$config);
}
@ -339,7 +339,7 @@ class Config
* merge the database config with the global config
* Global config overrides db
*/
private static function mergeDb()
private static function loadDB()
{
$db_config = [];
@ -352,10 +352,13 @@ class Config
// possibly table config doesn't exist yet
}
self::$config = array_replace_recursive($db_config, self::$config);
// load graph types from the database
self::loadGraphsFromDb($db_config);
return $db_config;
}
private static function loadGraphsFromDb()
private static function loadGraphsFromDb(&$config)
{
try {
$graph_types = GraphType::all()->toArray();
@ -377,20 +380,51 @@ class Config
$g[$key] = $v;
}
self::$config['graph_types'][$g['type']][$g['subtype']] = $g;
$config['graph_types'][$g['type']][$g['subtype']] = $g;
}
}
/**
* Proces the config after it has been loaded.
* Handle defaults that are set programmatically
*
* @param array $def_config
* @return array
*/
private static function processDefaults(&$def_config)
{
Arr::set($def_config, 'log_dir', base_path('logs'));
Arr::set($def_config, 'distributed_poller_name', php_uname('n'));
// set base_url from access URL
if (isset($_SERVER['SERVER_NAME']) && isset($_SERVER['SERVER_PORT'])) {
$port = $_SERVER['SERVER_PORT'] != 80 ? ':' . $_SERVER['SERVER_PORT'] : '';
// handle literal IPv6
$server = str_contains($_SERVER['SERVER_NAME'], ':') ? "[{$_SERVER['SERVER_NAME']}]" : $_SERVER['SERVER_NAME'];
Arr::set($def_config, 'base_url', "http://$server$port/");
}
// graph color copying
Arr::set($def_config, 'graph_colours.mega', array_merge(
(array)Arr::get($def_config, 'graph_colours.psychedelic', []),
(array)Arr::get($def_config, 'graph_colours.manycolours', []),
(array)Arr::get($def_config, 'graph_colours.default', []),
(array)Arr::get($def_config, 'graph_colours.mixed', [])
));
return $def_config;
}
/**
* Process the config after it has been loaded.
* Make sure certain variables have been set properly and
*
* @param bool $persist Save binary locations and other settings to the database.
*/
private static function processConfig($persist = true)
private static function processConfig()
{
if (!self::get('email_from')) {
self::set('email_from', '"' . self::get('project_name') . '" <' . self::get('email_user') . '@' . php_uname('n') . '>');
// If we're on SSL, let's properly detect it
if (isset($_SERVER['HTTPS'])) {
self::set('base_url', preg_replace('/^http:/', 'https:', self::get('base_url')));
self::set('secure_cookies', true);
}
// If we're on SSL, let's properly detect it
@ -398,7 +432,15 @@ class Config
self::set('base_url', preg_replace('/^http:/', 'https:', self::get('base_url')));
}
// Define some variables if they aren't set by user definition in config.php
if (self::get('secure_cookies')) {
ini_set('session.cookie_secure', 1);
}
if (!self::get('email_from')) {
self::set('email_from', '"' . self::get('project_name') . '" <' . self::get('email_user') . '@' . php_uname('n') . '>');
}
// Define some variables if they aren't set by user definition in config_definitions.json
self::setDefault('html_dir', '%s/html', ['install_dir']);
self::setDefault('rrd_dir', '%s/rrd', ['install_dir']);
self::setDefault('mib_dir', '%s/mibs', ['install_dir']);
@ -406,6 +448,9 @@ class Config
self::setDefault('log_file', '%s/%s.log', ['log_dir', 'project_id']);
self::setDefault('plugin_dir', '%s/plugins', ['html_dir']);
self::setDefault('temp_dir', sys_get_temp_dir() ?: '/tmp');
self::setDefault('irc_nick', '%s', ['project_name']);
self::setDefault('irc_chan.0', '##%s', ['project_id']);
self::setDefault('page_title_suffix', '%s', ['project_name']);
// self::setDefault('email_from', '"%s" <%s@' . php_uname('n') . '>', ['project_name', 'email_user']); // FIXME email_from set because alerting config
// deprecated variables
@ -414,10 +459,15 @@ class Config
self::deprecatedVariable('discovery_modules.cisco-vrf', 'discovery_modules.vrf');
self::deprecatedVariable('oxidized.group', 'oxidized.maps.group');
$persist = Eloquent::isConnected();
// make sure we have full path to binaries in case PATH isn't set
foreach (array('fping', 'fping6', 'snmpgetnext', 'rrdtool', 'traceroute', 'traceroute6') as $bin) {
if (!is_executable(self::get($bin))) {
self::set($bin, self::locateBinary($bin), $persist, $bin, "Path to $bin", 'external', 'paths');
if ($persist) {
self::persist($bin, self::locateBinary($bin));
} else {
self::set($bin, self::locateBinary($bin));
}
}
}
}
@ -514,4 +564,25 @@ class Config
}
return $binary;
}
private static function populateTime()
{
$now = time();
$now -= $now % 300;
self::set('time.now', $now);
self::set('time.onehour', $now - 3600); // time() - (1 * 60 * 60);
self::set('time.fourhour', $now - 14400); // time() - (4 * 60 * 60);
self::set('time.sixhour', $now - 21600); // time() - (6 * 60 * 60);
self::set('time.twelvehour', $now - 43200); // time() - (12 * 60 * 60);
self::set('time.day', $now - 86400); // time() - (24 * 60 * 60);
self::set('time.twoday', $now - 172800); // time() - (2 * 24 * 60 * 60);
self::set('time.week', $now - 604800); // time() - (7 * 24 * 60 * 60);
self::set('time.twoweek', $now - 1209600); // time() - (2 * 7 * 24 * 60 * 60);
self::set('time.month', $now - 2678400); // time() - (31 * 24 * 60 * 60);
self::set('time.twomonth', $now - 5356800); // time() - (2 * 31 * 24 * 60 * 60);
self::set('time.threemonth', $now - 8035200); // time() - (3 * 31 * 24 * 60 * 60);
self::set('time.sixmonth', $now - 16070400); // time() - (6 * 31 * 24 * 60 * 60);
self::set('time.year', $now - 31536000); // time() - (365 * 24 * 60 * 60);
self::set('time.twoyear', $now - 63072000); // time() - (2 * 365 * 24 * 60 * 60);
}
}

View File

@ -669,9 +669,9 @@ class IRCBot
{
$versions = version_info();
$schema_version = $versions['db_schema'];
$version = substr($versions['local_sha'], 0, 7);
$version = $versions['local_ver'];
$msg = $this->config['project_name_version'].', Version: '.$version.', DB schema: '.$schema_version.', PHP: '.PHP_VERSION;
$msg = $this->config['project_name'].', Version: '.$version.', DB schema: '.$schema_version.', PHP: '.PHP_VERSION;
return $this->respond($msg);
}//end _version()

View File

@ -0,0 +1,132 @@
<?php
/**
* DynamicConfig.php
*
* Class used by the webui to collect config definitions to create a dynamic config ui
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @package LibreNMS
* @link http://librenms.org
* @copyright 2019 Tony Murray
* @author Tony Murray <murraytony@gmail.com>
*/
namespace LibreNMS\Util;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use LibreNMS\Config;
class DynamicConfig
{
private $definitions;
public function __construct()
{
// prepare to mark overridden settings
$config = [];
@include base_path('config.php');
$this->definitions = collect(Config::getDefinitions())->map(function ($item, $key) use ($config) {
$item['overridden'] = Arr::has($config, $key);
return new DynamicConfigItem($key, $item);
});
}
/**
* Check if a setting is valid
*
* @param string $name
* @return boolean
*/
public function isValidSetting($name)
{
return $this->definitions->has($name) && $this->definitions->get($name)->isValid();
}
/**
* Get config item by name
*
* @param string $name
* @return DynamicConfigItem|null
*/
public function get($name)
{
return $this->definitions->get($name);
}
/**
* Get all groups defined
*
* @return \Illuminate\Support\Collection
*/
public function getGroups()
{
return $this->definitions->pluck('group')->unique()->filter()->prepend('global');
}
public function getSections()
{
/** @var Collection $sections */
$sections = $this->definitions->groupBy('group')->map(function ($items) {
return $items->pluck('section')->unique()->filter()->values();
})->sortBy(function ($item, $key) {
return $key;
});
$sections->prepend($sections->pull('', []), 'global'); // rename '' to global
return $sections;
}
/**
* Get all config definitions grouped by group then section.
*
* @return Collection
*/
public function getGrouped()
{
/** @var Collection $grouped */
$grouped = $this->definitions->filter->isValid()->sortBy('group')->groupBy('group')->map(function ($group) {
return $group->sortBy('section')->groupBy('section')->map->sortBy('order');
});
$grouped->prepend($grouped->pull(''), 'global'); // rename '' to global
return $grouped;
}
public function getByGroup($group, $subgroup = null)
{
return $this->definitions->filter(function ($item) use ($group, $subgroup) {
/** @var DynamicConfigItem $item */
if ($item->getGroup() != $group) {
return false;
}
if ($subgroup && $item->getSection() != $subgroup) {
return false;
}
return $item->isValid();
})->sortBy('order');
}
/**
* Get all config items keyed by name
*
* @return Collection
*/
public function all()
{
return $this->definitions;
}
}

View File

@ -0,0 +1,236 @@
<?php
/**
* DynamicConfigItem.php
*
* -Description-
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @package LibreNMS
* @link http://librenms.org
* @copyright 2019 Tony Murray
* @author Tony Murray <murraytony@gmail.com>
*/
namespace LibreNMS\Util;
use App\Models\Dashboard;
use LibreNMS\Config;
use Validator;
class DynamicConfigItem implements \ArrayAccess
{
public $name;
public $group;
public $section;
public $value;
public $type;
public $default;
public $overridden = false; // overridden by config.php
public $hidden = false;
public $required = false;
public $disabled = false;
public $options = [];
public $when;
public $pattern;
public $validate;
public $units;
public function __construct($name, $settings = [])
{
$this->name = $name;
$this->value = Config::get($this->name, $this->default);
foreach ($settings as $key => $value) {
$this->$key = $value;
}
}
/**
* Check given value is valid. Using the type of this config item and possibly other variables.
*
* @param $value
* @return bool|mixed
*/
public function checkValue($value)
{
if ($this->validate) {
return $this->buildValidator($value)->passes();
} elseif ($this->type == 'boolean') {
return filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) !== null;
} elseif ($this->type == 'integer') {
return filter_var($value, FILTER_VALIDATE_INT) || $value === "0" || $value === 0;
} elseif ($this->type == 'select') {
return in_array($value, array_keys($this->options));
} elseif ($this->type == 'email') {
return filter_var($value, FILTER_VALIDATE_EMAIL);
} elseif (in_array($this->type, ['text', 'password'])) {
return true;
}
return false;
}
public function getGroup()
{
return $this->group;
}
public function getSection()
{
return $this->section;
}
public function getValue()
{
return $this->value;
}
public function getOptions()
{
return array_reduce($this->options, function ($result, $option) {
$key = $this->optionTranslationKey($option);
$trans = __($key);
$result[$option] = ($trans === $key ? $option : $trans);
return $result;
}, []);
}
public function isHidden()
{
return $this->hidden;
}
public function isRequired()
{
return $this->required;
}
public function getType()
{
return $this->type;
}
public function hasDescription()
{
$key = $this->descriptionTranslationKey();
return __($key) !== $key;
}
public function hasHelp()
{
$key = $this->helpTranslationKey();
return __($key) !== $key;
}
public function hasUnits()
{
return isset($this->units);
}
public function getUnits()
{
return $this->hasUnits() ? __($this->units) : '';
}
public function getDescription()
{
$key = $this->descriptionTranslationKey();
$trans = __($key);
return $trans === $key ? $this->name : $trans;
}
public function getHelp()
{
return __($this->helpTranslationKey());
}
public function only($fields = [])
{
$array = [];
foreach ($fields as $field) {
$array[$field] = $this->$field;
}
return $array;
}
public function toArray()
{
return get_object_vars($this);
}
public function isValid()
{
return ($this->group == "" || $this->type) && !$this->hidden && !$this->disabled;
}
/**
* @param mixed $value The value that was validated
* @return string
*/
public function getValidationMessage($value)
{
return $this->validate
? implode(" \n", $this->buildValidator($value)->messages()->get('value'))
: __('settings.validate.' . $this->type, ['id' => $this->name, 'value' => is_array($value) ? json_encode($value) : $value]);
}
// ArrayAccess functions
public function offsetExists($offset)
{
return isset($this->$offset);
}
public function offsetGet($offset)
{
return isset($this->$offset) ? $this->$offset : null;
}
public function offsetSet($offset, $value)
{
$this->$offset = $value;
}
public function offsetUnset($offset)
{
unset($this->$offset);
}
public function getName()
{
return $this->name;
}
private function descriptionTranslationKey()
{
return "settings.settings.$this->name.description";
}
private function helpTranslationKey()
{
return "settings.settings.$this->name.help";
}
private function optionTranslationKey($option)
{
return "settings.settings.$this->name.options.$option";
}
private function buildValidator($value)
{
return Validator::make(['value' => $value], $this->validate);
}
}

View File

@ -215,7 +215,7 @@ if (!empty($argv[1])) {
}
} else {
c_echo(
"\n" . Config::get('project_name_version') . ' Add Host Tool
"\n". Config::get('project_name').' Add Host Tool
Usage (SNMPv1/2c) : ./addhost.php [-g <poller group>] [-f] [-b] [-p <port assoc mode>] <%Whostname%n> [community] [v1|v2c] [port] [' . $transports_regex . ']
Usage (SNMPv3) :

View File

@ -0,0 +1,66 @@
<?php
/**
* DashboardController.php
*
* -Description-
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @package LibreNMS
* @link http://librenms.org
* @copyright 2019 Tony Murray
* @author Tony Murray <murraytony@gmail.com>
*/
namespace App\Http\Controllers\Select;
use App\Models\Dashboard;
class DashboardController extends SelectController
{
protected function searchFields($request)
{
return ['dashboard_name'];
}
/**
* Defines the base query for this resource
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder
*/
protected function baseQuery($request)
{
return Dashboard::query()
->where('access', '>', 0)
->with('user')
->orderBy('user_id')
->orderBy('dashboard_name');
}
public function formatItem($dashboard)
{
/** @var Dashboard $dashboard */
return [
'id' => $dashboard->dashboard_id,
'text' => $this->describe($dashboard),
];
}
private function describe($dashboard)
{
return "{$dashboard->user->username}: {$dashboard->dashboard_name} ("
. ($dashboard->access == 1 ? __('read-only') : __('read-write')) . ')';
}
}

View File

@ -0,0 +1,107 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use LibreNMS\Util\DynamicConfig;
class SettingsController extends Controller
{
/**
* Display a listing of the resource.
*
* @param string $tab
* @param string $section
* @return \Illuminate\Http\Response
*/
public function index($tab = 'global', $section = '')
{
$data = [
'active_tab' => $tab,
'active_section' => $section,
];
return view('settings.index', $data);
}
/**
* Update the specified resource in storage.
*
* @param DynamicConfig $config
* @param \Illuminate\Http\Request $request
* @param string $id
* @return \Illuminate\Http\JsonResponse
*/
public function update(DynamicConfig $config, Request $request, $id)
{
$value = $request->get('value');
if (!$config->isValidSetting($id)) {
return $this->jsonResponse($id, ":id is not a valid setting", null, 400);
}
$current = \LibreNMS\Config::get($id);
$config_item = $config->get($id);
if (!$config_item->checkValue($value)) {
return $this->jsonResponse($id, $config_item->getValidationMessage($value), $current, 400);
}
if (\LibreNMS\Config::persist($id, $value)) {
return $this->jsonResponse($id, "Successfully set $id", $value);
}
return $this->jsonResponse($id, "Failed to update :id", $current, 400);
}
/**
* Remove the specified resource from storage.
*
* @param DynamicConfig $config
* @param string $id
* @return \Illuminate\Http\JsonResponse
*/
public function destroy(DynamicConfig $config, $id)
{
if (!$config->isValidSetting($id)) {
return $this->jsonResponse($id, ":id is not a valid setting", null, 400);
}
$dbConfig = \App\Models\Config::where('config_name', 'like', "$id%")->get();
if ($dbConfig->isEmpty()) {
return $this->jsonResponse($id, ":id is not set", $config->get($id)->default, 400);
}
$dbConfig->each->delete();
return $this->jsonResponse($id, ":id reset to default", $config->get($id)->default);
}
/**
* List all settings (excluding hidden ones and ones that don't have metadata)
*
* @param DynamicConfig $config
* @return JsonResponse
*/
public function listAll(DynamicConfig $config)
{
return response()->json($config->all()->filter->isValid());
}
/**
* @param string $id
* @param string $message
* @param mixed $value
* @param int $status
* @return \Illuminate\Http\JsonResponse
*/
protected function jsonResponse($id, $message, $value = null, $status = 200)
{
return new JsonResponse([
'message' => __($message, ['id' => $id]),
'value' => $value,
], $status);
}
}

View File

@ -29,21 +29,10 @@ class Config extends BaseModel
{
public $timestamps = false;
protected $table = 'config';
public $primaryKey = 'config_name';
public $incrementing = false;
public $primaryKey = 'config_id';
protected $fillable = [
'config_name',
'config_value',
'config_default',
'config_descr',
'config_group',
'config_sub_group',
];
protected $attributes = [
'config_default' => '',
'config_descr' => '',
'config_group' => '',
'config_sub_group' => '',
];
protected $casts = [
'config_default' => 'array'

View File

@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\ServiceProvider;
use Illuminate\Validation\Rule;
use LibreNMS\Config;
use LibreNMS\Permissions;
use LibreNMS\Util\IP;
@ -109,5 +110,14 @@ class AppServiceProvider extends ServiceProvider
$ip = substr($value, 0, strpos($value, '/') ?: strlen($value)); // allow prefixes too
return IP::isValid($ip) || Validate::hostname($value);
}, __('The :attribute must a valid IP address/network or hostname.'));
Validator::extend('zero_or_exists', function ($attribute, $value, $parameters, $validator) {
if ($value === 0) {
return true;
}
$validator = Validator::make([$attribute => $value], [$attribute => 'exists:' . implode(',', $parameters)]);
return $validator->passes();
}, __('validation.exists'));
}
}

View File

@ -37,6 +37,7 @@
"fico7489/laravel-pivot": "^3.0",
"influxdb/influxdb-php": "^1.14",
"laravel/laravel": "5.8.*",
"martinlindhe/laravel-vue-i18n-generator": "^0.1.40",
"oriceon/toastr-5-laravel": "dev-master",
"pear/console_color2": "^0.1",
"pear/console_table": "^1.3",
@ -46,6 +47,7 @@
"spatie/laravel-cors": "1.3.*",
"symfony/yaml": "^4.0",
"tecnickcom/tcpdf": "~6.2.0",
"tightenco/ziggy": "^0.7.1",
"wpb/string-blade-compiler": "3.8.x-dev",
"xjtuwangke/passwordhash": "dev-master"
},
@ -110,7 +112,8 @@
],
"post-install-cmd": [
"LibreNMS\\ComposerHelper::postInstall",
"Illuminate\\Foundation\\ComposerScripts::postInstall"
"Illuminate\\Foundation\\ComposerScripts::postInstall",
"@php artisan vue-i18n:generate"
],
"post-update-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postUpdate"

146
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "0d2febf6e8b134d2335777201692fa9b",
"content-hash": "64d21ebc508842b15a10ebbf99b6dc8e",
"packages": [
{
"name": "amenadiel/jpgraph",
@ -1556,6 +1556,59 @@
],
"time": "2019-06-18T20:09:29+00:00"
},
{
"name": "martinlindhe/laravel-vue-i18n-generator",
"version": "0.1.40",
"source": {
"type": "git",
"url": "https://github.com/martinlindhe/laravel-vue-i18n-generator.git",
"reference": "0da6b5b7782bf0a79e47f6c02d39115550638e1e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/martinlindhe/laravel-vue-i18n-generator/zipball/0da6b5b7782bf0a79e47f6c02d39115550638e1e",
"reference": "0da6b5b7782bf0a79e47f6c02d39115550638e1e",
"shasum": ""
},
"require": {
"illuminate/console": "~5.1.0|~5.2.0|~5.3.0|~5.4.0|~5.5.0|~5.6.0|~5.7.0|~5.8.0|~6.0",
"illuminate/support": "~5.1.0|~5.2.0|~5.3.0|~5.4.0|~5.5.0|~5.6.0|~5.7.0|~5.8.0|~6.0",
"php": ">=5.5.0"
},
"require-dev": {
"phpunit/phpunit": "~4.7"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"MartinLindhe\\VueInternationalizationGenerator\\GeneratorProvider"
]
}
},
"autoload": {
"psr-4": {
"MartinLindhe\\VueInternationalizationGenerator\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Martin Lindhe",
"email": "martin@ubique.se"
}
],
"description": "Generates a vue-i18n compatible include file from your Laravel translations.",
"homepage": "http://github.com/martinlindhe/laravel-vue-i18n-generator",
"keywords": [
"laravel",
"vue-i18n"
],
"time": "2019-09-03T16:59:43+00:00"
},
{
"name": "monolog/monolog",
"version": "1.24.0",
@ -4226,6 +4279,57 @@
],
"time": "2018-10-16T17:24:05+00:00"
},
{
"name": "tightenco/ziggy",
"version": "v0.7.1",
"source": {
"type": "git",
"url": "https://github.com/tightenco/ziggy.git",
"reference": "aa4c42aaec9516da892bc2e9f6e992582646af77"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/tightenco/ziggy/zipball/aa4c42aaec9516da892bc2e9f6e992582646af77",
"reference": "aa4c42aaec9516da892bc2e9f6e992582646af77",
"shasum": ""
},
"require": {
"laravel/framework": "~5.4"
},
"require-dev": {
"mikey179/vfsstream": "^1.6",
"orchestra/testbench": "~3.6"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Tightenco\\Ziggy\\ZiggyServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Tightenco\\Ziggy\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Matt Stauffer",
"email": "matt@tighten.co"
},
{
"name": "Daniel Coulbourne",
"email": "daniel@tighten.co"
}
],
"description": "Generates a Blade directive exporting all of your named Laravel routes. Also provides a nice route() helper function in JavaScript.",
"time": "2019-04-26T22:03:47+00:00"
},
{
"name": "tijsverkoyen/css-to-inline-styles",
"version": "2.2.1",
@ -5130,8 +5234,8 @@
"authors": [
{
"name": "Filipe Dobreira",
"role": "Developer",
"homepage": "https://github.com/filp"
"homepage": "https://github.com/filp",
"role": "Developer"
}
],
"description": "php error handling for cool kids",
@ -5746,18 +5850,18 @@
"authors": [
{
"name": "Arne Blankerts",
"role": "Developer",
"email": "arne@blankerts.de"
"email": "arne@blankerts.de",
"role": "Developer"
},
{
"name": "Sebastian Heuer",
"role": "Developer",
"email": "sebastian@phpeople.de"
"email": "sebastian@phpeople.de",
"role": "Developer"
},
{
"name": "Sebastian Bergmann",
"role": "Developer",
"email": "sebastian@phpunit.de"
"email": "sebastian@phpunit.de",
"role": "Developer"
}
],
"description": "Library for handling version information and constraints",
@ -6028,8 +6132,8 @@
"authors": [
{
"name": "Sebastian Bergmann",
"role": "lead",
"email": "sebastian@phpunit.de"
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
@ -6079,8 +6183,8 @@
"authors": [
{
"name": "Sebastian Bergmann",
"role": "lead",
"email": "sebastian@phpunit.de"
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "FilterIterator implementation that filters files based on a list of suffixes.",
@ -6121,8 +6225,8 @@
"authors": [
{
"name": "Sebastian Bergmann",
"role": "lead",
"email": "sebastian@phpunit.de"
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "Simple template engine.",
@ -6170,8 +6274,8 @@
"authors": [
{
"name": "Sebastian Bergmann",
"role": "lead",
"email": "sebastian@phpunit.de"
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "Utility class for timing",
@ -6301,8 +6405,8 @@
"authors": [
{
"name": "Sebastian Bergmann",
"role": "lead",
"email": "sebastian@phpunit.de"
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "The PHP Unit Testing framework.",
@ -6872,8 +6976,8 @@
"authors": [
{
"name": "Sebastian Bergmann",
"role": "lead",
"email": "sebastian@phpunit.de"
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "Library that helps with managing the version number of Git-hosted PHP projects",

View File

@ -0,0 +1,82 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Laravel translations path
|--------------------------------------------------------------------------
|
| The default path where the translations are stored by Laravel.
| Note: the path will be prepended to point to the App directory.
|
*/
'langPath' => '/resources/lang',
/*
|--------------------------------------------------------------------------
| Laravel translation files
|--------------------------------------------------------------------------
|
| You can choose which translation files to be generated.
| Note: leave this empty for all the translation files to be generated.
|
*/
'langFiles' => [
/*
'pagination',
'passwords'
*/
],
/*
|--------------------------------------------------------------------------
| Excluded files & folders
|--------------------------------------------------------------------------
|
| Exclude translation files, generic files or folders you don't need.
|
*/
'excludes' => [
/*
'validation',
'example.file',
'example-folder',
*/
],
/*
|--------------------------------------------------------------------------
| Output file
|--------------------------------------------------------------------------
|
| The javascript path where I will place the generated file.
| Note: the path will be prepended to point to the App directory.
|
*/
'jsPath' => '/resources/js/lang/',
'jsFile' => '/resources/js/vue-i18n-locales.generated.js',
/*
|--------------------------------------------------------------------------
| i18n library
|--------------------------------------------------------------------------
|
| Specify the library you use for localization.
| Options are vue-i18n or vuex-i18n.
|
*/
'i18nLib' => 'vue-i18n',
/*
|--------------------------------------------------------------------------
| Output messages
|--------------------------------------------------------------------------
|
| Specify if the library should show "written to" messages
| after generating json files.
|
*/
'showOutputMessages' => false,
];

View File

@ -13,5 +13,5 @@ $init_modules = ['nodb'];
require __DIR__ . '/includes/init.php';
if (isCli()) {
echo Config::json_encode();
echo Config::toJson();
}

View File

@ -0,0 +1,48 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class RemoveConfigDefinitionFromDb extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('config', function (Blueprint $table) {
$table->dropColumn([
'config_default',
'config_descr',
'config_group',
'config_group_order',
'config_sub_group',
'config_sub_group_order',
'config_hidden',
'config_disabled',
]);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('config', function (Blueprint $table) {
$table->string('config_default', 512);
$table->string('config_descr', 100);
$table->string('config_group', 50);
$table->integer('config_group_order')->default(0);
$table->string('config_sub_group', 50);
$table->integer('config_sub_group_order')->default(0);
$table->enum('config_hidden', ['0','1'])->default('0');
$table->enum('config_disabled', ['0','1'])->default('0');
});
}
}

View File

@ -12,7 +12,6 @@ class DatabaseSeeder extends Seeder
public function run()
{
$this->call(DefaultAlertTemplateSeeder::class);
$this->call(DefaultConfigSeeder::class);
$this->call(DefaultWidgetSeeder::class);
$this->call(DefaultLegacySchemaSeeder::class);
}

View File

@ -1,683 +0,0 @@
<?php
use Illuminate\Database\Seeder;
class DefaultConfigSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$config_values = [
[
"config_name" => "alert.ack_until_clear",
"config_value" => "false",
"config_default" => "false",
"config_descr" => "Default acknowledge until alert clears",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "alert.admins",
"config_value" => "true",
"config_default" => "true",
"config_descr" => "Alert administrators",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "alert.default_copy",
"config_value" => "true",
"config_default" => "true",
"config_descr" => "Copy all email alerts to default contact",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "alert.default_if_none",
"config_value" => "false",
"config_default" => "false",
"config_descr" => "Send mail to default contact if no other contacts are found",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "alert.default_mail",
"config_value" => '""',
"config_default" => '""',
"config_descr" => "The default mail contact",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "alert.default_only",
"config_value" => "true",
"config_default" => "true",
"config_descr" => "Only alert default mail contact",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "alert.disable",
"config_value" => "false",
"config_default" => "false",
"config_descr" => "Stop alerts being generated",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "alert.fixed-contacts",
"config_value" => "true",
"config_default" => "true",
"config_descr" => "If TRUE any changes to sysContact or users emails will not be honoured whilst alert is active",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "alert.globals",
"config_value" => "true",
"config_default" => "true",
"config_descr" => "Alert read only administrators",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "alert.syscontact",
"config_value" => "true",
"config_default" => "true",
"config_descr" => "Issue alerts to sysContact",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "alert.tolerance_window",
"config_value" => "5",
"config_default" => "5",
"config_descr" => "Tolerance window in seconds",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "alert.transports.mail",
"config_value" => "false",
"config_default" => "false",
"config_descr" => "Mail alerting transport",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "transports",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0",
],
[
"config_name" => "alert.users",
"config_value" => "0",
"config_default" => "0",
"config_descr" => "Issue alerts to normal users",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "email_auto_tls",
"config_value" => "true",
"config_default" => "true",
"config_descr" => "Enable / disable Auto TLS support",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "email_backend",
"config_value" => '"mail"',
"config_default" => '"mail"',
"config_descr" => "The backend to use for sending email, can be mail, sendmail or smtp",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "email_from",
"config_value" => "null",
"config_default" => "null",
"config_descr" => "Email address used for sending emails (from)",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "email_html",
"config_value" => "false",
"config_default" => "false",
"config_descr" => "Send HTML emails",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "email_sendmail_path",
"config_value" => '"/usr/sbin/sendmail"',
"config_default" => '"/usr/sbin/sendmail"',
"config_descr" => "Location of sendmail if using this option",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "email_smtp_auth",
"config_value" => "false",
"config_default" => "false",
"config_descr" => "Enable / disable smtp authentication",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "email_smtp_host",
"config_value" => '"localhost"',
"config_default" => '"localhost"',
"config_descr" => "SMTP Host for sending email if using this option",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "email_smtp_password",
"config_value" => "null",
"config_default" => "null",
"config_descr" => "SMTP Auth password",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "email_smtp_port",
"config_value" => "25",
"config_default" => "25",
"config_descr" => "SMTP port setting",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "email_smtp_secure",
"config_value" => '""',
"config_default" => '""',
"config_descr" => "Enable / disable encryption (use tls or ssl)",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "email_smtp_timeout",
"config_value" => "10",
"config_default" => "10",
"config_descr" => "SMTP timeout setting",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "email_smtp_username",
"config_value" => "null",
"config_default" => "null",
"config_descr" => "SMTP Auth username",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "email_user",
"config_value" => '"LibreNMS"',
"config_default" => '"LibreNMS"',
"config_descr" => "Name used as part of the from address",
"config_group" => "alerting",
"config_group_order" => "0",
"config_sub_group" => "general",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "fping",
"config_value" => '"/usr/sbin/fping"',
"config_default" => '"fping"',
"config_descr" => "Path to fping",
"config_group" => "external",
"config_group_order" => "0",
"config_sub_group" => "paths",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "fping6",
"config_value" => '"/usr/sbin/fping6"',
"config_default" => '"fping6"',
"config_descr" => "Path to fping6",
"config_group" => "external",
"config_group_order" => "0",
"config_sub_group" => "paths",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "geoloc.api_key",
"config_value" => '""',
"config_default" => '""',
"config_descr" => "Geocoding API Key (Required to function)",
"config_group" => "external",
"config_group_order" => "0",
"config_sub_group" => "location",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "geoloc.engine",
"config_value" => '""',
"config_default" => '""',
"config_descr" => "Geocoding Engine",
"config_group" => "external",
"config_group_order" => "0",
"config_sub_group" => "location",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "oxidized.default_group",
"config_value" => '""',
"config_default" => '""',
"config_descr" => "Set the default group returned",
"config_group" => "external",
"config_group_order" => "0",
"config_sub_group" => "oxidized",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "oxidized.enabled",
"config_value" => "false",
"config_default" => "false",
"config_descr" => "Enable Oxidized support",
"config_group" => "external",
"config_group_order" => "0",
"config_sub_group" => "oxidized",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "oxidized.features.versioning",
"config_value" => "false",
"config_default" => "false",
"config_descr" => "Enable Oxidized config versioning",
"config_group" => "external",
"config_group_order" => "0",
"config_sub_group" => "oxidized",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "oxidized.group_support",
"config_value" => "false",
"config_default" => "false",
"config_descr" => "Enable the return of groups to Oxidized",
"config_group" => "external",
"config_group_order" => "0",
"config_sub_group" => "oxidized",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "oxidized.reload_nodes",
"config_value" => "false",
"config_default" => "false",
"config_descr" => "Reload Oxidized nodes list, each time a device is added",
"config_group" => "external",
"config_group_order" => "0",
"config_sub_group" => "oxidized",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "oxidized.url",
"config_value" => '""',
"config_default" => '""',
"config_descr" => "Oxidized API url",
"config_group" => "external",
"config_group_order" => "0",
"config_sub_group" => "oxidized",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "peeringdb.enabled",
"config_value" => "false",
"config_default" => "false",
"config_descr" => "Enable PeeringDB lookup (data is downloaded with daily.sh)",
"config_group" => "external",
"config_group_order" => "0",
"config_sub_group" => "peeringdb",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "rrd.heartbeat",
"config_value" => "600",
"config_default" => "600",
"config_descr" => "Change the rrd heartbeat value (default 600)",
"config_group" => "external",
"config_group_order" => "0",
"config_sub_group" => "rrdtool",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "rrd.step",
"config_value" => "300",
"config_default" => "300",
"config_descr" => "Change the rrd step value (default 300)",
"config_group" => "external",
"config_group_order" => "0",
"config_sub_group" => "rrdtool",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "rrdtool",
"config_value" => '"/usr/bin/rrdtool"',
"config_default" => '"/usr/bin/rrdtool"',
"config_descr" => "Path to rrdtool",
"config_group" => "external",
"config_group_order" => "0",
"config_sub_group" => "rrdtool",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "rrdtool_tune",
"config_value" => "false",
"config_default" => "false",
"config_descr" => "Auto tune maximum value for rrd port files",
"config_group" => "external",
"config_group_order" => "0",
"config_sub_group" => "rrdtool",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "snmpgetnext",
"config_value" => '"/usr/bin/snmpgetnext"',
"config_default" => '"snmpgetnext"',
"config_descr" => "Path to snmpgetnext",
"config_group" => "external",
"config_group_order" => "0",
"config_sub_group" => "paths",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "unix-agent.connection-timeout",
"config_value" => "10",
"config_default" => "10",
"config_descr" => "Unix-agent connection timeout",
"config_group" => "external",
"config_group_order" => "0",
"config_sub_group" => "unix-agent",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "unix-agent.port",
"config_value" => "6556",
"config_default" => "6556",
"config_descr" => "Default port for the Unix-agent (check_mk)",
"config_group" => "external",
"config_group_order" => "0",
"config_sub_group" => "unix-agent",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "unix-agent.read-timeout",
"config_value" => "10",
"config_default" => "10",
"config_descr" => "Unix-agent read timeout",
"config_group" => "external",
"config_group_order" => "0",
"config_sub_group" => "unix-agent",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "webui.availability_map_box_size",
"config_value" => "165",
"config_default" => "165",
"config_descr" => "Input desired tile width in pixels for box size in full view",
"config_group" => "webui",
"config_group_order" => "0",
"config_sub_group" => "graph",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "webui.availability_map_compact",
"config_value" => "false",
"config_default" => "false",
"config_descr" => "Availability map old view",
"config_group" => "webui",
"config_group_order" => "0",
"config_sub_group" => "graph",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "webui.availability_map_sort_status",
"config_value" => "false",
"config_default" => "false",
"config_descr" => "Sort devices and services by status",
"config_group" => "webui",
"config_group_order" => "0",
"config_sub_group" => "graph",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "webui.availability_map_use_device_groups",
"config_value" => "false",
"config_default" => "false",
"config_descr" => "Enable usage of device groups filter",
"config_group" => "webui",
"config_group_order" => "0",
"config_sub_group" => "graph",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "webui.default_dashboard_id",
"config_value" => "0",
"config_default" => "0",
"config_descr" => "Global default dashboard_id for all users who do not have their own default set",
"config_group" => "webui",
"config_group_order" => "0",
"config_sub_group" => "dashboard",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "webui.dynamic_graphs",
"config_value" => "false",
"config_default" => "false",
"config_descr" => "Enable dynamic graphs",
"config_group" => "webui",
"config_group_order" => "0",
"config_sub_group" => "graph",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "webui.global_search_result_limit",
"config_value" => "8",
"config_default" => "8",
"config_descr" => "Global search results limit",
"config_group" => "webui",
"config_group_order" => "0",
"config_sub_group" => "search",
"config_sub_group_order" => "0",
"config_hidden" => "1",
"config_disabled" => "0"
],
[
"config_name" => "webui.graph_stacked",
"config_value" => "false",
"config_default" => "false",
"config_descr" => "Display stacked graphs instead of inverted graphs",
"config_group" => "webui",
"config_group_order" => "0",
"config_sub_group" => "graph",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "webui.graph_type",
"config_value" => '"png"',
"config_default" => '"png"',
"config_descr" => "Set the default graph type",
"config_group" => "webui",
"config_group_order" => "0",
"config_sub_group" => "graph",
"config_sub_group_order" => "0",
"config_hidden" => "0",
"config_disabled" => "0"
],
[
"config_name" => "webui.min_graph_height",
"config_value" => "300",
"config_default" => "300",
"config_descr" => "Minimum Graph Height",
"config_group" => "webui",
"config_group_order" => "0",
"config_sub_group" => "graph",
"config_sub_group_order" => "0",
"config_hidden" => "1",
"config_disabled" => "0"
]
];
$existing = DB::table('config')->pluck('config_name');
\DB::table('config')->insert(array_filter($config_values, function ($entry) use ($existing) {
return !$existing->contains($entry['config_name']);
}));
}
}

View File

@ -21,7 +21,7 @@ $sqlparams = array();
$options = getopt('h:m:i:n:d::v::a::q', array('os:','type:'));
if (!isset($options['q'])) {
echo \LibreNMS\Config::get('project_name_version') . " Discovery\n";
echo \LibreNMS\Config::get('project_name')." Discovery\n";
}
if (isset($options['h'])) {

View File

@ -1,56 +1,94 @@
source: Developing/Dynamic-Config.md
path: blob/master/doc/
# Adding new config options to WebUI
# Adding new config settings
Adding support for users to update a new config option via the WebUI
is now a lot easier for general options. This document shows you how
to add a new config option and even section to the WebUI.
#### Update DB
Config settings are defined in `misc/config_definitions.json`
Firstly you will need to add the config option to the database. Here's an example:
You should give a little thought to the name of your config setting.
For example: a good setting for snmp community, would be `snmp.community`.
The dot notation is path and when the config is hydrated, it is converted to a nested array.
If the user is overriding the option in config.php it would use the format `$config['snmp']['community']`
```sql
insert into config (config_name,config_value,config_default,config_descr,config_group,config_group_order,config_sub_group,config_sub_group_order,config_hidden,config_disabled) values ('alert.tolerance_window','','','Tolerance window in seconds','alerting',0,'general',0,'0','0');
## Translation
The config definition system inherently supports translation. You must add the English names in the
`resoures/lang/en/settings.php` file (and other languages if you can).
Note: Right now the Vue.js translation is a little cumbersome. First you need to update the language
files, then run `./lnms vue-i18n:generate`, run `npm run development` or `npm run watch`, then hard refresh your web
page.
# Definition Format
For snmp.community, this is the definition:
```json
"snmp.community": {
"group": "poller",
"section": "snmp",
"order": 2,
"type": "array",
"default": [
"public"
]
}
```
This will determine the default config option for `$config['alert']['tolerance_window']`.
## Fields
#### Update WebUI
All fields are optional. To show in the web ui, group and section are required, order is recommended.
If the sub-section you want to add the new config option already
exists then update the relevant file within `html/pages/settings/`
otherwise you will need to create the new sub-section page. Here's an
example of this:
* `type`: Defines the type, there are a few predefined types and custom
types can be defined and implemented in a vue.js component
* `default`: the default value for this setting
* `options`: the options for the select type. An object with {"value1": "display string", "value2": "display string"}
* `validate`: Defines more complex validation than the default simple type check. Uses Laravel validation syntax.
* `group`: The web ui tab this is under
* `section`: A panel grouping settings in the web ui
* `order`: The order to display this setting within the section
[Commit example](https://github.com/librenms/librenms/commit/c5998f9ee27acdac0c0f7d3092fc830c51ff684c)
## Predefined Types
```php
<?php
* `string`: A string
* `integer`: A number
* `boolean`: A simple toggle switch
* `array`: A list of values that can be added, removed, and re-ordered.
* `select`: A dropdown box with predefined options. Requires the option field.
* `email`: Will validate the input is the correct format for an email
* `password`: Will mask the value of the input (but does not keep it fully private)
$no_refresh = true;
# Custom Types
$config_groups = get_config_by_group('alerting');
You may set the type field to a custom type and define a Vue.js component to display it to the user.
$mail_conf = array(
array('name' => 'alert.tolerance_window',
'descr' => 'Tolerance window for cron',
'type' => 'text',
),
);
The Vue.js component should be named as "SettingType" where type is the custom type entered with the first
letter capitalized. Vue.js components exist in the `resources/js/components` directory.
echo '
<div class="panel-group" id="accordion">
<form class="form-horizontal" role="form" action="" method="post">
' . csrf_field();
Here is an empty component named SettingType (make sure to rename it). It pulls in BaseSetting mixin for
basic setting code to reuse. You should review the BaseSetting component.
echo generate_dynamic_config_panel('Email transport',$config_groups,$mail_conf,'mail');
```vue
<template>
<div></div>
</template>
echo '
</form>
</div>
';
<script>
import BaseSetting from "./BaseSetting";
export default {
name: "SettingType",
mixins: [BaseSetting]
}
</script>
<style scoped>
</style>
```
And that should be it!
Using Vue.js is beyond the scope of this document. Documentation can be found at [https://vuejs.org/v2/guide/].

View File

@ -69,7 +69,7 @@ $config['nfsen_suffix'] = "_yourdomain_com";
The above is a very important bit as device names in NfSen are limited
to 21 characters. This means full domain names for devices can be very
problematic to squeeze in, so there for this chunk is usually removed.
problematic to squeeze in, so therefor this chunk is usually removed.
On a similar note, NfSen profiles for channels should be created with
the same name.
@ -78,7 +78,7 @@ the same name.
Below are the default settings used with nfdump for stats.
For more defailted information on that, please see nfdump(1).
For more defaulted information on that, please see nfdump(1).
```php
$config['nfsen_last_max'] = 153600;

File diff suppressed because one or more lines are too long

View File

@ -2255,3 +2255,109 @@ label {
.toast-top-right {
top: 34px;
}
.tooltip {
display: block !important;
z-index: 10000;
}
.tooltip .tooltip-inner {
background: black;
color: white;
border-radius: 16px;
padding: 5px 10px 4px;
}
.tooltip .tooltip-arrow {
width: 0;
height: 0;
border-style: solid;
position: absolute;
margin: 5px;
border-color: black;
z-index: 1;
}
.tooltip[x-placement^="top"] {
margin-bottom: 5px;
}
.tooltip[x-placement^="top"] .tooltip-arrow {
border-width: 5px 5px 0 5px;
border-left-color: transparent !important;
border-right-color: transparent !important;
border-bottom-color: transparent !important;
bottom: -5px;
left: calc(50% - 5px);
margin-top: 0;
margin-bottom: 0;
}
.tooltip[x-placement^="bottom"] {
margin-top: 5px;
}
.tooltip[x-placement^="bottom"] .tooltip-arrow {
border-width: 0 5px 5px 5px;
border-left-color: transparent !important;
border-right-color: transparent !important;
border-top-color: transparent !important;
top: -5px;
left: calc(50% - 5px);
margin-top: 0;
margin-bottom: 0;
}
.tooltip[x-placement^="right"] {
margin-left: 5px;
}
.tooltip[x-placement^="right"] .tooltip-arrow {
border-width: 5px 5px 5px 0;
border-left-color: transparent !important;
border-top-color: transparent !important;
border-bottom-color: transparent !important;
left: -5px;
top: calc(50% - 5px);
margin-left: 0;
margin-right: 0;
}
.tooltip[x-placement^="left"] {
margin-right: 5px;
}
.tooltip[x-placement^="left"] .tooltip-arrow {
border-width: 5px 0 5px 5px;
border-top-color: transparent !important;
border-right-color: transparent !important;
border-bottom-color: transparent !important;
right: -5px;
top: calc(50% - 5px);
margin-left: 0;
margin-right: 0;
}
.tooltip.popover .popover-inner {
background: #f9f9f9;
color: black;
padding: 24px;
border-radius: 5px;
box-shadow: 0 5px 30px rgba(0, 0, 0, .1);
}
.tooltip.popover .popover-arrow {
border-color: #f9f9f9;
}
.tooltip[aria-hidden='true'] {
visibility: hidden;
opacity: 0;
transition: opacity .15s, visibility .15s;
}
.tooltip[aria-hidden='false'] {
visibility: visible;
opacity: 1;
transition: opacity .15s;
}

File diff suppressed because one or more lines are too long

View File

@ -57,80 +57,6 @@ $(document).ready(function() {
});
});
// Checkbox config ajax calls
$("[name='global-config-check']").bootstrapSwitch('offColor','danger');
$('input[name="global-config-check"]').on('switchChange.bootstrapSwitch', function(event, state) {
event.preventDefault();
var $this = $(this);
var config_id = $this.data("config_id");
$.ajax({
type: 'POST',
url: 'ajax_form.php',
data: {type: "update-config-item", config_id: config_id, config_value: state},
dataType: "json",
success: function (data) {
if (data.status == 'ok') {
toastr.success('Config updated');
} else {
toastr.error(data.message);
}
},
error: function () {
toastr.error(data.message);
}
});
});
// Input field config ajax calls
$(document).on('blur', 'input[name="global-config-input"]', function(event) {
event.preventDefault();
var $this = $(this);
var config_id = $this.data("config_id");
var config_value = $this.val();
if ($this[0].checkValidity()) {
$.ajax({
type: 'POST',
url: 'ajax_form.php',
data: {type: "update-config-item", config_id: config_id, config_value: config_value},
dataType: "json",
success: function (data) {
if (data.status == 'ok') {
toastr.success('Config updated');
} else {
toastr.error(data.message);
}
},
error: function () {
toastr.error(data.message);
}
});
}
});
// Select config ajax calls
$( 'select[name="global-config-select"]').change(function(event) {
event.preventDefault();
var $this = $(this);
var config_id = $this.data("config_id");
var config_value = $this.val();
$.ajax({
type: 'POST',
url: 'ajax_form.php',
data: {type: "update-config-item", config_id: config_id, config_value: config_value},
dataType: "json",
success: function (data) {
if (data.status == 'ok') {
toastr.success('Config updated');
} else {
toastr.error(data.message);
}
},
error: function () {
toastr.error(data.message);
}
});
});
oldW=$(window).width();
oldH=$(window).height();
});

154
html/js/manifest.js Normal file
View File

@ -0,0 +1,154 @@
/******/ (function(modules) { // webpackBootstrap
/******/ // install a JSONP callback for chunk loading
/******/ function webpackJsonpCallback(data) {
/******/ var chunkIds = data[0];
/******/ var moreModules = data[1];
/******/ var executeModules = data[2];
/******/
/******/ // add "moreModules" to the modules object,
/******/ // then flag all "chunkIds" as loaded and fire callback
/******/ var moduleId, chunkId, i = 0, resolves = [];
/******/ for(;i < chunkIds.length; i++) {
/******/ chunkId = chunkIds[i];
/******/ if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
/******/ resolves.push(installedChunks[chunkId][0]);
/******/ }
/******/ installedChunks[chunkId] = 0;
/******/ }
/******/ for(moduleId in moreModules) {
/******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
/******/ modules[moduleId] = moreModules[moduleId];
/******/ }
/******/ }
/******/ if(parentJsonpFunction) parentJsonpFunction(data);
/******/
/******/ while(resolves.length) {
/******/ resolves.shift()();
/******/ }
/******/
/******/ // add entry modules from loaded chunk to deferred list
/******/ deferredModules.push.apply(deferredModules, executeModules || []);
/******/
/******/ // run deferred modules when all chunks ready
/******/ return checkDeferredModules();
/******/ };
/******/ function checkDeferredModules() {
/******/ var result;
/******/ for(var i = 0; i < deferredModules.length; i++) {
/******/ var deferredModule = deferredModules[i];
/******/ var fulfilled = true;
/******/ for(var j = 1; j < deferredModule.length; j++) {
/******/ var depId = deferredModule[j];
/******/ if(installedChunks[depId] !== 0) fulfilled = false;
/******/ }
/******/ if(fulfilled) {
/******/ deferredModules.splice(i--, 1);
/******/ result = __webpack_require__(__webpack_require__.s = deferredModule[0]);
/******/ }
/******/ }
/******/
/******/ return result;
/******/ }
/******/
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // object to store loaded and loading chunks
/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
/******/ // Promise = chunk loading, 0 = chunk loaded
/******/ var installedChunks = {
/******/ "/js/manifest": 0
/******/ };
/******/
/******/ var deferredModules = [];
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "/";
/******/
/******/ var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
/******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
/******/ jsonpArray.push = webpackJsonpCallback;
/******/ jsonpArray = jsonpArray.slice();
/******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
/******/ var parentJsonpFunction = oldJsonpFunction;
/******/
/******/
/******/ // run deferred modules from other chunks
/******/ checkDeferredModules();
/******/ })
/************************************************************************/
/******/ ([]);

49011
html/js/vendor.js Normal file

File diff suppressed because one or more lines are too long

6
html/mix-manifest.json Normal file
View File

@ -0,0 +1,6 @@
{
"/js/app.js": "/js/app.js",
"/css/app.css": "/css/app.css",
"/js/manifest.js": "/js/manifest.js",
"/js/vendor.js": "/js/vendor.js"
}

View File

@ -21,105 +21,6 @@
// Please don't edit this file -- make changes to the configuration array in config.php
//
// Default directories
$config['project_name'] = 'LibreNMS';
$config['project_id'] = strtolower($config['project_name']);
//$config['temp_dir'] = '/tmp';
$config['log_dir'] = $config['install_dir'].'/logs';
// MySQL Debug level
$config['mysql_log_level'] = 'ERROR';
//MySQL Settings
$config['db_port'] = 3306;
$config['db_socket'] = null;
$config['db_name'] = 'librenms';
$config['db_user'] = 'librenms';
$config['db_pass'] = null;
$config['db_socket'] = null;
// What is my own hostname (used to identify this host in its own database)
$config['own_hostname'] = 'localhost';
// Location of executables
//$config['fping'] = '/usr/sbin/fping';
//$config['fping6'] = '/usr/sbin/fping6';
// https://docs.librenms.org/Support/Configuration/#fping
$config['fping_options']['timeout'] = 500;
$config['fping_options']['count'] = 3;
$config['fping_options']['interval'] = 500;
$config['snmpwalk'] = '/usr/bin/snmpwalk';
$config['snmpget'] = '/usr/bin/snmpget';
$config['snmpbulkwalk'] = '/usr/bin/snmpbulkwalk';
$config['snmptranslate'] = '/usr/bin/snmptranslate';
$config['whois'] = '/usr/bin/whois';
$config['ping'] = '/bin/ping';
$config['mtr'] = '/usr/bin/mtr';
$config['nmap'] = '/usr/bin/nmap';
$config['nagios_plugins'] = '/usr/lib/nagios/plugins';
$config['ipmitool'] = '/usr/bin/ipmitool';
$config['virsh'] = '/usr/bin/virsh';
$config['dot'] = '/usr/bin/dot';
$config['sfdp'] = '/usr/bin/sfdp';
$config['nfdump'] = '/usr/bin/nfdump';
$config['slow_statistics'] = true;
// THIS WILL CHANGE TO FALSE IN FUTURE
// RRD Format Settings
// These should not normally be changed
// Though one could conceivably increase or decrease the size of each RRA if one had performance problems
// Or if one had a very fast I/O subsystem with no performance worries.
$config['rrd_rra'] = ' RRA:AVERAGE:0.5:1:2016 RRA:AVERAGE:0.5:6:1440 RRA:AVERAGE:0.5:24:1440 RRA:AVERAGE:0.5:288:1440 ';
$config['rrd_rra'] .= ' RRA:MIN:0.5:1:720 RRA:MIN:0.5:6:1440 RRA:MIN:0.5:24:775 RRA:MIN:0.5:288:797 ';
$config['rrd_rra'] .= ' RRA:MAX:0.5:1:720 RRA:MAX:0.5:6:1440 RRA:MAX:0.5:24:775 RRA:MAX:0.5:288:797 ';
$config['rrd_rra'] .= ' RRA:LAST:0.5:1:1440 ';
//$config['rrd']['heartbeat'] = 600;
//$config['rrd']['step'] = 300;
// RRDCacheD - Make sure it can write to your RRD dir!
// $config['rrdcached'] = "unix:/var/run/rrdcached.sock";
// Web Interface Settings
if (isset($_SERVER['SERVER_NAME']) && isset($_SERVER['SERVER_PORT'])) {
if (strpos($_SERVER['SERVER_NAME'], ':')) {
// Literal IPv6
$config['base_url'] = 'http://['.$_SERVER['SERVER_NAME'].']'.($_SERVER['SERVER_PORT'] != 80 ? ':'.$_SERVER['SERVER_PORT'] : '').'/';
} else {
$config['base_url'] = 'http://'.$_SERVER['SERVER_NAME'].($_SERVER['SERVER_PORT'] != 80 ? ':'.$_SERVER['SERVER_PORT'] : '').'/';
}
}
$config['project_home'] = 'http://www.librenms.org/';
$config['project_issues'] = 'https://community.librenms.org/c/help';
$config['github_api'] = 'https://api.github.com/repos/librenms/librenms/';
$config['site_style'] = 'light';
// Options are dark or light
$config['stylesheet'] = 'css/styles.css';
$config['mono_font'] = 'DejaVuSansMono';
$config['favicon'] = '';
$config['page_refresh'] = '300';
// Refresh the page every xx seconds, 0 to disable
$config['front_page'] = 'pages/front/tiles.php';
$config['front_page_settings']['top']['ports'] = 10;
$config['front_page_settings']['top']['devices'] = 10;
$config['front_page_down_box_limit'] = 10;
$config['vertical_summary'] = 0;
// Enable to use vertical summary on front page instead of horizontal
$config['top_ports'] = 1;
// This enables the top X ports box
$config['top_devices'] = 1;
// This enables the top X devices box
$config['page_title_prefix'] = '';
$config['page_title_suffix'] = $config['project_name'];
$config['timestamp_format'] = 'd-m-Y H:i:s';
$config['page_gen'] = 0;
$config['enable_lazy_load'] = true;
// display MySqL & PHP stats in footer?
$config['login_message'] = 'Unauthorised access or use shall render the user liable to criminal and/or civil prosecution.';
$config['public_status'] = false;
// Enable public accessable status page
$config['old_graphs'] = 1;
// RRDfiles from before the great rra reform. This is default for a while.
$config['int_customers'] = 1;
// Enable Customer Port Parsing
@ -160,28 +61,6 @@ $config['snmp']['transports'] = array(
'tcp6',
);
$config['snmp']['version'] = ['v2c', 'v3', 'v1'];
// Default version to use
// SNMPv1/2c default settings
$config['snmp']['community'][0] = 'public';
// Communities to try during adding hosts and discovery
$config['snmp']['port'] = 161;
// Port Client SNMP is running on
// SNMPv3 default settings
// The array can be expanded to give another set of parameters
// NOTE: If you change these, also change the equivalents in includes/defaults.inc.php - not sure why they are separate
$config['snmp']['v3'][0]['authlevel'] = 'noAuthNoPriv';
// noAuthNoPriv | authNoPriv | authPriv
$config['snmp']['v3'][0]['authname'] = 'root';
// User Name (required even for noAuthNoPriv)
$config['snmp']['v3'][0]['authpass'] = '';
// Auth Passphrase
$config['snmp']['v3'][0]['authalgo'] = 'MD5';
// MD5 | SHA
$config['snmp']['v3'][0]['cryptopass'] = '';
// Privacy (Encryption) Passphrase
$config['snmp']['v3'][0]['cryptoalgo'] = 'AES';
// AES | DES
// Devices must respond to icmp by default
$config['icmp_check'] = true;
@ -537,31 +416,7 @@ $config['smokeping']['pings'] = 20;
// $config['oxidized']['enabled'] = FALSE;//Set to TRUE
// $config['oxidized']['url'] = 'http://127.0.0.1:8888';// Set the Oxidized rest URL
// $config['oxidized']['reload_nodes'] = FALSE;//Set to TRUE, check documentation
// NFSen RRD dir.
$config['nfsen_enable'] = 0;
// $config['nfsen_split_char'] = "_";
// $config['nfsen_rrds'] = "/var/nfsen/profiles-stat/live/";
// $config['nfsen_suffix'] = "_yourdomain_com";
$config['nfsen_subdirlayout'] = 1;
$config['nfsen_last_max'] = 153600; // 48 hours ago in seconds
$config['nfsen_top_max'] = 500; // max topN value for stats
$config['nfsen_top_N']=array( 10, 20, 50, 100, 200, 500 );
$config['nfsen_top_default']=20;
$config['nfsen_stat_default']='srcip';
$config['nfsen_order_default']='packets';
$config['nfsen_last_default']=900;
$config['nfsen_lasts']=array(
'300'=>'5 minutes',
'600'=>'10 minutes',
'900'=>'15 minutes',
'1800'=>'30 minutes',
'3600'=>'1 hour',
'9600'=>'3 hours',
'38400'=>'12 hours',
'76800'=>'24 hours',
'115200'=>'36 hours',
'153600'=>'48 hours',
);
// Location Mapping
// Use this feature to map ugly locations to pretty locations
// config['location_map']['Under the Sink'] = "Under The Sink, The Office, London, UK";
@ -614,9 +469,6 @@ $config['bad_if_regexp'][] = '/^sl[0-9]/';
// Rewrite Interfaces
$config['rewrite_if_regexp']['/^cpu interface/'] = 'Mgmt';
// Storage default warning percentage
$config['storage_perc_warn'] = 60;
$config['ignore_mount_removable'] = 1;
// Ignore removable disk storage
$config['ignore_mount_network'] = 1;
@ -751,10 +603,6 @@ $config['libvirt_protocols'] = array(
'qemu+ssh',
'xen+ssh',
);
// Mechanisms used, add or remove if not using this on any of your machines.
// Hardcoded ASN descriptions
$config['astext'][65332] = 'Cymru FullBogon Feed';
$config['astext'][65333] = 'Cymru Bogon Feed';
// Nicer labels for the SLA types
$config['sla_type_labels']['echo'] = 'ICMP ping';
@ -989,9 +837,6 @@ $config['gui']['network-map']['style'] = 'new';//old is also va
// Show errored ports in the summary table on the dashboard
$config['summary_errors'] = 0;
// Default width of the availability map's tiles
$config['availability-map-width'] = 25;
// Default notifications Feed
$config['notifications']['LibreNMS'] = 'http://www.librenms.org/notifications.rss';
$config['notifications']['local'] = 'misc/notifications.rss';

View File

@ -621,39 +621,7 @@ $config['device_types'][$i]['icon'] = 'workstation.png';
//
// No changes below this line #
//
$config['project_name_version'] = $config['project_name'];
// Set some times needed by loads of scripts (it's dynamic, so we do it here!)
$config['time']['now'] = time();
$config['time']['now'] -= ($config['time']['now'] % 300);
$config['time']['onehour'] = ($config['time']['now'] - 3600);
// time() - (1 * 60 * 60);
$config['time']['fourhour'] = ($config['time']['now'] - 14400);
// time() - (4 * 60 * 60);
$config['time']['sixhour'] = ($config['time']['now'] - 21600);
// time() - (6 * 60 * 60);
$config['time']['twelvehour'] = ($config['time']['now'] - 43200);
// time() - (12 * 60 * 60);
$config['time']['day'] = ($config['time']['now'] - 86400);
// time() - (24 * 60 * 60);
$config['time']['twoday'] = ($config['time']['now'] - 172800);
// time() - (2 * 24 * 60 * 60);
$config['time']['week'] = ($config['time']['now'] - 604800);
// time() - (7 * 24 * 60 * 60);
$config['time']['twoweek'] = ($config['time']['now'] - 1209600);
// time() - (2 * 7 * 24 * 60 * 60);
$config['time']['month'] = ($config['time']['now'] - 2678400);
// time() - (31 * 24 * 60 * 60);
$config['time']['twomonth'] = ($config['time']['now'] - 5356800);
// time() - (2 * 31 * 24 * 60 * 60);
$config['time']['threemonth'] = ($config['time']['now'] - 8035200);
// time() - (3 * 31 * 24 * 60 * 60);
$config['time']['sixmonth'] = ($config['time']['now'] - 16070400);
// time() - (6 * 31 * 24 * 60 * 60);
$config['time']['year'] = ($config['time']['now'] - 31536000);
// time() - (365 * 24 * 60 * 60);
$config['time']['twoyear'] = ($config['time']['now'] - 63072000);
// time() - (2 * 365 * 24 * 60 * 60);
// IPMI sensor type mappings
$config['ipmi_unit']['Volts'] = 'voltage';
$config['ipmi_unit']['degrees C'] = 'temperature';

View File

@ -932,7 +932,7 @@ function send_mail($emails, $subject, $message, $html = false)
$mail->addAddress($email, $email_name);
}
$mail->Subject = $subject;
$mail->XMailer = Config::get('project_name_version');
$mail->XMailer = Config::get('project_name');
$mail->CharSet = 'utf-8';
$mail->WordWrap = 76;
$mail->Body = $message;

View File

@ -1,43 +0,0 @@
<?php
/*
* LibreNMS
*
* Copyright (c) 2014 Neil Lathwood <https://github.com/laf/ http://www.lathwood.co.uk>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version. Please see LICENSE.txt at the top level of
* the source code distribution for details.
*/
header('Content-type: text/plain');
// FUA
if (!Auth::user()->hasGlobalAdmin()) {
die('ERROR: You need to be admin');
}
if (!is_numeric($_POST['config_id'])) {
echo 'error with data';
exit;
} else {
if ($_POST['state'] == 'true') {
$state = 1;
} elseif ($_POST['state'] == 'false') {
$state = 0;
} else {
$state = 0;
}
$update = dbUpdate(array('config_disabled' => $state), 'config', '`config_id` = ?', array($_POST['config_id']));
if (!empty($update) || $update == '0') {
echo 'success';
exit;
} else {
echo 'error';
exit;
}
}//end if

View File

@ -1,36 +0,0 @@
<?php
/*
* LibreNMS
*
* Copyright (c) 2014 Neil Lathwood <https://github.com/laf/ http://www.lathwood.co.uk>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version. Please see LICENSE.txt at the top level of
* the source code distribution for details.
*/
header('Content-type: text/plain');
// FUA
if (!Auth::user()->hasGlobalAdmin()) {
die('ERROR: You need to be admin');
}
if (!is_numeric($_POST['config_id']) || empty($_POST['data'])) {
echo 'error with data';
exit;
} else {
$data = mres($_POST['data']);
$update = dbUpdate(array('config_value' => json_encode($data, JSON_UNESCAPED_SLASHES)), 'config', '`config_id` = ?', array($_POST['config_id']));
if (!empty($update) || $update == '0') {
echo 'success';
exit;
} else {
echo 'error';
exit;
}
}

View File

@ -1,310 +0,0 @@
<?php
/*
* LibreNMS
*
* Copyright (c) 2014 Neil Lathwood <https://github.com/laf/ http://www.lathwood.co.uk/fa>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version. Please see LICENSE.txt at the top level of
* the source code distribution for details.
*/
header('Content-type: application/json');
if (!Auth::user()->hasGlobalAdmin()) {
$response = array(
'status' => 'error',
'message' => 'Need to be admin',
);
echo _json_encode($response);
exit;
}
$action = mres($_POST['action']);
$config_group = mres($_POST['config_group']);
$config_sub_group = mres($_POST['config_sub_group']);
$config_name = mres($_POST['config_name']);
$config_value = mres($_POST['config_value']);
$config_extra = mres($_POST['config_extra']);
$config_room_id = mres($_POST['config_room_id']);
$config_from = mres($_POST['config_from']);
$config_userkey = mres($_POST['config_userkey']);
$config_user = mres($_POST['config_user']);
$config_to = mres($_POST['config_to']);
$config_token = mres($_POST['config_token']);
$status = 'error';
$message = 'Error with config';
if ($action == 'remove' || preg_match('/^remove-.*$/', $action)) {
$config_id = mres($_POST['config_id']);
if (empty($config_id)) {
$message = 'No config id passed';
} else {
if (dbDelete('config', '`config_id`=?', array($config_id))) {
if ($action == 'remove-slack') {
dbDelete('config', "`config_name` LIKE 'alert.transports.slack.$config_id.%'");
} elseif ($action == 'remove-discord') {
dbDelete('config', "`config_name` LIKE 'alert.transports.discord.$config_id.%'");
} elseif ($action == 'remove-rocket') {
dbDelete('config', "`config_name` LIKE 'alert.transports.rocket.$config_id.%'");
} elseif ($action == 'remove-hipchat') {
dbDelete('config', "`config_name` LIKE 'alert.transports.hipchat.$config_id.%'");
} elseif ($action == 'remove-pushover') {
dbDelete('config', "`config_name` LIKE 'alert.transports.pushover.$config_id.%'");
} elseif ($action == 'remove-boxcar') {
dbDelete('config', "`config_name` LIKE 'alert.transports.boxcar.$config_id.%'");
} elseif ($action == 'remove-telegram') {
dbDelete('config', "`config_name` LIKE 'alert.transports.telegram.$config_id.%'");
} elseif ($action == 'remove-clickatell') {
dbDelete('config', "`config_name` LIKE 'alert.transports.clickatell.$config_id.%'");
} elseif ($action == 'remove-playsms') {
dbDelete('config', "`config_name` LIKE 'alert.transports.playsms.$config_id.%'");
} elseif ($action == 'remove-smseagle') {
dbDelete('config', "`config_name` LIKE 'alert.transports.smseagle.$config_id.%'");
}
$status = 'ok';
$message = 'Config item removed';
} else {
$message = 'General error, could not remove config';
}
}
} elseif ($action == 'add-slack') {
if (empty($config_value)) {
$message = 'No Slack url provided';
} else {
$config_id = dbInsert(array('config_name' => 'alert.transports.slack.', 'config_value' => $config_value, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $config_value, 'config_descr' => 'Slack Transport'), 'config');
if ($config_id > 0) {
dbUpdate(array('config_name' => 'alert.transports.slack.'.$config_id.'.url'), 'config', 'config_id=?', array($config_id));
$status = 'ok';
$message = 'Config item created';
$extras = explode('\n', $config_extra);
foreach ($extras as $option) {
list($k,$v) = explode('=', $option, 2);
if (!empty($k) || !empty($v)) {
dbInsert(array('config_name' => 'alert.transports.slack.'.$config_id.'.'.$k, 'config_value' => $v, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $v, 'config_descr' => 'Slack Transport'), 'config');
}
}
} else {
$message = 'Could not create config item';
}
}
} elseif ($action == 'add-discord') {
if (empty($config_value)) {
$message = 'No Discord url provided';
} else {
$config_id = dbInsert(array('config_name' => 'alert.transports.discord.', 'config_value' => $config_value, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $config_value, 'config_descr' => 'Discord Transport'), 'config');
if ($config_id > 0) {
dbUpdate(array('config_name' => 'alert.transports.discord.'.$config_id.'.url'), 'config', 'config_id=?', array($config_id));
$status = 'ok';
$message = 'Config item created';
$extras = explode('\n', $config_extra);
foreach ($extras as $option) {
list($k,$v) = explode('=', $option, 2);
if (!empty($k) || !empty($v)) {
dbInsert(array('config_name' => 'alert.transports.discord.'.$config_id.'.'.$k, 'config_value' => $v, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $v, 'config_descr' => 'Discord Transport'), 'config');
}
}
} else {
$message = 'Could not create config item';
}
}
} elseif ($action == 'add-rocket') {
if (empty($config_value)) {
$message = 'No Rocket.Chat url provided';
} else {
$config_id = dbInsert(array('config_name' => 'alert.transports.rocket.', 'config_value' => $config_value, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $config_value, 'config_descr' => 'Rocket.Chat Transport'), 'config');
if ($config_id > 0) {
dbUpdate(array('config_name' => 'alert.transports.rocket.'.$config_id.'.url'), 'config', 'config_id=?', array($config_id));
$status = 'ok';
$message = 'Config item created';
$extras = explode('\n', $config_extra);
foreach ($extras as $option) {
list($k,$v) = explode('=', $option, 2);
if (!empty($k) || !empty($v)) {
dbInsert(array('config_name' => 'alert.transports.rocket.'.$config_id.'.'.$k, 'config_value' => $v, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $v, 'config_descr' => 'Rocket.Chat Transport'), 'config');
}
}
} else {
$message = 'Could not create config item';
}
}
} elseif ($action == 'add-hipchat') {
if (empty($config_value) || empty($config_room_id) || empty($config_from)) {
$message = 'No hipchat url, room id or from provided';
} else {
$config_id = dbInsert(array('config_name' => 'alert.transports.hipchat.', 'config_value' => $config_value, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $config_value, 'config_descr' => 'Hipchat Transport'), 'config');
if ($config_id > 0) {
dbUpdate(array('config_name' => 'alert.transports.hipchat.'.$config_id.'.url'), 'config', 'config_id=?', array($config_id));
$additional_id['room_id'] = dbInsert(array('config_name' => 'alert.transports.hipchat.'.$config_id.'.room_id', 'config_value' => $config_room_id, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $config_room_id, 'config_descr' => 'Hipchat URL'), 'config');
$additional_id['from'] = dbInsert(array('config_name' => 'alert.transports.hipchat.'.$config_id.'.from', 'config_value' => $config_from, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $config_from, 'config_descr' => 'Hipchat From'), 'config');
$status = 'ok';
$message = 'Config item created';
$extras = explode('\n', $config_extra);
foreach ($extras as $option) {
list($k,$v) = explode('=', $option, 2);
if (!empty($k) || !empty($v)) {
dbInsert(array('config_name' => 'alert.transports.hipchat.'.$config_id.'.'.$k, 'config_value' => $v, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $v, 'config_descr' => 'Hipchat '.$v), 'config');
}
}
} else {
$message = 'Could not create config item';
}
}//end if
} elseif ($action == 'add-pushover') {
if (empty($config_value) || empty($config_userkey)) {
$message = 'No pushover appkey or userkey provided';
} else {
$config_id = dbInsert(array('config_name' => 'alert.transports.pushover.', 'config_value' => $config_value, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $config_value, 'config_descr' => 'Pushover Transport'), 'config');
if ($config_id > 0) {
dbUpdate(array('config_name' => 'alert.transports.pushover.'.$config_id.'.appkey'), 'config', 'config_id=?', array($config_id));
$additional_id['userkey'] = dbInsert(array('config_name' => 'alert.transports.pushover.'.$config_id.'.userkey', 'config_value' => $config_userkey, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $config_userkey, 'config_descr' => 'Pushver Userkey'), 'config');
$status = 'ok';
$message = 'Config item created';
$extras = explode('\n', $config_extra);
foreach ($extras as $option) {
list($k,$v) = explode('=', $option, 2);
if (!empty($k) || !empty($v)) {
dbInsert(array('config_name' => 'alert.transports.pushover.'.$config_id.'.'.$k, 'config_value' => $v, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $v, 'config_descr' => 'Pushover '.$v), 'config');
}
}
} else {
$message = 'Could not create config item';
}
}
} elseif ($action == 'add-boxcar') {
if (empty($config_value)) {
$message = 'No Boxcar access token provided';
} else {
$config_id = dbInsert(array('config_name' => 'alert.transports.boxcar.', 'config_value' => $config_value, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $config_value, 'config_descr' => 'Boxcar Transport'), 'config');
if ($config_id > 0) {
dbUpdate(array('config_name' => 'alert.transports.boxcar.'.$config_id.'.access_token'), 'config', 'config_id=?', array($config_id));
$status = 'ok';
$message = 'Config item created';
$extras = explode('\n', $config_extra);
foreach ($extras as $option) {
list($k,$v) = explode('=', $option, 2);
if (!empty($k) || !empty($v)) {
dbInsert(array('config_name' => 'alert.transports.boxcar.'.$config_id.'.'.$k, 'config_value' => $v, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $v, 'config_descr' => 'Boxcar '.$v), 'config');
}
}
} else {
$message = 'Could not create config item';
}
}
} elseif ($action == 'add-telegram') {
if (empty($config_value)) {
$message = 'No Telegram chat id provided';
} else {
$config_id = dbInsert(array('config_name' => 'alert.transports.telegram.', 'config_value' => $config_value, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $config_value, 'config_descr' => 'Telegram Transport'), 'config');
if ($config_id > 0) {
dbUpdate(array('config_name' => 'alert.transports.telegram.'.$config_id.'.chat_id'), 'config', 'config_id=?', array($config_id));
dbInsert(array('config_name' => 'alert.transports.telegram.'.$config_id.'.token', 'config_value' => $config_extra, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $config_extra, 'config_descr' => 'Telegram token'), 'config');
$status = 'ok';
$message = 'Config item created';
} else {
$message = 'Could not create config item';
}
}
} elseif ($action == 'add-clickatell') {
if (empty($config_value)) {
$message = 'No Clickatell token provided';
} elseif (empty($config_to)) {
$message = 'No mobile numbers provided';
} else {
$config_id = dbInsert(array('config_name' => 'alert.transports.clickatell.', 'config_value' => $config_value, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $config_value, 'config_descr' => 'Clickatell Transport'), 'config');
if ($config_id > 0) {
dbUpdate(array('config_name' => 'alert.transports.clickatell.'.$config_id.'.token'), 'config', 'config_id=?', array($config_id));
$status = 'ok';
$message = 'Config item created';
$mobiles = explode('\n', $config_to);
$x=0;
foreach ($mobiles as $mobile) {
if (!empty($mobile)) {
dbInsert(array('config_name' => 'alert.transports.clickatell.'.$config_id.'.to.'.$x, 'config_value' => $mobile, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $v, 'config_descr' => 'Clickatell mobile'), 'config');
$x++;
}
}
} else {
$message = 'Could not create config item';
}
}
} elseif ($action == 'add-playsms') {
if (empty($config_value)) {
$message = 'No PlaySMS URL provided';
} elseif (empty($config_user)) {
$message = 'No PlaySMS User provided';
} elseif (empty($config_to)) {
$message = 'No mobile numbers provided';
} else {
$config_id = dbInsert(array('config_name' => 'alert.transports.playsms.', 'config_value' => $config_value, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $config_value, 'config_descr' => 'PlaySMS Transport'), 'config');
if ($config_id > 0) {
dbUpdate(array('config_name' => 'alert.transports.playsms.'.$config_id.'.url'), 'config', 'config_id=?', array($config_id));
$additional_id['from'] = dbInsert(array('config_name' => 'alert.transports.playsms.'.$config_id.'.from', 'config_value' => $config_from, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $config_from, 'config_descr' => 'PlaySMS From'), 'config');
$additional_id['user'] = dbInsert(array('config_name' => 'alert.transports.playsms.'.$config_id.'.user', 'config_value' => $config_user, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $config_user, 'config_descr' => 'PlaySMS User'), 'config');
$additional_id['token'] = dbInsert(array('config_name' => 'alert.transports.playsms.'.$config_id.'.token', 'config_value' => $config_token, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $config_token, 'config_descr' => 'PlaySMS Token'), 'config');
$status = 'ok';
$message = 'Config item created';
$mobiles = explode('\n', $config_to);
$x=0;
foreach ($mobiles as $mobile) {
if (!empty($mobile)) {
dbInsert(array('config_name' => 'alert.transports.playsms.'.$config_id.'.to.'.$x, 'config_value' => $mobile, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $v, 'config_descr' => 'PlaySMS mobile'), 'config');
$x++;
}
}
} else {
$message = 'Could not create config item';
}
}
} elseif ($action == 'add-smseagle') {
if (empty($config_value)) {
$message = 'No SMSEagle URL provided';
} elseif (empty($config_user)) {
$message = 'No SMSEagle User provided';
} elseif (empty($config_to)) {
$message = 'No mobile numbers provided';
} else {
$config_id = dbInsert(array('config_name' => 'alert.transports.smseagle.', 'config_value' => $config_value, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $config_value, 'config_descr' => 'SMSEagle Transport'), 'config');
if ($config_id > 0) {
dbUpdate(array('config_name' => 'alert.transports.smseagle.'.$config_id.'.url'), 'config', 'config_id=?', array($config_id));
$additional_id['user'] = dbInsert(array('config_name' => 'alert.transports.smseagle.'.$config_id.'.user', 'config_value' => $config_user, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $config_user, 'config_descr' => 'SMSEagle User'), 'config');
$additional_id['token'] = dbInsert(array('config_name' => 'alert.transports.smseagle.'.$config_id.'.token', 'config_value' => $config_token, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $config_token, 'config_descr' => 'SMSEagle Token'), 'config');
$status = 'ok';
$message = 'Config item created';
$mobiles = explode('\n', $config_to);
$x=0;
foreach ($mobiles as $mobile) {
if (!empty($mobile)) {
dbInsert(array('config_name' => 'alert.transports.smseagle.'.$config_id.'.to.'.$x, 'config_value' => $mobile, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $v, 'config_descr' => 'SMSEagle mobile'), 'config');
$x++;
}
}
} else {
$message = 'Could not create config item';
}
}
} else {
if (empty($config_group) || empty($config_sub_group) || empty($config_name) || empty($config_value)) {
$message = 'Missing config name or value';
} else {
$config_id = dbInsert(array('config_name' => $config_name, 'config_value' => $config_value, 'config_group' => $config_group, 'config_sub_group' => $config_sub_group, 'config_default' => $config_value, 'config_descr' => 'API Transport'), 'config');
if ($config_id > 0) {
dbUpdate(array('config_name' => $config_name.$config_id), 'config', 'config_id=?', array($config_id));
$status = 'ok';
$message = 'Config item created';
} else {
$message = 'Could not create config item';
}
}
}//end if
$response = array(
'status' => $status,
'message' => $message,
'config_id' => $config_id,
'additional_id' => $additional_id,
);
echo _json_encode($response);

View File

@ -1,109 +0,0 @@
<?php
/*
* LibreNMS
*
* Copyright (c) 2014 Neil Lathwood <https://github.com/laf/ http://www.lathwood.co.uk/fa>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version. Please see LICENSE.txt at the top level of
* the source code distribution for details.
*/
if (!Auth::user()->hasGlobalAdmin()) {
header('Content-type: text/plain');
die('ERROR: You need to be admin');
}
$config_id = mres($_POST['config_id']);
$action = mres($_POST['action']);
$config_type = mres($_POST['config_type']);
$status = 'error';
if (!is_numeric($config_id)) {
$message = 'ERROR: No config item';
} elseif ($action == 'update-textarea') {
$extras = explode(PHP_EOL, $_POST['config_value']);
$x=0;
$db_id = [];
foreach ($extras as $option) {
list($k,$v) = explode('=', $option, 2);
if (!empty($k) || !empty($v)) {
if ($config_type == 'slack') {
$db_id[] = dbInsert(array('config_name' => 'alert.transports.slack.'.$config_id.'.'.$k, 'config_value' => $v, 'config_group' => 'alerting', 'config_sub_group' => 'transports', 'config_default' => $v, 'config_descr' => 'Slack Transport'), 'config');
} elseif ($config_type == 'rocket') {
$db_id[] = dbInsert(array('config_name' => 'alert.transports.rocket.'.$config_id.'.'.$k, 'config_value' => $v, 'config_group' => 'alerting', 'config_sub_group' => 'transports', 'config_default' => $v, 'config_descr' => 'Rocket.Chat Transport'), 'config');
} elseif ($config_type == 'hipchat') {
$db_id[] = dbInsert(array('config_name' => 'alert.transports.hipchat.'.$config_id.'.'.$k, 'config_value' => $v, 'config_group' => 'alerting', 'config_sub_group' => 'transports', 'config_default' => $v, 'config_descr' => 'Hipchat Transport'), 'config');
} elseif ($config_type == 'pushover') {
$db_id[] = dbInsert(array('config_name' => 'alert.transports.pushover.'.$config_id.'.'.$k, 'config_value' => $v, 'config_group' => 'alerting', 'config_sub_group' => 'transports', 'config_default' => $v, 'config_descr' => 'Pushover Transport'), 'config');
} elseif ($config_type == 'boxcar') {
$db_id[] = dbInsert(array('config_name' => 'alert.transports.boxcar.'.$config_id.'.'.$k, 'config_value' => $v, 'config_group' => 'alerting', 'config_sub_group' => 'transports', 'config_default' => $v, 'config_descr' => 'Boxcar Transport'), 'config');
} elseif ($config_type == 'clickatell') {
$db_id[] = dbInsert(array('config_name' => 'alert.transports.clickatell.to.'.$x, 'config_value' => $k, 'config_group' => 'alerting', 'config_sub_group' => 'transports', 'config_default' => $v, 'config_descr' => 'Clickatell Transport'), 'config');
$x++;
} elseif ($config_type == 'playsms') {
$db_id[] = dbInsert(array('config_name' => 'alert.transports.playsms.to.'.$x, 'config_value' => $k, 'config_group' => 'alerting', 'config_sub_group' => 'transports', 'config_default' => $v, 'config_descr' => 'PlaySMS Transport'), 'config');
$x++;
} elseif ($config_type == 'smseagle') {
$db_id[] = dbInsert(array('config_name' => 'alert.transports.smseagle.to.'.$x, 'config_value' => $k, 'config_group' => 'alerting', 'config_sub_group' => 'transports', 'config_default' => $v, 'config_descr' => 'SMSEagle Transport'), 'config');
$x++;
}
}
}
if (!empty($db_id) || empty($_POST['config_value'])) {
if (empty($_POST['config_value'])) {
$db_id = [0];
}
$placeholders = dbGenPlaceholders(count($db_id));
if ($config_type == 'slack') {
dbDelete('config', "(`config_name` LIKE 'alert.transports.slack.$config_id.%' AND `config_name` != 'alert.transports.slack.$config_id.url' AND `config_id` NOT IN $placeholders)", $db_id);
} elseif ($config_type == 'rocket') {
dbDelete('config', "(`config_name` LIKE 'alert.transports.rocket.$config_id.%' AND `config_name` != 'alert.transports.rocket.$config_id.url' AND `config_id` NOT IN $placeholders)", $db_id);
} elseif ($config_type == 'hipchat') {
dbDelete('config', "(`config_name` LIKE 'alert.transports.hipchat.$config_id.%' AND (`config_name` != 'alert.transports.hipchat.$config_id.url' AND `config_name` != 'alert.transports.hipchat.$config_id.room_id' AND `config_name` != 'alert.transports.hipchat.$config_id.from') AND `config_id` NOT IN $placeholders)", $db_id);
} elseif ($config_type == 'pushover') {
dbDelete('config', "(`config_name` LIKE 'alert.transports.pushover.$config_id.%' AND (`config_name` != 'alert.transports.pushover.$config_id.appkey' AND `config_name` != 'alert.transports.pushover.$config_id.userkey') AND `config_id` NOT IN $placeholders)", $db_id);
} elseif ($config_type == 'boxcar') {
dbDelete('config', "(`config_name` LIKE 'alert.transports.boxcar.$config_id.%' AND (`config_name` != 'alert.transports.boxcar.$config_id.access_token' AND `config_name` != 'alert.transports.boxcar.$config_id.userkey') AND `config_id` NOT IN $placeholders)", $db_id);
} elseif ($config_type == 'clickatell') {
dbDelete('config', "(`config_name` LIKE 'alert.transports.clickatell.to.%' AND `config_id` NOT IN $placeholders)", $db_id);
} elseif ($config_type == 'playsms') {
dbDelete('config', "(`config_name` LIKE 'alert.transports.playsms.to.%' AND `config_id` NOT IN $placeholders)", $db_id);
} elseif ($config_type == 'smseagle') {
dbDelete('config', "(`config_name` LIKE 'alert.transports.smseagle.to.%' AND `config_id` NOT IN $placeholders)", $db_id);
}
}
$message = 'Config item has been updated:';
$status = 'ok';
} else {
$state = $_POST['config_value'];
if (filter_var($value, FILTER_VALIDATE_INT)) {
$state = (int)$value;
} elseif (filter_var($value, FILTER_VALIDATE_FLOAT)) {
$state = (float)$value;
} elseif (filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) !== null) {
$state = filter_var($value, FILTER_VALIDATE_BOOLEAN);
}
$update = dbUpdate(['config_value' => json_encode($state, JSON_UNESCAPED_SLASHES)], 'config', '`config_id`=?', array($config_id));
if (!empty($update) || $update == '0') {
$message = 'Alert rule has been updated.';
$status = 'ok';
} else {
$message = 'ERROR: Alert rule has not been updated.';
}
}//end if
$response = array(
'status' => $status,
'message' => $message,
);
header('Content-type: application/json');
echo _json_encode($response);

View File

@ -914,18 +914,6 @@ function clean_bootgrid($string)
return $output;
}//end clean_bootgrid()
function get_config_by_group($group)
{
return \App\Models\Config::query()->where('config_group', $group)->get()->map(function ($config_item) {
if ($config_item['config_value'] === true) {
$config_item['config_checked'] = 'checked';
}
return $config_item;
})->keyBy('config_name')->toArray();
}//end get_config_by_group()
function get_url()
{
// http://stackoverflow.com/questions/2820723/how-to-get-base-url-with-php
@ -1038,95 +1026,6 @@ function dynamic_override_config($type, $name, $device)
}
}//end dynamic_override_config()
function generate_dynamic_config_panel($title, $config_groups, $items = array(), $transport = '', $end_panel = true)
{
$anchor = md5($title);
$output = '
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#' . $anchor . '"><i class="fa fa-caret-down"></i> ' . $title . '</a>
';
if (!empty($transport)) {
$output .= '<button name="test-alert" id="test-alert" type="button" data-transport="' . $transport . '" class="btn btn-primary btn-xs pull-right">Test transport</button>';
}
$output .= '
</h4>
</div>
<div id="' . $anchor . '" class="panel-collapse collapse">
<div class="panel-body">
';
if (!empty($items)) {
foreach ($items as $item) {
$output .= '
<div class="form-group has-feedback ' . (isset($item['class']) ? $item['class'] : '') . '">
<label for="' . $item['name'] . '"" class="col-sm-4 control-label">' . $item['descr'] . ' </label>
<div data-toggle="tooltip" title="' . $config_groups[$item['name']]['config_descr'] . '" class="toolTip fa fa-fw fa-lg fa-question-circle"></div>
<div class="col-sm-4">
';
if ($item['type'] == 'checkbox') {
$output .= '<input id="' . $item['name'] . '" type="checkbox" name="global-config-check" ' . $config_groups[$item['name']]['config_checked'] . ' data-on-text="Yes" data-off-text="No" data-size="small" data-config_id="' . $config_groups[$item['name']]['config_id'] . '">';
} elseif ($item['type'] == 'text') {
$pattern = isset($item['pattern']) ? ' pattern="' . $item['pattern'] . '"' : "";
$pattern .= isset($item['required']) && $item['required'] ? " required" : "";
$output .= '
<input id="' . $item['name'] . '" class="form-control validation" type="text" name="global-config-input" value="' . $config_groups[$item['name']]['config_value'] . '" data-config_id="' . $config_groups[$item['name']]['config_id'] . '"' . $pattern . '>
<span class="form-control-feedback"><i class="fa" aria-hidden="true"></i></span>
';
} elseif ($item['type'] == 'password') {
$output .= '
<input id="' . $item['name'] . '" class="form-control" type="password" name="global-config-input" value="' . $config_groups[$item['name']]['config_value'] . '" data-config_id="' . $config_groups[$item['name']]['config_id'] . '">
<span class="form-control-feedback"><i class="fa" aria-hidden="true"></i></span>
';
} elseif ($item['type'] == 'numeric') {
$output .= '
<input id="' . $item['name'] . '" class="form-control" onkeypress="return (event.charCode == 8 || event.charCode == 0) ? null : event.charCode >= 48 && event.charCode <= 57" type="text" name="global-config-input" value="' . $config_groups[$item['name']]['config_value'] . '" data-config_id="' . $config_groups[$item['name']]['config_id'] . '">
<span class="glyphicon form-control-feedback" aria-hidden="true"></span>
';
} elseif ($item['type'] == 'select') {
$output .= '
<select id="' . ($config_groups[$item['name']]['name'] ?: $item['name']) . '" class="form-control" name="global-config-select" data-config_id="' . $config_groups[$item['name']]['config_id'] . '">
';
if (!empty($item['options'])) {
foreach ($item['options'] as $option) {
if (gettype($option) == 'string') {
/* for backwards-compatibility */
$tmp_opt = $option;
$option = array(
'value' => $tmp_opt,
'description' => $tmp_opt,
);
}
$output .= '<option value="' . $option['value'] . '"';
if ($option['value'] == $config_groups[$item['name']]['config_value']) {
$output .= ' selected';
}
$output .= '>' . $option['description'] . '</option>';
}
}
$output .= '
</select>
<span class="form-control-feedback"><i class="fa" aria-hidden="true"></i></span>
';
}
$output .= '
</div>
</div>
';
}
}
if ($end_panel === true) {
$output .= '
</div>
</div>
</div>
';
}
return $output;
}//end generate_dynamic_config_panel()
/**
* Return the rows from 'ports' for all ports of a certain type as parsed by port_descr_parser.
* One or an array of strings can be provided as an argument; if an array is passed, all ports matching

View File

@ -1,118 +0,0 @@
<?php
/**
* settings.inc.php
*
* Web page to display settings
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @package LibreNMS
* @link http://librenms.org
* @copyright 2015 Daniel Preussker <f0o@devilcode.org>
* @copyright 2016 Tony Murray <murraytony@gmail.com>
* @author f0o <f0o@devilcode.org>
*/
?>
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<span id="message"></span>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-xs-12">
<?php
if (Auth::user()->hasGlobalAdmin()) {
echo '<ul class="nav nav-tabs">';
$pages = dbFetchRows("SELECT DISTINCT `config_group` FROM `config` WHERE `config_group` IS NOT NULL AND `config_group` != ''");
array_unshift($pages, array('config_group' => 'Global')); // Add Global tab
$curr_page = basename($vars['sub'] ?? 'Global');
foreach ($pages as $sub_page) {
$sub_page = $sub_page['config_group'];
$page_name = ucfirst($sub_page) . ' Settings';
echo '<li';
if ($sub_page == $curr_page) {
echo ' class="active"';
}
echo '><a href="';
echo generate_url(array(
'page' => 'settings',
'sub' => $sub_page
));
echo '">' . $page_name . '</a></li>';
}
echo '</ul></div></div><br />';
if ($curr_page != 'Global') {
if (file_exists("includes/html/pages/settings/$curr_page.inc.php")) {
require_once "includes/html/pages/settings/$curr_page.inc.php";
} else {
print_error("This settings page doesn't exist, please go to the main settings page");
}
} else {
/**
* Array-To-Table
* @param array $a N-Dimensional, Associative Array
* @return string
*/
function a2t($a)
{
$excluded = array(
'db_pass',
'email_smtp_password',
'password',
'auth_ad_bindpassword',
);
ksort($a);
$r = '<table class="table table-condensed table-hover"><tbody>';
foreach ($a as $k => $v) {
if (!empty($v)) {
if (!in_array($k, $excluded, true)) {
$r .= '<tr><td class="col-md-2"><i><b>' . $k . '</b></i></td><td class="col-md-10">';
$r .= is_array($v) ? a2t($v) : '<code>' . wordwrap($v, 75, '<br/>') . '</code>';
$r .= '</td></tr>';
}
}
}
$r .= '</tbody></table>';
return $r;
}
echo '<div class="table-responsive">' . a2t(\LibreNMS\Config::getAll()) . '</div>';
if ($debug && Auth::user()->hasGlobalAdmin()) {
echo("<pre>");
print_r(\LibreNMS\Config::getAll());
echo("</pre>");
}
}
} else {
include 'includes/html/error-no-perm.inc.php';
}
?>
</div>

View File

@ -1,272 +0,0 @@
<?php
/*
* LibreNMS
*
* Copyright (c) 2014 Neil Lathwood <https://github.com/laf/ http://www.lathwood.co.uk/fa>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version. Please see LICENSE.txt at the top level of
* the source code distribution for details.
*/
use LibreNMS\Config;
$no_refresh = true;
$config_groups = get_config_by_group('alerting');
if (Config::has('base_url') && filter_var(Config::get('base_url') . '/' . $_SERVER['REQUEST_URI'], FILTER_VALIDATE_URL)) {
$callback = Config::get('base_url') . '/' . $_SERVER['REQUEST_URI'] . '/';
} else {
$callback = get_url().'/';
}
$callback = urlencode($callback);
$general_conf = array(
array('name' => 'alert.disable',
'descr' => 'Disable alerting',
'type' => 'checkbox',
),
array('name' => 'alert.admins',
'descr' => 'Issue alerts to admins',
'type' => 'checkbox',
),
array('name' => 'alert.globals',
'descr' => 'Issue alerts to read only users',
'type' => 'checkbox',
),
array('name' => 'alert.users',
'descr' => 'Issue alerts to normal users',
'type' => 'checkbox',
),
array('name' => 'alert.syscontact',
'descr' => 'Issue alerts to sysContact',
'type' => 'checkbox',
),
array('name' => 'alert.default_only',
'descr' => 'Send alerts to default contact only',
'type' => 'checkbox',
),
array('name' => 'alert.default_copy',
'descr' => 'Copy all email alerts to default contact',
'type' => 'checkbox',
),
array('name' => 'alert.default_mail',
'descr' => 'Default contact',
'type' => 'text',
'pattern' => '[a-zA-Z0-9_\-\.\+]+@[a-zA-Z0-9_\-\.]+\.[a-zA-Z]{2,18}',
),
array('name' => 'alert.tolerance_window',
'descr' => 'Tolerance window for cron',
'type' => 'numeric',
'required' => true,
),
array('name' => 'alert.fixed-contacts',
'descr' => 'Updates to contact email addresses not honored',
'type' => 'checkbox',
),
[
'name' => 'alert.ack_until_clear',
'descr' => 'Default acknowledge until alert clears option',
'type' => 'checkbox',
]
);
$mail_conf = [
[
'name' => 'alert.transports.mail',
'descr' => 'Enable email alerting',
'type' => 'checkbox',
],
[
'name' => 'email_backend',
'descr' => 'How to deliver mail',
'options' => Config::get('email_backend_options', ['mail', 'sendmail', 'smtp']),
'type' => 'select',
],
[
'name' => 'email_user',
'descr' => 'From name',
'type' => 'text',
],
[
'name' => 'email_from',
'descr' => 'From email address',
'type' => 'text',
'pattern' => '[a-zA-Z0-9_\-\.\+]+@[a-zA-Z0-9_\-\.]+\.[a-zA-Z]{2,18}',
],
[
'name' => 'email_html',
'descr' => 'Use HTML emails',
'type' => 'checkbox',
],
[
'name' => 'email_sendmail_path',
'descr' => 'Sendmail path',
'type' => 'text',
'class' => 'sendmail-form',
],
[
'name' => 'email_smtp_host',
'descr' => 'SMTP Host',
'type' => 'text',
'pattern' => '[a-zA-Z0-9_\-\.]+',
'class' => 'smtp-form',
],
[
'name' => 'email_smtp_port',
'descr' => 'SMTP Port',
'type' => 'numeric',
'class' => 'smtp-form',
'required' => true,
],
[
'name' => 'email_smtp_timeout',
'descr' => 'SMTP Timeout',
'type' => 'numeric',
'class' => 'smtp-form',
'required' => true,
],
[
'name' => 'email_smtp_secure',
'descr' => 'SMTP Secure',
'type' => 'select',
'class' => 'smtp-form',
'options' => Config::get('email_smtp_secure_options', ['', 'tls', 'ssl']),
],
[
'name' => 'email_auto_tls',
'descr' => 'SMTP Auto TLS Support',
'type' => 'select',
'class' => 'smtp-form',
'options' => ['true', 'false'],
],
[
'name' => 'email_smtp_auth',
'descr' => 'SMTP Authentication',
'type' => 'checkbox',
'class' => 'smtp-form',
],
[
'name' => 'email_smtp_username',
'descr' => 'SMTP Authentication Username',
'type' => 'text',
'class' => 'smtp-form',
],
[
'name' => 'email_smtp_password',
'descr' => 'SMTP Authentication Password',
'type' => 'password',
'class' => 'smtp-form',
],
];
echo '
<div class="panel-group" id="accordion">
<form class="form-horizontal" role="form" action="" method="post">
';
echo csrf_field();
echo generate_dynamic_config_panel('General alert settings', $config_groups, $general_conf);
echo generate_dynamic_config_panel('Email options', $config_groups, $mail_conf);
echo '
</form>
</div>
';
?>
<script>
$(".toolTip").tooltip();
$('#email_backend').change(function () {
var type = this.value;
if (type === 'sendmail') {
$('.smtp-form').hide();
$('.sendmail-form').show();
} else if (type === 'smtp') {
$('.sendmail-form').hide();
$('.smtp-form').show();
} else {
$('.smtp-form').hide();
$('.sendmail-form').hide();
}
}).change(); // trigger initially
apiIndex = 0;
$( 'select[name="global-config-select"]').change(function(event) {
event.preventDefault();
var $this = $(this);
var config_id = $this.data("config_id");
var config_value = $this.val();
$.ajax({
type: 'POST',
url: 'ajax_form.php',
data: {type: "update-config-item", config_id: config_id, config_value: config_value},
dataType: "json",
success: function (data) {
if (data.status == 'ok') {
$this.closest('.form-group').addClass('has-success');
$this.next().addClass('fa-check');
setTimeout(function(){
$this.closest('.form-group').removeClass('has-success');
$this.next().removeClass('fa-check');
}, 2000);
} else {
$(this).closest('.form-group').addClass('has-error');
$this.next().addClass('fa-times');
setTimeout(function(){
$this.closest('.form-group').removeClass('has-error');
$this.next().removeClass('fa-times');
}, 2000);
}
},
error: function () {
$("#message").html('<div class="alert alert-danger">An error occurred.</div>');
}
});
});
$(document).on('blur', 'textarea[name="global-config-textarea"]', function(event) {
event.preventDefault();
var $this = $(this);
var config_id = $this.data("config_id");
var config_value = $this.val();
var config_type = $this.data("type");
$.ajax({
type: 'POST',
url: 'ajax_form.php',
data: {type: "update-config-item", action: 'update-textarea', config_type: config_type, config_id: config_id, config_value: config_value},
dataType: "json",
success: function (data) {
if (data.status == 'ok') {
$this.closest('.form-group').addClass('has-success');
$this.next().addClass('fa-check');
setTimeout(function(){
$this.closest('.form-group').removeClass('has-success');
$this.next().removeClass('fa-check');
}, 2000);
} else {
$(this).closest('.form-group').addClass('has-error');
$this.next().addClass('fa-times');
setTimeout(function(){
$this.closest('.form-group').removeClass('has-error');
$this.next().removeClass('fa-times');
}, 2000);
}
},
error: function () {
$("#message").html('<div class="alert alert-danger">An error occurred.</div>');
}
});
});
</script>

View File

@ -1,127 +0,0 @@
<?php
$no_refresh = true;
$config_groups = get_config_by_group('external');
$location_conf = [
[
'name' => 'geoloc.engine',
'descr' => 'Geocoding Engine',
'type' => 'select',
'options' => [
['value' => 'google', 'description' => 'Google Maps'],
['value' => 'openstreetmap', 'description' => 'OpenStreetMap'],
['value' => 'mapquest', 'description' => 'MapQuest'],
['value' => 'bing', 'description' => 'Bing Maps'],
]
],
[
'name' => 'geoloc.api_key',
'descr' => 'Geocoding API Key',
'type' => 'text',
'class' => 'geoloc_api_key'
],
];
$oxidized_conf = array(
array('name' => 'oxidized.enabled',
'descr' => 'Enable Oxidized support',
'type' => 'checkbox',
),
array('name' => 'oxidized.url',
'descr' => 'URL to your Oxidized API',
'type' => 'text',
'pattern' => '[a-zA-Z0-9]{1,5}://.*',
'required' => true,
),
array('name' => 'oxidized.features.versioning',
'descr' => 'Enable config versioning access',
'type' => 'checkbox',
),
array('name' => 'oxidized.group_support',
'descr' => 'Enable the return of groups to Oxidized',
'type' => 'checkbox',
),
array('name' => 'oxidized.default_group',
'descr' => 'Set the default group returned',
'type' => 'text',
),
array('name' => 'oxidized.reload_nodes',
'descr' => 'Reload Oxidized nodes list, each time a device is added',
'type' => 'checkbox',
),
);
$unixagent_conf = array(
array('name' => 'unix-agent.port',
'descr' => 'Default unix-agent port',
'type' => 'numeric',
'required' => true,
),
array('name' => 'unix-agent.connection-timeout',
'descr' => 'Connection timeout',
'type' => 'numeric',
'required' => true,
),
array('name' => 'unix-agent.read-timeout',
'descr' => 'Read timeout',
'type' => 'numeric',
'required' => true,
),
);
$rrdtool_conf = array(
array('name' => 'rrdtool',
'descr' => 'Path to rrdtool binary',
'type' => 'text',
),
array('name' => 'rrdtool_tune',
'descr' => 'Tune all rrd port files to use max values',
'type' => 'checkbox',
),
array('name' => 'rrd.step',
'descr' => 'Change the rrd step value (default 300)',
'type' => 'numeric',
'required' => true,
),
array('name' => 'rrd.heartbeat',
'descr' => 'Change the rrd heartbeat value (default 600)',
'type' => 'numeric',
'required' => true,
),
);
$peeringdb_conf = array(
array('name' => 'peeringdb.enabled',
'descr' => 'Enable PeeringDB lookup (data is downloaded with daily.sh)',
'type' => 'checkbox',
),
);
echo '
<div class="panel-group" id="accordion">
<form class="form-horizontal" role="form" action="" method="post">
';
echo csrf_field();
echo generate_dynamic_config_panel('Location Geocoding', $config_groups, $location_conf);
echo generate_dynamic_config_panel('Oxidized integration', $config_groups, $oxidized_conf);
echo generate_dynamic_config_panel('Unix-agent integration', $config_groups, $unixagent_conf);
echo generate_dynamic_config_panel('RRDTool Setup', $config_groups, $rrdtool_conf);
echo generate_dynamic_config_panel('PeeringDB Integration', $config_groups, $peeringdb_conf);
?>
</form>
</div>
<script>
$('#geoloc\\.engine').change(function () {
var engine = this.value;
if (engine === 'openstreetmap') {
$('.geoloc_api_key').hide();
} else {
$('.geoloc_api_key').show();
}
}).change(); // trigger initially
</script>

View File

@ -1,97 +0,0 @@
<?php
$no_refresh = true;
$config_groups = get_config_by_group('webui');
$search_conf = array(
array('name' => 'webui.global_search_result_limit',
'descr' => 'Set the max search result limit',
'type' => 'numeric',
'required' => true,
),
);
$graph_conf = [
[
'name' => 'webui.min_graph_height',
'descr' => 'Set the minimum graph height',
'type' => 'numeric',
'required' => true,
],
[
'name' => 'webui.graph_type',
'descr' => 'Set the graph type',
'type' => 'select',
'options' => [
'png' => 'png',
'svg' => 'svg',
],
],
[
'name' => 'webui.graph_stacked',
'descr' => 'Use stacked graphs',
'type' => 'checkbox',
],
[
'name' => 'webui.dynamic_graphs',
'descr' => 'Enable dynamic graphs',
'type' => 'checkbox',
]
];
$availability_map_conf = array(
array('name' => 'webui.availability_map_compact',
'descr' => 'Availability map compact view',
'type' => 'checkbox',
),
array('name' => 'webui.availability_map_sort_status',
'descr' => 'Sort devices by status',
'type' => 'checkbox',
),
array('name' => 'webui.availability_map_use_device_groups',
'descr' => 'Use device groups filter',
'type' => 'checkbox',
),
array('name' => 'webui.availability_map_box_size',
'descr' => 'Availability box width',
'type' => 'numeric',
),
);
$dashboard_conf = array(
array('name' => 'webui.default_dashboard_id',
'descr' => 'Set global default dashboard id',
'type' => 'select',
'options' => dbFetchRows(
"SELECT 0 as `value`, 'no default dashboard' as `description`
UNION ALL
SELECT `dashboards`.`dashboard_id` as `value`,
CONCAT( `users`.`username`, ':', `dashboards`.`dashboard_name`,
CASE
WHEN `dashboards`.`access` = 1 THEN ' (shared, read-only)'
WHEN `dashboards`.`access` = 2 THEN ' (shared, read-write)'
ELSE ''
END
) as `description`
FROM `dashboards` JOIN `users` ON `users`.`user_id` = `dashboards`.`user_id`
WHERE `dashboards`.`access` > 0;"
),
),
);
echo '
<div class="panel-group" id="accordion">
<form class="form-horizontal" role="form" action="" method="post">
';
echo csrf_field();
echo generate_dynamic_config_panel('Graph settings', $config_groups, $graph_conf);
echo generate_dynamic_config_panel('Search settings', $config_groups, $search_conf);
echo generate_dynamic_config_panel('Availability map settings', $config_groups, $availability_map_conf);
echo generate_dynamic_config_panel('Dashboard settings', $config_groups, $dashboard_conf);
echo '
</form>
</div>
';

View File

@ -92,7 +92,7 @@ foreach (dbFetchRows($sql, $param) as $sensor) {
unset($link_array['height'], $link_array['width'], $link_array['legend']);
$link_graph = generate_url($link_array);
$link = generate_url(array('page' => 'device', 'device' => $sensor['device_id'], 'tab' => $tab, 'metric' => $sensor['sensor_class']));
$link = generate_url(array('page' => 'device', 'device' => $sensor['device_id'], 'tab' => $group, 'metric' => $sensor['sensor_class']));
$overlib_content = '<div style="width: 580px;"><span class="overlib-text">'.$sensor['hostname'].' - '.$sensor['sensor_descr'].'</span>';
foreach (array('day', 'week', 'month', 'year') as $period) {

View File

@ -16,7 +16,7 @@
*/
$table = 'sensors';
$tab = 'health';
$group = 'health';
$translations = 'sensors';
include 'sensors-common.php';

View File

@ -555,11 +555,9 @@ foreach ($ports as $port) {
$port['update']['ifIndex'] = $ifIndex;
}
if (Config::get('slow_statistics') == true) {
$port['update']['poll_time'] = $polled;
$port['update']['poll_prev'] = $port['poll_time'];
$port['update']['poll_period'] = $polled_period;
}
$port['update']['poll_time'] = $polled;
$port['update']['poll_prev'] = $port['poll_time'];
$port['update']['poll_period'] = $polled_period;
if ($device['os'] === 'airos-af' && $port['ifAlias'] === 'eth0') {
$airos_stats = snmpwalk_cache_oid($device, '.1.3.6.1.4.1.41112.1.3.3.1', array(), 'UBNT-AirFIBER-MIB');
@ -755,10 +753,8 @@ foreach ($ports as $port) {
$port_update = 'update_extended';
}
if (Config::get('slow_statistics') == true) {
$port[$port_update][$oid] = set_numeric($this_port[$oid]);
$port[$port_update][$oid . '_prev'] = set_numeric($port[$oid]);
}
$port[$port_update][$oid] = set_numeric($this_port[$oid]);
$port[$port_update][$oid . '_prev'] = set_numeric($port[$oid]);
$oid_prev = $oid . '_prev';
if (isset($port[$oid])) {
@ -771,11 +767,8 @@ foreach ($ports as $port) {
$port['stats'][$oid . '_rate'] = $oid_rate;
$port['stats'][$oid . '_diff'] = $oid_diff;
if (Config::get('slow_statistics') == true) {
$port[$port_update][$oid . '_rate'] = $oid_rate;
$port[$port_update][$oid . '_delta'] = $oid_diff;
}
$port[$port_update][$oid . '_rate'] = $oid_rate;
$port[$port_update][$oid . '_delta'] = $oid_diff;
d_echo("\n $oid ($oid_diff B) $oid_rate Bps $polled_period secs\n");

3780
misc/config_definitions.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -398,14 +398,6 @@ config:
- { Field: config_id, Type: 'int(10) unsigned', 'Null': false, Extra: auto_increment }
- { Field: config_name, Type: varchar(255), 'Null': false, Extra: '' }
- { Field: config_value, Type: varchar(512), 'Null': false, Extra: '' }
- { Field: config_default, Type: varchar(512), 'Null': false, Extra: '' }
- { Field: config_descr, Type: varchar(100), 'Null': false, Extra: '' }
- { Field: config_group, Type: varchar(50), 'Null': false, Extra: '' }
- { Field: config_group_order, Type: int(11), 'Null': false, Extra: '', Default: '0' }
- { Field: config_sub_group, Type: varchar(50), 'Null': false, Extra: '' }
- { Field: config_sub_group_order, Type: int(11), 'Null': false, Extra: '', Default: '0' }
- { Field: config_hidden, Type: 'enum(''0'',''1'')', 'Null': false, Extra: '', Default: '0' }
- { Field: config_disabled, Type: 'enum(''0'',''1'')', 'Null': false, Extra: '', Default: '0' }
Indexes:
PRIMARY: { Name: PRIMARY, Columns: [config_id], Unique: true, Type: BTREE }
uniqueindex_configname: { Name: uniqueindex_configname, Columns: [config_name], Unique: true, Type: BTREE }

9731
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -11,15 +11,23 @@
},
"devDependencies": {
"axios": "^0.19",
"bootstrap": "^4.1.0",
"cross-env": "^5.1",
"bootstrap": "^4.3.1",
"cross-env": "^5.2.1",
"jquery": "^3.2",
"laravel-mix": "^4.0.7",
"lodash": "^4.17.13",
"laravel-mix": "^4.1.4",
"lodash": "^4.17.15",
"popper.js": "^1.12",
"resolve-url-loader": "^2.3.1",
"sass": "^1.15.2",
"sass-loader": "^7.1.0",
"vue": "^2.5.17"
"sass": "^1.23.0",
"sass-loader": "^7.3.1",
"vue": "^2.6.10",
"vue-template-compiler": "^2.6.10"
},
"dependencies": {
"v-tooltip": "^2.0.2",
"vue-i18n": "^8.14.1",
"vue-js-toggle-button": "^1.3.2",
"vue-select": "^3.2.0",
"vuedraggable": "^2.23.2"
}
}

View File

@ -33,7 +33,7 @@ $init_modules = ['polling', 'alerts', 'laravel'];
require __DIR__ . '/includes/init.php';
$poller_start = microtime(true);
echo Config::get('base_url') . " Poller\n";
echo Config::get('project_name')." Poller\n";
$options = getopt('h:m:i:n:r::d::v::a::f::q');

View File

@ -1,13 +1,14 @@
/**
* First we will load all of this project's JavaScript dependencies which
* includes Vue and other libraries. It is a great starting point when
* building robust, powerful web applications using Vue and Laravel.
*/
require('./bootstrap');
window.Vue = require('vue');
import { i18n } from "./plugins/i18n.js"; // translation
/**
* The following block of code may be used to automatically register your
@ -20,6 +21,27 @@ window.Vue = require('vue');
const files = require.context('./', true, /\.vue$/i);
files.keys().map(key => Vue.component(key.split('/').pop().split('.')[0], files(key).default));
import ToggleButton from 'vue-js-toggle-button'
Vue.use(ToggleButton);
import VTooltip from 'v-tooltip'
Vue.use(VTooltip);
import vSelect from 'vue-select'
Vue.component('v-select', vSelect);
Vue.mixin({
methods: {
route: route
}
});
Vue.filter('ucfirst', function (value) {
if (!value) return '';
value = value.toString();
return value.charAt(0).toUpperCase() + value.slice(1)
});
/**
* Next, we will create a fresh Vue application instance and attach it to
* the page. Then, you may begin adding components to this application
@ -28,4 +50,5 @@ files.keys().map(key => Vue.component(key.split('/').pop().split('.')[0], files(
const app = new Vue({
el: '#app',
i18n,
});

View File

@ -8,9 +8,9 @@ window._ = require('lodash');
try {
window.Popper = require('popper.js').default;
window.$ = window.jQuery = require('jquery');
// window.$ = window.jQuery = require('jquery');
require('bootstrap');
// require('bootstrap');
} catch (e) {}
/**

View File

@ -0,0 +1,66 @@
<!--
- Accordian.vue
-
- Accordion component contains multiple
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- @package LibreNMS
- @link http://librenms.org
- @copyright 2019 Tony Murray
- @author Tony Murray <murraytony@gmail.com>
-->
<template>
<div class="panel-group" role="tablist">
<slot></slot>
</div>
</template>
<script>
export default {
name: "Accordion",
props: {
multiple: {
type: Boolean,
default: false
}
},
methods: {
setActive(name) {
this.$children.forEach((item) => {
if (item.slug() === name) {
item.isActive = true;
}
})
},
activeChanged(name) {
if (!this.multiple) {
this.$children.forEach((item)=> {
if (item.slug() !== name) {
item.isActive = false
}
})
}
}
},
mounted() {
this.$on('expanded', this.activeChanged);
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,94 @@
<!--
- AccordionItem.vue
-
- Accordion Entry should be inside an Accordion component
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- @package LibreNMS
- @link http://librenms.org
- @copyright 2019 Tony Murray
- @author Tony Murray <murraytony@gmail.com>
-->
<template>
<div class="panel panel-default">
<div class="panel-heading" role="tab" :id="slug()">
<h4 class="panel-title">
<a class="accordion-item-trigger" :class="{'collapsed': !isActive}" role="button" data-parent="#accordion" @click="isActive = !isActive" :data-href="hash()">
<i class="fa fa-chevron-down accordion-item-trigger-icon"></i>
<i v-if="icon" :class="['fa', 'fa-fw', icon]"></i>
{{ text || name }}
</a>
</h4>
</div>
<transition-collapse-height>
<div :id="slug() + '-content'" v-if="isActive" :class="['panel-collapse', 'collapse', {'in': isActive}]" role="tabpanel">
<div class="panel-body">
<slot></slot>
</div>
</div>
</transition-collapse-height>
</div>
</template>
<script>
export default {
name: "AccordionItem",
props: {
name: {
type: String,
required: true
},
text: String,
active: Boolean,
icon: String
},
data() {
return {
isActive: this.active
}
},
mounted() {
if (window.location.hash === this.hash()) {
this.isActive = true;
}
},
watch: {
active(active) {
this.isActive = active;
},
isActive(active) {
this.$parent.$emit(active ? 'expanded' : 'collapsed', this.slug())
}
},
methods: {
slug() {
return this.name.toString().toLowerCase().replace(/\s+/g, '-');
},
hash() {
return '#' + this.slug();
}
}
}
</script>
<style scoped>
.accordion-item-trigger-icon {
transition: transform 0.2s ease;
}
.accordion-item-trigger.collapsed .accordion-item-trigger-icon {
transform: rotate(-90deg);
}
</style>

View File

@ -0,0 +1,37 @@
<!--
- BaseSetting.vue
-
- Description-
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- @package LibreNMS
- @link http://librenms.org
- @copyright 2019 Tony Murray
- @author Tony Murray <murraytony@gmail.com>
-->
<script>
export default {
name: "BaseSetting",
props: {
name: {type: String, required: true},
value: {required: true},
disabled: Boolean,
required: Boolean,
pattern: String,
options: {}
}
}
</script>

View File

@ -0,0 +1,147 @@
<!--
- LibrenmsSetting.vue
-
- Description-
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- @package LibreNMS
- @link http://librenms.org
- @copyright 2019 Tony Murray
- @author Tony Murray <murraytony@gmail.com>
-->
<template>
<div :class="['form-group', 'has-feedback', setting.class, feedback]">
<label :for="setting.name" class="col-sm-5 control-label" v-tooltip="setting.name">
{{ getDescription() }}
<span v-if="setting.units !== null">({{ setting.units }})</span>
</label>
<div class="col-sm-5" v-tooltip="setting.disabled ? $t('settings.readonly') : false">
<component :is="getComponent()"
:value="value"
:name="setting.name"
:pattern="setting.pattern"
:disabled="setting.overridden"
:required="setting.required"
:options="setting.options"
@input="changeValue($event)"
@change="changeValue($event)"
></component>
<span class="form-control-feedback"></span>
</div>
<div class="col-sm-2">
<button v-show="showUndo()" @click="resetToInitial" class="btn btn-primary" type="button" v-tooltip="$t('Undo')"><i class="fa fa-undo"></i></button>
<button v-show="showResetToDefault()" @click="resetToDefault" class="btn btn-default" type="button" v-tooltip="$t('Reset to default')"><i class="fa fa-refresh"></i>
</button>
<div v-if="hasHelp()" v-tooltip="{content: getHelp(), trigger: 'hover click'}" class="fa fa-fw fa-lg fa-question-circle"></div>
</div>
</div>
</template>
<script>
export default {
name: "LibrenmsSetting",
props: {
'setting': {type: Object, required: true}
},
data() {
return {
value: this.setting.value,
feedback: ''
}
},
methods: {
persistValue(value) {
axios.put(route('settings.update', this.setting.name), {value: value})
.then((response) => {
this.value = response.data.value;
this.$emit('setting-updated', {name: this.setting.name, value: this.value});
this.feedback = 'has-success';
setTimeout(() => this.feedback = '', 3000);
})
.catch((error) => {
this.value = error.response.data.value;
this.$emit('setting-updated', {name: this.setting.name, value: this.value});
this.feedback = 'has-error';
setTimeout(() => this.feedback = '', 3000);
toastr.error(error.response.data.message);
})
},
debouncePersistValue: _.debounce(function (value) {
this.persistValue(value)
}, 500),
changeValue(value) {
if (['select', 'boolean'].includes(this.setting.type)) {
// no need to debounce
this.persistValue(value);
} else {
this.debouncePersistValue(value);
}
this.value = value
},
getDescription() {
let key = 'settings.settings.' + this.setting.name + '.description';
return (this.$te(key) || this.$te(key, this.$i18n.fallbackLocale)) ? this.$t(key) : this.setting.name;
},
getHelp() {
let help = this.$t('settings.settings.' + this.setting.name + '.help');
if (this.setting.overridden) {
help += "</p><p>" + this.$t('settings.readonly')
}
return help
},
hasHelp() {
var key = 'settings.settings.' + this.setting.name + '.help';
return this.$te(key) || this.$te(key, this.$i18n.fallbackLocale)
},
resetToDefault() {
axios.delete(route('settings.destroy', this.setting.name))
.then((response) => {
this.value = response.data.value;
this.feedback = 'has-success';
setTimeout(() => this.feedback = '', 3000);
})
.catch((error) => {
this.feedback = 'has-error';
setTimeout(() => this.feedback = '', 3000);
toastr.error(error.response.data.message);
})
},
resetToInitial() {
this.changeValue(this.setting.value)
},
showResetToDefault() {
return this.setting.default !== null
&& !this.setting.overridden
&& !_.isEqual(this.value, this.setting.default)
},
showUndo() {
return !_.isEqual(this.setting.value, this.value);
},
getComponent() {
// snake to studly
const component = 'Setting' + this.setting.type.toString()
.replace(/(-[a-z]|^[a-z])/g, (group) => group.toUpperCase().replace('-', ''));
return typeof Vue.options.components[component] !== 'undefined' ? component : 'SettingNull';
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,174 @@
<!--
- LibrenmsSettings.vue
-
- Description-
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- @package LibreNMS
- @link http://librenms.org
- @copyright 2019 Tony Murray
- @author Tony Murray <murraytony@gmail.com>
-->
<template>
<tabs @tab-selected="tabChanged" :selected="this.tab">
<template v-slot:header>
<form class="form-inline" @submit.prevent>
<div class="input-group">
<input id="settings-search" type="search" class="form-control" placeholder="Filter Settings" v-model.trim="search_phrase">
</div>
</form>
</template>
<tab name="global" :selected="'global' === tab" :text="$t('settings.groups.global')">
<ul class="settings-list">
<li v-for="setting in settings"><strong>{{ setting.name }}</strong> <code>{{ setting.value }}</code></li>
</ul>
</tab>
<tab v-for="(sections, group) in groups" :key="group" :name="group" :selected="group === tab" :text="$t('settings.groups.' + group)">
<accordion @expanded="sectionExpanded" @collapsed="sectionCollapsed">
<accordion-item v-for="(items, item) in groups[group]" :key="item" :name="item" :text="$t('settings.sections.' + group + '.' + item)" :active="item === section">
<form class="form-horizontal" @submit.prevent>
<librenms-setting
v-for="setting in items"
:key="setting"
:setting="settings[setting]"
v-show="settingShown(setting)"
@setting-updated="updateSetting($event.name, $event.value)"
></librenms-setting>
</form>
</accordion-item>
</accordion>
</tab>
</tabs>
</template>
<script>
export default {
name: "LibrenmsSettings",
props: {
prefix: String,
initialTab: {type: String, default: 'alerting'},
initialSection: String
},
data() {
return {
tab: this.initialTab,
section: this.initialSection,
search_phrase: '',
settings: {}
}
},
methods: {
tabChanged(group) {
if (this.tab !== group) {
this.tab = group;
this.section = null;
this.updateUrl();
}
},
sectionExpanded(section) {
this.section = section;
this.updateUrl()
},
sectionCollapsed(section) {
if (this.section === section) { // don't do anything if section was changed instead of just closed
this.section = null;
this.updateUrl();
}
},
updateUrl() {
let slug = this.tab;
if (this.section) {
slug += '/' + this.section
}
window.history.pushState(slug, '', this.prefix + '/' + slug)
},
handleBack(event) {
[this.tab, this.section] = event.state.split('/');
},
loadData(response) {
this.settings = response.data;
},
updateSetting(name, value) {
this.$set(this.settings[name], 'value', value)
},
settingShown(setting_name) {
let setting = this.settings[setting_name];
if (setting.when === null) {
return true;
}
switch (setting.when.operator) {
case 'equals':
return this.settings[setting.when.setting].value === setting.when.value;
case 'in':
return setting.when.value.includes(this.settings[setting.when.setting].value);
default:
return true;
}
}
},
mounted() {
window.onpopstate = this.handleBack; // handle back button
axios.get(route('settings.list')).then((response) => this.settings = response.data)
},
computed: {
groups() {
// populate layout data
let groups = {};
for (const key of Object.keys(this.settings)) {
let setting = this.settings[key];
// filter
if (!setting.name.includes(this.search_phrase)) {
continue
}
if (setting.group) {
if (!(setting.group in groups)) {
groups[setting.group] = {};
}
if (setting.section) {
if (!(setting.section in groups[setting.group])) {
groups[setting.group][setting.section] = [];
}
// insert based on order
groups[setting.group][setting.section].splice(setting.order, 0, setting.name);
}
}
}
// sort groups
return Object.keys(groups).sort().reduce((a, c) => (a[c] = groups[c], a), {});
}
}
}
</script>
<style scoped>
#settings-search {
border-radius: 4px
}
#settings-search::-webkit-search-cancel-button {
-webkit-appearance: searchfield-cancel-button;
}
ul.settings-list {
list-style-type: none;
}
</style>

View File

@ -0,0 +1,104 @@
<!--
- SettingArray.vue
-
- Description-
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- @package LibreNMS
- @link http://librenms.org
- @copyright 2019 Tony Murray
- @author Tony Murray <murraytony@gmail.com>
-->
<template>
<div v-tooltip="disabled ? $t('settings.readonly') : false">
<draggable v-model="localList" @end="dragged()" :disabled="disabled">
<div v-for="(item, index) in localList" class="input-group">
<span :class="['input-group-addon', disabled ? 'disabled' : '']">{{ index+1 }}.</span>
<input type="text"
class="form-control"
:value="item"
:readonly="disabled"
@blur="updateItem(index, $event.target.value)"
@keyup.enter="updateItem(index, $event.target.value)"
>
<span class="input-group-btn">
<button v-if="!disabled" @click="removeItem(index)" type="button" class="btn btn-danger"><i class="fa fa-minus-circle"></i></button>
</span>
</div>
</draggable>
<div v-if="!disabled">
<div class="input-group">
<input type="text" v-model="newItem" @keyup.enter="addItem" class="form-control">
<span class="input-group-btn">
<button @click="addItem" type="button" class="btn btn-primary"><i class="fa fa-plus-circle"></i></button>
</span>
</div>
</div>
</div>
</template>
<script>
import BaseSetting from "./BaseSetting";
import draggable from 'vuedraggable'
export default {
name: "SettingArray",
mixins: [BaseSetting],
components: {
draggable,
},
data() {
return {
localList: this.value,
newItem: ""
}
},
methods: {
addItem() {
this.localList.push(this.newItem);
this.$emit('input', this.localList);
this.newItem = "";
},
removeItem(index) {
this.localList.splice(index, 1);
this.$emit('input', this.localList);
},
updateItem(index, value) {
this.localList[index] = value;
this.$emit('input', this.localList);
},
dragged() {
this.$emit('input', this.localList);
}
},
watch: {
value(updated) {
// careful to avoid loops with this
this.localList = updated;
}
}
}
</script>
<style scoped>
.input-group {
margin-bottom: 3px;
}
.input-group-addon:not(.disabled) {
cursor: move;
}
</style>

View File

@ -0,0 +1,47 @@
<!--
- SettingBoolean.vue
-
- Description-
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- @package LibreNMS
- @link http://librenms.org
- @copyright 2019 Tony Murray
- @author Tony Murray <murraytony@gmail.com>
-->
<template>
<toggle-button
:name="name"
:value="value"
@change="$emit('change', $event.value)"
:sync="true"
:required="required"
:disabled="disabled"
></toggle-button>
</template>
<script>
import BaseSetting from "./BaseSetting";
export default {
name: "SettingBoolean",
mixins: [BaseSetting]
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,67 @@
<!--
- SettingDashboardSelect.vue
-
- Description-
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- @package LibreNMS
- @link http://librenms.org
- @copyright 2019 Tony Murray
- @author Tony Murray <murraytony@gmail.com>
-->
<template>
<v-select
:options="localOptions"
label="text"
:clearable="false"
:value="selected"
@input="$emit('input', $event.id)"
:required="required"
:disabled="disabled"
>
</v-select>
</template>
<script>
import BaseSetting from "./BaseSetting";
export default {
name: "SettingDashboardSelect",
mixins: [BaseSetting],
data() {
return {
ajaxData: {results: []},
default: {id: 0, text: this.$t('No Default Dashboard')}
}
},
mounted() {
axios.get(route('ajax.select.dashboard')).then((response) => this.ajaxData = response.data);
},
computed: {
localOptions() {
return [this.default].concat(this.ajaxData.results)
},
selected() {
return this.value === 0 ? this.default : this.ajaxData.results.find(dash => dash.id === this.value);
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,47 @@
<!--
- SettingEmail.vue
-
- Description-
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- @package LibreNMS
- @link http://librenms.org
- @copyright 2019 Tony Murray
- @author Tony Murray <murraytony@gmail.com>
-->
<template>
<input type="email" class="form-control"
:name="name"
:value="value"
@input="$emit('input', $event.target.value)"
:pattern="pattern"
:required="required"
:disabled="disabled"
>
</template>
<script>
import BaseSetting from "./BaseSetting";
export default {
name: "SettingEmail",
mixins: [BaseSetting]
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,52 @@
<!--
- IntegerSetting.vue
-
- Description-
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- @package LibreNMS
- @link http://librenms.org
- @copyright 2019 Tony Murray
- @author Tony Murray <murraytony@gmail.com>
-->
<template>
<input type="number" class="form-control"
:name="name"
:value="value"
@input="$emit('input', parseNumber($event.target.value))"
:required="required"
:disabled="disabled"
>
</template>
<script>
import BaseSetting from "./BaseSetting";
export default {
name: "SettingInteger",
mixins: [BaseSetting],
methods: {
parseNumber(number) {
let value = parseFloat(number);
return isNaN(value) ? number : value;
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,107 @@
<!--
- SettingAuthLdapGroups.vue
-
- Description-
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- @package LibreNMS
- @link http://librenms.org
- @copyright 2019 Tony Murray
- @author Tony Murray <murraytony@gmail.com>
-->
<template>
<div class="form-inline" v-tooltip="disabled ? $t('settings.readonly') : false">
<div v-for="(data, group) in localList" class="input-group">
<input type="text"
class="form-control"
:value="group"
:readonly="disabled"
@blur="updateItem(group, $event.target.value)"
@keyup.enter="updateItem(group, $event.target.value)"
>
<span class="input-group-btn" style="width:0;"></span>
<select class="form-control" @change="updateLevel(group, $event.target.value)">
<option value="1" :selected="data.level === 1">{{ $t('Normal') }}</option>
<option value="5" :selected="data.level === 5">{{ $t('Global Read') }}</option>
<option value="10" :selected="data.level === 10">{{ $t('Admin') }}</option>
</select>
<span class="input-group-btn">
<button v-if="!disabled" @click="removeItem(group)" type="button" class="btn btn-danger"><i class="fa fa-minus-circle"></i></button>
</span>
</div>
<div v-if="!disabled">
<div class="input-group">
<input type="text" class="form-control" v-model="newItem">
<span class="input-group-btn" style="width:0;"></span>
<select class="form-control" v-model="newItemLevel">
<option value="1">{{ $t('Normal') }}</option>
<option value="5">{{ $t('Global Read') }}</option>
<option value="10">{{ $t('Admin') }}</option>
</select>
<span class="input-group-btn">
<button @click="addItem" type="button" class="btn btn-primary"><i class="fa fa-plus-circle"></i></button>
</span>
</div>
</div>
</div>
</template>
<script>
import BaseSetting from "./BaseSetting";
export default {
name: "SettingLdapGroups",
mixins: [BaseSetting],
data() {
return {
localList: this.value,
newItem: "",
newItemLevel: 1
}
},
methods: {
addItem() {
this.$set(this.localList, this.newItem, {level: this.newItemLevel});
this.newItem = "";
this.newItemLevel = 1;
},
removeItem(index) {
this.$delete(this.localList, index);
},
updateItem(oldValue, newValue) {
this.localList = Object.keys(this.localList).reduce((newList, current) => {
let key = (current === oldValue ? newValue : current);
newList[key] = this.localList[current];
return newList;
}, {});
},
updateLevel(group, level) {
this.$set(this.localList, group, {level: level})
}
},
watch: {
localList() {
this.$emit('input', this.localList)
}
}
}
</script>
<style scoped>
.input-group {
padding-bottom: 3px;
}
</style>

View File

@ -0,0 +1,40 @@
<!--
- SettingNull.vue
-
- Description-
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- @package LibreNMS
- @link http://librenms.org
- @copyright 2019 Tony Murray
- @author Tony Murray <murraytony@gmail.com>
-->
<template>
<div>Invalid type for: {{ name }}</div>
</template>
<script>
export default {
name: "SettingNull",
props: ['name']
}
</script>
<style scoped>
div {
color: red;
}
</style>

View File

@ -0,0 +1,47 @@
<!--
- SettingPassword.vue
-
- Description-
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- @package LibreNMS
- @link http://librenms.org
- @copyright 2019 Tony Murray
- @author Tony Murray <murraytony@gmail.com>
-->
<template>
<input type="password" class="form-control"
:name="name"
:value="value"
@input="$emit('input', $event.target.value)"
:pattern="pattern"
:required="required"
:disabled="disabled"
>
</template>
<script>
import BaseSetting from "./BaseSetting";
export default {
name: "SettingPassword",
mixins: [BaseSetting]
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,58 @@
<!--
- SettingSelect.vue
-
- Description-
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- @package LibreNMS
- @link http://librenms.org
- @copyright 2019 Tony Murray
- @author Tony Murray <murraytony@gmail.com>
-->
<template>
<select class="form-control"
:name="name"
:value="value"
@input="$emit('input', $event.target.value)"
:required="required"
:disabled="disabled"
>
<option v-for="(text, option) in options"
:value="option"
:selected="value === option"
v-text="getText(name, text)"
></option>
</select>
</template>
<script>
import BaseSetting from "./BaseSetting";
export default {
name: "SettingSelect",
mixins: [BaseSetting],
methods: {
getText(name, text) {
const key = `settings.settings.${name}.options.${text}`;
return this.$te(key) ? this.$t(key) : text;
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,158 @@
<!--
- SettingSnmpv3auth.vue
-
- Description-
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- @package LibreNMS
- @link http://librenms.org
- @copyright 2019 Tony Murray
- @author Tony Murray <murraytony@gmail.com>
-->
<template>
<div>
<draggable v-model="localList" @end="dragged()" :disabled="disabled">
<div v-for="(item, id) in localList">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{{ id+1 }}. <span class="pull-right text-danger" @click="removeItem(id)" v-if="!disabled"><i class="fa fa-minus-circle"></i></span></h3>
</div>
<div class="panel-body">
<form @onsubmit.prevent>
<div class="form-group">
<div class="col-sm-12">
<select class="form-control" id="authlevel" v-model="item.authlevel" :disabled="disabled" @change="updateItem(id, $event.target.id, $event.target.value)">
<option value="noAuthNoPriv" v-text="$t('settings.settings.snmp.v3.level.noAuthNoPriv')"></option>
<option value="authNoPriv" v-text="$t('settings.settings.snmp.v3.level.authNoPriv')"></option>
<option value="authPriv" v-text="$t('settings.settings.snmp.v3.level.authPriv')"></option>
</select>
</div>
</div>
<fieldset name="algo" v-show="item.authlevel.toString().substring(0, 4) === 'auth'" :disabled="disabled">
<legend class="h4" v-text="$t('settings.settings.snmp.v3.auth')"></legend>
<div class="form-group">
<label for="authalgo" class="col-sm-3 control-label" v-text="$t('settings.settings.snmp.v3.fields.authalgo')"></label>
<div class="col-sm-9">
<select class="form-control" id="authalgo" name="authalgo" v-model="item.authalgo" @change="updateItem(id, $event.target.id, $event.target.value)">
<option value="MD5">MD5</option>
<option value="AES">AES</option>
</select>
</div>
</div>
<div class="form-group">
<label for="authname" class="col-sm-3 control-label" v-text="$t('settings.settings.snmp.v3.fields.authname')"></label>
<div class="col-sm-9">
<input type="text" class="form-control" id="authname" :value="item.authname" @input="updateItem(id, $event.target.id, $event.target.value)">
</div>
</div>
<div class="form-group">
<label for="authpass" class="col-sm-3 control-label" v-text="$t('settings.settings.snmp.v3.fields.authpass')"></label>
<div class="col-sm-9">
<input type="text" class="form-control" id="authpass" :value="item.authpass" @input="updateItem(id, $event.target.id, $event.target.value)">
</div>
</div>
</fieldset>
<fieldset name="crypt" v-show="item.authlevel === 'authPriv'" :disabled="disabled">
<legend class="h4" v-text="$t('settings.settings.snmp.v3.crypto')"></legend>
<div class="form-group">
<label for="cryptoalgo" class="col-sm-3 control-label">Cryptoalgo</label>
<div class="col-sm-9">
<select class="form-control" id="cryptoalgo" v-model="item.cryptoalgo" @change="updateItem(id, $event.target.id, $event.target.value)">
<option value="AES">AES</option>
<option value="DES">DES</option>
</select>
</div>
</div>
<div class="form-group">
<label for="cryptopass" class="col-sm-3 control-label" v-text="$t('settings.settings.snmp.v3.fields.authpass')"></label>
<div class="col-sm-9">
<input type="text" class="form-control" id="cryptopass" :value="item.cryptopass" @input="updateItem(id, $event.target.id, $event.target.value)">
</div>
</div>
</fieldset>
</form>
</div>
</div>
</div>
</draggable>
<div class="row snmp3-add-button" v-if="!disabled">
<div class="col-sm-12">
<button class="btn btn-primary" @click="addItem()"><i class="fa fa-plus-circle"></i> {{ $t('New') }}</button>
</div>
</div>
</div>
</template>
<script>
import BaseSetting from "./BaseSetting";
export default {
name: "SettingSnmp3auth",
mixins: [BaseSetting],
data() {
return {
localList: this.value
}
},
methods: {
addItem() {
this.localList.push({
authlevel: 'noAuthNoPriv',
authalgo: 'MD5',
authname: '',
authpass: '',
cryptoalgo: 'AES',
cryptopass: ''
});
this.$emit('input', this.localList);
},
removeItem(index) {
this.localList.splice(index, 1);
this.$emit('input', this.localList);
},
updateItem(index, key, value) {
this.localList[index][key] = value;
this.$emit('input', this.localList);
},
dragged() {
this.$emit('input', this.localList);
}
},
watch: {
value($value) {
this.localList = $value;
}
}
}
</script>
<style scoped>
.authlevel {
font-size: 18px;
text-align: left;
}
.fa-minus-circle {
cursor: pointer;
}
.snmp3-add-button {
margin-top: 5px;
}
</style>

View File

@ -0,0 +1,47 @@
<!--
- SettingText.vue
-
- Description-
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- @package LibreNMS
- @link http://librenms.org
- @copyright 2019 Tony Murray
- @author Tony Murray <murraytony@gmail.com>
-->
<template>
<input type="text" class="form-control"
:name="name"
:value="value"
@input="$emit('input', $event.target.value)"
:pattern="pattern"
:required="required"
:disabled="disabled"
>
</template>
<script>
import BaseSetting from "./BaseSetting";
export default {
name: "SettingText",
mixins: [BaseSetting]
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,50 @@
<!--
- Tab.vue
-
- Description-
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- @package LibreNMS
- @link http://librenms.org
- @copyright 2019 Tony Murray
- @author Tony Murray <murraytony@gmail.com>
-->
<template>
<div v-show="isActive" role="tabpanel" class="tab-pane" :id="name">
<slot></slot>
</div>
</template>
<script>
export default {
name: "Tab",
props: {
name: { required: true },
text: String,
selected: {type: Boolean, default: false},
icon: String
},
data() {
return {
isActive: this.selected
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,125 @@
<!--
- Tabs.vue
-
- Description-
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- @package LibreNMS
- @link http://librenms.org
- @copyright 2019 Tony Murray
- @author Tony Murray <murraytony@gmail.com>
-->
<template>
<div>
<div class="panel with-nav-tabs panel-default">
<div class="panel-heading">
<ul class="nav nav-tabs" role="tablist">
<li v-for="tab in tabs" :key="tab.name" :class="{ 'active': tab.isActive }" role="presentation">
<a role="tab" :aria-controls="tab.name" @click="activeTab = tab.name">
<i v-if="tab.icon" :class="['fa', 'fa-fw', tab.icon]"></i>
{{ tab.text || tab.name }}&nbsp;
</a>
</li>
<li class="pull-right">
<slot name="header"></slot>
</li>
</ul>
</div>
<div class="panel-body">
<slot></slot>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Tabs",
props: {selected: String},
data() {
return {
tabs: [],
activeTab: null
};
},
created() {
this.tabs = this.$children;
},
mounted() {
this.activeTab = this.selected;
},
watch: {
selected(name) {
this.activeTab = name
},
activeTab(name) {
this.tabs.forEach(tab => tab.isActive = (tab.name === name));
this.$emit('tab-selected', name)
}
}
}
</script>
<style scoped>
.panel.with-nav-tabs .panel-heading{
padding: 5px 5px 0 5px;
}
.panel.with-nav-tabs .nav-tabs{
border-bottom: none;
}
.panel.with-nav-tabs .nav-justified{
margin-bottom: -1px;
}
.with-nav-tabs.panel-default .nav-tabs > li > a,
.with-nav-tabs.panel-default .nav-tabs > li > a:hover,
.with-nav-tabs.panel-default .nav-tabs > li > a:focus {
color: #777;
}
.with-nav-tabs.panel-default .nav-tabs > .open > a,
.with-nav-tabs.panel-default .nav-tabs > .open > a:hover,
.with-nav-tabs.panel-default .nav-tabs > .open > a:focus,
.with-nav-tabs.panel-default .nav-tabs > li > a:hover,
.with-nav-tabs.panel-default .nav-tabs > li > a:focus {
color: #777;
background-color: #ddd;
border-color: transparent;
}
.with-nav-tabs.panel-default .nav-tabs > li.active > a,
.with-nav-tabs.panel-default .nav-tabs > li.active > a:hover,
.with-nav-tabs.panel-default .nav-tabs > li.active > a:focus {
color: #555;
background-color: #fff;
border-color: #ddd;
border-bottom-color: transparent;
}
.with-nav-tabs.panel-default .nav-tabs > li.dropdown .dropdown-menu {
background-color: #f5f5f5;
border-color: #ddd;
}
.with-nav-tabs.panel-default .nav-tabs > li.dropdown .dropdown-menu > li > a {
color: #777;
}
.with-nav-tabs.panel-default .nav-tabs > li.dropdown .dropdown-menu > li > a:hover,
.with-nav-tabs.panel-default .nav-tabs > li.dropdown .dropdown-menu > li > a:focus {
background-color: #ddd;
}
.with-nav-tabs.panel-default .nav-tabs > li.dropdown .dropdown-menu > .active > a,
.with-nav-tabs.panel-default .nav-tabs > li.dropdown .dropdown-menu > .active > a:hover,
.with-nav-tabs.panel-default .nav-tabs > li.dropdown .dropdown-menu > .active > a:focus {
color: #fff;
background-color: #555;
}
</style>

View File

@ -0,0 +1,90 @@
<!--
- TransitionCollapseHeight.vue
-
- Description-
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- @package LibreNMS
- @link http://librenms.org
- @copyright 2019 Tony Murray
- @author Tony Murray <murraytony@gmail.com>
-->
<template>
<transition
enter-active-class="enter-active"
leave-active-class="leave-active"
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@before-leave="beforeLeave"
@leave="leave"
@after-leave="afterLeave"
>
<slot />
</transition>
</template>
<script>
export default {
name: "TransitionCollapseHeight",
methods: {
beforeEnter(el) {
requestAnimationFrame(() => {
if (!el.style.height) {
el.style.height = '0px';
}
el.style.display = null;
});
},
enter(el) {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
el.style.height = el.scrollHeight + 'px';
});
});
},
afterEnter(el) {
el.style.height = null;
},
beforeLeave(el) {
requestAnimationFrame(() => {
if (!el.style.height) {
el.style.height = el.offsetHeight + 'px';
}
});
},
leave(el) {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
el.style.height = '0px';
});
});
},
afterLeave(el) {
el.style.height = null;
}
}
}
</script>
<style scoped>
.enter-active,
.leave-active {
overflow: hidden;
transition: height 0.2s linear;
}
</style>

View File

@ -0,0 +1,71 @@
/*
* i18n.js
*
* Load vue.js i18n support
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @package LibreNMS
* @link http://librenms.org
* @copyright 2019 Tony Murray
* @author Tony Murray <murraytony@gmail.com>
*/
import Vue from 'vue'
import VueI18n from 'vue-i18n'
// import en from '../lang/en.js'
import messages from '../vue-i18n-locales.generated.js';
Vue.use(VueI18n);
export const i18n = new VueI18n({
locale: document.querySelector('html').getAttribute('lang'),
fallbackLocale: 'en',
silentFallbackWarn: true,
silentTranslationWarn: true,
messages: messages,
});
// re-enable after vue-i8ln-generator is working for split locales
/*
const loadedLanguages = ['en']; // our default language that is preloaded
function setI18nLanguage (lang) {
i18n.locale = lang
axios.defaults.headers.common['Accept-Language'] = lang
document.querySelector('html').setAttribute('lang', lang)
return lang
}
export function loadLanguageAsync(lang) {
// If the same language
if (i18n.locale === lang) {
return Promise.resolve(setI18nLanguage(lang))
}
// If the language was already loaded
if (loadedLanguages.includes(lang)) {
return Promise.resolve(setI18nLanguage(lang))
}
// If the language hasn't been loaded yet
return import(`../lang/${lang}.js`).then(
messages => {
i18n.setLocaleMessage(lang, messages.default)
loadedLanguages.push(lang)
return setI18nLanguage(lang)
}
)
}
*/

View File

@ -0,0 +1,719 @@
<?php
return [
'readonly' => 'Set in config.php, remove from config.php to enable.',
'groups' => [
'alerting' => 'Alerting',
'auth' => 'Authentication',
'external' => 'External',
'global' => 'Global',
'os' => 'OS',
'poller' => 'Poller',
'system' => 'System',
'webui' => 'Web UI',
],
'sections' => [
'alerting' => [
'general' => 'General Alert Settings',
'email' => 'Email Options',
],
'auth' => [
'general' => 'General Authentication Settings',
'ad' => 'Active Directory Settings',
'ldap' => 'LDAP Settings'
],
'external' => [
'binaries' => 'Binary Locations',
'location' => 'Location Settings',
'oxidized' => 'Oxidized Integration',
'peeringdb' => 'PeeringDB Integration',
'nfsen' => 'NfSen Integration',
'unix-agent' => 'Unix-Agent Integration',
],
'poller' => [
'ping' => 'Ping',
'rrdtool' => 'RRDTool Setup',
'snmp' => 'SNMP',
],
'system' => [
'cleanup' => 'Cleanup',
'proxy' => 'Proxy',
'updates' => 'Updates',
'server' => 'Server',
],
'webui' => [
'availability-map' => 'Availability Map Settings',
'graph' => 'Graph Settings',
'dashboard' => 'Dashboard Settings',
'search' => 'Search Settings',
'style' => 'Style',
]
],
'settings' => [
'active_directory' => [
'users_purge' => [
'description' => 'Keep inactive users for',
'help' => 'Users will be deleted from LibreNMS after this may days of not logging in. 0 means never and users will be recreated if the user logs back in.',
]
],
'addhost_alwayscheckip' => [
'description' => 'Check for duplicate IP when adding devices',
'help' => 'If a host is added as an ip address it is checked to ensure the ip is not already present. If the ip is present the host is not added. If host is added by hostname this check is not performed. If the setting is true hostnames are resolved and the check is also performed. This helps prevents accidental duplicate hosts.'
],
'alert' => [
'ack_until_clear' => [
'description' => 'Default acknowledge until alert clears option',
'help' => 'Default acknowledge until alert clears'
],
'admins' => [
'description' => 'Issue alerts to admins',
'help' => 'Alert administrators'
],
'default_copy' => [
'description' => 'Copy all email alerts to default contact',
'help' => 'Copy all email alerts to default contact'
],
'default_if_none' => [
'description' => 'cannot set in webui?',
'help' => 'Send mail to default contact if no other contacts are found'
],
'default_mail' => [
'description' => 'Default contact',
'help' => 'The default mail contact'
],
'default_only' => [
'description' => 'Send alerts to default contact only',
'help' => 'Only alert default mail contact'
],
'disable' => [
'description' => 'Disable alerting',
'help' => 'Stop alerts being generated'
],
'fixed-contacts' => [
'description' => 'Updates to contact email addresses not honored',
'help' => 'If TRUE any changes to sysContact or users emails will not be honoured whilst alert is active'
],
'globals' => [
'description' => 'Issue alerts to read only users',
'help' => 'Alert read only administrators'
],
'syscontact' => [
'description' => 'Issue alerts to sysContact',
'help' => 'Send alert to email in SNMP sysContact'
],
'transports' => [
'mail' => [
'description' => 'Enable email alerting',
'help' => 'Mail alerting transport'
]
],
'tolerance_window' => [
'description' => 'Tolerance window for cron',
'help' => 'Tolerance window in seconds'
],
'users' => [
'description' => 'Issue alerts to normal users',
'help' => 'Alert normal users'
]
],
'alert_log_purge' => [
'description' => 'Alert log entries older than',
'help' => 'Cleanup done by daily.sh',
],
'allow_unauth_graphs' => [
'description' => 'Allow unauthenticated graph access',
'help' => 'Allows any one to access graphs without login'
],
'allow_unauth_graphs_cidr' => [
'description' => 'Allow the given networks graph access',
'help' => 'Allow the given networks unauthenticated graph access (does not apply when unauthenticated graphs is enabled)'
],
'api_demo' => [
'description' => 'This is the demo'
],
'apps' => [
'powerdns-recursor' => [
'api-key' => [
'description' => 'API key for PowerDNS Recursor',
'help' => 'API key for the PowerDNS Recursor app when connecting directly'
],
'https' => [
'description' => 'PowerDNS Recursor use HTTPS?',
'help' => 'Use HTTPS instead of HTTP for the PowerDNS Recursor app when connecting directly'
],
'port' => [
'description' => 'PowerDNS Recursor port',
'help' => 'TCP port to use for the PowerDNS Recursor app when connecting directly'
]
]
],
'astext' => [
'description' => 'Key to hold cache of autonomous systems descriptions'
],
'auth_ad_base_dn' => [
'description' => 'Base DN',
'help' => 'groups and users must be under this dn. Example: dc=example,dc=com'
],
'auth_ad_check_certificates' => [
'description' => 'Check certificate',
'help' => 'Check certificates for validity. Some servers use self signed certificates, disabling this allows those.'
],
'auth_ad_group_filter' => [
'description' => 'Group LDAP filter',
'help' => 'Active Directory LDAP filter for selecting groups'
],
'auth_ad_groups' => [
'description' => 'Group access',
'help' => 'Define groups that have access and level'
],
'auth_ad_user_filter' => [
'description' => 'User LDAP filter',
'help' => 'Active Directory LDAP filter for selecting users'
],
'auth_ldap_attr' => [
'uid' => [
'description' => 'Attribute to check username against',
'help' => 'Attribute used to identify users by username'
]
],
'auth_ldap_binddn' => [
'description' => 'Bind DN (overrides bind username)',
'help' => 'Full DN of bind user'
],
'auth_ldap_bindpassword' => [
'description' => 'Bind password',
'help' => 'Password for bind user'
],
'auth_ldap_binduser' => [
'description' => 'Bind username',
'help' => 'Used to query the LDAP server when no user is logged in (alerts, API, etc)'
],
'auth_ad_binddn' => [
'description' => 'Bind DN (overrides bind username)',
'help' => 'Full DN of bind user'
],
'auth_ad_bindpassword' => [
'description' => 'Bind password',
'help' => 'Password for bind user'
],
'auth_ad_binduser' => [
'description' => 'Bind username',
'help' => 'Used to query the AD server when no user is logged in (alerts, API, etc)'
],
'auth_ldap_cache_ttl' => [
'description' => 'LDAP cache expiration',
'help' => 'Temporarily stores LDAP query results. Improves speeds, but the data may be stale.',
],
'auth_ldap_debug' => [
'description' => 'Show debug',
'help' => 'Shows debug information. May expose private information, do not leave enabled.'
],
'auth_ldap_emailattr' => [
'description' => 'Mail attribute'
],
'auth_ldap_group' => [
'description' => 'Access group DN',
'help' => 'Distinguished name for a group to give normal level access. Example: cn=groupname,ou=groups,dc=example,dc=com'
],
'auth_ldap_groupbase' => [
'description' => 'Group base DN',
'help' => 'Distinguished name to search for groups Example: ou=group,dc=example,dc=com'
],
'auth_ldap_groupmemberattr' => [
'description' => 'Group member attribute'
],
'auth_ldap_groupmembertype' => [
'description' => 'Find group members by',
'options' => [
'username' => 'Username',
'fulldn' => 'Full DN (using prefix and suffix)',
'puredn' => 'DN Search (search using uid attribute)'
]
],
'auth_ldap_groups' => [
'description' => 'Group access',
'help' => 'Define groups that have access and level'
],
'auth_ldap_port' => [
'description' => 'LDAP port',
'help' => 'Port to connect to servers on. For LDAP it should be 389, for LDAPS it should be 636'
],
'auth_ldap_prefix' => [
'description' => 'User prefix',
'help' => 'Used to turn a username into a distinguished name'
],
'auth_ldap_server' => [
'description' => 'LDAP Server(s)',
'help' => 'Set server(s), space separated. Prefix with ldaps:// for ssl'
],
'auth_ldap_starttls' => [
'description' => 'Use STARTTLS',
'help' => 'Use STARTTLS to secure the connection. Alternative to LDAPS.',
'options' => [
'disabled' => 'Disabled',
'optional' => 'Optional',
'required' => 'Required'
]
],
'auth_ldap_suffix' => [
'description' => 'User suffix',
'help' => 'Used to turn a username into a distinguished name'
],
'auth_ldap_timeout' => [
'description' => 'Connection timeout',
'help' => 'If one or more servers are unresponsive, higher timeouts will cause slow access. To low may cause connection failures in some cases',
],
'auth_ldap_uid_attribute' => [
'description' => 'Unique ID attribute',
'help' => 'LDAP attribute to use to identify users, must be numeric'
],
'auth_ldap_userdn' => [
'description' => 'Use full user DN',
'help' => "Uses a user's full DN as the value of the member attribute in a group instead of member: username using the prefix and suffix. (its member: uid=username,ou=groups,dc=domain,dc=com)"
],
'auth_ldap_version' => [
'description' => 'LDAP version',
'help' => 'LDAP version to use to talk to the server. Usually this should be v3',
'options' => [
"2" => "2",
"3" => "3"
]
],
'auth_mechanism' => [
'description' => 'Authorization Method (Caution!)',
'help' => "Authorization method. Caution, you may lose the ability to log in. You can override this back to mysql by setting \$config['auth_mechanism'] = 'mysql'; in your config.php",
'options' => [
'mysql' => 'MySQL (default)',
'active_directory' => 'Active Directory',
'ldap' => 'LDAP',
'radius' => 'Radius',
'http-auth' => 'HTTP Authentication',
'ad-authorization' => 'Externally authenticated AD',
'ldap-authorization' => 'Externally authenticated LDAP',
'sso' => 'Single Sign On'
]
],
'auth_remember' => [
'description' => 'Remember me duration',
'help' => 'Number of days to keep a user logged in when checking the remember me checkbox at log in.',
],
'authlog_purge' => [
'description' => 'Auth log entries older than (days)',
'help' => 'Cleanup done by daily.sh'
],
'device_perf_purge' => [
'description' => 'Device performance entries older than (days)',
'help' => 'Cleanup done by daily.sh'
],
'email_auto_tls' => [
'description' => 'Enable / disable Auto TLS support',
'options' => [
'true' => 'Yes',
'false' => 'No'
]
],
'email_backend' => [
'description' => 'How to deliver mail',
'help' => 'The backend to use for sending email, can be mail, sendmail or SMTP',
'options' => [
'mail' => 'mail',
'sendmail' => 'sendmail',
'smtp' => 'SMTP'
]
],
'email_from' => [
'description' => 'From email address',
'help' => 'Email address used for sending emails (from)'
],
'email_html' => [
'description' => 'Use HTML emails',
'help' => 'Send HTML emails'
],
'email_sendmail_path' => [
'description' => 'Location of sendmail if using this option'
],
'email_smtp_auth' => [
'description' => 'Enable / disable smtp authentication'
],
'email_smtp_host' => [
'description' => 'SMTP Host for sending email if using this option'
],
'email_smtp_password' => [
'description' => 'SMTP Auth password'
],
'email_smtp_port' => [
'description' => 'SMTP port setting'
],
'email_smtp_secure' => [
'description' => 'Enable / disable encryption (use tls or ssl)',
'options' => [
'' => 'Disabled',
'tls' => 'TLS',
'ssl' => 'SSL'
]
],
'email_smtp_timeout' => [
'description' => 'SMTP timeout setting'
],
'email_smtp_username' => [
'description' => 'SMTP Auth username'
],
'email_user' => [
'description' => 'From name',
'help' => 'Name used as part of the from address'
],
'eventlog_purge' => [
'description' => 'Event log entries older than (days)',
'help' => 'Cleanup done by daily.sh'
],
'favicon' => [
'description' => 'Favicon',
'help' => 'Overrides the default favicon.'
],
'fping' => [
'description' => 'Path to fping'
],
'fping6' => [
'description' => 'Path to fping6'
],
'fping_options' => [
'count' => [
'description' => 'fping count',
'help' => 'The number of pings to send when checking if a host is up or down via icmp'
],
'interval' => [
'description' => 'fping interval',
'help' => 'The amount of milliseconds to wait between pings',
],
'timeout' => [
'description' => 'fping timeout',
'help' => 'The amount of milliseconds to wait for an echo response before giving up',
]
],
'geoloc' => [
'api_key' => [
'description' => 'Geocoding API Key',
'help' => 'Geocoding API Key (Required to function)'
],
'engine' => [
'description' => 'Geocoding Engine',
'options' => [
'google' => 'Google Maps',
'openstreetmap' => 'OpenStreetMap',
'mapquest' => 'MapQuest',
'bing' => 'Bing Maps'
]
]
],
'http_proxy' => [
'description' => 'HTTP(S) Proxy',
'help' => 'Set this as a fallback if http_proxy or https_proxy environment variable is not available.'
],
'ipmitool' => [
'description' => 'Path to ipmtool'
],
'login_message' => [
'description' => 'Logon Message',
'help' => 'Displayed on the login page'
],
'mono_font' => [
'description' => 'Monospaced Font',
],
'mtr' => [
'description' => 'Path to mtr'
],
'nfsen_enable' => [
'description' => 'Enable NfSen',
'help' => 'Enable Integration with NfSen',
],
'nfsen_rrds' => [
'description' => 'NfSen RRD Directories',
'help' => 'This value specifies where your NFSen RRD files are located.'
],
'nfsen_subdirlayout' => [
'description' => 'Set NfSen subdir layout',
'help' => 'This must match the subdir layout you have set in NfSen. 1 is the default.',
],
'nfsen_last_max' => [
'description' => 'Last Max'
],
'nfsen_top_max' => [
'description' => 'Top Max',
'help' => 'Max topN value for stats',
],
'nfsen_top_N' => [
'description' => 'Top N'
],
'nfsen_top_default' => [
'description' => 'Default Top N'
],
'nfsen_stat_default' => [
'description' => 'Default Stat'
],
'nfsen_order_default' => [
'description' => 'Default Order'
],
'nfsen_last_default' => [
'description' => 'Default Last'
],
'nfsen_lasts' => [
'description' => 'Default Last Options'
],
'nfsen_split_char' => [
'description' => 'Split Char',
'help' => 'This value tells us what to replace the full stops `.` in the devices hostname with. Usually: `_`'
],
'nfsen_suffix' => [
'description' => 'File name suffix',
'help' => 'This is a very important bit as device names in NfSen are limited to 21 characters. This means full domain names for devices can be very problematic to squeeze in, so therefor this chunk is usually removed.'
],
'nmap' => [
'description' => 'Path to nmap'
],
'own_hostname' => [
'description' => 'LibreNMS hostname',
'help' => 'Should be set to the hostname/ip the librenms server is added as'
],
'oxidized' => [
'default_group' => [
'description' => 'Set the default group returned'
],
'enabled' => [
'description' => 'Enable Oxidized support'
],
'features' => [
'versioning' => [
'description' => 'Enable config versioning access',
'help' => 'Enable Oxidized config versioning (requires git backend)'
]
],
'group_support' => [
'description' => 'Enable the return of groups to Oxidized'
],
'reload_nodes' => [
'description' => 'Reload Oxidized nodes list, each time a device is added'
],
'url' => [
'description' => 'URL to your Oxidized API',
'help' => 'Oxidized API url (For example: http://127.0.0.1:8888)'
]
],
'peeringdb' => [
'enabled' => [
'description' => 'Enable PeeringDB lookup',
'help' => 'Enable PeeringDB lookup (data is downloaded with daily.sh)'
]
],
'perf_times_purge' => [
'description' => 'Poller performance log entries older than (days)',
'help' => 'Cleanup done by daily.sh'
],
'ping' => [
'description' => 'Path to ping'
],
'ports_fdb_purge' => [
'description' => 'Port FDB entries older than',
'help' => 'Cleanup done by daily.sh'
],
'public_status' => [
'description' => 'Show status publicly',
'help' => 'Shows the status of some devices on the logon page without authentication.'
],
'rrd' => [
'heartbeat' => [
'description' => 'Change the rrd heartbeat value (default 600)'
],
'step' => [
'description' => 'Change the rrd step value (default 300)'
]
],
'rrd_dir' => [
'description' => 'RRD Location',
'help' => 'Location of rrd files. Default is rrd inside the LibreNMS directory. Changing this setting does not move the rrd files.'
],
'rrd_rra' => [
'description' => 'RRD Format Settings',
'help' => 'These cannot be changed without deleting your existing RRD files. Though one could conceivably increase or decrease the size of each RRA if one had performance problems or if one had a very fast I/O subsystem with no performance worries.'
],
'rrdcached' => [
'description' => 'Enable rrdcached (socket)',
'help' => 'Enables rrdcached by setting the location of the rrdcached socket. Can be unix or network socket (unix:/run/rrdcached.sock or localhost:42217)'
],
'rrdtool' => [
'description' => 'Path to rrdtool'
],
'rrdtool_tune' => [
'description' => 'Tune all rrd port files to use max values',
'help' => 'Auto tune maximum value for rrd port files'
],
'sfdp' => [
'description' => 'Path to sfdp'
],
'site_style' => [
'description' => 'Set the site css style',
'options' => [
'blue' => 'Blue',
'dark' => 'Dark',
'light' => 'Light',
'mono' => 'Mono',
]
],
'snmp' => [
'transports' => [
'description' => 'Transport (priority)',
'help' => 'Select enabled transports and order them as you want them to be tried.'
],
'version' => [
'description' => 'Version (priority)',
'help' => 'Select enabled versions and order them as you want them to be tried.'
],
'community' => [
'description' => 'Communities (priority)',
'help' => 'Enter community strings for v1 and v2c and order them as you want them to be tried'
],
'max_repeaters' => [
'description' => 'Max Repeaters',
'help' => 'Set repeaters to use for SNMP bulk requests'
],
'port' => [
'description' => 'Port',
'help' => 'Set the tcp/udp port to be used for SNMP'
],
'v3' => [
'description' => 'SNMP v3 Authentication (priority)',
'help' => 'Set up v3 authentication variables and order them as you want them to be tried',
'auth' => 'Auth',
'crypto' => 'Crypto',
'fields' => [
'authalgo' => 'Algorithm',
'authlevel' => 'Level',
'authname' => 'Username',
'authpass' => 'Password',
'cryptoalgo' => 'Algorithm',
'cryptopass' => 'Password'
],
'level' => [
'noAuthNoPriv' => 'No Authentication, No Privacy',
'authNoPriv' => 'Authentication, No Privacy',
'authPriv' => 'Authentication and Privacy'
]
]
],
'snmpbulkwalk' => [
'description' => 'Path to snmpbulkwalk'
],
'snmpget' => [
'description' => 'Path to snmpget'
],
'snmpgetnext' => [
'description' => 'Path to snmpgetnext'
],
'snmptranslate' => [
'description' => 'Path to snmptranslate'
],
'snmpwalk' => [
'description' => 'Path to snmpwalk'
],
'syslog_filter' => [
'description' => 'Filter syslog messages containing'
],
'syslog_purge' => [
'description' => 'Syslog entries older than (days)',
'help' => 'Cleanup done by daily.sh'
],
'traceroute' => [
'description' => 'Path to traceroute'
],
'traceroute6' => [
'description' => 'Path to traceroute6'
],
'unix-agent' => [
'connection-timeout' => [
'description' => 'Unix-agent connection timeout'
],
'port' => [
'description' => 'Default unix-agent port',
'help' => 'Default port for the unix-agent (check_mk)'
],
'read-timeout' => [
'description' => 'Unix-agent read timeout'
]
],
'update' => [
'description' => 'Enable updates in ./daily.sh'
],
'update_channel' => [
'description' => 'Set update Channel',
'options' => [
'master' => 'master',
'release' => 'release'
]
],
'virsh' => [
'description' => 'Path to virsh'
],
'webui' => [
'availability_map_box_size' => [
'description' => 'Availability box width',
'help' => 'Input desired tile width in pixels for box size in full view'
],
'availability_map_compact' => [
'description' => 'Availability map compact view',
'help' => 'Availability map view with small indicators'
],
'availability_map_sort_status' => [
'description' => 'Sort by status',
'help' => 'Sort devices and services by status'
],
'availability_map_use_device_groups' => [
'description' => 'Use device groups filter',
'help' => 'Enable usage of device groups filter'
],
'default_dashboard_id' => [
'description' => 'Default dashboard',
'help' => 'Global default dashboard_id for all users who do not have their own default set'
],
'dynamic_graphs' => [
'description' => 'Enable dynamic graphs',
'help' => 'Enable dynamic graphs, enables zooming and panning on graphs'
],
'global_search_result_limit' => [
'description' => 'Set the max search result limit',
'help' => 'Global search results limit'
],
'graph_stacked' => [
'description' => 'Use stacked graphs',
'help' => 'Display stacked graphs instead of inverted graphs'
],
'graph_type' => [
'description' => 'Set the graph type',
'help' => 'Set the default graph type',
'options' => [
'png' => 'PNG',
'svg' => 'SVG'
]
],
'min_graph_height' => [
'description' => 'Set the minimum graph height',
'help' => 'Minimum Graph Height (default: 300)'
]
],
'whois' => [
'description' => 'Path to whois'
]
],
'units' => [
'days' => 'days',
'ms' => 'ms',
'seconds' => 'seconds',
],
'validate' => [
'boolean' => ':value is not a valid boolean',
'email' => ':value is not a valid email',
'integer' => ':value is not an integer',
'password' => 'The password is incorrect',
'select' => ':value is not an allowed value',
'text' => ':value is not allowed',
'array' => 'Invalid format',
]
];

View File

@ -1,8 +1,11 @@
// Fonts
@import url('https://fonts.googleapis.com/css?family=Nunito');
//@import url('https://fonts.googleapis.com/css?family=Nunito');
// Variables
@import 'variables';
// Bootstrap
@import '~bootstrap/scss/bootstrap';
//@import '~bootstrap/scss/bootstrap'; // Conflicts with normal css
// Vue Select
@import "~vue-select/src/scss/vue-select.scss";

View File

@ -4,7 +4,6 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>{{ $pagetitle }}</title>
<base href="{{ LibreNMS\Config::get('base_url') }}" />
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@if(!LibreNMS\Config::get('favicon', false))
@ -40,12 +39,13 @@
<link href="{{ asset('css/select2.min.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('css/select2-bootstrap.min.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('css/query-builder.default.min.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset(LibreNMS\Config::get('stylesheet', 'css/styles.css')) }}?ver=20190603" rel="stylesheet" type="text/css" />
<link href="{{ asset(LibreNMS\Config::get('stylesheet', 'css/styles.css')) }}?ver=20190912" rel="stylesheet" type="text/css" />
<link href="{{ asset('css/' . LibreNMS\Config::get('site_style', 'light') . '.css?ver=632417642') }}" rel="stylesheet" type="text/css" />
@foreach(LibreNMS\Config::get('webui.custom_css', []) as $custom_css)
<link href="{{ $custom_css }}" rel="stylesheet" type="text/css" />
@endforeach
@yield('css')
@stack('styles')
<script src="{{ asset('js/polyfill.min.js') }}"></script>
<script src="{{ asset('js/jquery.min.js') }}"></script>
@ -112,5 +112,6 @@
{!! Toastr::render() !!}
@stack('scripts')
</body>
</html>

View File

@ -0,0 +1,27 @@
@extends('layouts.librenmsv1')
@section('title', __('Settings'))
@section('content')
<div class="container">
<div id="app">
<librenms-settings
prefix="{{ url('settings') }}"
initial-tab="{{ $active_tab }}"
initial-section="{{ $active_section }}"
></librenms-settings>
</div>
</div>
@endsection
@push('styles')
<link href="{{ mix('/css/app.css') }}?v=10132019" rel="stylesheet">
@endpush
@push('scripts')
@routes
<script src="{{ mix('/js/manifest.js') }}?v=10132019"></script>
<script src="{{ mix('/js/vendor.js') }}?v=10132019"></script>
<script src="{{ mix('/js/app.js') }}?v=10132019"></script>
@endpush

View File

@ -0,0 +1,43 @@
<form class="form-inline">
<div class="input-group">
<input id="settings-search" type="search" class="form-control" placeholder="@lang('Search Settings')" style="border-radius: 4px">
</div>
</form>
@push('scripts')
<script>
var settings_suggestions = new Bloodhound({
queryTokenizer: Bloodhound.tokenizers.whitespace,
datumTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: "ajax/bloodhound/settings?term=%QUERY",
wildcard: "%QUERY"
}
});
var settings_search = $('#settings-search').typeahead({
hint: true,
highlight: true,
minLength: 1
},
{
source: settings_suggestions.ttAdapter(),
async: true,
displayKey: 'description',
valueKey: 'name',
templates: {
suggestion: Handlebars.compile('<p><strong>@{{name}}</strong> - <small>@{{description}}</small></p>')
},
limit: 20
}).on('typeahead:select', function (ev, suggestion) {
$('.settings-group-tabs a[href="#tab-' + suggestion.group + '"]').tab('show');
$('#' + suggestion.group + '-' + suggestion.section ).collapse('show');
$('#' + suggestion.name).focus();
settings_search.typeahead('val','');
}).on('keyup', function (e) {
// on enter go to the first selection
if (e.which === 13) {
$('.tt-selectable').first().click();
}
});
</script>
@endpush

View File

@ -0,0 +1,51 @@
@extends('layouts.librenmsv1')
@section('content')
<div class="container-fluid">
<div class="container">
<div id="app" v-cloak>
<tabs>
<template v-slot:header>
<div>Banana</div>
</template>
<tab name="One" selected icon="fa-ambulance" selected>
<accordion :multiple="false">
<accordion-item name="Test" icon="fa-wrench" active>
Test Content
</accordion-item>
<accordion-item name="Test 2" icon="fa-archive">
Test 2 Content
</accordion-item>
<accordion-item name="Test 3" icon="fa-android">
Test 3 Content
</accordion-item>
</accordion>
</tab>
<tab name="Dave" icon="fa-bomb">
Dave's not here man
</tab>
</tabs>
</div>
</div>
<hr />
</div>
@endsection
@section('css')
<style>
[v-cloak] > * { display:none; }
[v-cloak]::before {
content: " ";
display: block;
width: 16px;
height: 16px;
background-image: url('data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA==');
}
</style>
@endsection
@push('scripts')
@routes
<script src="{{ asset('js/app.js') }}"></script>
@endpush

View File

@ -17,9 +17,9 @@ Auth::routes();
// WebUI
Route::group(['middleware' => ['auth', '2fa'], 'guard' => 'auth'], function () {
// Test
Route::get('/laravel', function () {
return view('laravel');
});
Route::get('/vue/{sub?}', function () {
return view('vue');
})->where('sub', '.*');
// pages
Route::resource('device-groups', 'DeviceGroupController');
@ -29,8 +29,18 @@ Route::group(['middleware' => ['auth', '2fa'], 'guard' => 'auth'], function () {
Route::get('about', 'AboutController@index');
Route::get('authlog', 'UserController@authlog');
// admin pages
Route::group(['guard' => 'admin'], function () {
Route::get('settings/{tab?}/{section?}', 'SettingsController@index')->name('settings');
Route::put('settings/{name}', 'SettingsController@update')->name('settings.update');
Route::delete('settings/{name}', 'SettingsController@destroy')->name('settings.destroy');
});
// old route redirects
Route::permanentRedirect('poll-log', 'pollers/tab=log/');
Route::get('settings/sub={tab}', function ($tab) {
return redirect("settings/$tab");
});
// Two Factor Auth
Route::group(['prefix' => '2fa', 'namespace' => 'Auth'], function () {
@ -56,6 +66,9 @@ Route::group(['middleware' => ['auth', '2fa'], 'guard' => 'auth'], function () {
Route::post('ripe/raw', 'RipeNccApiController@raw');
});
Route::get('settings/list', 'SettingsController@listAll')->name('settings.list');
// form ajax handlers, perhaps should just be page controllers
Route::group(['prefix' => 'form', 'namespace' => 'Form'], function () {
Route::resource('widget-settings', 'WidgetSettingsController');
@ -65,6 +78,7 @@ Route::group(['middleware' => ['auth', '2fa'], 'guard' => 'auth'], function () {
Route::group(['prefix' => 'select', 'namespace' => 'Select'], function () {
Route::get('application', 'ApplicationController');
Route::get('bill', 'BillController');
Route::get('dashboard', 'DashboardController')->name('ajax.select.dashboard');
Route::get('device', 'DeviceController');
Route::get('device-field', 'DeviceFieldController');
Route::get('device-group', 'DeviceGroupController');

View File

@ -46,7 +46,7 @@ if ($opt['f']) {
}
if (! $port_id && ! $port_id_file || ($port_id && $port_id_file)) {
print $console_color->convert(\LibreNMS\Config::get('project_name_version') . ' Port purge tool
print $console_color->convert(\LibreNMS\Config::get('project_name').' Port purge tool
-p <port_id> Purge single port by it\'s port-id
-f <file> Purge a list of ports, read port-ids from <file>, one on each line.
A filename of - means reading from STDIN.

View File

@ -56,7 +56,7 @@ class LoginTest extends DuskTestCase
$user = factory(User::class)->create([
'password' => password_hash($password, PASSWORD_DEFAULT)
]);
Config::set('twofactor', true, true); // set to db
Config::persist('twofactor', true); // set to db
UserPref::setPref($user, 'twofactor', [
'key' => '5P3FLXBX7NU3ZBFOTWZL2GL5MKFEWBOA', // known key: 634456, 613687, 064292
'fails' => 0,

View File

@ -142,9 +142,9 @@ class ConfigTest extends TestCase
$query->delete();
$this->assertFalse($query->exists(), "$key should not be set, clean database");
Config::set($key, 'one', true);
Config::persist($key, 'one');
$this->assertEquals('one', $query->value('config_value'));
Config::set($key, 'two', true);
Config::persist($key, 'two');
$this->assertEquals('two', $query->value('config_value'));
$this->dbTearDown();

View File

@ -11,5 +11,7 @@ const mix = require('laravel-mix');
|
*/
mix.js('resources/js/app.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css');
mix.setPublicPath('html/')
.js('resources/js/app.js', 'js')
.sass('resources/sass/app.scss', 'css')
.extract();