Merge branch 'release/v5.0' into devel
Signed-off-by: DL6ER <dl6er@dl6er.de>
@@ -137,6 +137,9 @@ if (isset($_GET['getAllQueries']) && $auth)
|
||||
case 7:
|
||||
$query_type = "TXT";
|
||||
break;
|
||||
case 8:
|
||||
$query_type = "NAPTR";
|
||||
break;
|
||||
default:
|
||||
$query_type = "UNKN";
|
||||
break;
|
||||
|
||||
@@ -68,14 +68,9 @@
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<p><strong>Important:</strong> Note that black- and whitelisted domains are not automatically applied on this page to avoid restarting the DNS service too often. Instead, click on this button, to have the new settings become effective:</p>
|
||||
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
<button class="btn btn-lg btn-primary btn-block" id="gravityBtn" disabled="true">Update black-/whitelists</button>
|
||||
|
||||
<script src="scripts/pi-hole/js/auditlog.js"></script>
|
||||
|
||||
|
||||
@@ -29,11 +29,11 @@
|
||||
<div class="box-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="ex1">Name:</label>
|
||||
<label for="domain">Domain:</label>
|
||||
<input id="domain" type="url" class="form-control" placeholder="Add a domain (example.com or sub.example.com)" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="ex2">Description:</label>
|
||||
<label for="ip">IP Address:</label>
|
||||
<input id="ip" type="text" class="form-control" placeholder="Associated IP address" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -45,34 +45,39 @@ $token = $_SESSION['token'];
|
||||
<label>Query status:</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-2">
|
||||
<div class="col-md-3">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" id="type_gravity" checked>Blocked (exact)</label>
|
||||
<label><input type="checkbox" id="type_forwarded" checked><strong>Permitted: </strong>forwarded</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" id="type_cached" checked><strong>Permitted: </strong>cached</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="col-md-3">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" id="type_forwarded" checked>OK (forwarded)</label>
|
||||
<label><input type="checkbox" id="type_gravity" checked><strong>Blocked: </strong>gravity</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" id="type_external" checked><strong>Blocked: </strong>external</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="col-md-3">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" id="type_cached" checked>OK (cached)</label>
|
||||
<label><input type="checkbox" id="type_blacklist" checked><strong>Blocked: </strong>exact blacklist</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" id="type_regex" checked><strong>Blocked: </strong>regex blacklist</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="col-md-3">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" id="type_regex" checked>Blocked (regex/wildcard)</label>
|
||||
<label><input type="checkbox" id="type_gravity_CNAME" checked><strong>Blocked: </strong>gravity (CNAME)</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" id="type_blacklist" checked>Blocked (blacklist)</label>
|
||||
<label><input type="checkbox" id="type_blacklist_CNAME" checked><strong>Blocked: </strong>exact blacklist (CNAME)</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" id="type_external" checked>Blocked (external)</label>
|
||||
<label><input type="checkbox" id="type_regex_CNAME" checked><strong>Blocked: </strong>regex blacklist (CNAME)</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<!-- Alerts -->
|
||||
<div id="alInfo" class="alert alert-info alert-dismissible fade in" role="alert" hidden="true">
|
||||
<button type="button" class="close" data-hide="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
Updating...
|
||||
Updating...this may take a while. <strong>Please do not navigate away from or close this page.</strong>
|
||||
</div>
|
||||
<div id="alSuccess" class="alert alert-success alert-dismissible fade in" role="alert" hidden="true">
|
||||
<button type="button" class="close" data-hide="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer clearfix">
|
||||
<strong>Hint:</strong> Please run <code>pihole -g</code> or update your gravity list <a href="gravity.php">online</a> after modifying your adlists.
|
||||
<button id="btnAdd" class="btn btn-primary pull-right">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -72,6 +73,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="scripts/pi-hole/js/groups-common.js"></script>
|
||||
<script src="scripts/pi-hole/js/groups-adlists.js"></script>
|
||||
|
||||
<?php
|
||||
|
||||
@@ -30,11 +30,12 @@
|
||||
<label for="ex1">Known clients:</label>
|
||||
<select id="select" class="form-control" placeholder="">
|
||||
<option disabled selected>Loading...</option>
|
||||
</select>
|
||||
</select><br>
|
||||
<input id="ip-custom" type="text" class="form-control" disabled placeholder="Client IP address (IPv4 or IPv6, CIDR subnetting available, optional)" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="ex2">Custom client:</label>
|
||||
<input id="ip-custom" type="text" class="form-control" disabled placeholder="Client IP address (IPv4 or IPv6, optional)" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off">
|
||||
<label for="ex3">Comment:</label>
|
||||
<input id="new_comment" type="text" class="form-control" placeholder="Client description (optional)">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -59,6 +60,7 @@
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>IP address</th>
|
||||
<th>Comment</th>
|
||||
<th>Group assignment</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
@@ -72,6 +74,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="scripts/pi-hole/js/groups-common.js"></script>
|
||||
<script src="scripts/pi-hole/js/groups-clients.js"></script>
|
||||
|
||||
<?php
|
||||
|
||||
@@ -6,57 +6,110 @@
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
require "scripts/pi-hole/php/header.php";
|
||||
$type = "all";
|
||||
$pagetitle = "Domain";
|
||||
$adjective = "";
|
||||
if (isset($_GET['type']) && ($_GET['type'] === "white" || $_GET['type'] === "black")) {
|
||||
$type = $_GET['type'];
|
||||
$pagetitle = ucfirst($type)."list";
|
||||
$adjective = $type."listed";
|
||||
}
|
||||
?>
|
||||
|
||||
<!-- Title -->
|
||||
<div class="page-header">
|
||||
<h1>Domain group management</h1>
|
||||
<h1><?php echo $pagetitle; ?> management</h1>
|
||||
</div>
|
||||
|
||||
<!-- Domain Input -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="box" id="add-group">
|
||||
<!-- /.box-header -->
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">
|
||||
Add a new domain
|
||||
Add a new <?php echo $adjective; ?> domain or regex filter
|
||||
</h3>
|
||||
</div>
|
||||
<!-- /.box-header -->
|
||||
<div class="box-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="ex1">Domain:</label>
|
||||
<input id="new_domain" type="url" class="form-control" placeholder="Domain to be added" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label for="ex2">Type:</label>
|
||||
<select id="new_type" class="form-control">
|
||||
<option value="0">Exact whitelist</option>
|
||||
<option value="1">Exact blacklist</option>
|
||||
<option value="2">Regex whitelist</option>
|
||||
<option value="3">Regex blacklist</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label for="ex3">Comment:</label>
|
||||
<input id="new_comment" type="text" class="form-control" placeholder="Domain description (optional)">
|
||||
<div class="nav-tabs-custom">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a aria-expanded="true" href="#tab_domain" data-toggle="tab">Domain</a></li>
|
||||
<li class=""><a aria-expanded="false" href="#tab_regex" data-toggle="tab">RegEx filter</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<!-- Domain tab -->
|
||||
<div id="tab_domain" class="tab-pane active in">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="new_domain">Domain:</label>
|
||||
<div class="input-group">
|
||||
<input id="new_domain" type="url" class="form-control active" placeholder="Domain to be added" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off">
|
||||
<span class="input-group-addon">
|
||||
<input type="checkbox" id="wildcard_checkbox">
|
||||
wildcard</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<strong><i class="fa fa-question-circle"></i> Checkbox "wildcard":</span></strong> Check this box if you want to involve
|
||||
all subdomains. The entered domain will be converted to a RegEx filter while adding.
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 form-group">
|
||||
<label for="new_domain_comment">Comment:</label>
|
||||
<input id="new_domain_comment" type="text" class="form-control" placeholder="Description (optional)">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- RegEx tab -->
|
||||
<div id="tab_regex" class="tab-pane">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="new_regex">Regular Expression:</label>
|
||||
<input id="new_regex" type="text" class="form-control active" placeholder="RegEx to be added">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<strong>Hint:</strong> Need help to write a proper RegEx rule? Have a look at our online
|
||||
<a href="https://docs.pi-hole.net/ftldns/regex/tutorial" rel="noopener" target="_blank">
|
||||
regular expressions tutorial</a>.
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label for="new_regex_comment">Comment:</label>
|
||||
<input id="new_regex_comment" type="text" class="form-control" placeholder="Description (optional)">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn-toolbar pull-right" role="toolbar" aria-label="Toolbar with buttons">
|
||||
<?php if ( $type !== "white" ) { ?>
|
||||
<div class="btn-group" role="group" aria-label="Third group">
|
||||
<button type="button" class="btn btn-primary" id="add2black">Add to Blacklist</button>
|
||||
</div>
|
||||
<?php } if ( $type !== "black" ) { ?>
|
||||
<div class="btn-group" role="group" aria-label="Third group">
|
||||
<button type="button" class="btn btn-primary" id="add2white">Add to Whitelist</button>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer clearfix">
|
||||
<button id="btnAdd" class="btn btn-primary pull-right">Add</button>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Domain List -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="box" id="domains-list">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">
|
||||
List of configured domains
|
||||
List of <?php echo $adjective; ?> entries
|
||||
</h3>
|
||||
</div>
|
||||
<!-- /.box-header -->
|
||||
@@ -65,7 +118,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Domain</th>
|
||||
<th>Domain/RegEx</th>
|
||||
<th>Type</th>
|
||||
<th>Status</th>
|
||||
<th>Comment</th>
|
||||
@@ -82,6 +135,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="scripts/pi-hole/js/groups-common.js"></script>
|
||||
<script src="scripts/pi-hole/js/groups-domains.js"></script>
|
||||
|
||||
<?php
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="scripts/pi-hole/js/groups-common.js"></script>
|
||||
<script src="scripts/pi-hole/js/groups.js"></script>
|
||||
|
||||
<?php
|
||||
|
||||
|
Before Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 508 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "Pi-hole Admin Console",
|
||||
"short_name": "Pi-hole",
|
||||
"icons": [
|
||||
{
|
||||
"src": "android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#367fa9",
|
||||
"background_color": "#367fa9",
|
||||
"display": "standalone"
|
||||
}
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
@@ -1,84 +0,0 @@
|
||||
<?php /*
|
||||
* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
require "scripts/pi-hole/php/header.php";
|
||||
|
||||
$list = $_GET['l'] ?? '';
|
||||
|
||||
if ($list == "white")
|
||||
{
|
||||
$listtitle = "Whitelist";
|
||||
$heading = "whitelisting";
|
||||
}
|
||||
elseif ($list == "black")
|
||||
{
|
||||
$listtitle = "Blacklist";
|
||||
$heading = "blocking";
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "Invalid list parameter ".$list;
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
die();
|
||||
}
|
||||
?>
|
||||
<!-- Send list type to JS -->
|
||||
<div id="list-type" hidden><?php echo $list ?></div>
|
||||
|
||||
<!-- Title -->
|
||||
<div class="page-header">
|
||||
<h1><?php echo $listtitle; ?></h1>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="ex1">Domain:</label>
|
||||
<input id="domain" type="url" class="form-control" placeholder="Add a domain (example.com)" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="ex2">Comment:</label>
|
||||
<div class="form-group input-group">
|
||||
<input id="comment" type="text" class="form-control" placeholder="Include a comment (optional)">
|
||||
<span class="input-group-btn">
|
||||
<button id="btnAdd" class="btn btn-default" type="button">Add (exact)</button>
|
||||
<button id="btnAddWildcard" class="btn btn-default" type="button">Add (wildcard)</button>
|
||||
<button id="btnAddRegex" class="btn btn-default" type="button">Add (regex)</button>
|
||||
<button id="btnRefresh" class="btn btn-default" type="button"><i class="fa fa-sync"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Alerts -->
|
||||
<div id="alInfo" class="alert alert-info alert-dismissible fade in" role="alert" hidden="true">
|
||||
<button type="button" class="close" data-hide="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
Adding to the <?php echo $listtitle; ?>...
|
||||
</div>
|
||||
<div id="alSuccess" class="alert alert-success alert-dismissible fade in" role="alert" hidden="true">
|
||||
<button type="button" class="close" data-hide="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<span id="success-message"></span>
|
||||
</div>
|
||||
<div id="alFailure" class="alert alert-danger alert-dismissible fade in" role="alert" hidden="true">
|
||||
<button type="button" class="close" data-hide="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
Failure! Something went wrong, see output below:<br/><br/><pre><span id="err"></span></pre>
|
||||
</div>
|
||||
<div id="alWarning" class="alert alert-warning alert-dismissible fade in" role="alert" hidden="true">
|
||||
<button type="button" class="close" data-hide="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
At least one domain was already present, see output below:<br/><br/><pre><span id="warn"></span></pre>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Domain List -->
|
||||
<h3 id="h3-exact" hidden="true">Exact <?php echo $heading; ?></h3>
|
||||
<ul class="list-group" id="list"></ul>
|
||||
<h3 id="h3-regex" hidden="true"><a href="https://docs.pi-hole.net/ftldns/regex/overview/" rel="noopener" target="_blank" title="Click for Pi-hole Regex documentation">Regex</a> & Wildcard <?php echo $heading; ?></h3>
|
||||
<ul class="list-group" id="list-regex"></ul>
|
||||
|
||||
<script src="scripts/pi-hole/js/list.js"></script>
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
?>
|
||||
@@ -155,8 +155,8 @@ if(strlen($showing) > 0)
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
<label><input type="checkbox" id="autofilter" checked="true"> Apply filtering on click on Type, Domain, and Clients</label><br/>
|
||||
<button type="button" id="resetButton" hidden="true" class="btn btn-default btn-sm text-red">Clear Filters</button>
|
||||
<label><input type="checkbox" id="autofilter"> Apply filtering on click on Type, Domain, and Clients</label><br/>
|
||||
<button type="button" id="resetButton" hidden="true">Clear Filters</button>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
// Define global variables
|
||||
var auditList = [],
|
||||
auditTimeout;
|
||||
var auditTimeout = null;
|
||||
|
||||
// Credit: http://stackoverflow.com/questions/1787322/htmlspecialchars-equivalent-in-javascript/4835406#4835406
|
||||
function escapeHtml(text) {
|
||||
@@ -46,7 +45,7 @@ function updateTopLists() {
|
||||
domain = escapeHtml(domain);
|
||||
url = '<a href="queries.php?domain=' + domain + '">' + domain + "</a>";
|
||||
domaintable.append(
|
||||
"<tr> <td>" +
|
||||
"<tr><td>" +
|
||||
url +
|
||||
"</td> <td>" +
|
||||
data.top_queries[domain] +
|
||||
@@ -68,7 +67,7 @@ function updateTopLists() {
|
||||
printdomain +
|
||||
"</a> (wildcard blocked)";
|
||||
adtable.append(
|
||||
"<tr> <td>" +
|
||||
"<tr><td>" +
|
||||
url +
|
||||
"</td> <td>" +
|
||||
data.top_ads[domain] +
|
||||
@@ -77,7 +76,7 @@ function updateTopLists() {
|
||||
} else {
|
||||
url = '<a href="queries.php?domain=' + printdomain + '">' + printdomain + "</a>";
|
||||
adtable.append(
|
||||
"<tr> <td>" +
|
||||
"<tr><td>" +
|
||||
url +
|
||||
"</td> <td>" +
|
||||
data.top_ads[domain] +
|
||||
@@ -89,8 +88,14 @@ function updateTopLists() {
|
||||
|
||||
$("#domain-frequency .overlay").hide();
|
||||
$("#ad-frequency .overlay").hide();
|
||||
// Update top lists data every second
|
||||
setTimeout(updateTopLists, 1000);
|
||||
// Update top lists data every ten seconds
|
||||
// Updates are also triggered by button actions
|
||||
// and reset the running timer
|
||||
if (auditTimeout !== null) {
|
||||
window.clearTimeout(auditTimeout);
|
||||
}
|
||||
|
||||
auditTimeout = setTimeout(updateTopLists, 10000);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -99,10 +104,32 @@ function add(domain, list) {
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/add.php",
|
||||
method: "post",
|
||||
data: { domain: domain, list: list, token: token }
|
||||
data: { domain: domain, list: list, token: token },
|
||||
success: function() {
|
||||
updateTopLists();
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
console.log(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function blacklistUrl(url) {
|
||||
// We add to audit last as it will reload the table on success
|
||||
add(url, "black");
|
||||
add(url, "audit");
|
||||
}
|
||||
|
||||
function whitelistUrl(url) {
|
||||
// We add to audit last as it will reload the table on success
|
||||
add(url, "white");
|
||||
add(url, "audit");
|
||||
}
|
||||
|
||||
function auditUrl(url) {
|
||||
add(url, "audit");
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
// Pull in data via AJAX
|
||||
updateTopLists();
|
||||
@@ -110,11 +137,9 @@ $(document).ready(function() {
|
||||
$("#domain-frequency tbody").on("click", "button", function() {
|
||||
var url = $(this)
|
||||
.parents("tr")[0]
|
||||
.textContent.split(" ")[0];
|
||||
.textContent.split(" ")[0];
|
||||
if ($(this).context.textContent === " Blacklist") {
|
||||
add(url, "audit");
|
||||
add(url, "black");
|
||||
$("#gravityBtn").prop("disabled", false);
|
||||
blacklistUrl(url);
|
||||
} else {
|
||||
auditUrl(url);
|
||||
}
|
||||
@@ -123,36 +148,11 @@ $(document).ready(function() {
|
||||
$("#ad-frequency tbody").on("click", "button", function() {
|
||||
var url = $(this)
|
||||
.parents("tr")[0]
|
||||
.textContent.split(" ")[0]
|
||||
.split(" ")[0];
|
||||
.textContent.split(" ")[0];
|
||||
if ($(this).context.textContent === " Whitelist") {
|
||||
add(url, "audit");
|
||||
add(url, "white");
|
||||
$("#gravityBtn").prop("disabled", false);
|
||||
whitelistUrl(url);
|
||||
} else {
|
||||
auditUrl(url);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function auditUrl(url) {
|
||||
if (auditList.indexOf(url) > -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (auditTimeout) {
|
||||
clearTimeout(auditTimeout);
|
||||
}
|
||||
|
||||
auditList.push(url);
|
||||
// wait 3 seconds to see if more domains need auditing
|
||||
// and batch them all into a single request
|
||||
auditTimeout = setTimeout(function() {
|
||||
add(auditList.join(" "), "audit");
|
||||
auditList = [];
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
$("#gravityBtn").on("click", function() {
|
||||
window.location.replace("gravity.php?go");
|
||||
});
|
||||
|
||||
@@ -195,6 +195,18 @@ function getQueryTypes() {
|
||||
queryType.push([6, 7, 8]);
|
||||
}
|
||||
|
||||
if ($("#type_gravity_CNAME").prop("checked")) {
|
||||
queryType.push(9);
|
||||
}
|
||||
|
||||
if ($("#type_regex_CNAME").prop("checked")) {
|
||||
queryType.push(10);
|
||||
}
|
||||
|
||||
if ($("#type_blacklist_CNAME").prop("checked")) {
|
||||
queryType.push(11);
|
||||
}
|
||||
|
||||
return queryType.join(",");
|
||||
}
|
||||
|
||||
@@ -282,13 +294,13 @@ $(document).ready(function() {
|
||||
break;
|
||||
case 4:
|
||||
color = "red";
|
||||
fieldtext = "Blocked <br class='hidden-lg'>(regex/wildcard)";
|
||||
fieldtext = "Blocked <br class='hidden-lg'>(regex blacklist)";
|
||||
buttontext =
|
||||
'<button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button>';
|
||||
break;
|
||||
case 5:
|
||||
color = "red";
|
||||
fieldtext = "Blocked <br class='hidden-lg'>(blacklist)";
|
||||
fieldtext = "Blocked <br class='hidden-lg'>(exact blacklist)";
|
||||
buttontext =
|
||||
'<button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button>';
|
||||
break;
|
||||
@@ -307,6 +319,24 @@ $(document).ready(function() {
|
||||
fieldtext = "Blocked <br class='hidden-lg'>(external, NXRA)";
|
||||
buttontext = "";
|
||||
break;
|
||||
case 9:
|
||||
color = "red";
|
||||
fieldtext = "Blocked (gravity, CNAME)";
|
||||
buttontext =
|
||||
'<button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button>';
|
||||
break;
|
||||
case 10:
|
||||
color = "red";
|
||||
fieldtext = "Blocked <br class='hidden-lg'>(regex blacklist, CNAME)";
|
||||
buttontext =
|
||||
'<button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button>';
|
||||
break;
|
||||
case 11:
|
||||
color = "red";
|
||||
fieldtext = "Blocked <br class='hidden-lg'>(exact blacklist, CNAME)";
|
||||
buttontext =
|
||||
'<button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button>';
|
||||
break;
|
||||
default:
|
||||
color = "black";
|
||||
fieldtext = "Unknown";
|
||||
|
||||
@@ -5,71 +5,11 @@
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
/* global moment:false */
|
||||
/* global utils:false */
|
||||
|
||||
var table;
|
||||
var groups = [];
|
||||
var token = $("#token").text();
|
||||
var info = null;
|
||||
|
||||
function showAlert(type, icon, title, message) {
|
||||
var opts = {};
|
||||
title = " <strong>" + title + "</strong><br>";
|
||||
switch (type) {
|
||||
case "info":
|
||||
opts = {
|
||||
type: "info",
|
||||
icon: "far fa-clock",
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
info = $.notify(opts);
|
||||
break;
|
||||
case "success":
|
||||
opts = {
|
||||
type: "success",
|
||||
icon: icon,
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
|
||||
break;
|
||||
case "warning":
|
||||
opts = {
|
||||
type: "warning",
|
||||
icon: "fas fa-exclamation-triangle",
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
|
||||
break;
|
||||
case "error":
|
||||
opts = {
|
||||
type: "danger",
|
||||
icon: "fas fa-times",
|
||||
title: " <strong>Error, something went wrong!</strong><br>",
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
var token = $("#token").html();
|
||||
|
||||
function get_groups() {
|
||||
$.post(
|
||||
@@ -83,19 +23,11 @@ function get_groups() {
|
||||
);
|
||||
}
|
||||
|
||||
function datetime(date) {
|
||||
return moment.unix(Math.floor(date)).format("Y-MM-DD HH:mm:ss z");
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$("#btnAdd").on("click", addAdlist);
|
||||
|
||||
get_groups();
|
||||
|
||||
$("#select").on("change", function() {
|
||||
$("#ip-custom").val("");
|
||||
$("#ip-custom").prop("disabled", $("#select option:selected").val() !== "custom");
|
||||
});
|
||||
// Disable autocorrect in the search box
|
||||
var input = document.querySelector("input[type=search]");
|
||||
input.setAttribute("autocomplete", "off");
|
||||
@@ -121,46 +53,56 @@ function initTable() {
|
||||
{ data: null, width: "80px", orderable: false }
|
||||
],
|
||||
drawCallback: function() {
|
||||
$(".deleteAdlist").on("click", deleteAdlist);
|
||||
$('button[id^="deleteAdlist_"]').on("click", deleteAdlist);
|
||||
},
|
||||
rowCallback: function(row, data) {
|
||||
$(row).attr("data-id", data.id);
|
||||
var tooltip =
|
||||
"Added: " +
|
||||
datetime(data.date_added) +
|
||||
utils.datetime(data.date_added) +
|
||||
"\nLast modified: " +
|
||||
datetime(data.date_modified) +
|
||||
utils.datetime(data.date_modified) +
|
||||
"\nDatabase ID: " +
|
||||
data.id;
|
||||
$("td:eq(0)", row).html(
|
||||
'<code id="address" title="' + tooltip + '">' + data.address + "</code>"
|
||||
'<code id="address_' +
|
||||
data.id +
|
||||
'" title="' +
|
||||
tooltip +
|
||||
'" class="breakall">' +
|
||||
data.address +
|
||||
"</code>"
|
||||
);
|
||||
|
||||
var disabled = data.enabled === 0;
|
||||
$("td:eq(1)", row).html(
|
||||
'<input type="checkbox" id="status"' + (disabled ? "" : " checked") + ">"
|
||||
'<input type="checkbox" id="status_' + data.id + '"' + (disabled ? "" : " checked") + ">"
|
||||
);
|
||||
var status = $("#status", row);
|
||||
status.bootstrapToggle({
|
||||
var statusEl = $("#status_" + data.id, row);
|
||||
statusEl.bootstrapToggle({
|
||||
on: "Enabled",
|
||||
off: "Disabled",
|
||||
size: "small",
|
||||
onstyle: "success",
|
||||
width: "80px"
|
||||
});
|
||||
status.on("change", editAdlist);
|
||||
statusEl.on("change", editAdlist);
|
||||
|
||||
$("td:eq(2)", row).html(
|
||||
'<input id="comment" class="form-control"><input id="id" type="hidden" value="' +
|
||||
data.id +
|
||||
'">'
|
||||
);
|
||||
var comment = $("#comment", row);
|
||||
comment.val(data.comment);
|
||||
comment.on("change", editAdlist);
|
||||
$("td:eq(2)", row).html('<input id="comment_' + data.id + '" class="form-control">');
|
||||
var commentEl = $("#comment_" + data.id, row);
|
||||
commentEl.val(data.comment);
|
||||
commentEl.on("change", editAdlist);
|
||||
|
||||
$("td:eq(3)", row).empty();
|
||||
$("td:eq(3)", row).append('<select id="multiselect" multiple="multiple"></select>');
|
||||
var sel = $("#multiselect", row);
|
||||
$("td:eq(3)", row).append(
|
||||
'<div id="selectHome_' +
|
||||
data.id +
|
||||
'">' +
|
||||
'<select id="multiselect_' +
|
||||
data.id +
|
||||
'" multiple="multiple"></select></div>'
|
||||
);
|
||||
var selectEl = $("#multiselect_" + data.id, row);
|
||||
// Add all known groups
|
||||
for (var i = 0; i < groups.length; i++) {
|
||||
var extra = "";
|
||||
@@ -168,7 +110,7 @@ function initTable() {
|
||||
extra = " (disabled)";
|
||||
}
|
||||
|
||||
sel.append(
|
||||
selectEl.append(
|
||||
$("<option />")
|
||||
.val(groups[i].id)
|
||||
.text(groups[i].name + extra)
|
||||
@@ -176,19 +118,51 @@ function initTable() {
|
||||
}
|
||||
|
||||
// Select assigned groups
|
||||
sel.val(data.groups);
|
||||
selectEl.val(data.groups);
|
||||
// Initialize multiselect
|
||||
sel.multiselect({ includeSelectAllOption: true });
|
||||
sel.on("change", editAdlist);
|
||||
selectEl.multiselect({
|
||||
includeSelectAllOption: true,
|
||||
buttonContainer: '<div id="container_' + data.id + '" class="btn-group"/>',
|
||||
maxHeight: 200,
|
||||
onDropdownShown: function() {
|
||||
var el = $("#container_" + data.id);
|
||||
var top = el[0].getBoundingClientRect().top;
|
||||
var bottom = $(window).height() - top - el.height();
|
||||
if (bottom < 200) {
|
||||
el.addClass("dropup");
|
||||
}
|
||||
|
||||
if (bottom > 200) {
|
||||
el.removeClass("dropup");
|
||||
}
|
||||
|
||||
var offset = el.offset();
|
||||
$("body").append(el);
|
||||
el.css("position", "absolute");
|
||||
el.css("top", offset.top + "px");
|
||||
el.css("left", offset.left + "px");
|
||||
},
|
||||
onDropdownHide: function() {
|
||||
var el = $("#container_" + data.id);
|
||||
var home = $("#selectHome_" + data.id);
|
||||
home.append(el);
|
||||
el.removeAttr("style");
|
||||
}
|
||||
});
|
||||
selectEl.on("change", editAdlist);
|
||||
|
||||
var button =
|
||||
'<button class="btn btn-danger btn-xs deleteAdlist" type="button" data-id="' +
|
||||
'<button class="btn btn-danger btn-xs" type="button" id="deleteAdlist_' +
|
||||
data.id +
|
||||
'">' +
|
||||
'<span class="far fa-trash-alt"></span>' +
|
||||
"</button>";
|
||||
$("td:eq(4)", row).html(button);
|
||||
},
|
||||
dom:
|
||||
"<'row'<'col-sm-4'l><'col-sm-8'f>>" +
|
||||
"<'row'<'col-sm-12'<'table-responsive'tr>>>" +
|
||||
"<'row'<'col-sm-5'i><'col-sm-7'p>>",
|
||||
lengthMenu: [
|
||||
[10, 25, 50, 100, -1],
|
||||
[10, 25, 50, 100, "All"]
|
||||
@@ -236,10 +210,11 @@ function addAdlist() {
|
||||
var address = $("#new_address").val();
|
||||
var comment = $("#new_comment").val();
|
||||
|
||||
showAlert("info", "", "Adding adlist...", address);
|
||||
utils.disableAll();
|
||||
utils.showAlert("info", "", "Adding adlist...", address);
|
||||
|
||||
if (address.length === 0) {
|
||||
showAlert("warning", "", "Warning", "Please specify an adlist address");
|
||||
utils.showAlert("warning", "", "Warning", "Please specify an adlist address");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -254,17 +229,19 @@ function addAdlist() {
|
||||
token: token
|
||||
},
|
||||
success: function(response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
showAlert("success", "fas fa-plus", "Successfully added adlist", address);
|
||||
utils.showAlert("success", "fas fa-plus", "Successfully added adlist", address);
|
||||
$("#new_address").val("");
|
||||
$("#new_comment").val("");
|
||||
table.ajax.reload();
|
||||
} else {
|
||||
showAlert("error", "", "Error while adding new adlist: ", response.message);
|
||||
utils.showAlert("error", "", "Error while adding new adlist: ", response.message);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert("error", "", "Error while adding new adlist: ", jqXHR.responseText);
|
||||
utils.enableAll();
|
||||
utils.showAlert("error", "", "Error while adding new adlist: ", jqXHR.responseText);
|
||||
console.log(exception);
|
||||
}
|
||||
});
|
||||
@@ -273,29 +250,40 @@ function addAdlist() {
|
||||
function editAdlist() {
|
||||
var elem = $(this).attr("id");
|
||||
var tr = $(this).closest("tr");
|
||||
var id = tr.find("#id").val();
|
||||
var status = tr.find("#status").is(":checked") ? 1 : 0;
|
||||
var comment = tr.find("#comment").val();
|
||||
var groups = tr.find("#multiselect").val();
|
||||
var address = tr.find("#address").text();
|
||||
var id = tr.attr("data-id");
|
||||
var status = tr.find("#status_" + id).is(":checked") ? 1 : 0;
|
||||
var comment = tr.find("#comment_" + id).val();
|
||||
var groups = tr.find("#multiselect_" + id).val();
|
||||
var address = tr.find("#address_" + id).text();
|
||||
|
||||
var done = "edited";
|
||||
var not_done = "editing";
|
||||
if (elem === "status" && status === 1) {
|
||||
done = "enabled";
|
||||
not_done = "enabling";
|
||||
} else if (elem === "status" && status === 0) {
|
||||
done = "disabled";
|
||||
not_done = "disabling";
|
||||
} else if (elem === "comment") {
|
||||
done = "edited comment of";
|
||||
not_done = "editing comment of";
|
||||
} else if (elem === "multiselect") {
|
||||
done = "edited groups of";
|
||||
not_done = "editing groups of";
|
||||
switch (elem) {
|
||||
case "status_" + id:
|
||||
if (status === 0) {
|
||||
done = "disabled";
|
||||
not_done = "disabling";
|
||||
} else if (status === 1) {
|
||||
done = "enabled";
|
||||
not_done = "enabling";
|
||||
}
|
||||
|
||||
break;
|
||||
case "comment_" + id:
|
||||
done = "edited comment of";
|
||||
not_done = "editing comment of";
|
||||
break;
|
||||
case "multiselect_" + id:
|
||||
done = "edited groups of";
|
||||
not_done = "editing groups of";
|
||||
break;
|
||||
default:
|
||||
alert("bad element or invalid data-id!");
|
||||
return;
|
||||
}
|
||||
|
||||
showAlert("info", "", "Editing adlist...", address);
|
||||
utils.disableAll();
|
||||
utils.showAlert("info", "", "Editing adlist...", address);
|
||||
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
@@ -310,10 +298,16 @@ function editAdlist() {
|
||||
token: token
|
||||
},
|
||||
success: function(response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
showAlert("success", "fas fa-pencil-alt", "Successfully " + done + " adlist ", address);
|
||||
utils.showAlert(
|
||||
"success",
|
||||
"fas fa-pencil-alt",
|
||||
"Successfully " + done + " adlist ",
|
||||
address
|
||||
);
|
||||
} else {
|
||||
showAlert(
|
||||
utils.showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while " + not_done + " adlist with ID " + id,
|
||||
@@ -322,7 +316,8 @@ function editAdlist() {
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert(
|
||||
utils.enableAll();
|
||||
utils.showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while " + not_done + " adlist with ID " + id,
|
||||
@@ -334,27 +329,32 @@ function editAdlist() {
|
||||
}
|
||||
|
||||
function deleteAdlist() {
|
||||
var id = $(this).attr("data-id");
|
||||
var tr = $(this).closest("tr");
|
||||
var address = tr.find("#address").text();
|
||||
var id = tr.attr("data-id");
|
||||
var address = tr.find("#address_" + id).text();
|
||||
|
||||
showAlert("info", "", "Deleting adlist...", address);
|
||||
utils.disableAll();
|
||||
utils.showAlert("info", "", "Deleting adlist...", address);
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: { action: "delete_adlist", id: id, token: token },
|
||||
success: function(response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
showAlert("success", "far fa-trash-alt", "Successfully deleted adlist ", address);
|
||||
utils.showAlert("success", "far fa-trash-alt", "Successfully deleted adlist ", address);
|
||||
table
|
||||
.row(tr)
|
||||
.remove()
|
||||
.draw(false);
|
||||
} else showAlert("error", "", "Error while deleting adlist with ID " + id, response.message);
|
||||
} else {
|
||||
utils.showAlert("error", "", "Error while deleting adlist with ID " + id, response.message);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert("error", "", "Error while deleting adlist with ID " + id, jqXHR.responseText);
|
||||
utils.enableAll();
|
||||
utils.showAlert("error", "", "Error while deleting adlist with ID " + id, jqXHR.responseText);
|
||||
console.log(exception);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -5,69 +5,11 @@
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
/* global utils:false */
|
||||
|
||||
var table;
|
||||
var groups = [];
|
||||
var token = $("#token").text();
|
||||
var info = null;
|
||||
|
||||
function showAlert(type, icon, title, message) {
|
||||
var opts = {};
|
||||
title = " <strong>" + title + "</strong><br>";
|
||||
switch (type) {
|
||||
case "info":
|
||||
opts = {
|
||||
type: "info",
|
||||
icon: "far fa-clock",
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
info = $.notify(opts);
|
||||
break;
|
||||
case "success":
|
||||
opts = {
|
||||
type: "success",
|
||||
icon: icon,
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
|
||||
break;
|
||||
case "warning":
|
||||
opts = {
|
||||
type: "warning",
|
||||
icon: "fas fa-exclamation-triangle",
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
|
||||
break;
|
||||
case "error":
|
||||
opts = {
|
||||
type: "danger",
|
||||
icon: "fas fa-times",
|
||||
title: " <strong>Error, something went wrong!</strong><br>",
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
var token = $("#token").html();
|
||||
|
||||
function reload_client_suggestions() {
|
||||
$.post(
|
||||
@@ -75,6 +17,7 @@ function reload_client_suggestions() {
|
||||
{ action: "get_unconfigured_clients", token: token },
|
||||
function(data) {
|
||||
var sel = $("#select");
|
||||
var customWasSelected = sel.val() === "custom";
|
||||
sel.empty();
|
||||
for (var key in data) {
|
||||
if (!Object.prototype.hasOwnProperty.call(data, key)) {
|
||||
@@ -96,8 +39,11 @@ function reload_client_suggestions() {
|
||||
sel.append(
|
||||
$("<option />")
|
||||
.val("custom")
|
||||
.text("Custom, specified on the right")
|
||||
.text("Custom, specified below...")
|
||||
);
|
||||
if (customWasSelected) {
|
||||
sel.val("custom");
|
||||
}
|
||||
},
|
||||
"json"
|
||||
);
|
||||
@@ -144,29 +90,56 @@ function initTable() {
|
||||
columns: [
|
||||
{ data: "id", visible: false },
|
||||
{ data: "ip" },
|
||||
{ data: "comment" },
|
||||
{ data: "groups", searchable: false },
|
||||
{ data: "name", width: "80px", orderable: false }
|
||||
],
|
||||
drawCallback: function() {
|
||||
$(".deleteClient").on("click", deleteClient);
|
||||
$('button[id^="deleteClient_"]').on("click", deleteClient);
|
||||
},
|
||||
rowCallback: function(row, data) {
|
||||
var tooltip = "Database ID: " + data.id;
|
||||
$(row).attr("data-id", data.id);
|
||||
var tooltip =
|
||||
"Added: " +
|
||||
utils.datetime(data.date_added) +
|
||||
"\nLast modified: " +
|
||||
utils.datetime(data.date_modified) +
|
||||
"\nDatabase ID: " +
|
||||
data.id;
|
||||
var ip_name =
|
||||
'<code id="ip" title="' +
|
||||
tooltip +
|
||||
'">' +
|
||||
data.ip +
|
||||
'</code><input id="id" type="hidden" value="' +
|
||||
'<code id="ip_' +
|
||||
data.id +
|
||||
'">';
|
||||
'" title="' +
|
||||
tooltip +
|
||||
'" class="breakall">' +
|
||||
data.ip +
|
||||
"</code>";
|
||||
if (data.name !== null && data.name.length > 0)
|
||||
ip_name += '<br><code id="name" title="' + tooltip + '">' + data.name + "</code>";
|
||||
ip_name +=
|
||||
'<br><code id="name_' +
|
||||
data.id +
|
||||
'" title="' +
|
||||
tooltip +
|
||||
'" class="breakall">' +
|
||||
data.name +
|
||||
"</code>";
|
||||
$("td:eq(0)", row).html(ip_name);
|
||||
|
||||
$("td:eq(1)", row).empty();
|
||||
$("td:eq(1)", row).append('<select id="multiselect" multiple="multiple"></select>');
|
||||
var sel = $("#multiselect", row);
|
||||
$("td:eq(1)", row).html('<input id="comment_' + data.id + '" class="form-control">');
|
||||
var commentEl = $("#comment_" + data.id, row);
|
||||
commentEl.val(data.comment);
|
||||
commentEl.on("change", editClient);
|
||||
|
||||
$("td:eq(2)", row).empty();
|
||||
$("td:eq(2)", row).append(
|
||||
'<div id="selectHome_' +
|
||||
data.id +
|
||||
'">' +
|
||||
'<select id="multiselect_' +
|
||||
data.id +
|
||||
'" multiple="multiple"></select></div>'
|
||||
);
|
||||
var selectEl = $("#multiselect_" + data.id, row);
|
||||
// Add all known groups
|
||||
for (var i = 0; i < groups.length; i++) {
|
||||
var extra = "";
|
||||
@@ -174,7 +147,7 @@ function initTable() {
|
||||
extra = " (disabled)";
|
||||
}
|
||||
|
||||
sel.append(
|
||||
selectEl.append(
|
||||
$("<option />")
|
||||
.val(groups[i].id)
|
||||
.text(groups[i].name + extra)
|
||||
@@ -182,19 +155,51 @@ function initTable() {
|
||||
}
|
||||
|
||||
// Select assigned groups
|
||||
sel.val(data.groups);
|
||||
selectEl.val(data.groups);
|
||||
// Initialize multiselect
|
||||
sel.multiselect({ includeSelectAllOption: true });
|
||||
sel.on("change", editClient);
|
||||
selectEl.multiselect({
|
||||
includeSelectAllOption: true,
|
||||
buttonContainer: '<div id="container_' + data.id + '" class="btn-group"/>',
|
||||
maxHeight: 200,
|
||||
onDropdownShown: function() {
|
||||
var el = $("#container_" + data.id);
|
||||
var top = el[0].getBoundingClientRect().top;
|
||||
var bottom = $(window).height() - top - el.height();
|
||||
if (bottom < 200) {
|
||||
el.addClass("dropup");
|
||||
}
|
||||
|
||||
if (bottom > 200) {
|
||||
el.removeClass("dropup");
|
||||
}
|
||||
|
||||
var offset = el.offset();
|
||||
$("body").append(el);
|
||||
el.css("position", "absolute");
|
||||
el.css("top", offset.top + "px");
|
||||
el.css("left", offset.left + "px");
|
||||
},
|
||||
onDropdownHide: function() {
|
||||
var el = $("#container" + data.id);
|
||||
var home = $("#selectHome" + data.id);
|
||||
home.append(el);
|
||||
el.removeAttr("style");
|
||||
}
|
||||
});
|
||||
selectEl.on("change", editClient);
|
||||
|
||||
var button =
|
||||
'<button class="btn btn-danger btn-xs deleteClient" type="button" data-id="' +
|
||||
'<button class="btn btn-danger btn-xs" type="button" id="deleteClient_' +
|
||||
data.id +
|
||||
'">' +
|
||||
'<span class="far fa-trash-alt"></span>' +
|
||||
"</button>";
|
||||
$("td:eq(2)", row).html(button);
|
||||
$("td:eq(3)", row).html(button);
|
||||
},
|
||||
dom:
|
||||
"<'row'<'col-sm-4'l><'col-sm-8'f>>" +
|
||||
"<'row'<'col-sm-12'<'table-responsive'tr>>>" +
|
||||
"<'row'<'col-sm-5'i><'col-sm-7'p>>",
|
||||
lengthMenu: [
|
||||
[10, 25, 50, 100, -1],
|
||||
[10, 25, 50, 100, "All"]
|
||||
@@ -240,14 +245,32 @@ function initTable() {
|
||||
|
||||
function addClient() {
|
||||
var ip = $("#select").val();
|
||||
var comment = $("#new_comment").val();
|
||||
if (ip === "custom") {
|
||||
ip = $("#ip-custom").val();
|
||||
}
|
||||
|
||||
showAlert("info", "", "Adding client...", ip);
|
||||
utils.disableAll();
|
||||
utils.showAlert("info", "", "Adding client...", ip);
|
||||
|
||||
if (ip.length === 0) {
|
||||
showAlert("warning", "", "Warning", "Please specify a client IP address");
|
||||
utils.enableAll();
|
||||
utils.showAlert("warning", "", "Warning", "Please specify a client IP address");
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate IP address (may contain CIDR details)
|
||||
var ipv6format = ip.includes(":");
|
||||
|
||||
if (!ipv6format && !utils.validateIPv4CIDR(ip)) {
|
||||
utils.enableAll();
|
||||
utils.showAlert("warning", "", "Warning", "Invalid IPv4 address!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ipv6format && !utils.validateIPv6CIDR(ip)) {
|
||||
utils.enableAll();
|
||||
utils.showAlert("warning", "", "Warning", "Invalid IPv6 address!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -255,18 +278,20 @@ function addClient() {
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: { action: "add_client", ip: ip, token: token },
|
||||
data: { action: "add_client", ip: ip, comment: comment, token: token },
|
||||
success: function(response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
showAlert("success", "fas fa-plus", "Successfully added client", ip);
|
||||
utils.showAlert("success", "fas fa-plus", "Successfully added client", ip);
|
||||
reload_client_suggestions();
|
||||
table.ajax.reload();
|
||||
} else {
|
||||
showAlert("error", "", "Error while adding new client", response.message);
|
||||
utils.showAlert("error", "", "Error while adding new client", response.message);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert("error", "", "Error while adding new client", jqXHR.responseText);
|
||||
utils.enableAll();
|
||||
utils.showAlert("error", "", "Error while adding new client", jqXHR.responseText);
|
||||
console.log(exception);
|
||||
}
|
||||
});
|
||||
@@ -275,16 +300,26 @@ function addClient() {
|
||||
function editClient() {
|
||||
var elem = $(this).attr("id");
|
||||
var tr = $(this).closest("tr");
|
||||
var id = tr.find("#id").val();
|
||||
var groups = tr.find("#multiselect").val();
|
||||
var ip = tr.find("#ip").text();
|
||||
var name = tr.find("#name").text();
|
||||
var id = tr.attr("data-id");
|
||||
var groups = tr.find("#multiselect_" + id).val();
|
||||
var ip = tr.find("#ip_" + id).text();
|
||||
var name = tr.find("#name_" + id).text();
|
||||
var comment = tr.find("#comment_" + id).val();
|
||||
|
||||
var done = "edited";
|
||||
var not_done = "editing";
|
||||
if (elem === "multiselect") {
|
||||
done = "edited groups of";
|
||||
not_done = "editing groups of";
|
||||
switch (elem) {
|
||||
case "multiselect_" + id:
|
||||
done = "edited groups of";
|
||||
not_done = "editing groups of";
|
||||
break;
|
||||
case "comment_" + id:
|
||||
done = "edited comment of";
|
||||
not_done = "editing comment of";
|
||||
break;
|
||||
default:
|
||||
alert("bad element or invalid data-id!");
|
||||
return;
|
||||
}
|
||||
|
||||
var ip_name = ip;
|
||||
@@ -292,21 +327,34 @@ function editClient() {
|
||||
ip_name += " (" + name + ")";
|
||||
}
|
||||
|
||||
showAlert("info", "", "Editing client...", ip_name);
|
||||
utils.disableAll();
|
||||
utils.showAlert("info", "", "Editing client...", ip_name);
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: { action: "edit_client", id: id, groups: groups, token: token },
|
||||
data: {
|
||||
action: "edit_client",
|
||||
id: id,
|
||||
groups: groups,
|
||||
token: token,
|
||||
comment: comment
|
||||
},
|
||||
success: function(response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
showAlert("success", "fas fa-plus", "Successfully " + done + " client", ip_name);
|
||||
utils.showAlert("success", "fas fa-plus", "Successfully " + done + " client", ip_name);
|
||||
} else {
|
||||
showAlert("error", "Error while " + not_done + " client with ID " + id, response.message);
|
||||
utils.showAlert(
|
||||
"error",
|
||||
"Error while " + not_done + " client with ID " + id,
|
||||
response.message
|
||||
);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert(
|
||||
utils.enableAll();
|
||||
utils.showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while " + not_done + " client with ID " + id,
|
||||
@@ -318,36 +366,39 @@ function editClient() {
|
||||
}
|
||||
|
||||
function deleteClient() {
|
||||
var id = $(this).attr("data-id");
|
||||
var tr = $(this).closest("tr");
|
||||
var ip = tr.find("#ip").text();
|
||||
var name = tr.find("#name").text();
|
||||
var id = tr.attr("data-id");
|
||||
var ip = tr.find("#ip_" + id).text();
|
||||
var name = tr.find("#name_" + id).text();
|
||||
|
||||
var ip_name = ip;
|
||||
if (name.length > 0) {
|
||||
ip_name += " (" + name + ")";
|
||||
}
|
||||
|
||||
showAlert("info", "", "Deleting client...", ip_name);
|
||||
utils.disableAll();
|
||||
utils.showAlert("info", "", "Deleting client...", ip_name);
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: { action: "delete_client", id: id, token: token },
|
||||
success: function(response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
showAlert("success", "far fa-trash-alt", "Successfully deleted client ", ip_name);
|
||||
utils.showAlert("success", "far fa-trash-alt", "Successfully deleted client ", ip_name);
|
||||
table
|
||||
.row(tr)
|
||||
.remove()
|
||||
.draw(false);
|
||||
reload_client_suggestions();
|
||||
} else {
|
||||
showAlert("error", "", "Error while deleting client with ID " + id, response.message);
|
||||
utils.showAlert("error", "", "Error while deleting client with ID " + id, response.message);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert("error", "", "Error while deleting client with ID " + id, jqXHR.responseText);
|
||||
utils.enableAll();
|
||||
utils.showAlert("error", "", "Error while deleting client with ID " + id, jqXHR.responseText);
|
||||
console.log(exception);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2020 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
/* global moment:false */
|
||||
|
||||
var info = null;
|
||||
function showAlert(type, icon, title, message) {
|
||||
var opts = {};
|
||||
title = " <strong>" + title + "</strong><br>";
|
||||
switch (type) {
|
||||
case "info":
|
||||
opts = {
|
||||
type: "info",
|
||||
icon: "glyphicon glyphicon-time",
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
info = $.notify(opts);
|
||||
break;
|
||||
case "success":
|
||||
opts = {
|
||||
type: "success",
|
||||
icon: icon,
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
|
||||
break;
|
||||
case "warning":
|
||||
opts = {
|
||||
type: "warning",
|
||||
icon: "glyphicon glyphicon-warning-sign",
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
|
||||
break;
|
||||
case "error":
|
||||
opts = {
|
||||
type: "danger",
|
||||
icon: "glyphicon glyphicon-remove",
|
||||
title: " <strong>Error, something went wrong!</strong><br>",
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
function datetime(date) {
|
||||
return moment.unix(Math.floor(date)).format("Y-MM-DD HH:mm:ss z");
|
||||
}
|
||||
|
||||
function disableAll() {
|
||||
$("input").attr("disabled", true);
|
||||
$("select").attr("disabled", true);
|
||||
$("button").attr("disabled", true);
|
||||
$("textarea").attr("disabled", true);
|
||||
}
|
||||
|
||||
function enableAll() {
|
||||
$("input").attr("disabled", false);
|
||||
$("select").attr("disabled", false);
|
||||
$("button").attr("disabled", false);
|
||||
$("textarea").attr("disabled", false);
|
||||
|
||||
// Enable custom input field only if applicable
|
||||
var ip = $("#select") ? $("#select").val() : null;
|
||||
if (ip !== null && ip !== "custom") {
|
||||
ip = $("#ip-custom").attr("disabled", true);
|
||||
}
|
||||
}
|
||||
|
||||
// Pi-hole IPv4/CIDR validator by DL6ER, see regexr.com/50csh
|
||||
function validateIPv4CIDR(ip) {
|
||||
// One IPv4 element is 8bit: 0 - 256
|
||||
var ipv4elem = "(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)";
|
||||
// CIDR for IPv4 is 1 - 32 bit
|
||||
var v4cidr = "(\\/([1-9]|[1-2][0-9]|3[0-2])){0,1}";
|
||||
var ipv4validator = new RegExp(
|
||||
"^" + ipv4elem + "\\." + ipv4elem + "\\." + ipv4elem + "\\." + ipv4elem + v4cidr + "$"
|
||||
);
|
||||
return ipv4validator.test(ip);
|
||||
}
|
||||
|
||||
// Pi-hole IPv6/CIDR validator by DL6ER, see regexr.com/50csn
|
||||
function validateIPv6CIDR(ip) {
|
||||
// One IPv6 element is 16bit: 0000 - FFFF
|
||||
var ipv6elem = "[0-9A-Fa-f]{1,4}";
|
||||
// CIDR for IPv6 is 1- 128 bit
|
||||
var v6cidr = "(\\/([1-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])){0,1}";
|
||||
var ipv6validator = new RegExp(
|
||||
"^(((?:" +
|
||||
ipv6elem +
|
||||
"))((?::" +
|
||||
ipv6elem +
|
||||
"))*::((?:" +
|
||||
ipv6elem +
|
||||
"))((?::" +
|
||||
ipv6elem +
|
||||
"))*|((?:" +
|
||||
ipv6elem +
|
||||
"))((?::" +
|
||||
ipv6elem +
|
||||
")){7})" +
|
||||
v6cidr +
|
||||
"$"
|
||||
);
|
||||
return ipv6validator.test(ip);
|
||||
}
|
||||
|
||||
window.utils = (function() {
|
||||
return {
|
||||
showAlert: showAlert,
|
||||
datetime: datetime,
|
||||
disableAll: disableAll,
|
||||
enableAll: enableAll,
|
||||
validateIPv4CIDR: validateIPv4CIDR,
|
||||
validateIPv6CIDR: validateIPv6CIDR
|
||||
};
|
||||
})();
|
||||
@@ -5,71 +5,13 @@
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
/* global moment:false */
|
||||
/* global utils:false */
|
||||
|
||||
var table;
|
||||
var groups = [];
|
||||
var token = $("#token").text();
|
||||
var info = null;
|
||||
|
||||
function showAlert(type, icon, title, message) {
|
||||
var opts = {};
|
||||
title = " <strong>" + title + "</strong><br>";
|
||||
switch (type) {
|
||||
case "info":
|
||||
opts = {
|
||||
type: "info",
|
||||
icon: "far fa-clock",
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
info = $.notify(opts);
|
||||
break;
|
||||
case "success":
|
||||
opts = {
|
||||
type: "success",
|
||||
icon: icon,
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
|
||||
break;
|
||||
case "warning":
|
||||
opts = {
|
||||
type: "warning",
|
||||
icon: "fas fa-exclamation-triangle",
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
|
||||
break;
|
||||
case "error":
|
||||
opts = {
|
||||
type: "danger",
|
||||
icon: "fas fa-times",
|
||||
title: " <strong>Error, something went wrong!</strong><br>",
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
var token = $("#token").html();
|
||||
var GETDict = {};
|
||||
var showtype = "all";
|
||||
|
||||
function get_groups() {
|
||||
$.post(
|
||||
@@ -83,18 +25,32 @@ function get_groups() {
|
||||
);
|
||||
}
|
||||
|
||||
function datetime(date) {
|
||||
return moment.unix(Math.floor(date)).format("Y-MM-DD HH:mm:ss z");
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$("#btnAdd").on("click", addDomain);
|
||||
window.location.search
|
||||
.substr(1)
|
||||
.split("&")
|
||||
.forEach(function(item) {
|
||||
GETDict[item.split("=")[0]] = item.split("=")[1];
|
||||
});
|
||||
|
||||
get_groups();
|
||||
if ("type" in GETDict && (GETDict.type === "white" || GETDict.type === "black")) {
|
||||
showtype = GETDict.type;
|
||||
}
|
||||
|
||||
$("#select").on("change", function() {
|
||||
$("#ip-custom").val("");
|
||||
$("#ip-custom").prop("disabled", $("#select option:selected").val() !== "custom");
|
||||
// sync description fields, reset inactive inputs on tab change
|
||||
$('a[data-toggle="tab"]').on("shown.bs.tab", function() {
|
||||
var tabHref = $(this).attr("href");
|
||||
var val;
|
||||
if (tabHref === "#tab_domain") {
|
||||
val = $("#new_regex_comment").val();
|
||||
$("#new_domain_comment").val(val);
|
||||
$("#new_regex").val("");
|
||||
} else if (tabHref === "#tab_regex") {
|
||||
val = $("#new_domain_comment").val();
|
||||
$("#new_regex_comment").val(val);
|
||||
$("#new_domain").val("");
|
||||
$("#wildcard_checkbox").prop("checked", false);
|
||||
}
|
||||
});
|
||||
// Disable autocorrect in the search box
|
||||
var input = document.querySelector("input[type=search]");
|
||||
@@ -102,13 +58,17 @@ $(document).ready(function() {
|
||||
input.setAttribute("autocorrect", "off");
|
||||
input.setAttribute("autocapitalize", "off");
|
||||
input.setAttribute("spellcheck", false);
|
||||
|
||||
$("#add2black, #add2white").on("click", addDomain);
|
||||
|
||||
get_groups();
|
||||
});
|
||||
|
||||
function initTable() {
|
||||
table = $("#domainsTable").DataTable({
|
||||
ajax: {
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
data: { action: "get_domains", token: token },
|
||||
data: { action: "get_domains", showtype: showtype, token: token },
|
||||
type: "POST"
|
||||
},
|
||||
order: [[0, "asc"]],
|
||||
@@ -122,90 +82,163 @@ function initTable() {
|
||||
{ data: null, width: "80px", orderable: false }
|
||||
],
|
||||
drawCallback: function() {
|
||||
$(".deleteDomain").on("click", deleteDomain);
|
||||
$('button[id^="deleteDomain_"]').on("click", deleteDomain);
|
||||
},
|
||||
rowCallback: function(row, data) {
|
||||
$(row).attr("data-id", data.id);
|
||||
var tooltip =
|
||||
"Added: " +
|
||||
datetime(data.date_added) +
|
||||
utils.datetime(data.date_added) +
|
||||
"\nLast modified: " +
|
||||
datetime(data.date_modified) +
|
||||
utils.datetime(data.date_modified) +
|
||||
"\nDatabase ID: " +
|
||||
data.id;
|
||||
$("td:eq(0)", row).html(
|
||||
'<code id="domain" title="' + tooltip + '">' + data.domain + "</code>"
|
||||
'<code id="domain_' +
|
||||
data.id +
|
||||
'" title="' +
|
||||
tooltip +
|
||||
'" class="breakall">' +
|
||||
data.domain +
|
||||
"</code>"
|
||||
);
|
||||
|
||||
$("td:eq(1)", row).html(
|
||||
'<select id="type" class="form-control">' +
|
||||
var whitelist_options = "";
|
||||
if (showtype === "all" || showtype === "white") {
|
||||
whitelist_options =
|
||||
'<option value="0"' +
|
||||
(data.type === 0 ? " selected" : "") +
|
||||
">Exact whitelist</option>" +
|
||||
'<option value="1"' +
|
||||
(data.type === 1 ? " selected" : "") +
|
||||
">Exact blacklist</option>" +
|
||||
'<option value="2"' +
|
||||
(data.type === 2 ? " selected" : "") +
|
||||
">Regex whitelist</option>" +
|
||||
">Regex whitelist</option>";
|
||||
}
|
||||
|
||||
var blacklist_options = "";
|
||||
if (showtype === "all" || showtype === "black") {
|
||||
blacklist_options =
|
||||
'<option value="1"' +
|
||||
(data.type === 1 ? " selected " : " ") +
|
||||
">Exact blacklist</option>" +
|
||||
'<option value="3"' +
|
||||
(data.type === 3 ? " selected" : "") +
|
||||
">Regex blacklist</option>" +
|
||||
">Regex blacklist</option>";
|
||||
}
|
||||
|
||||
$("td:eq(1)", row).html(
|
||||
'<select id="type_' +
|
||||
data.id +
|
||||
'" class="form-control">' +
|
||||
whitelist_options +
|
||||
blacklist_options +
|
||||
"</select>"
|
||||
);
|
||||
$("#type", row).on("change", editDomain);
|
||||
var typeEl = $("#type_" + data.id, row);
|
||||
typeEl.on("change", editDomain);
|
||||
|
||||
var disabled = data.enabled === 0;
|
||||
$("td:eq(2)", row).html(
|
||||
'<input type="checkbox" id="status"' + (disabled ? "" : " checked") + ">"
|
||||
'<input type="checkbox" id="status_' + data.id + '"' + (disabled ? "" : " checked") + ">"
|
||||
);
|
||||
$("#status", row).bootstrapToggle({
|
||||
var statusEl = $("#status_" + data.id, row);
|
||||
statusEl.bootstrapToggle({
|
||||
on: "Enabled",
|
||||
off: "Disabled",
|
||||
size: "small",
|
||||
onstyle: "success",
|
||||
width: "80px"
|
||||
});
|
||||
$("#status", row).on("change", editDomain);
|
||||
statusEl.on("change", editDomain);
|
||||
|
||||
$("td:eq(3)", row).html(
|
||||
'<input id="comment" class="form-control"><input id="id" type="hidden" value="' +
|
||||
data.id +
|
||||
'">'
|
||||
);
|
||||
$("#comment", row).val(data.comment);
|
||||
$("#comment", row).on("change", editDomain);
|
||||
$("td:eq(3)", row).html('<input id="comment_' + data.id + '" class="form-control">');
|
||||
var commentEl = $("#comment_" + data.id, row);
|
||||
commentEl.val(data.comment);
|
||||
commentEl.on("change", editDomain);
|
||||
|
||||
$("td:eq(4)", row).empty();
|
||||
$("td:eq(4)", row).append('<select id="multiselect" multiple="multiple"></select>');
|
||||
var sel = $("#multiselect", row);
|
||||
// Add all known groups
|
||||
for (var i = 0; i < groups.length; i++) {
|
||||
var extra = "";
|
||||
if (!groups[i].enabled) {
|
||||
extra = " (disabled)";
|
||||
// Show group assignment field only if in full domain management mode
|
||||
if (table.column(5).visible()) {
|
||||
$("td:eq(4)", row).empty();
|
||||
$("td:eq(4)", row).append(
|
||||
'<div id="selectHome_' +
|
||||
data.id +
|
||||
'">' +
|
||||
'<select id="multiselect_' +
|
||||
data.id +
|
||||
'" multiple="multiple"></select></div>'
|
||||
);
|
||||
var selectEl = $("#multiselect_" + data.id, row);
|
||||
// Add all known groups
|
||||
for (var i = 0; i < groups.length; i++) {
|
||||
var extra = "";
|
||||
if (!groups[i].enabled) {
|
||||
extra = " (disabled)";
|
||||
}
|
||||
|
||||
selectEl.append(
|
||||
$("<option />")
|
||||
.val(groups[i].id)
|
||||
.text(groups[i].name + extra)
|
||||
);
|
||||
}
|
||||
|
||||
sel.append(
|
||||
$("<option />")
|
||||
.val(groups[i].id)
|
||||
.text(groups[i].name + extra)
|
||||
);
|
||||
// Select assigned groups
|
||||
selectEl.val(data.groups);
|
||||
// Initialize multiselect
|
||||
selectEl.multiselect({
|
||||
includeSelectAllOption: true,
|
||||
buttonContainer: '<div id="container_' + data.id + '" class="btn-group"/>',
|
||||
maxHeight: 200,
|
||||
onDropdownShown: function() {
|
||||
var el = $("#container_" + data.id);
|
||||
var top = el[0].getBoundingClientRect().top;
|
||||
var bottom = $(window).height() - top - el.height();
|
||||
if (bottom < 200) {
|
||||
el.addClass("dropup");
|
||||
}
|
||||
|
||||
if (bottom > 200) {
|
||||
el.removeClass("dropup");
|
||||
}
|
||||
|
||||
var offset = el.offset();
|
||||
$("body").append(el);
|
||||
el.css("position", "absolute");
|
||||
el.css("top", offset.top + "px");
|
||||
el.css("left", offset.left + "px");
|
||||
},
|
||||
onDropdownHide: function() {
|
||||
var el = $("#container_" + data.id);
|
||||
var home = $("#selectHome_" + data.id);
|
||||
home.append(el);
|
||||
el.removeAttr("style");
|
||||
}
|
||||
});
|
||||
selectEl.on("change", editDomain);
|
||||
}
|
||||
|
||||
// Select assigned groups
|
||||
sel.val(data.groups);
|
||||
// Initialize multiselect
|
||||
sel.multiselect({ includeSelectAllOption: true });
|
||||
sel.on("change", editDomain);
|
||||
// Highlight row (if url parameter "domainid=" is used)
|
||||
if ("domainid" in GETDict && data.id === parseInt(GETDict.domainid)) {
|
||||
$(row)
|
||||
.find("td")
|
||||
.addClass("highlight");
|
||||
}
|
||||
|
||||
var button =
|
||||
'<button class="btn btn-danger btn-xs deleteDomain" type="button" data-id="' +
|
||||
'<button class="btn btn-danger btn-xs" type="button" id="deleteDomain_' +
|
||||
data.id +
|
||||
'">' +
|
||||
'<span class="far fa-trash-alt"></span>' +
|
||||
"</button>";
|
||||
$("td:eq(5)", row).html(button);
|
||||
if (table.column(5).visible()) {
|
||||
$("td:eq(5)", row).html(button);
|
||||
} else {
|
||||
$("td:eq(4)", row).html(button);
|
||||
}
|
||||
},
|
||||
dom:
|
||||
"<'row'<'col-sm-4'l><'col-sm-8'f>>" +
|
||||
"<'row'<'col-sm-12'<'table-responsive'tr>>>" +
|
||||
"<'row'<'col-sm-5'i><'col-sm-7'p>>",
|
||||
lengthMenu: [
|
||||
[10, 25, 50, 100, -1],
|
||||
[10, 25, 50, 100, "All"]
|
||||
@@ -230,8 +263,22 @@ function initTable() {
|
||||
data.search.search = "";
|
||||
// Reset visibility of ID column
|
||||
data.columns[0].visible = false;
|
||||
// Show group assignment column only on full page
|
||||
data.columns[5].visible = showtype === "all";
|
||||
// Apply loaded state to table
|
||||
return data;
|
||||
},
|
||||
initComplete: function() {
|
||||
if ("domainid" in GETDict) {
|
||||
var pos = table
|
||||
.column(0, { order: "current" })
|
||||
.data()
|
||||
.indexOf(parseInt(GETDict.domainid));
|
||||
if (pos >= 0) {
|
||||
var page = Math.floor(pos / table.page.info().length);
|
||||
table.page(page).draw(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -250,14 +297,53 @@ function initTable() {
|
||||
}
|
||||
|
||||
function addDomain() {
|
||||
var domain = $("#new_domain").val();
|
||||
var type = $("#new_type").val();
|
||||
var comment = $("#new_comment").val();
|
||||
var action = this.id;
|
||||
var tabHref = $('a[data-toggle="tab"][aria-expanded="true"]').attr("href");
|
||||
var wildcardEl = $("#wildcard_checkbox");
|
||||
var wildcard_checked = wildcardEl.prop("checked");
|
||||
var type;
|
||||
|
||||
showAlert("info", "", "Adding domain...", domain);
|
||||
// current tab's inputs
|
||||
var domain_regex, domainEl, commentEl;
|
||||
if (tabHref === "#tab_domain") {
|
||||
domain_regex = "domain";
|
||||
domainEl = $("#new_domain");
|
||||
commentEl = $("#new_domain_comment");
|
||||
} else if (tabHref === "#tab_regex") {
|
||||
domain_regex = "regex";
|
||||
domainEl = $("#new_regex");
|
||||
commentEl = $("#new_regex_comment");
|
||||
}
|
||||
|
||||
if (domain.length === 0) {
|
||||
showAlert("warning", "", "Warning", "Please specify a domain");
|
||||
var domain = domainEl.val();
|
||||
var comment = commentEl.val();
|
||||
|
||||
utils.disableAll();
|
||||
utils.showAlert("info", "", "Adding " + domain_regex + "...", domain);
|
||||
|
||||
if (domain.length > 0) {
|
||||
// strip "*." if specified by user in wildcard mode
|
||||
if (domain_regex === "domain" && wildcard_checked && domain.startsWith("*.")) {
|
||||
domain = domain.substr(2);
|
||||
}
|
||||
|
||||
// determine list type
|
||||
if (domain_regex === "domain" && action === "add2black" && wildcard_checked) {
|
||||
type = "3W";
|
||||
} else if (domain_regex === "domain" && action === "add2black" && !wildcard_checked) {
|
||||
type = "1";
|
||||
} else if (domain_regex === "domain" && action === "add2white" && wildcard_checked) {
|
||||
type = "2W";
|
||||
} else if (domain_regex === "domain" && action === "add2white" && !wildcard_checked) {
|
||||
type = "0";
|
||||
} else if (domain_regex === "regex" && action === "add2black") {
|
||||
type = "3";
|
||||
} else if (domain_regex === "regex" && action === "add2white") {
|
||||
type = "2";
|
||||
}
|
||||
} else {
|
||||
utils.enableAll();
|
||||
utils.showAlert("warning", "", "Warning", "Please specify a " + domain_regex);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -273,15 +359,20 @@ function addDomain() {
|
||||
token: token
|
||||
},
|
||||
success: function(response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
showAlert("success", "fas fa-plus", "Successfully added domain", domain);
|
||||
$("#new_domain").val("");
|
||||
$("#new_comment").val("");
|
||||
utils.showAlert("success", "fas fa-plus", "Successfully added " + domain_regex, domain);
|
||||
domainEl.val("");
|
||||
commentEl.val("");
|
||||
wildcardEl.prop("checked", false);
|
||||
table.ajax.reload();
|
||||
} else showAlert("error", "", "Error while adding new domain", response.message);
|
||||
} else {
|
||||
utils.showAlert("error", "", "Error while adding new " + domain_regex, response.message);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert("error", "", "Error while adding new domain", jqXHR.responseText);
|
||||
utils.enableAll();
|
||||
utils.showAlert("error", "", "Error while adding new " + domain_regex, jqXHR.responseText);
|
||||
console.log(exception);
|
||||
}
|
||||
});
|
||||
@@ -290,36 +381,60 @@ function addDomain() {
|
||||
function editDomain() {
|
||||
var elem = $(this).attr("id");
|
||||
var tr = $(this).closest("tr");
|
||||
var domain = tr.find("#domain").text();
|
||||
var id = tr.find("#id").val();
|
||||
var type = tr.find("#type").val();
|
||||
var status = tr.find("#status").is(":checked") ? 1 : 0;
|
||||
var comment = tr.find("#comment").val();
|
||||
var groups = tr.find("#multiselect").val();
|
||||
var id = tr.attr("data-id");
|
||||
var domain = tr.find("#domain_" + id).text();
|
||||
var type = tr.find("#type_" + id).val();
|
||||
var status = tr.find("#status_" + id).is(":checked") ? 1 : 0;
|
||||
var comment = tr.find("#comment_" + id).val();
|
||||
|
||||
// Show group assignment field only if in full domain management mode
|
||||
// if not included, just use the row data.
|
||||
var rowData = table.row(tr).data();
|
||||
var groups = table.column(5).visible() ? tr.find("#multiselect_" + id).val() : rowData.groups;
|
||||
|
||||
var domain_regex;
|
||||
if (type === "0" || type === "1") {
|
||||
domain_regex = "domain";
|
||||
} else if (type === "2" || type === "3") {
|
||||
domain_regex = "regex";
|
||||
}
|
||||
|
||||
var done = "edited";
|
||||
var not_done = "editing";
|
||||
if (elem === "status" && status === 1) {
|
||||
done = "enabled";
|
||||
not_done = "enabling";
|
||||
} else if (elem === "status" && status === 0) {
|
||||
done = "disabled";
|
||||
not_done = "disabling";
|
||||
} else if (elem === "name") {
|
||||
done = "edited name of";
|
||||
not_done = "editing name of";
|
||||
} else if (elem === "comment") {
|
||||
done = "edited comment of";
|
||||
not_done = "editing comment of";
|
||||
} else if (elem === "type") {
|
||||
done = "edited type of";
|
||||
not_done = "editing type of";
|
||||
} else if (elem === "multiselect") {
|
||||
done = "edited groups of";
|
||||
not_done = "editing groups of";
|
||||
switch (elem) {
|
||||
case "status_" + id:
|
||||
if (status === 0) {
|
||||
done = "disabled";
|
||||
not_done = "disabling";
|
||||
} else if (status === 1) {
|
||||
done = "enabled";
|
||||
not_done = "enabling";
|
||||
}
|
||||
|
||||
break;
|
||||
case "name_" + id:
|
||||
done = "edited name of";
|
||||
not_done = "editing name of";
|
||||
break;
|
||||
case "comment_" + id:
|
||||
done = "edited comment of";
|
||||
not_done = "editing comment of";
|
||||
break;
|
||||
case "type_" + id:
|
||||
done = "edited type of";
|
||||
not_done = "editing type of";
|
||||
break;
|
||||
case "multiselect_" + id:
|
||||
done = "edited groups of";
|
||||
not_done = "editing groups of";
|
||||
break;
|
||||
default:
|
||||
alert("bad element or invalid data-id!");
|
||||
return;
|
||||
}
|
||||
|
||||
showAlert("info", "", "Editing domain...", name);
|
||||
utils.disableAll();
|
||||
utils.showAlert("info", "", "Editing " + domain_regex + "...", name);
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
@@ -334,21 +449,28 @@ function editDomain() {
|
||||
token: token
|
||||
},
|
||||
success: function(response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
showAlert("success", "fas fa-pencil-alt", "Successfully " + done + " domain", domain);
|
||||
utils.showAlert(
|
||||
"success",
|
||||
"fas fa-pencil-alt",
|
||||
"Successfully " + done + " " + domain_regex,
|
||||
domain
|
||||
);
|
||||
} else
|
||||
showAlert(
|
||||
utils.showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while " + not_done + " domain with ID " + id,
|
||||
"Error while " + not_done + " " + domain_regex + " with ID " + id,
|
||||
response.message
|
||||
);
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert(
|
||||
utils.enableAll();
|
||||
utils.showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while " + not_done + " domain with ID " + id,
|
||||
"Error while " + not_done + " " + domain_regex + " with ID " + id,
|
||||
jqXHR.responseText
|
||||
);
|
||||
console.log(exception);
|
||||
@@ -357,27 +479,55 @@ function editDomain() {
|
||||
}
|
||||
|
||||
function deleteDomain() {
|
||||
var id = $(this).attr("data-id");
|
||||
var tr = $(this).closest("tr");
|
||||
var domain = tr.find("#domain").text();
|
||||
var id = tr.attr("data-id");
|
||||
var domain = tr.find("#domain_" + id).text();
|
||||
var type = tr.find("#type_" + id).val();
|
||||
|
||||
showAlert("info", "", "Deleting domain...", domain);
|
||||
var domain_regex;
|
||||
if (type === "0" || type === "1") {
|
||||
domain_regex = "domain";
|
||||
} else if (type === "2" || type === "3") {
|
||||
domain_regex = "regex";
|
||||
}
|
||||
|
||||
utils.disableAll();
|
||||
utils.showAlert("info", "", "Deleting " + domain_regex + "...", domain);
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: { action: "delete_domain", id: id, token: token },
|
||||
success: function(response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
showAlert("success", "far fa-trash-alt", "Successfully deleted domain", domain);
|
||||
utils.showAlert(
|
||||
"success",
|
||||
"far fa-trash-alt",
|
||||
"Successfully deleted " + domain_regex,
|
||||
domain
|
||||
);
|
||||
table
|
||||
.row(tr)
|
||||
.remove()
|
||||
.draw(false);
|
||||
} else showAlert("error", "", "Error while deleting domain with ID " + id, response.message);
|
||||
} else {
|
||||
utils.showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while deleting " + domain_regex + " with ID " + id,
|
||||
response.message
|
||||
);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert("error", "", "Error while deleting domain with ID " + id, jqXHR.responseText);
|
||||
utils.enableAll();
|
||||
utils.showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while deleting " + domain_regex + " with ID " + id,
|
||||
jqXHR.responseText
|
||||
);
|
||||
console.log(exception);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -5,74 +5,10 @@
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
/* global moment:false */
|
||||
/* global utils:false */
|
||||
|
||||
var table;
|
||||
var token = $("#token").text();
|
||||
var info = null;
|
||||
|
||||
function showAlert(type, icon, title, message) {
|
||||
var opts = {};
|
||||
title = " <strong>" + title + "</strong><br>";
|
||||
switch (type) {
|
||||
case "info":
|
||||
opts = {
|
||||
type: "info",
|
||||
icon: "far fa-clock",
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
info = $.notify(opts);
|
||||
break;
|
||||
case "success":
|
||||
opts = {
|
||||
type: "success",
|
||||
icon: icon,
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
|
||||
break;
|
||||
case "warning":
|
||||
opts = {
|
||||
type: "warning",
|
||||
icon: "fas fa-exclamation-triangle",
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
|
||||
break;
|
||||
case "error":
|
||||
opts = {
|
||||
type: "danger",
|
||||
icon: "fas fa-times",
|
||||
title: " <strong>Error, something went wrong!</strong><br>",
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
function datetime(date) {
|
||||
return moment.unix(Math.floor(date)).format("Y-MM-DD HH:mm:ss z");
|
||||
}
|
||||
var token = $("#token").html();
|
||||
|
||||
$(document).ready(function() {
|
||||
$("#btnAdd").on("click", addGroup);
|
||||
@@ -92,51 +28,48 @@ $(document).ready(function() {
|
||||
{ data: null, width: "60px", orderable: false }
|
||||
],
|
||||
drawCallback: function() {
|
||||
$(".deleteGroup").on("click", deleteGroup);
|
||||
$('button[id^="deleteGroup_"]').on("click", deleteGroup);
|
||||
},
|
||||
rowCallback: function(row, data) {
|
||||
$(row).attr("data-id", data.id);
|
||||
var tooltip =
|
||||
"Added: " +
|
||||
datetime(data.date_added) +
|
||||
utils.datetime(data.date_added) +
|
||||
"\nLast modified: " +
|
||||
datetime(data.date_modified) +
|
||||
utils.datetime(data.date_modified) +
|
||||
"\nDatabase ID: " +
|
||||
data.id;
|
||||
$("td:eq(0)", row).html(
|
||||
'<input id="name" title="' +
|
||||
tooltip +
|
||||
'" class="form-control"><input id="id" type="hidden" value="' +
|
||||
data.id +
|
||||
'">'
|
||||
'<input id="name_' + data.id + '" title="' + tooltip + '" class="form-control">'
|
||||
);
|
||||
var name = $("#name", row);
|
||||
name.val(data.name);
|
||||
name.on("change", editGroup);
|
||||
var nameEl = $("#name_" + data.id, row);
|
||||
nameEl.val(data.name);
|
||||
nameEl.on("change", editGroup);
|
||||
|
||||
var disabled = data.enabled === 0;
|
||||
$("td:eq(1)", row).html(
|
||||
'<input type="checkbox" id="status"' + (disabled ? "" : " checked") + ">"
|
||||
'<input type="checkbox" id="status_' + data.id + '"' + (disabled ? "" : " checked") + ">"
|
||||
);
|
||||
var status = $("#status", row);
|
||||
status.bootstrapToggle({
|
||||
var statusEl = $("#status_" + data.id, row);
|
||||
statusEl.bootstrapToggle({
|
||||
on: "Enabled",
|
||||
off: "Disabled",
|
||||
size: "small",
|
||||
onstyle: "success",
|
||||
width: "80px"
|
||||
});
|
||||
status.on("change", editGroup);
|
||||
statusEl.on("change", editGroup);
|
||||
|
||||
$("td:eq(2)", row).html('<input id="desc" class="form-control">');
|
||||
$("td:eq(2)", row).html('<input id="desc_' + data.id + '" class="form-control">');
|
||||
var desc = data.description !== null ? data.description : "";
|
||||
$("#desc", row).val(desc);
|
||||
$("#desc", row).on("change", editGroup);
|
||||
var descEl = $("#desc_" + data.id, row);
|
||||
descEl.val(desc);
|
||||
descEl.on("change", editGroup);
|
||||
|
||||
$("td:eq(3)", row).empty();
|
||||
if (data.id !== 0) {
|
||||
var button =
|
||||
" " +
|
||||
'<button class="btn btn-danger btn-xs deleteGroup" type="button" data-id="' +
|
||||
'<button class="btn btn-danger btn-xs" type="button" id="deleteGroup_' +
|
||||
data.id +
|
||||
'">' +
|
||||
'<span class="far fa-trash-alt"></span>' +
|
||||
@@ -144,6 +77,10 @@ $(document).ready(function() {
|
||||
$("td:eq(3)", row).html(button);
|
||||
}
|
||||
},
|
||||
dom:
|
||||
"<'row'<'col-sm-4'l><'col-sm-8'f>>" +
|
||||
"<'row'<'col-sm-12'<'table-responsive'tr>>>" +
|
||||
"<'row'<'col-sm-5'i><'col-sm-7'p>>",
|
||||
lengthMenu: [
|
||||
[10, 25, 50, 100, -1],
|
||||
[10, 25, 50, 100, "All"]
|
||||
@@ -191,10 +128,11 @@ function addGroup() {
|
||||
var name = $("#new_name").val();
|
||||
var desc = $("#new_desc").val();
|
||||
|
||||
showAlert("info", "", "Adding group...", name);
|
||||
utils.disableAll();
|
||||
utils.showAlert("info", "", "Adding group...", name);
|
||||
|
||||
if (name.length === 0) {
|
||||
showAlert("warning", "", "Warning", "Please specify a group name");
|
||||
utils.showAlert("warning", "", "Warning", "Please specify a group name");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -204,17 +142,19 @@ function addGroup() {
|
||||
dataType: "json",
|
||||
data: { action: "add_group", name: name, desc: desc, token: token },
|
||||
success: function(response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
showAlert("success", "fas fa-plus", "Successfully added group", name);
|
||||
utils.showAlert("success", "fas fa-plus", "Successfully added group", name);
|
||||
$("#new_name").val("");
|
||||
$("#new_desc").val("");
|
||||
table.ajax.reload();
|
||||
} else {
|
||||
showAlert("error", "", "Error while adding new group", response.message);
|
||||
utils.showAlert("error", "", "Error while adding new group", response.message);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert("error", "", "Error while adding new group", jqXHR.responseText);
|
||||
utils.enableAll();
|
||||
utils.showAlert("error", "", "Error while adding new group", jqXHR.responseText);
|
||||
console.log(exception);
|
||||
}
|
||||
});
|
||||
@@ -223,28 +163,39 @@ function addGroup() {
|
||||
function editGroup() {
|
||||
var elem = $(this).attr("id");
|
||||
var tr = $(this).closest("tr");
|
||||
var id = tr.find("#id").val();
|
||||
var name = tr.find("#name").val();
|
||||
var status = tr.find("#status").is(":checked") ? 1 : 0;
|
||||
var desc = tr.find("#desc").val();
|
||||
var id = tr.attr("data-id");
|
||||
var name = tr.find("#name_" + id).val();
|
||||
var status = tr.find("#status_" + id).is(":checked") ? 1 : 0;
|
||||
var desc = tr.find("#desc_" + id).val();
|
||||
|
||||
var done = "edited";
|
||||
var not_done = "editing";
|
||||
if (elem === "status" && status === 1) {
|
||||
done = "enabled";
|
||||
not_done = "enabling";
|
||||
} else if (elem === "status" && status === 0) {
|
||||
done = "disabled";
|
||||
not_done = "disabling";
|
||||
} else if (elem === "name") {
|
||||
done = "edited name of";
|
||||
not_done = "editing name of";
|
||||
} else if (elem === "desc") {
|
||||
done = "edited description of";
|
||||
not_done = "editing description of";
|
||||
switch (elem) {
|
||||
case "status_" + id:
|
||||
if (status === 0) {
|
||||
done = "disabled";
|
||||
not_done = "disabling";
|
||||
} else if (status === 1) {
|
||||
done = "enabled";
|
||||
not_done = "enabling";
|
||||
}
|
||||
|
||||
break;
|
||||
case "name_" + id:
|
||||
done = "edited name of";
|
||||
not_done = "editing name of";
|
||||
break;
|
||||
case "desc_" + id:
|
||||
done = "edited description of";
|
||||
not_done = "editing description of";
|
||||
break;
|
||||
default:
|
||||
alert("bad element or invalid data-id!");
|
||||
return;
|
||||
}
|
||||
|
||||
showAlert("info", "", "Editing group...", name);
|
||||
utils.disableAll();
|
||||
utils.showAlert("info", "", "Editing group...", name);
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
@@ -258,10 +209,11 @@ function editGroup() {
|
||||
token: token
|
||||
},
|
||||
success: function(response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
showAlert("success", "fas fa-pencil-alt", "Successfully " + done + " group", name);
|
||||
utils.showAlert("success", "fas fa-pencil-alt", "Successfully " + done + " group", name);
|
||||
} else {
|
||||
showAlert(
|
||||
utils.showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while " + not_done + " group with ID " + id,
|
||||
@@ -270,7 +222,8 @@ function editGroup() {
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert(
|
||||
utils.enableAll();
|
||||
utils.showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while " + not_done + " group with ID " + id,
|
||||
@@ -282,29 +235,32 @@ function editGroup() {
|
||||
}
|
||||
|
||||
function deleteGroup() {
|
||||
var id = $(this).attr("data-id");
|
||||
var tr = $(this).closest("tr");
|
||||
var name = tr.find("#name").val();
|
||||
var id = tr.attr("data-id");
|
||||
var name = tr.find("#name_" + id).val();
|
||||
|
||||
showAlert("info", "", "Deleting group...", name);
|
||||
utils.disableAll();
|
||||
utils.showAlert("info", "", "Deleting group...", name);
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: { action: "delete_group", id: id, token: token },
|
||||
success: function(response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
showAlert("success", "far fa-trash-alt", "Successfully deleted group ", name);
|
||||
utils.showAlert("success", "far fa-trash-alt", "Successfully deleted group ", name);
|
||||
table
|
||||
.row(tr)
|
||||
.remove()
|
||||
.draw(false);
|
||||
} else {
|
||||
showAlert("error", "", "Error while deleting group with ID " + id, response.message);
|
||||
utils.showAlert("error", "", "Error while deleting group with ID " + id, response.message);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert("error", "", "Error while deleting group with ID " + id, jqXHR.responseText);
|
||||
utils.enableAll();
|
||||
utils.showAlert("error", "", "Error while deleting group with ID " + id, jqXHR.responseText);
|
||||
console.log(exception);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -792,8 +792,9 @@ $(document).ready(function() {
|
||||
label: function(tooltipItems, data) {
|
||||
if (tooltipItems.datasetIndex === 1) {
|
||||
var percentage = 0.0;
|
||||
var total = parseInt(data.datasets[0].data[tooltipItems.index]);
|
||||
var permitted = parseInt(data.datasets[0].data[tooltipItems.index]);
|
||||
var blocked = parseInt(data.datasets[1].data[tooltipItems.index]);
|
||||
var total = permitted + blocked;
|
||||
if (total > 0) {
|
||||
percentage = (100.0 * blocked) / total;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,12 @@ jQuery.extend(jQuery.fn.dataTableExt.oSort, {
|
||||
}
|
||||
|
||||
var i, item;
|
||||
// Use the first IP in case there is a list of IPs
|
||||
// for a given device
|
||||
if (Array.isArray(a)) {
|
||||
a = a[0];
|
||||
}
|
||||
|
||||
var m = a.split("."),
|
||||
n = a.split(":"),
|
||||
x = "",
|
||||
|
||||
@@ -1,287 +0,0 @@
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
// IE likes to cache too much :P
|
||||
$.ajaxSetup({ cache: false });
|
||||
|
||||
// Get PHP info
|
||||
var token = $("#token").text();
|
||||
var listType = $("#list-type").html();
|
||||
var fullName = listType === "white" ? "Whitelist" : "Blacklist";
|
||||
|
||||
function addListEntry(entry, index, list, button, type) {
|
||||
var disabled = [];
|
||||
if (entry.enabled === "0") disabled.push("individual");
|
||||
// For entry.group_enabled we either get "0" (= disabled by a group),
|
||||
// "1" (= enabled by a group), or "" (= not managed by a group)
|
||||
if (entry.group_enabled === "0") disabled.push("group");
|
||||
|
||||
var used = disabled.length === 0 ? "used" : "not-used";
|
||||
var comment = entry.comment.length > 0 ? " - " + entry.comment : "";
|
||||
var disabled_message =
|
||||
disabled.length > 0 ? " - disabled due to " + disabled.join(" + ") + " setting" : "";
|
||||
var date_added = new Date(parseInt(entry.date_added) * 1000);
|
||||
var date_modified = new Date(parseInt(entry.date_modified) * 1000);
|
||||
var tooltip =
|
||||
"Added: " + date_added.toLocaleString() + "\nModified: " + date_modified.toLocaleString();
|
||||
list.append(
|
||||
'<li id="' +
|
||||
index +
|
||||
'" class="list-group-item ' +
|
||||
used +
|
||||
' clearfix">' +
|
||||
'<span title="' +
|
||||
tooltip +
|
||||
'" data-toggle="tooltip" data-placement="right">' +
|
||||
entry.domain +
|
||||
comment +
|
||||
disabled_message +
|
||||
"</span>" +
|
||||
'<button class="btn btn-danger btn-xs pull-right" type="button">' +
|
||||
'<span class="far fa-trash-alt"></span></button></li>'
|
||||
);
|
||||
// Handle button
|
||||
$(button + " #" + index).on("click", "button", function() {
|
||||
sub(index, entry.domain, type);
|
||||
});
|
||||
}
|
||||
|
||||
function refresh(fade) {
|
||||
var list = $("#list");
|
||||
var listw = $("#list-regex");
|
||||
if (fade) {
|
||||
list.fadeOut(100);
|
||||
listw.fadeOut(100);
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/get.php",
|
||||
method: "get",
|
||||
data: { list: listType },
|
||||
success: function(response) {
|
||||
list.html("");
|
||||
listw.html("");
|
||||
|
||||
if (
|
||||
(listType === "black" &&
|
||||
response.blacklist.length === 0 &&
|
||||
response.regex_blacklist.length === 0) ||
|
||||
(listType === "white" &&
|
||||
response.whitelist.length === 0 &&
|
||||
response.regex_whitelist.length === 0)
|
||||
) {
|
||||
$("h3").hide();
|
||||
list.html(
|
||||
'<div class="alert alert-info" role="alert">Your ' + fullName + " is empty!</div>"
|
||||
);
|
||||
} else {
|
||||
var data, data2;
|
||||
if (listType === "white") {
|
||||
data = response.whitelist.sort();
|
||||
data2 = response.regex_whitelist.sort();
|
||||
} else if (listType === "black") {
|
||||
data = response.blacklist.sort();
|
||||
data2 = response.regex_blacklist.sort();
|
||||
}
|
||||
|
||||
if (data.length > 0) {
|
||||
$("#h3-exact").fadeIn(100);
|
||||
}
|
||||
|
||||
if (data2.length > 0) {
|
||||
$("#h3-regex").fadeIn(100);
|
||||
}
|
||||
|
||||
data.forEach(function(entry, index) {
|
||||
addListEntry(entry, index, list, "#list", "exact");
|
||||
});
|
||||
data2.forEach(function(entry, index) {
|
||||
addListEntry(entry, index, listw, "#list-regex", listType + "_regex");
|
||||
});
|
||||
}
|
||||
|
||||
list.fadeIn(100);
|
||||
listw.fadeIn(100);
|
||||
},
|
||||
error: function() {
|
||||
$("#alFailure").show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener("load", refresh(false));
|
||||
|
||||
function sub(index, entry, arg) {
|
||||
var list = "#list";
|
||||
var heading = "#h3-exact";
|
||||
var locallistType = listType;
|
||||
if (arg === "black_regex" || arg === "white_regex") {
|
||||
list = "#list-regex";
|
||||
heading = "#h3-regex";
|
||||
locallistType = arg;
|
||||
}
|
||||
|
||||
var alInfo = $("#alInfo");
|
||||
var alSuccess = $("#alSuccess");
|
||||
var alFailure = $("#alFailure");
|
||||
var err = $("#err");
|
||||
var msg = $("#success-message");
|
||||
|
||||
var domain = $(list + " #" + index);
|
||||
domain.hide("highlight");
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/sub.php",
|
||||
method: "post",
|
||||
data: { domain: entry, list: locallistType, token: token },
|
||||
success: function(response) {
|
||||
if (response.indexOf("Success") === -1) {
|
||||
alFailure.show();
|
||||
err.html(response);
|
||||
alFailure.delay(8000).fadeOut(2000, function() {
|
||||
alFailure.hide();
|
||||
});
|
||||
alInfo.delay(8000).fadeOut(2000, function() {
|
||||
alInfo.hide();
|
||||
});
|
||||
} else {
|
||||
alSuccess.show();
|
||||
msg.html(response);
|
||||
alSuccess.delay(1000).fadeOut(2000, function() {
|
||||
alSuccess.hide();
|
||||
});
|
||||
alInfo.delay(1000).fadeOut(2000, function() {
|
||||
alInfo.hide();
|
||||
});
|
||||
domain.remove();
|
||||
if ($(list + " li").length === 0) {
|
||||
$(heading).fadeOut(100);
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
alert("Failed to remove the domain!");
|
||||
domain.show({ queue: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function add(type) {
|
||||
var domain = $("#domain");
|
||||
if (domain.val().length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var comment = $("#comment");
|
||||
|
||||
var alInfo = $("#alInfo");
|
||||
var alSuccess = $("#alSuccess");
|
||||
var alFailure = $("#alFailure");
|
||||
var alWarning = $("#alWarning");
|
||||
var err = $("#err");
|
||||
var msg = $("#success-message");
|
||||
alInfo.show();
|
||||
alSuccess.hide();
|
||||
alFailure.hide();
|
||||
alWarning.hide();
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/add.php",
|
||||
method: "post",
|
||||
data: { domain: domain.val().trim(), comment: comment.val(), list: type, token: token },
|
||||
success: function(response) {
|
||||
if (response.indexOf("Success") === -1) {
|
||||
alFailure.show();
|
||||
err.html(response);
|
||||
alFailure.delay(8000).fadeOut(2000, function() {
|
||||
alFailure.hide();
|
||||
});
|
||||
alInfo.delay(8000).fadeOut(2000, function() {
|
||||
alInfo.hide();
|
||||
});
|
||||
} else {
|
||||
alSuccess.show();
|
||||
msg.html(response);
|
||||
alSuccess.delay(1000).fadeOut(2000, function() {
|
||||
alSuccess.hide();
|
||||
});
|
||||
alInfo.delay(1000).fadeOut(2000, function() {
|
||||
alInfo.hide();
|
||||
});
|
||||
domain.val("");
|
||||
comment.val("");
|
||||
refresh(true);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
alFailure.show();
|
||||
err.html("");
|
||||
alFailure.delay(1000).fadeOut(2000, function() {
|
||||
alFailure.hide();
|
||||
});
|
||||
alInfo.delay(1000).fadeOut(2000, function() {
|
||||
alInfo.hide();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Handle enter button for adding domains
|
||||
$(document).keypress(function(e) {
|
||||
if (e.which === 13 && $("#domain,#comment").is(":focus")) {
|
||||
// Enter was pressed, and the input has focus
|
||||
add(listType);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle buttons
|
||||
$("#btnAdd").on("click", function() {
|
||||
add(listType);
|
||||
});
|
||||
|
||||
$("#btnAddWildcard").on("click", function() {
|
||||
add(listType + "_wild");
|
||||
});
|
||||
|
||||
$("#btnAddRegex").on("click", function() {
|
||||
add(listType + "_regex");
|
||||
});
|
||||
|
||||
$("#btnRefresh").on("click", function() {
|
||||
refresh(true);
|
||||
});
|
||||
|
||||
// Handle hiding of alerts
|
||||
$(function() {
|
||||
$("[data-hide]").on("click", function() {
|
||||
$(this)
|
||||
.closest("." + $(this).attr("data-hide"))
|
||||
.hide();
|
||||
});
|
||||
});
|
||||
|
||||
// Wrap form-group's buttons to next line when viewed on a small screen
|
||||
$(window).on("resize", function() {
|
||||
if ($(window).width() < 991) {
|
||||
$(".form-group.input-group")
|
||||
.removeClass("input-group")
|
||||
.addClass("input-group-block");
|
||||
$(".form-group.input-group-block > input").css("margin-bottom", "5px");
|
||||
$(".form-group.input-group-block > .input-group-btn")
|
||||
.removeClass("input-group-btn")
|
||||
.addClass("btn-block text-center");
|
||||
} else {
|
||||
$(".form-group.input-group-block")
|
||||
.removeClass("input-group-block")
|
||||
.addClass("input-group");
|
||||
$(".form-group.input-group > input").css("margin-bottom", "");
|
||||
$(".form-group.input-group > .btn-block.text-center")
|
||||
.removeClass("btn-block text-center")
|
||||
.addClass("input-group-btn");
|
||||
}
|
||||
});
|
||||
$(document).ready(function() {
|
||||
$(window).trigger("resize");
|
||||
});
|
||||
@@ -93,30 +93,41 @@ $(document).ready(function() {
|
||||
$("td:eq(5)", row).html("Never");
|
||||
}
|
||||
|
||||
// Set hostname to "N/A" if not available
|
||||
// Set hostname to "unknown" if not available
|
||||
if (!data.name || data.name.length === 0) {
|
||||
$("td:eq(3)", row).html("N/A");
|
||||
$("td:eq(3)", row).html("<em>unknown</em>");
|
||||
}
|
||||
|
||||
// Set number of queries to localized string (add thousand separators)
|
||||
$("td:eq(6)", row).html(data.numQueries.toLocaleString());
|
||||
|
||||
var ips = data.ip;
|
||||
var shortips = ips;
|
||||
if (ips.length > MAXIPDISPLAY) {
|
||||
shortips = ips.slice(0, MAXIPDISPLAY - 1);
|
||||
shortips.push("...");
|
||||
var ips = [];
|
||||
var maxiter = Math.min(data.ip.length, MAXIPDISPLAY);
|
||||
for (var index = 0; index < maxiter; index++) {
|
||||
var ip = data.ip[index];
|
||||
ips.push('<a href="queries.php?client=' + ip + '">' + ip + "</a>");
|
||||
}
|
||||
|
||||
$("td:eq(0)", row).html(shortips.join("<br>"));
|
||||
if (data.ip.length > MAXIPDISPLAY) {
|
||||
// We hit the maximum above, add "..." to symbolize we would
|
||||
// have more to show here
|
||||
ips.push("...");
|
||||
}
|
||||
|
||||
$("td:eq(0)", row).html(ips.join("<br>"));
|
||||
$("td:eq(0)", row).hover(function() {
|
||||
this.title = ips.join("\n");
|
||||
this.title = data.ip.join("\n");
|
||||
});
|
||||
|
||||
// MAC + Vendor field if available
|
||||
if (data.macVendor && data.macVendor.length > 0) {
|
||||
$("td:eq(1)", row).html(data.hwaddr + "<br/>" + data.macVendor);
|
||||
}
|
||||
|
||||
// Hide mock MAC addresses
|
||||
if (data.hwaddr.startsWith("ip-")) {
|
||||
$("td:eq(1)", row).text("N/A");
|
||||
}
|
||||
},
|
||||
dom:
|
||||
"<'row'<'col-sm-12'f>>" +
|
||||
|
||||
@@ -108,7 +108,7 @@ function handleAjaxError(xhr, textStatus) {
|
||||
}
|
||||
|
||||
function autofilter() {
|
||||
return document.getElementById("autofilter").checked;
|
||||
return $("#autofilter").prop("checked");
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
@@ -166,7 +166,13 @@ $(document).ready(function() {
|
||||
}
|
||||
|
||||
// Query status
|
||||
var blocked, fieldtext, buttontext, colorClass;
|
||||
var blocked,
|
||||
fieldtext,
|
||||
buttontext,
|
||||
colorClass,
|
||||
isCNAME = false,
|
||||
regexLink = false;
|
||||
|
||||
switch (data[4]) {
|
||||
case "1":
|
||||
blocked = true;
|
||||
@@ -192,14 +198,19 @@ $(document).ready(function() {
|
||||
case "4":
|
||||
blocked = true;
|
||||
colorClass = "text-red";
|
||||
fieldtext = "Blocked <br class='hidden-lg'>(regex/wildcard)";
|
||||
fieldtext = "Blocked <br class='hidden-lg'>(regex blacklist)";
|
||||
|
||||
if (data.length > 9 && data[9] > 0) {
|
||||
regexLink = true;
|
||||
}
|
||||
|
||||
buttontext =
|
||||
'<button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button>';
|
||||
break;
|
||||
case "5":
|
||||
blocked = true;
|
||||
colorClass = "text-red";
|
||||
fieldtext = "Blocked <br class='hidden-lg'>(blacklist)";
|
||||
fieldtext = "Blocked <br class='hidden-lg'>(exact blacklist)";
|
||||
buttontext =
|
||||
'<button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button>';
|
||||
break;
|
||||
@@ -221,6 +232,35 @@ $(document).ready(function() {
|
||||
fieldtext = "Blocked <br class='hidden-lg'>(external, NXRA)";
|
||||
buttontext = "";
|
||||
break;
|
||||
case "9":
|
||||
blocked = true;
|
||||
colorClass = "text-red";
|
||||
fieldtext = "Blocked (gravity, CNAME)";
|
||||
buttontext =
|
||||
'<button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button>';
|
||||
isCNAME = true;
|
||||
break;
|
||||
case "10":
|
||||
blocked = true;
|
||||
colorClass = "text-red";
|
||||
fieldtext = "Blocked <br class='hidden-lg'>(regex blacklist, CNAME)";
|
||||
|
||||
if (data.length > 9 && data[9] > 0) {
|
||||
regexLink = true;
|
||||
}
|
||||
|
||||
buttontext =
|
||||
'<button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button>';
|
||||
isCNAME = true;
|
||||
break;
|
||||
case "11":
|
||||
blocked = true;
|
||||
colorClass = "text-red";
|
||||
fieldtext = "Blocked <br class='hidden-lg'>(exact blacklist, CNAME)";
|
||||
buttontext =
|
||||
'<button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button>';
|
||||
isCNAME = true;
|
||||
break;
|
||||
default:
|
||||
blocked = false;
|
||||
colorClass = "text-black";
|
||||
@@ -232,6 +272,33 @@ $(document).ready(function() {
|
||||
$("td:eq(4)", row).html(fieldtext);
|
||||
$("td:eq(6)", row).html(buttontext);
|
||||
|
||||
if (regexLink) {
|
||||
$("td:eq(4)", row).hover(
|
||||
function() {
|
||||
this.title = "Click to show matching regex filter";
|
||||
this.style.color = "#72afd2";
|
||||
},
|
||||
function() {
|
||||
this.style.color = "";
|
||||
}
|
||||
);
|
||||
$("td:eq(4)", row).click(function() {
|
||||
var new_tab = window.open("groups-domains.php?domainid=" + data[9], "_blank");
|
||||
if (new_tab) {
|
||||
new_tab.focus();
|
||||
}
|
||||
});
|
||||
$("td:eq(4)", row).addClass("underline");
|
||||
$("td:eq(4)", row).addClass("pointer");
|
||||
}
|
||||
|
||||
// Add domain in CNAME chain causing the query to have been blocked
|
||||
var domain = data[2];
|
||||
var CNAME_domain = data[8];
|
||||
if (isCNAME) {
|
||||
$("td:eq(2)", row).text(domain + "\n(blocked " + CNAME_domain + ")");
|
||||
}
|
||||
|
||||
// Check for existence of sixth column and display only if not Pi-holed
|
||||
var replytext;
|
||||
if (data.length > 6 && !blocked) {
|
||||
@@ -382,14 +449,16 @@ $(document).ready(function() {
|
||||
// Domain
|
||||
api.$("td:eq(2)").click(function() {
|
||||
if (autofilter()) {
|
||||
api.search(this.textContent).draw();
|
||||
var domain = this.textContent.split("\n")[0];
|
||||
api.search(domain).draw();
|
||||
$("#resetButton").show();
|
||||
}
|
||||
});
|
||||
api.$("td:eq(2)").hover(
|
||||
function() {
|
||||
if (autofilter()) {
|
||||
this.title = "Click to show only queries with domain " + this.textContent;
|
||||
var domain = this.textContent.split("\n")[0];
|
||||
this.title = "Click to show only queries with domain " + domain;
|
||||
this.style.color = "#72afd2";
|
||||
} else {
|
||||
this.title = "";
|
||||
@@ -428,10 +497,10 @@ $(document).ready(function() {
|
||||
|
||||
$("#all-queries tbody").on("click", "button", function() {
|
||||
var data = tableApi.row($(this).parents("tr")).data();
|
||||
if (data[4] === "1" || data[4] === "4" || data[4] === "5") {
|
||||
add(data[2], "white");
|
||||
} else {
|
||||
if (data[4] === "2" || data[4] === "3") {
|
||||
add(data[2], "black");
|
||||
} else {
|
||||
add(data[2], "white");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -439,10 +508,25 @@ $(document).ready(function() {
|
||||
tableApi.search("").draw();
|
||||
$("#resetButton").hide();
|
||||
});
|
||||
|
||||
// Disable autocorrect in the search box
|
||||
var input = document.querySelector("input[type=search]");
|
||||
input.setAttribute("autocomplete", "off");
|
||||
input.setAttribute("autocorrect", "off");
|
||||
input.setAttribute("autocapitalize", "off");
|
||||
input.setAttribute("spellcheck", false);
|
||||
|
||||
var chkbox_data = localStorage.getItem("query_log_filter_chkbox");
|
||||
if (chkbox_data !== null) {
|
||||
// Restore checkbox state
|
||||
$("#autofilter").prop("checked", chkbox_data === "true");
|
||||
} else {
|
||||
// Initialize checkbox
|
||||
$("#autofilter").prop("checked", true);
|
||||
localStorage.setItem("query_log_filter_chkbox", true);
|
||||
}
|
||||
|
||||
$("#autofilter").click(function() {
|
||||
localStorage.setItem("query_log_filter_chkbox", $("#autofilter").prop("checked"));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -217,28 +217,6 @@ $(document).ready(function() {
|
||||
$('[data-toggle="tooltip"]').tooltip({ html: true, container: "body" });
|
||||
});
|
||||
|
||||
// Handle list deletion
|
||||
$("button[id^='adlist-btn-']").on("click", function(e) {
|
||||
var id = parseInt($(this).context.id.replace(/[^\d.]/g, ""), 10);
|
||||
e.preventDefault();
|
||||
|
||||
var status = $('input[name="adlist-del-' + id + '"]').is(":checked");
|
||||
var textType = status ? "none" : "line-through";
|
||||
|
||||
// Check hidden delete box (or reset)
|
||||
$('input[name="adlist-del-' + id + '"]').prop("checked", !status);
|
||||
// Untick and disable check box (or reset)
|
||||
$('input[name="adlist-enable-' + id + '"]')
|
||||
.prop("checked", status)
|
||||
.prop("disabled", !status);
|
||||
// Strike through text (or reset)
|
||||
$('a[id="adlist-text-' + id + '"]').css("text-decoration", textType);
|
||||
// Highlight that the button has to be clicked in order to make the change live
|
||||
$('button[id="blockinglistsaveupdate"]')
|
||||
.addClass("btn-danger")
|
||||
.css("font-weight", "bold");
|
||||
});
|
||||
|
||||
// Change "?tab=" parameter in URL for save and reload
|
||||
$(".nav-tabs a").on("shown.bs.tab", function(e) {
|
||||
var tab = e.target.hash.substring(1);
|
||||
|
||||
@@ -16,22 +16,38 @@ if (empty($api)) {
|
||||
list_verify($list);
|
||||
}
|
||||
|
||||
// Split individual domains into array
|
||||
$domains = preg_split('/\s+/', trim($_POST['domain']));
|
||||
|
||||
// Get comment if available
|
||||
$comment = null;
|
||||
if(isset($_POST['comment'])) {
|
||||
$comment = trim($_POST['comment']);
|
||||
}
|
||||
|
||||
// Convert domain name to IDNA ASCII form for international domains
|
||||
// Do this only for exact domains, not for regex filters
|
||||
// Only do it when the php-intl extension is available
|
||||
if (extension_loaded("intl") && ($list === "white" || $list === "black")) {
|
||||
foreach($domains as &$domain)
|
||||
{
|
||||
$domain = idn_to_ascii($domain);
|
||||
}
|
||||
}
|
||||
|
||||
// Only check domains we add to the exact lists.
|
||||
// Regex are validated by FTL during import
|
||||
$check_lists = ["white","black","audit"];
|
||||
if(in_array($list, $check_lists)) {
|
||||
check_domain();
|
||||
check_domain($domains);
|
||||
}
|
||||
|
||||
// Split individual domains into array
|
||||
$domains = preg_split('/\s+/', trim($_POST['domain']));
|
||||
$comment = trim($_POST['comment']);
|
||||
|
||||
require_once("func.php");
|
||||
require_once("database.php");
|
||||
$GRAVITYDB = getGravityDBFilename();
|
||||
$db = SQLite3_connect($GRAVITYDB, SQLITE3_OPEN_READWRITE);
|
||||
|
||||
$reload = true;
|
||||
switch($list) {
|
||||
case "white":
|
||||
$domains = array_map('strtolower', $domains);
|
||||
@@ -60,7 +76,8 @@ switch($list) {
|
||||
break;
|
||||
|
||||
case "audit":
|
||||
echo add_to_table($db, "domain_audit", $domains, $comment);
|
||||
$reload = false;
|
||||
echo add_to_table($db, "domain_audit", $domains);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -68,5 +85,8 @@ switch($list) {
|
||||
}
|
||||
|
||||
// Reload lists in pihole-FTL after having added something
|
||||
echo shell_exec("sudo pihole restartdns reload");
|
||||
if ($reload) {
|
||||
echo shell_exec("sudo pihole restartdns reload-lists");
|
||||
}
|
||||
?>
|
||||
|
||||
|
||||
@@ -113,15 +113,12 @@ function check_csrf($token) {
|
||||
}
|
||||
}
|
||||
|
||||
function check_domain() {
|
||||
if(isset($_POST['domain'])){
|
||||
$domains = preg_split('/\s+/', $_POST['domain']);
|
||||
foreach($domains as $domain)
|
||||
{
|
||||
$validDomain = is_valid_domain_name($domain);
|
||||
if(!$validDomain){
|
||||
log_and_die(htmlspecialchars($domain. ' is not a valid domain'));
|
||||
}
|
||||
function check_domain(&$domains) {
|
||||
foreach($domains as &$domain)
|
||||
{
|
||||
$validDomain = is_valid_domain_name($domain);
|
||||
if(!$validDomain){
|
||||
log_and_die(htmlspecialchars($domain. ' is not a valid domain'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ function SQLite3_connect($filename, $mode=SQLITE3_OPEN_READONLY)
|
||||
* @param $type integer The target type (0 = exact whitelist, 1 = exact blacklist, 2 = regex whitelist, 3 = regex blacklist)
|
||||
* @return string Success/error and number of processed domains
|
||||
*/
|
||||
function add_to_table($db, $table, $domains, $comment, $wildcardstyle=false, $returnnum=false, $type=-1)
|
||||
function add_to_table($db, $table, $domains, $comment=null, $wildcardstyle=false, $returnnum=false, $type=-1)
|
||||
{
|
||||
if(!is_int($type))
|
||||
{
|
||||
@@ -107,6 +107,16 @@ function add_to_table($db, $table, $domains, $comment, $wildcardstyle=false, $re
|
||||
return "Error: Unable to begin transaction for $table table.";
|
||||
}
|
||||
|
||||
// To which column should the record be added to?
|
||||
if ($table === "adlist")
|
||||
{
|
||||
$field = "address";
|
||||
}
|
||||
else
|
||||
{
|
||||
$field = "domain";
|
||||
}
|
||||
|
||||
// Get initial count of domains in this table
|
||||
if($type === -1)
|
||||
{
|
||||
@@ -119,13 +129,15 @@ function add_to_table($db, $table, $domains, $comment, $wildcardstyle=false, $re
|
||||
$initialcount = intval($db->querySingle($countquery));
|
||||
|
||||
// Prepare INSERT SQLite statememt
|
||||
if($type === -1)
|
||||
{
|
||||
$querystr = "INSERT OR IGNORE INTO $table (domain,comment) VALUES (:domain, :comment);";
|
||||
}
|
||||
else
|
||||
{
|
||||
$querystr = "INSERT OR IGNORE INTO $table (domain,comment,type) VALUES (:domain, :comment, $type);";
|
||||
$bindcomment = false;
|
||||
if($table === "domain_audit") {
|
||||
$querystr = "INSERT OR IGNORE INTO $table ($field) VALUES (:$field);";
|
||||
} elseif($type === -1) {
|
||||
$querystr = "INSERT OR IGNORE INTO $table ($field,comment) VALUES (:$field, :comment);";
|
||||
$bindcomment = true;
|
||||
} else {
|
||||
$querystr = "INSERT OR IGNORE INTO $table ($field,comment,type) VALUES (:$field, :comment, $type);";
|
||||
$bindcomment = true;
|
||||
}
|
||||
$stmt = $db->prepare($querystr);
|
||||
|
||||
@@ -135,7 +147,7 @@ function add_to_table($db, $table, $domains, $comment, $wildcardstyle=false, $re
|
||||
if($returnnum)
|
||||
return 0;
|
||||
else
|
||||
return "Error: Failed to prepare statement for $table table (type = $type).";
|
||||
return "Error: Failed to prepare statement for $table table (type = $type, field = $field).";
|
||||
}
|
||||
|
||||
// Loop over domains and inject the lines into the database
|
||||
@@ -149,8 +161,10 @@ function add_to_table($db, $table, $domains, $comment, $wildcardstyle=false, $re
|
||||
if($wildcardstyle)
|
||||
$domain = "(\\.|^)".str_replace(".","\\.",$domain)."$";
|
||||
|
||||
$stmt->bindValue(":domain", $domain, SQLITE3_TEXT);
|
||||
$stmt->bindValue(":comment", $comment, SQLITE3_TEXT);
|
||||
$stmt->bindValue(":$field", $domain, SQLITE3_TEXT);
|
||||
if($bindcomment) {
|
||||
$stmt->bindValue(":comment", $comment, SQLITE3_TEXT);
|
||||
}
|
||||
|
||||
if($stmt->execute() && $stmt->reset())
|
||||
$num++;
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
<?php
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
if(!isset($_GET['list']))
|
||||
die("Missing parameter");
|
||||
|
||||
$listtype = $_GET['list'];
|
||||
|
||||
require_once("func.php");
|
||||
|
||||
require("database.php");
|
||||
$GRAVITYDB = getGravityDBFilename();
|
||||
$db = SQLite3_connect($GRAVITYDB);
|
||||
|
||||
function getTableContent($type) {
|
||||
global $db;
|
||||
$entries = array();
|
||||
$querystr = implode(" ",array("SELECT domainlist.*,\"group\".enabled as group_enabled",
|
||||
"FROM domainlist",
|
||||
"LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id",
|
||||
"LEFT JOIN \"group\" ON \"group\".id = domainlist_by_group.group_id",
|
||||
"WHERE type = $type",
|
||||
"GROUP BY domain;"));
|
||||
$results = $db->query($querystr);
|
||||
|
||||
while($results !== false && $res = $results->fetchArray(SQLITE3_ASSOC))
|
||||
{
|
||||
array_push($entries, $res);
|
||||
}
|
||||
|
||||
return $entries;
|
||||
}
|
||||
|
||||
function filterArray(&$inArray) {
|
||||
$outArray = array();
|
||||
foreach ($inArray as $key => $value)
|
||||
{
|
||||
if (is_array($value))
|
||||
{
|
||||
$outArray[htmlspecialchars($key)] = filterArray($value);
|
||||
}
|
||||
else
|
||||
{
|
||||
$outArray[htmlspecialchars($key)] = htmlspecialchars($value);
|
||||
}
|
||||
}
|
||||
return $outArray;
|
||||
}
|
||||
|
||||
switch ($listtype)
|
||||
{
|
||||
case "white":
|
||||
$exact = array("whitelist" => getTableContent(ListType::whitelist));
|
||||
$regex = array("regex_whitelist" => getTableContent(ListType::regex_whitelist));
|
||||
$list = array_merge($exact, $regex);
|
||||
break;
|
||||
|
||||
case "black":
|
||||
$exact = array("blacklist" => getTableContent(ListType::blacklist));
|
||||
$regex = array("regex_blacklist" => getTableContent(ListType::regex_blacklist));
|
||||
$list = array_merge($exact, $regex);
|
||||
break;
|
||||
|
||||
default:
|
||||
die("Invalid list parameter");
|
||||
break;
|
||||
}
|
||||
// Protect against XSS attacks
|
||||
$output = filterArray($list);
|
||||
|
||||
// Return results
|
||||
header('Content-type: application/json');
|
||||
echo json_encode($output);
|
||||
@@ -49,32 +49,35 @@ if ($_POST['action'] == 'get_groups') {
|
||||
}
|
||||
echo json_encode(array('data' => $data));
|
||||
} catch (\Exception $ex) {
|
||||
return JSON_error($ex->getMessage());
|
||||
JSON_error($ex->getMessage());
|
||||
}
|
||||
} elseif ($_POST['action'] == 'add_group') {
|
||||
// Add new group
|
||||
try {
|
||||
$names = explode(' ', $_POST['name']);
|
||||
$stmt = $db->prepare('INSERT INTO "group" (name,description) VALUES (:name,:desc)');
|
||||
if (!$stmt) {
|
||||
throw new Exception('While preparing statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':name', $_POST['name'], SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding name: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':desc', $_POST['desc'], SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding desc: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing: ' . $db->lastErrorMsg());
|
||||
foreach ($names as $name) {
|
||||
if (!$stmt->bindValue(':name', $name, SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding name: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing: ' . $db->lastErrorMsg());
|
||||
}
|
||||
}
|
||||
|
||||
$reload = true;
|
||||
return JSON_success();
|
||||
JSON_success();
|
||||
} catch (\Exception $ex) {
|
||||
return JSON_error($ex->getMessage());
|
||||
JSON_error($ex->getMessage());
|
||||
}
|
||||
} elseif ($_POST['action'] == 'edit_group') {
|
||||
// Edit group identified by ID
|
||||
@@ -111,9 +114,9 @@ if ($_POST['action'] == 'get_groups') {
|
||||
}
|
||||
|
||||
$reload = true;
|
||||
return JSON_success();
|
||||
JSON_success();
|
||||
} catch (\Exception $ex) {
|
||||
return JSON_error($ex->getMessage());
|
||||
JSON_error($ex->getMessage());
|
||||
}
|
||||
} elseif ($_POST['action'] == 'delete_group') {
|
||||
// Delete group identified by ID
|
||||
@@ -142,9 +145,9 @@ if ($_POST['action'] == 'get_groups') {
|
||||
}
|
||||
}
|
||||
$reload = true;
|
||||
return JSON_success();
|
||||
JSON_success();
|
||||
} catch (\Exception $ex) {
|
||||
return JSON_error($ex->getMessage());
|
||||
JSON_error($ex->getMessage());
|
||||
}
|
||||
} elseif ($_POST['action'] == 'get_clients') {
|
||||
// List all available groups
|
||||
@@ -157,7 +160,6 @@ if ($_POST['action'] == 'get_groups') {
|
||||
throw new Exception('Error while querying gravity\'s client table: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
|
||||
$data = array();
|
||||
while (($res = $query->fetchArray(SQLITE3_ASSOC)) !== false) {
|
||||
$group_query = $db->query('SELECT group_id FROM client_by_group WHERE client_id = ' . $res['id'] . ';');
|
||||
@@ -182,7 +184,7 @@ if ($_POST['action'] == 'get_groups') {
|
||||
// There will always be a result. Unknown host names are NULL
|
||||
$name_result = $result->fetchArray(SQLITE3_ASSOC);
|
||||
$res['name'] = $name_result['name'];
|
||||
|
||||
|
||||
$groups = array();
|
||||
while ($gres = $group_query->fetchArray(SQLITE3_ASSOC)) {
|
||||
array_push($groups, $gres['group_id']);
|
||||
@@ -193,7 +195,7 @@ if ($_POST['action'] == 'get_groups') {
|
||||
|
||||
echo json_encode(array('data' => $data));
|
||||
} catch (\Exception $ex) {
|
||||
return JSON_error($ex->getMessage());
|
||||
JSON_error($ex->getMessage());
|
||||
}
|
||||
} elseif ($_POST['action'] == 'get_unconfigured_clients') {
|
||||
// List all available clients WITHOUT already configured clients
|
||||
@@ -209,7 +211,7 @@ if ($_POST['action'] == 'get_groups') {
|
||||
// Loop over results
|
||||
$ips = array();
|
||||
while ($res = $query->fetchArray(SQLITE3_ASSOC)) {
|
||||
$ips[$res['ip']] = $res['name'];
|
||||
$ips[$res['ip']] = $res['name'] !== null ? $res['name'] : '';
|
||||
}
|
||||
$FTLdb->close();
|
||||
|
||||
@@ -227,32 +229,66 @@ if ($_POST['action'] == 'get_groups') {
|
||||
|
||||
echo json_encode($ips);
|
||||
} catch (\Exception $ex) {
|
||||
return JSON_error($ex->getMessage());
|
||||
JSON_error($ex->getMessage());
|
||||
}
|
||||
} elseif ($_POST['action'] == 'add_client') {
|
||||
// Add new client
|
||||
try {
|
||||
$stmt = $db->prepare('INSERT INTO client (ip) VALUES (:ip)');
|
||||
$ips = explode(' ', $_POST['ip']);
|
||||
$stmt = $db->prepare('INSERT INTO client (ip,comment) VALUES (:ip,:comment)');
|
||||
if (!$stmt) {
|
||||
throw new Exception('While preparing statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':ip', $_POST['ip'], SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding ip: ' . $db->lastErrorMsg());
|
||||
foreach ($ips as $ip) {
|
||||
if (!$stmt->bindValue(':ip', $ip, SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding ip: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
$comment = $_POST['comment'];
|
||||
if (strlen($comment) == 0) {
|
||||
// Store NULL in database for empty comments
|
||||
$comment = null;
|
||||
}
|
||||
if (!$stmt->bindValue(':comment', $comment, SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding comment: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing: ' . $db->lastErrorMsg());
|
||||
}
|
||||
}
|
||||
|
||||
$reload = true;
|
||||
JSON_success();
|
||||
} catch (\Exception $ex) {
|
||||
JSON_error($ex->getMessage());
|
||||
}
|
||||
} elseif ($_POST['action'] == 'edit_client') {
|
||||
// Edit client identified by ID
|
||||
try {
|
||||
$stmt = $db->prepare('UPDATE client SET comment=:comment WHERE id = :id');
|
||||
if (!$stmt) {
|
||||
throw new Exception('While preparing statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
$comment = $_POST['comment'];
|
||||
if (strlen($comment) == 0) {
|
||||
// Store NULL in database for empty comments
|
||||
$comment = null;
|
||||
}
|
||||
if (!$stmt->bindValue(':comment', $comment, SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding comment: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':id', intval($_POST['id']), SQLITE3_INTEGER)) {
|
||||
throw new Exception('While binding id: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
$reload = true;
|
||||
return JSON_success();
|
||||
} catch (\Exception $ex) {
|
||||
return JSON_error($ex->getMessage());
|
||||
}
|
||||
} elseif ($_POST['action'] == 'edit_client') {
|
||||
// Edit client identified by ID
|
||||
try {
|
||||
$stmt = $db->prepare('DELETE FROM client_by_group WHERE client_id = :id');
|
||||
if (!$stmt) {
|
||||
throw new Exception('While preparing DELETE statement: ' . $db->lastErrorMsg());
|
||||
@@ -265,22 +301,22 @@ if ($_POST['action'] == 'get_groups') {
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing DELETE statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
|
||||
$db->query('BEGIN TRANSACTION;');
|
||||
foreach ($_POST['groups'] as $gid) {
|
||||
$stmt = $db->prepare('INSERT INTO client_by_group (client_id,group_id) VALUES(:id,:gid);');
|
||||
if (!$stmt) {
|
||||
throw new Exception('While preparing INSERT INTO statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
|
||||
if (!$stmt->bindValue(':id', intval($_POST['id']), SQLITE3_INTEGER)) {
|
||||
throw new Exception('While binding id: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
|
||||
if (!$stmt->bindValue(':gid', intval($gid), SQLITE3_INTEGER)) {
|
||||
throw new Exception('While binding gid: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing INSERT INTO statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
@@ -288,9 +324,9 @@ if ($_POST['action'] == 'get_groups') {
|
||||
$db->query('COMMIT;');
|
||||
|
||||
$reload = true;
|
||||
return JSON_success();
|
||||
JSON_success();
|
||||
} catch (\Exception $ex) {
|
||||
return JSON_error($ex->getMessage());
|
||||
JSON_error($ex->getMessage());
|
||||
}
|
||||
} elseif ($_POST['action'] == 'delete_client') {
|
||||
// Delete client identified by ID
|
||||
@@ -322,14 +358,20 @@ if ($_POST['action'] == 'get_groups') {
|
||||
}
|
||||
|
||||
$reload = true;
|
||||
return JSON_success();
|
||||
JSON_success();
|
||||
} catch (\Exception $ex) {
|
||||
return JSON_error($ex->getMessage());
|
||||
JSON_error($ex->getMessage());
|
||||
}
|
||||
} elseif ($_POST['action'] == 'get_domains') {
|
||||
// List all available groups
|
||||
try {
|
||||
$query = $db->query('SELECT * FROM domainlist;');
|
||||
$limit = "";
|
||||
if (isset($_POST["showtype"]) && $_POST["showtype"] === "white"){
|
||||
$limit = " WHERE type = 0 OR type = 2";
|
||||
} elseif (isset($_POST["showtype"]) && $_POST["showtype"] === "black"){
|
||||
$limit = " WHERE type = 1 OR type = 3";
|
||||
}
|
||||
$query = $db->query('SELECT * FROM domainlist'.$limit);
|
||||
if (!$query) {
|
||||
throw new Exception('Error while querying gravity\'s domainlist table: ' . $db->lastErrorMsg());
|
||||
}
|
||||
@@ -340,23 +382,35 @@ if ($_POST['action'] == 'get_groups') {
|
||||
if (!$group_query) {
|
||||
throw new Exception('Error while querying gravity\'s domainlist_by_group table: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
|
||||
$groups = array();
|
||||
while ($gres = $group_query->fetchArray(SQLITE3_ASSOC)) {
|
||||
array_push($groups, $gres['group_id']);
|
||||
}
|
||||
$res['groups'] = $groups;
|
||||
if (extension_loaded("intl") &&
|
||||
($res['type'] === ListType::whitelist ||
|
||||
$res['type'] === ListType::blacklist) ) {
|
||||
$utf8_domain = idn_to_utf8($res['domain']);
|
||||
// Convert domain name to international form
|
||||
// if applicable and extension is available
|
||||
if($res['domain'] !== $utf8_domain)
|
||||
{
|
||||
$res['domain'] = $utf8_domain.' ('.$res['domain'].')';
|
||||
}
|
||||
}
|
||||
array_push($data, $res);
|
||||
}
|
||||
|
||||
|
||||
echo json_encode(array('data' => $data));
|
||||
} catch (\Exception $ex) {
|
||||
return JSON_error($ex->getMessage());
|
||||
JSON_error($ex->getMessage());
|
||||
}
|
||||
} elseif ($_POST['action'] == 'add_domain') {
|
||||
// Add new domain
|
||||
try {
|
||||
$domains = explode(' ', $_POST['domain']);
|
||||
$stmt = $db->prepare('INSERT INTO domainlist (domain,type,comment) VALUES (:domain,:type,:comment)');
|
||||
if (!$stmt) {
|
||||
throw new Exception('While preparing statement: ' . $db->lastErrorMsg());
|
||||
@@ -364,21 +418,6 @@ if ($_POST['action'] == 'get_groups') {
|
||||
|
||||
$type = intval($_POST['type']);
|
||||
|
||||
$domain = $_POST['domain'];
|
||||
if($type === ListType::whitelist || $type === ListType::blacklist)
|
||||
{
|
||||
// If adding to the exact lists, we convert the domain lower case and check whether it is valid
|
||||
$domain = strtolower($domain);
|
||||
if(filter_var($domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) === false)
|
||||
{
|
||||
throw new Exception('Domain ' . htmlentities(utf8_encode($domain)) . 'is not a valid domain.');
|
||||
}
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':domain', $domain, SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding domain: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':type', $type, SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding type: ' . $db->lastErrorMsg());
|
||||
}
|
||||
@@ -387,14 +426,39 @@ if ($_POST['action'] == 'get_groups') {
|
||||
throw new Exception('While binding comment: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing: ' . $db->lastErrorMsg());
|
||||
foreach ($domains as $domain) {
|
||||
// Convert domain name to IDNA ASCII form for international domains
|
||||
$domain = idn_to_ascii($domain);
|
||||
|
||||
if(strlen($_POST['type']) === 2 && $_POST['type'][1] === 'W')
|
||||
{
|
||||
// Apply wildcard-style formatting
|
||||
$domain = "(\\.|^)".str_replace(".","\\.",$domain)."$";
|
||||
}
|
||||
|
||||
if($type === ListType::whitelist || $type === ListType::blacklist)
|
||||
{
|
||||
// If adding to the exact lists, we convert the domain lower case and check whether it is valid
|
||||
$domain = strtolower($domain);
|
||||
if(filter_var($domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) === false)
|
||||
{
|
||||
throw new Exception('Domain ' . htmlentities(utf8_encode($domain)) . 'is not a valid domain.');
|
||||
}
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':domain', $domain, SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding domain: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing: ' . $db->lastErrorMsg());
|
||||
}
|
||||
}
|
||||
|
||||
$reload = true;
|
||||
return JSON_success();
|
||||
JSON_success();
|
||||
} catch (\Exception $ex) {
|
||||
return JSON_error($ex->getMessage());
|
||||
JSON_error($ex->getMessage());
|
||||
}
|
||||
} elseif ($_POST['action'] == 'edit_domain') {
|
||||
// Edit domain identified by ID
|
||||
@@ -434,44 +498,46 @@ if ($_POST['action'] == 'get_groups') {
|
||||
throw new Exception('While executing: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
$stmt = $db->prepare('DELETE FROM domainlist_by_group WHERE domainlist_id = :id');
|
||||
if (!$stmt) {
|
||||
throw new Exception('While preparing DELETE statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':id', intval($_POST['id']), SQLITE3_INTEGER)) {
|
||||
throw new Exception('While binding id: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing DELETE statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
$db->query('BEGIN TRANSACTION;');
|
||||
foreach ($_POST['groups'] as $gid) {
|
||||
$stmt = $db->prepare('INSERT INTO domainlist_by_group (domainlist_id,group_id) VALUES(:id,:gid);');
|
||||
if (isset($_POST['groups'])) {
|
||||
$stmt = $db->prepare('DELETE FROM domainlist_by_group WHERE domainlist_id = :id');
|
||||
if (!$stmt) {
|
||||
throw new Exception('While preparing INSERT INTO statement: ' . $db->lastErrorMsg());
|
||||
throw new Exception('While preparing DELETE statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
|
||||
if (!$stmt->bindValue(':id', intval($_POST['id']), SQLITE3_INTEGER)) {
|
||||
throw new Exception('While binding id: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':gid', intval($gid), SQLITE3_INTEGER)) {
|
||||
throw new Exception('While binding gid: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing INSERT INTO statement: ' . $db->lastErrorMsg());
|
||||
throw new Exception('While executing DELETE statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
$db->query('BEGIN TRANSACTION;');
|
||||
foreach ($_POST['groups'] as $gid) {
|
||||
$stmt = $db->prepare('INSERT INTO domainlist_by_group (domainlist_id,group_id) VALUES(:id,:gid);');
|
||||
if (!$stmt) {
|
||||
throw new Exception('While preparing INSERT INTO statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':id', intval($_POST['id']), SQLITE3_INTEGER)) {
|
||||
throw new Exception('While binding id: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':gid', intval($gid), SQLITE3_INTEGER)) {
|
||||
throw new Exception('While binding gid: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing INSERT INTO statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
}
|
||||
$db->query('COMMIT;');
|
||||
}
|
||||
$db->query('COMMIT;');
|
||||
|
||||
$reload = true;
|
||||
return JSON_success();
|
||||
JSON_success();
|
||||
} catch (\Exception $ex) {
|
||||
return JSON_error($ex->getMessage());
|
||||
JSON_error($ex->getMessage());
|
||||
}
|
||||
} elseif ($_POST['action'] == 'delete_domain') {
|
||||
// Delete domain identified by ID
|
||||
@@ -503,9 +569,9 @@ if ($_POST['action'] == 'get_groups') {
|
||||
}
|
||||
|
||||
$reload = true;
|
||||
return JSON_success();
|
||||
JSON_success();
|
||||
} catch (\Exception $ex) {
|
||||
return JSON_error($ex->getMessage());
|
||||
JSON_error($ex->getMessage());
|
||||
}
|
||||
} elseif ($_POST['action'] == 'get_adlists') {
|
||||
// List all available groups
|
||||
@@ -521,7 +587,7 @@ if ($_POST['action'] == 'get_groups') {
|
||||
if (!$group_query) {
|
||||
throw new Exception('Error while querying gravity\'s adlist_by_group table: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
|
||||
$groups = array();
|
||||
while ($gres = $group_query->fetchArray(SQLITE3_ASSOC)) {
|
||||
array_push($groups, $gres['group_id']);
|
||||
@@ -533,32 +599,40 @@ if ($_POST['action'] == 'get_groups') {
|
||||
|
||||
echo json_encode(array('data' => $data));
|
||||
} catch (\Exception $ex) {
|
||||
return JSON_error($ex->getMessage());
|
||||
JSON_error($ex->getMessage());
|
||||
}
|
||||
} elseif ($_POST['action'] == 'add_adlist') {
|
||||
// Add new adlist
|
||||
try {
|
||||
$addresses = explode(' ', $_POST['address']);
|
||||
|
||||
$stmt = $db->prepare('INSERT INTO adlist (address,comment) VALUES (:address,:comment)');
|
||||
if (!$stmt) {
|
||||
throw new Exception('While preparing statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':address', $_POST['address'], SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding address: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':comment', $_POST['comment'], SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding comment: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing: ' . $db->lastErrorMsg());
|
||||
foreach ($addresses as $address) {
|
||||
if(preg_match("/[^a-zA-Z0-9:\/?&%=~._-]/", $address) !== 0) {
|
||||
throw new Exception('Invalid adlist URL');
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':address', $address, SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding address: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing: ' . $db->lastErrorMsg());
|
||||
}
|
||||
}
|
||||
|
||||
$reload = true;
|
||||
return JSON_success();
|
||||
JSON_success();
|
||||
} catch (\Exception $ex) {
|
||||
return JSON_error($ex->getMessage());
|
||||
JSON_error($ex->getMessage());
|
||||
}
|
||||
} elseif ($_POST['action'] == 'edit_adlist') {
|
||||
// Edit adlist identified by ID
|
||||
@@ -572,7 +646,7 @@ if ($_POST['action'] == 'get_groups') {
|
||||
if ($status !== 0) {
|
||||
$status = 1;
|
||||
}
|
||||
|
||||
|
||||
if (!$stmt->bindValue(':enabled', $status, SQLITE3_INTEGER)) {
|
||||
throw new Exception('While binding enabled: ' . $db->lastErrorMsg());
|
||||
}
|
||||
@@ -606,22 +680,22 @@ if ($_POST['action'] == 'get_groups') {
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing DELETE statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
|
||||
$db->query('BEGIN TRANSACTION;');
|
||||
foreach ($_POST['groups'] as $gid) {
|
||||
$stmt = $db->prepare('INSERT INTO adlist_by_group (adlist_id,group_id) VALUES(:id,:gid);');
|
||||
if (!$stmt) {
|
||||
throw new Exception('While preparing INSERT INTO statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
|
||||
if (!$stmt->bindValue(':id', intval($_POST['id']), SQLITE3_INTEGER)) {
|
||||
throw new Exception('While binding id: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
|
||||
if (!$stmt->bindValue(':gid', intval($gid), SQLITE3_INTEGER)) {
|
||||
throw new Exception('While binding gid: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing INSERT INTO statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
@@ -629,9 +703,9 @@ if ($_POST['action'] == 'get_groups') {
|
||||
$db->query('COMMIT;');
|
||||
|
||||
$reload = true;
|
||||
return JSON_success();
|
||||
JSON_success();
|
||||
} catch (\Exception $ex) {
|
||||
return JSON_error($ex->getMessage());
|
||||
JSON_error($ex->getMessage());
|
||||
}
|
||||
} elseif ($_POST['action'] == 'delete_adlist') {
|
||||
// Delete adlist identified by ID
|
||||
@@ -663,9 +737,9 @@ if ($_POST['action'] == 'get_groups') {
|
||||
}
|
||||
|
||||
$reload = true;
|
||||
return JSON_success();
|
||||
JSON_success();
|
||||
} catch (\Exception $ex) {
|
||||
return JSON_error($ex->getMessage());
|
||||
JSON_error($ex->getMessage());
|
||||
}
|
||||
} else {
|
||||
log_and_die('Requested action not supported!');
|
||||
|
||||
@@ -183,15 +183,15 @@
|
||||
<!-- Tell the browser to be responsive to screen width -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link rel="shortcut icon" href="img/favicon.png" type="image/x-icon">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="img/favicon.png">
|
||||
<link rel="icon" href="img/logo.svg" type="image/png" sizes="192x192">
|
||||
<link rel="icon" href="img/logo.svg" type="image/png" sizes="160x160">
|
||||
<link rel="icon" href="img/logo.svg" type="image/png" sizes="96x96">
|
||||
<meta name="theme-color" content="#367fa9">
|
||||
<link rel="apple-touch-icon" href="img/favicons/apple-touch-icon.png" sizes="180x180">
|
||||
<link rel="icon" href="img/favicons/favicon-32x32.png" sizes="32x32" type="image/png">
|
||||
<link rel="icon" href="img/favicons/favicon-16x16.png" sizes="16x16" type="image/png">
|
||||
<link rel="manifest" href="img/favicons/manifest.json">
|
||||
<link rel="mask-icon" href="img/favicons/safari-pinned-tab.svg" color="#367fa9">
|
||||
<link rel="shortcut icon" href="img/favicons/favicon.ico">
|
||||
<meta name="msapplication-TileColor" content="#367fa9">
|
||||
<meta name="msapplication-TileImage" content="img/logo.svg">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="msapplication-TileImage" content="img/favicons/mstile-150x150.png">
|
||||
<meta name="theme-color" content="#367fa9">
|
||||
|
||||
<link rel="stylesheet" href="style/vendor/SourceSansPro/SourceSansPro.css">
|
||||
<link rel="stylesheet" href="style/vendor/bootstrap/css/bootstrap.min.css">
|
||||
@@ -420,13 +420,13 @@ if($auth) {
|
||||
</div>
|
||||
<!-- sidebar menu: : style can be found in sidebar.less -->
|
||||
<?php
|
||||
if($scriptname === "list.php")
|
||||
if($scriptname === "groups-domains.php" && isset($_GET['type']))
|
||||
{
|
||||
if($_GET["l"] === "white")
|
||||
if($_GET["type"] === "white")
|
||||
{
|
||||
$scriptname = "whitelist";
|
||||
}
|
||||
elseif($_GET["l"] === "black")
|
||||
elseif($_GET["type"] === "black")
|
||||
{
|
||||
$scriptname = "blacklist";
|
||||
}
|
||||
@@ -478,13 +478,13 @@ if($auth) {
|
||||
</li>
|
||||
<!-- Whitelist -->
|
||||
<li<?php if($scriptname === "whitelist"){ ?> class="active"<?php } ?>>
|
||||
<a href="list.php?l=white">
|
||||
<a href="groups-domains.php?type=white">
|
||||
<i class="fa fa-check-circle "></i> <span>Whitelist</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- Blacklist -->
|
||||
<li<?php if($scriptname === "blacklist"){ ?> class="active"<?php } ?>>
|
||||
<a href="list.php?l=black">
|
||||
<a href="groups-domains.php?type=black">
|
||||
<i class="fa fa-ban"></i> <span>Blacklist</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -35,7 +35,7 @@ function istrue(&$argument) {
|
||||
// Credit: http://stackoverflow.com/a/4694816/2087442
|
||||
function validDomain($domain_name)
|
||||
{
|
||||
$validChars = preg_match("/^([_a-z\d](-*[_a-z\d])*)(\.([_a-z\d](-*[a-z\d])*))*(\.([a-z\d])*)*$/i", $domain_name);
|
||||
$validChars = preg_match("/^([_a-z\d](-*[_a-z\d])*)(\.([_a-z\d](-*[a-z\d])*))*(\.([_a-z\d])*)*$/i", $domain_name);
|
||||
$lengthCheck = preg_match("/^.{1,253}$/", $domain_name);
|
||||
$labelLengthCheck = preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name);
|
||||
return ( $validChars && $lengthCheck && $labelLengthCheck ); //length of each label
|
||||
@@ -44,7 +44,7 @@ function validDomain($domain_name)
|
||||
function validDomainWildcard($domain_name)
|
||||
{
|
||||
// There has to be either no or at most one "*" at the beginning of a line
|
||||
$validChars = preg_match("/^((\*.)?[_a-z\d](-*[_a-z\d])*)(\.([_a-z\d](-*[a-z\d])*))*(\.([a-z\d])*)*$/i", $domain_name);
|
||||
$validChars = preg_match("/^((\*.)?[_a-z\d](-*[_a-z\d])*)(\.([_a-z\d](-*[a-z\d])*))*(\.([_a-z\d])*)*$/i", $domain_name);
|
||||
$lengthCheck = preg_match("/^.{1,253}$/", $domain_name);
|
||||
$labelLengthCheck = preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name);
|
||||
return ( $validChars && $lengthCheck && $labelLengthCheck ); //length of each label
|
||||
@@ -174,24 +174,6 @@ function readDNSserversList()
|
||||
}
|
||||
|
||||
require_once("database.php");
|
||||
$adlist = [];
|
||||
function readAdlists()
|
||||
{
|
||||
// Reset list
|
||||
$list = [];
|
||||
$db = SQLite3_connect(getGravityDBFilename());
|
||||
if ($db)
|
||||
{
|
||||
$results = $db->query("SELECT * FROM adlist");
|
||||
|
||||
while($results !== false && $res = $results->fetchArray(SQLITE3_ASSOC))
|
||||
{
|
||||
array_push($list, $res);
|
||||
}
|
||||
$db->close();
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
function addStaticDHCPLease($mac, $ip, $hostname) {
|
||||
global $error, $success, $dhcp_static_leases;
|
||||
@@ -251,8 +233,6 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
|
||||
}
|
||||
}
|
||||
|
||||
// Read available adlists
|
||||
$adlist = readAdlists();
|
||||
// Read available DNS server list
|
||||
$DNSserverslist = readDNSserversList();
|
||||
|
||||
@@ -555,9 +535,9 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
|
||||
$adminemail = trim($_POST["adminemail"]);
|
||||
if(strlen($adminemail) == 0 || !isset($adminemail))
|
||||
{
|
||||
$adminemail = 'noadminemail';
|
||||
$adminemail = '';
|
||||
}
|
||||
elseif(!validEmail($adminemail))
|
||||
if(strlen($adminemail) > 0 && !validEmail($adminemail))
|
||||
{
|
||||
$error .= "Administrator email address (".htmlspecialchars($adminemail).") is invalid!<br>";
|
||||
}
|
||||
@@ -698,41 +678,6 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
|
||||
|
||||
break;
|
||||
|
||||
case "adlists":
|
||||
foreach ($adlist as $key => $value)
|
||||
{
|
||||
if(isset($_POST["adlist-del-".$key]))
|
||||
{
|
||||
// Delete list
|
||||
exec("sudo pihole -a adlist del ".escapeshellcmd($value["address"]));
|
||||
}
|
||||
elseif(isset($_POST["adlist-enable-".$key]) && $value["enabled"] !== 1)
|
||||
{
|
||||
// Is not enabled, but should be
|
||||
exec("sudo pihole -a adlist enable ".escapeshellcmd($value["address"]));
|
||||
|
||||
}
|
||||
elseif(!isset($_POST["adlist-enable-".$key]) && $value["enabled"] === 1)
|
||||
{
|
||||
// Is enabled, but shouldn't be
|
||||
exec("sudo pihole -a adlist disable ".escapeshellcmd($value["address"]));
|
||||
}
|
||||
}
|
||||
|
||||
if(strlen($_POST["newuserlists"]) > 1)
|
||||
{
|
||||
$domains = array_filter(preg_split('/\r\n|[\r\n]/', $_POST["newuserlists"]));
|
||||
$comment = "'".$_POST["newusercomment"]."'";
|
||||
foreach($domains as $domain)
|
||||
{
|
||||
exec("sudo pihole -a adlist add ".escapeshellcmd($domain)." ".escapeshellcmd($comment));
|
||||
}
|
||||
}
|
||||
|
||||
// Reread available adlists
|
||||
$adlist = readAdlists();
|
||||
break;
|
||||
|
||||
case "privacyLevel":
|
||||
$level = intval($_POST["privacylevel"]);
|
||||
if($level >= 0 && $level <= 4)
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
<?php
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
require_once('auth.php');
|
||||
|
||||
$type = $_POST['list'];
|
||||
|
||||
// Perform all of the authentication for list editing
|
||||
// when NOT invoked and authenticated from API
|
||||
if (empty($api)) {
|
||||
list_verify($type);
|
||||
}
|
||||
|
||||
// Split individual domains into array
|
||||
$domains = preg_split('/\s+/', trim($_POST['domain']));
|
||||
|
||||
require_once("func.php");
|
||||
|
||||
require("database.php");
|
||||
$GRAVITYDB = getGravityDBFilename();
|
||||
$db = SQLite3_connect($GRAVITYDB, SQLITE3_OPEN_READWRITE);
|
||||
|
||||
switch($type) {
|
||||
case "white":
|
||||
echo remove_from_table($db, "domainlist", $domains, false, ListType::whitelist);
|
||||
break;
|
||||
|
||||
case "black":
|
||||
echo remove_from_table($db, "domainlist", $domains, false, ListType::blacklist);
|
||||
break;
|
||||
|
||||
case "white_regex":
|
||||
echo remove_from_table($db, "domainlist", $domains, false, ListType::regex_whitelist);
|
||||
break;
|
||||
|
||||
case "black_regex":
|
||||
echo remove_from_table($db, "domainlist", $domains, false, ListType::regex_blacklist);
|
||||
break;
|
||||
|
||||
default:
|
||||
die("Invalid list!");
|
||||
}
|
||||
|
||||
// Reload lists in pihole-FTL after having removed something
|
||||
echo shell_exec("sudo pihole restartdns reload");
|
||||
?>
|
||||
@@ -185,7 +185,7 @@ function archive_restore_table($file, $table, $flush=false)
|
||||
/**
|
||||
* Create table rows from an uploaded archive file
|
||||
*
|
||||
* @param $file object The file of the file in the archive to import
|
||||
* @param $file object The file in the archive to import
|
||||
* @param $table string The target table
|
||||
* @param $flush boolean Whether to flush the table before importing the archived data
|
||||
* @param $wildcardstyle boolean Whether to format the input domains in legacy wildcard notation
|
||||
@@ -193,22 +193,69 @@ function archive_restore_table($file, $table, $flush=false)
|
||||
*/
|
||||
function archive_insert_into_table($file, $table, $flush=false, $wildcardstyle=false)
|
||||
{
|
||||
global $db, $flushed_tables;
|
||||
global $db;
|
||||
|
||||
$domains = array_filter(explode("\n",file_get_contents($file)));
|
||||
// Return early if we cannot extract the lines in the file
|
||||
if(is_null($domains))
|
||||
return 0;
|
||||
|
||||
// Flush table if requested, only flush each table once
|
||||
if($flush && !in_array($table, $flushed_tables))
|
||||
{
|
||||
$db->exec("DELETE FROM ".$table);
|
||||
array_push($flushed_tables, $table);
|
||||
// Generate comment
|
||||
$prefix = "phar:///tmp/";
|
||||
if (substr($file, 0, strlen($prefix)) == $prefix) {
|
||||
$file = substr($file, strlen($prefix));
|
||||
}
|
||||
$comment = "Imported from ".$file;
|
||||
|
||||
// Determine table and type to import to
|
||||
$type = null;
|
||||
if($table === "whitelist") {
|
||||
$table = "domainlist";
|
||||
$type = ListType::whitelist;
|
||||
} else if($table === "blacklist") {
|
||||
$table = "domainlist";
|
||||
$type = ListType::blacklist;
|
||||
} else if($table === "regex_blacklist") {
|
||||
$table = "domainlist";
|
||||
$type = ListType::regex_blacklist;
|
||||
} else if($table === "domain_audit") {
|
||||
$table = "domain_audit";
|
||||
$type = -1; // -1 -> not used inside add_to_table()
|
||||
} else if($table === "adlist") {
|
||||
$table = "adlist";
|
||||
$type = -1; // -1 -> not used inside add_to_table()
|
||||
}
|
||||
|
||||
// Flush table if requested
|
||||
if($flush) {
|
||||
flush_table($table, $type);
|
||||
}
|
||||
|
||||
// Add domains to requested table
|
||||
return add_to_table($db, $table, $domains, $wildcardstyle, true);
|
||||
return add_to_table($db, $table, $domains, $comment, $wildcardstyle, true, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush table if requested. This subroutine flushes each table only once
|
||||
*
|
||||
* @param $table string The target table
|
||||
* @param $type integer Type of item to flush in table (applies only to domainlist table)
|
||||
*/
|
||||
function flush_table($table, $type=null)
|
||||
{
|
||||
global $db, $flushed_tables;
|
||||
|
||||
if(!in_array($table, $flushed_tables))
|
||||
{
|
||||
if($type !== null) {
|
||||
$sql = "DELETE FROM ".$table." WHERE type = ".$type;
|
||||
array_push($flushed_tables, $table.$type);
|
||||
} else {
|
||||
$sql = "DELETE FROM ".$table;
|
||||
array_push($flushed_tables, $table);
|
||||
}
|
||||
$db->exec($sql);
|
||||
}
|
||||
}
|
||||
|
||||
function archive_add_directory($path,$subdir="")
|
||||
@@ -311,7 +358,14 @@ if(isset($_POST["action"]))
|
||||
if(isset($_POST["auditlog"]) && $file->getFilename() === "auditlog.list")
|
||||
{
|
||||
$num = archive_insert_into_table($file, "domain_audit", $flushtables);
|
||||
echo "Processed blacklist (regex) (".$num." entries)<br>\n";
|
||||
echo "Processed audit log (".$num." entries)<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
if(isset($_POST["adlist"]) && $file->getFilename() === "adlists.list")
|
||||
{
|
||||
$num = archive_insert_into_table($file, "adlist", $flushtables);
|
||||
echo "Processed adlists (".$num." entries)<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -244,67 +244,10 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists"
|
||||
<div class="col-md-12">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Blocklists used to generate Pi-hole's Gravity: <?php echo count($adlist); ?></h3>
|
||||
<h3 class="box-title">Blocklists used to generate Pi-hole's Gravity</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-bordered dt-responsive nowrap">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:1%">Enabled</th>
|
||||
<th>List</th>
|
||||
<th>Added</th>
|
||||
<th>Last modified</th>
|
||||
<th>Comment</th>
|
||||
<th style="width:1%">Delete</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($adlist as $key => $value) { ?>
|
||||
<tr>
|
||||
<td>
|
||||
<input type="checkbox" name="adlist-enable-<?php echo $key; ?>" <?php if ($value["enabled"] === 1){ ?>checked<?php } ?>>
|
||||
</td>
|
||||
<td>
|
||||
<a href="<?php echo htmlentities($value["address"]); ?>" target="_new" id="adlist-text-<?php echo $key; ?>"><?php echo htmlentities($value["address"]); ?></a>
|
||||
</td>
|
||||
<td>
|
||||
<?php echo date(DateTime::RFC2822, intval($value["date_added"])); ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php echo date(DateTime::RFC2822, intval($value["date_modified"])); ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php echo htmlentities($value["comment"]); ?>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<button class="btn btn-danger btn-xs" id="adlist-btn-<?php echo $key; ?>">
|
||||
<span class="far fa-trash-alt"></span>
|
||||
</button>
|
||||
<input type="checkbox" name="adlist-del-<?php echo $key; ?>" hidden>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-xs-6">
|
||||
<label for="newuserlists">Domain:</label>
|
||||
<input name="newuserlists" type="url" class="form-control" placeholder="Enter a URL to add a new blocklist" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off">
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<label for="newusercomment">Comment:</label>
|
||||
<input name="newusercomment" type="text" class="form-control" placeholder="Include a comment (optional)">
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="field" value="adlists">
|
||||
<input type="hidden" name="token" value="<?php echo $token ?>">
|
||||
</div>
|
||||
<div class="box-footer clearfix">
|
||||
<button type="submit" class="btn btn-primary" name="submit" value="save" id="blockinglistsave">Save</button>
|
||||
<span><strong>Important: </strong>Save and Update when you're done!</span>
|
||||
<button type="submit" class="btn btn-primary pull-right" name="submit" id="blockinglistsaveupdate" value="saveupdate">Save and Update</button>
|
||||
<p>Please use the <a href="groups-adlists.php">group management pages</a> to edit the blocklists used by Pi-hole.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -238,3 +238,25 @@
|
||||
.text-vivid-blue {
|
||||
color: #36f !important;
|
||||
}
|
||||
|
||||
td.highlight {
|
||||
background-color: yellow;
|
||||
}
|
||||
|
||||
code.breakall
|
||||
{
|
||||
white-space: -moz-pre-wrap;
|
||||
white-space: -pre-wrap;
|
||||
white-space: -o-pre-wrap;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word; /* Internet Explorer 5.5+ */
|
||||
}
|
||||
|
||||
.underline {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||