mirror of
https://github.com/ShaYmez/FreeSTAR-Status-Engine.git
synced 2026-06-02 22:24:52 -04:00
Revert "Merge pull request #135 from server-status-project/master"
This reverts commit8d571547f5, reversing changes made toe96df7015e.
This commit is contained in:
+67
-15
@@ -2,6 +2,7 @@
|
||||
//DIR Because of include problems
|
||||
require_once(__DIR__ . "/incident.php");
|
||||
require_once(__DIR__ . "/service.php");
|
||||
require_once(__DIR__ . "/service-group.php");
|
||||
require_once(__DIR__ . "/user.php");
|
||||
require_once(__DIR__ . "/token.php");
|
||||
/**
|
||||
@@ -20,7 +21,7 @@ class Constellation
|
||||
public function render_incidents($future=false, $offset=0, $limit = 5, $admin = 0){
|
||||
if ($offset<0)
|
||||
{
|
||||
$offset = 0;
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
$limit = (isset($_GET['limit'])?$_GET['limit']:5);
|
||||
@@ -37,7 +38,7 @@ class Constellation
|
||||
}
|
||||
else if (count($incidents["incidents"]) &&!$ajax)
|
||||
{
|
||||
if ($offset)
|
||||
if ($offset)
|
||||
{
|
||||
echo '<noscript><div class="centered"><a href="'.WEB_URL.'/?offset='.($offset-$limit).'×tamp='.$timestamp.'" class="btn btn-default">'._("Back").'</a></div></noscript>';
|
||||
}
|
||||
@@ -66,12 +67,13 @@ class Constellation
|
||||
/**
|
||||
* Renders service status - in admin page it returns array so it can be processed further.
|
||||
* @param boolean $admin
|
||||
* @return array of services
|
||||
* @return array of services
|
||||
*/
|
||||
public function render_status($admin = false, $heading = true){
|
||||
global $mysqli;
|
||||
|
||||
$query = $mysqli->query("SELECT id, name FROM services");
|
||||
|
||||
//$query = $mysqli->query("SELECT id, name, description FROM services");
|
||||
$query = $mysqli->query("SELECT services.id, services.name, services.description, services_groups.name as group_name FROM services LEFT JOIN services_groups ON services.group_id=services_groups.id ORDER BY services_groups.name ");
|
||||
$array = array();
|
||||
if ($query->num_rows){
|
||||
$timestamp = time();
|
||||
@@ -86,12 +88,12 @@ class Constellation
|
||||
$tmp = $sql->get_result();
|
||||
if ($tmp->num_rows)
|
||||
{
|
||||
$array[] = new Service($result['id'], $result['name'], $tmp->fetch_assoc()['type']);
|
||||
$array[] = new Service($result['id'], $result['name'], $result['description'], $result['group_name'], $tmp->fetch_assoc()['type']);
|
||||
}
|
||||
else{
|
||||
$array[] = new Service($result['id'], $result['name']);
|
||||
$array[] = new Service($result['id'], $result['name'], $result['description'], $result['group_name']);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($heading)
|
||||
{
|
||||
echo Service::current_status($array);
|
||||
@@ -102,11 +104,27 @@ class Constellation
|
||||
}
|
||||
if (!$admin)
|
||||
{
|
||||
echo '<div id="status-container" class="clearfix">';
|
||||
?>
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
//echo '<div id="status-container" class="clearfix">';
|
||||
//$arrCompletedGroups = array();
|
||||
foreach($array as $service){
|
||||
//print_r($service);
|
||||
//if ( !empty($service->group_name) && !in_array($service->group_name, $arrCompletedGroups)) {
|
||||
//print $service->name;
|
||||
// $arrCompletedGroups[] = $service['group_name'];
|
||||
// $service->render(true);
|
||||
//} else {
|
||||
$service->render();
|
||||
//}
|
||||
}
|
||||
echo '</div>';
|
||||
echo '</ul>';
|
||||
//echo '</div>';
|
||||
}
|
||||
else{
|
||||
return $array;
|
||||
@@ -131,14 +149,14 @@ class Constellation
|
||||
$limit--;
|
||||
$more = false;
|
||||
if ($query->num_rows>$limit){
|
||||
$more = true;
|
||||
$more = true;
|
||||
}
|
||||
if ($query->num_rows){
|
||||
while(($result = $query->fetch_assoc()) && $limit-- > 0)
|
||||
{
|
||||
// Add service id and service names to an array in the Incident class
|
||||
$stmt_service = $mysqli->prepare("SELECT services.id,services.name FROM services
|
||||
INNER JOIN services_status ON services.id = services_status.service_id
|
||||
$stmt_service = $mysqli->prepare("SELECT services.id,services.name FROM services
|
||||
INNER JOIN services_status ON services.id = services_status.service_id
|
||||
WHERE services_status.status_id = ?");
|
||||
$stmt_service->bind_param("i", $result['status_id']);
|
||||
$stmt_service->execute();
|
||||
@@ -156,6 +174,40 @@ class Constellation
|
||||
"incidents" => $array
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$constellation = new Constellation();
|
||||
|
||||
function render_warning($header, $message, $show_link = false, $url = null, $link_text = null)
|
||||
{
|
||||
$this->render_alert('alert-warning', $header, $message, $show_link, $url, $link_text);
|
||||
}
|
||||
function render_success($header, $message, $show_link = false, $url = null, $link_text = null)
|
||||
{
|
||||
$this->render_alert('alert-success', $header, $message, $show_link, $url, $link_text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders an alert on screen with an optional button to return to a given URL
|
||||
* @param string alert_type - Type of warning to render alert-danger, alert-warning, alert-success etc
|
||||
* @param string header - Title of warning
|
||||
* @param string message - Message to display
|
||||
* @param boolean show_link - True if button is to be displayed
|
||||
* @param string url - URL for button
|
||||
* @param string link_txt - Text for button
|
||||
* @return void
|
||||
*/
|
||||
function render_alert($alert_type, $header, $message, $show_link = false, $url = null, $link_text = null)
|
||||
{
|
||||
echo '<div><h1></h1>
|
||||
<div class="alert '.$alert_type.'" role="alert">
|
||||
<h4 class="alert-heading">'.$header.'</h4>
|
||||
<hr>
|
||||
<p class="mb-0">'.$message.'</p>
|
||||
</div></div>';
|
||||
if ( $show_link ) {
|
||||
echo '<div class="clearfix"><a href="'.$url.'" class="btn btn-success" role="button">'.$link_text.'</a></div>';
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$constellation = new Constellation();
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
|
||||
class SSDB
|
||||
{
|
||||
function execute($conn,$sql){
|
||||
if ($conn->query($sql) === TRUE) {
|
||||
return true;
|
||||
} else {
|
||||
return $conn->error;
|
||||
}
|
||||
}
|
||||
function getSetting($conn,$setting){
|
||||
$sql = "SELECT value FROM settings WHERE setting='".$setting."'";
|
||||
$result = $conn->query($sql);
|
||||
|
||||
if ($result->num_rows == 1) {
|
||||
while($row = $result->fetch_assoc()) {
|
||||
return $row["value"];
|
||||
}
|
||||
} else {
|
||||
return "null";
|
||||
}
|
||||
}
|
||||
function setSetting($conn,$settingname,$settingvalue){
|
||||
$sql = "INSERT INTO settings (setting,value) VALUES ('".$settingname."','".$settingvalue."');";
|
||||
if ($conn->query($sql) === TRUE) {
|
||||
return true;
|
||||
} else {
|
||||
return $conn->error;
|
||||
}
|
||||
|
||||
}
|
||||
function deleteSetting($conn,$settingname){
|
||||
$sql = "DELETE FROM settings WHERE setting=\"".$settingname."\";";
|
||||
if ($conn->query($sql) === TRUE) {
|
||||
return true;
|
||||
} else {
|
||||
return $conn->error;
|
||||
}
|
||||
|
||||
}
|
||||
function updateSetting($conn, $settingname, $settingvalue){
|
||||
$this->deleteSetting($conn, $settingname);
|
||||
$this->setSetting($conn, $settingname, $settingvalue);
|
||||
return true;
|
||||
}
|
||||
|
||||
function getBooleanSetting($conn, $setting) {
|
||||
if (trim($this->getSetting($conn, $setting)) == "yes"){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+28
-11
@@ -1,4 +1,6 @@
|
||||
<?php
|
||||
require_once(__DIR__ . "/notification.php");
|
||||
|
||||
/**
|
||||
* Class for creating and rendering an incident
|
||||
*/
|
||||
@@ -73,7 +75,7 @@ class Incident implements JsonSerializable
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes submitted form and adds incident unless problem is encountered,
|
||||
* Processes submitted form and adds incident unless problem is encountered,
|
||||
* calling this is possible only for admin or higher rank. Also checks requirements
|
||||
* for char limits.
|
||||
* @return void
|
||||
@@ -81,6 +83,9 @@ class Incident implements JsonSerializable
|
||||
public static function add()
|
||||
{
|
||||
global $mysqli, $message;
|
||||
//Sould be a better way to get this array...
|
||||
$statuses = array(_("Major outage"), _("Minor outage"), _("Planned maintenance"), _("Operational") );
|
||||
|
||||
$user_id = $_SESSION['user'];
|
||||
$type = $_POST['type'];
|
||||
$title = strip_tags($_POST['title']);
|
||||
@@ -124,7 +129,7 @@ class Incident implements JsonSerializable
|
||||
if (!empty($_POST['time']) && $type == 2){
|
||||
$input_time = (!empty($_POST['time_js'])?$_POST['time_js']: $_POST['time']);
|
||||
$input_end_time = (!empty($_POST['end_time_js'])?$_POST['end_time_js']: $_POST['end_time']);
|
||||
$time = strtotime($input_time);
|
||||
$time = strtotime($input_time);
|
||||
$end_time = strtotime($input_end_time);
|
||||
if (!$time)
|
||||
{
|
||||
@@ -147,7 +152,7 @@ class Incident implements JsonSerializable
|
||||
$time = time();
|
||||
$end_time = '';
|
||||
}
|
||||
|
||||
|
||||
$stmt = $mysqli->prepare("INSERT INTO status VALUES (NULL,?, ?, ?, ?, ?, ?)");
|
||||
$stmt->bind_param("issiii", $type, $title, $text, $time ,$end_time ,$user_id);
|
||||
$stmt->execute();
|
||||
@@ -155,16 +160,28 @@ class Incident implements JsonSerializable
|
||||
$status_id = $mysqli->insert_id;
|
||||
|
||||
foreach ($services as $service) {
|
||||
$stmt = $mysqli->prepare("INSERT INTO services_status VALUES (NULL,?, ?)");
|
||||
$stmt = $mysqli->prepare("INSERT INTO services_status VALUES (NULL,?, ?)");
|
||||
$stmt->bind_param("ii", $service, $status_id);
|
||||
$stmt->execute();
|
||||
$query = $stmt->get_result();
|
||||
}
|
||||
header("Location: ".WEB_URL."/admin");
|
||||
|
||||
// Perform notification to subscribers
|
||||
$notify = new Notification();
|
||||
$notify->populate_impacted_services($status_id);
|
||||
|
||||
$notify->type = $type;
|
||||
$notify->time = $time;
|
||||
$notify->title = $title;
|
||||
$notify->text = $text;
|
||||
$notify->status = $statuses[$type];
|
||||
|
||||
$notify->notify_subscribers();
|
||||
|
||||
header("Location: ".WEB_URL."/admin?sent=true");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renders incident
|
||||
* @param Boolean $admin - decides whether admin controls should be rendered
|
||||
@@ -174,7 +191,7 @@ class Incident implements JsonSerializable
|
||||
global $icons;
|
||||
global $classes, $user;
|
||||
$admin = $admin && (($user->get_rank()<=1) || ($user->get_username() == $this->username));
|
||||
|
||||
$Parsedown = new Parsedown();
|
||||
?>
|
||||
<article class="panel panel-<?php echo $classes[$this->type];?>">
|
||||
<div class="panel-heading icon">
|
||||
@@ -188,16 +205,16 @@ class Incident implements JsonSerializable
|
||||
<time class="pull-right timeago" datetime="<?php echo $this->date; ?>"><?php echo $this->date; ?></time>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<?php echo $this->text; ?>
|
||||
<?php echo $Parsedown->setBreaksEnabled(true)->text($this->text); ?>
|
||||
</div>
|
||||
<div class="panel-footer clearfix">
|
||||
<small>
|
||||
<?php echo _("Impacted service(s): ");
|
||||
foreach ( $this->service_name as $key => $value ) {
|
||||
foreach ( $this->service_name as $value ) {
|
||||
echo '<span class="label label-default">'.$value . '</span> ';
|
||||
}
|
||||
|
||||
if (isset($this->end_date)){?>
|
||||
if (isset($this->end_date)){?>
|
||||
<span class="pull-right"><?php echo strtotime($this->end_date)>time()?_("Ending"):_("Ended");?>: <time class="pull-right timeago" datetime="<?php echo $this->end_date; ?>"><?php echo $this->end_date; ?></time></span>
|
||||
<?php } ?>
|
||||
</small>
|
||||
@@ -217,4 +234,4 @@ class Incident implements JsonSerializable
|
||||
"username" => $this->username
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class that encapsulates everything that can be done with email
|
||||
*/
|
||||
|
||||
use PHPMailer\PHPMailer\PHPMailer; // Inclusion of namespace will not cause any issue even if PHPMailer is not used
|
||||
use PHPMailer\PHPMailer\Exception;
|
||||
|
||||
// TODO Any other way to handle this include? Should we just do the include form each index.php?
|
||||
if (file_exists("libs/php_idn/idna.php")) {
|
||||
require_once("libs/php_idn/idna.php");
|
||||
} else {
|
||||
// Include for Admin section
|
||||
require_once("../libs/php_idn/idna.php");
|
||||
}
|
||||
|
||||
class Mailer {
|
||||
|
||||
public function __construct(){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic function to submit mail messages via mail og PHPmailer()
|
||||
* @param String $to Email address to which mail should be sent
|
||||
* @param String $message Body of email
|
||||
* @param boolean $html Set to true if we are sending HTML Mailer
|
||||
* @return boolean True if success
|
||||
*/
|
||||
public function send_mail($to, $subject, $message, $html = true) {
|
||||
// TODO -Handle $to as an array in order to send to muliple recipients without having
|
||||
// to call the entire send_mail function over and over..
|
||||
|
||||
$content_type = ($html) ? 'text/html' : 'text/plain';
|
||||
|
||||
// Convert IDN/punycode domain to ascii
|
||||
// TODO Handle IDN in left hand side of email address
|
||||
if ( $this->is_utf8($to) ) {
|
||||
$elements = explode('@', $to);
|
||||
$domainpart = EncodePunycodeIDN(array_pop($elements)); // Convert domain part to ascii
|
||||
$to = $elements[0] . '@' . $domainpart; // Reassemble tge full email address
|
||||
}
|
||||
|
||||
// Send using PHP mailer if it is enabled
|
||||
if ( PHP_MAILER ) {
|
||||
require_once(PHP_MAILER_PATH .'/Exception.php'); /* Exception class. */
|
||||
require_once(PHP_MAILER_PATH .'/PHPMailer.php'); /* The main PHPMailer class. */
|
||||
|
||||
if ( PHP_MAILER_SMTP ) {
|
||||
require_once(PHP_MAILER_PATH .'/SMTP.php'); /* SMTP class, needed if you want to use SMTP. */
|
||||
}
|
||||
|
||||
$phpmail = new PHPMailer(false);
|
||||
|
||||
$phpmail->setFrom(MAILER_ADDRESS, MAILER_NAME);
|
||||
$phpmail->addReplyTo(MAILER_ADDRESS, MAILER_NAME);
|
||||
//$phpmail->Debugoutput = error_log;
|
||||
|
||||
// Define SMTP parameters if enabled
|
||||
if ( PHP_MAILER_SMTP ) {
|
||||
|
||||
$phpmail->isSMTP();
|
||||
$phpmail->Host = PHP_MAILER_HOST;
|
||||
$phpmail->Port = PHP_MAILER_PORT;
|
||||
$phpmail->SMTPSecure = PHP_MAILER_SECURE;
|
||||
//$phpmail->SMTPDebug = 2; // Enable for debugging
|
||||
|
||||
// Handle authentication for SMTP if enabled
|
||||
if ( !empty(PHP_MAILER_USER) ) {
|
||||
$phpmail->SMTPAuth = true;
|
||||
$phpmail->Username = PHP_MAILER_USER;
|
||||
$phpmail->Password = PHP_MAILER_PASS;
|
||||
}
|
||||
}
|
||||
|
||||
$phpmail->addAddress($to);
|
||||
$phpmail->Subject = $subject;
|
||||
// Send HMTL mail
|
||||
if ( $html ) {
|
||||
$phpmail->msgHtml($message);
|
||||
$phpmail->AltBody = $this->convert_html_to_plain_txt($message, false);
|
||||
} else {
|
||||
$phpmail->Body = $message; // Send plain text
|
||||
}
|
||||
|
||||
$phpmail->isHtml($html); // use htmlmail if enabled
|
||||
if ( ! $phpmail->send() ) {
|
||||
// TODO Log error message $phpmail->ErrorInfo;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
} else {
|
||||
// Use standard PHP mail() function
|
||||
$headers = "Content-Type: $content_type; \"charset=utf-8\" ".PHP_EOL;
|
||||
$headers .= "MIME-Version: 1.0 ".PHP_EOL;
|
||||
$headers .= "From: ".MAILER_NAME.' <'.MAILER_ADDRESS.'>'.PHP_EOL;
|
||||
$headers .= "Reply-To: ".MAILER_NAME.' <'.MAILER_ADDRESS.'>'.PHP_EOL;
|
||||
|
||||
mail($to, $subject, $message, $headers);
|
||||
// TODO log error message if mail fails
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Tries to verify the domain using dns request against an MX record of the domain part
|
||||
* of the passed email address. The code also handles IDN/Punycode formatted addresses which
|
||||
* contains utf8 characters.
|
||||
* Original code from https://stackoverflow.com/questions/19261987/how-to-check-if-an-email-address-is-real-or-valid-using-php/19262381
|
||||
* @param String $email Email address to check
|
||||
* @return boolean True if MX record exits, false if otherwise
|
||||
*/
|
||||
public function verify_domain($email){
|
||||
// TODO - Handle idn/punycode domain names without being dependent on PHP native libs.
|
||||
$domain = explode('@', $email);
|
||||
$domain = EncodePunycodeIDN(array_pop($domain).'.'); // Add dot at end of domain to avoid local domain lookups
|
||||
syslog(1,$domain);
|
||||
return checkdnsrr($domain, 'MX');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if string contains non-english characters (detect IDN/Punycode enabled domains)
|
||||
* Original code from: https://stackoverflow.com/questions/13120475/detect-non-english-chars-in-a-string
|
||||
* @param String $str String to check for extended characters
|
||||
* @return boolean True if extended characters, false otherwise
|
||||
*/
|
||||
public function is_utf8($str)
|
||||
{
|
||||
if (strlen($str) == strlen(utf8_decode($str))) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the input from an HTML email and convert it to plain text
|
||||
* This is commonly used when sending HTML emails as a backup for email clients who can only view, or who choose to only view,
|
||||
* Original code from https://github.com/DukeOfMarshall/PHP---JSON-Email-Verification/blob/master/EmailVerify.class.php
|
||||
* plain text emails
|
||||
* @param string $content The body part of the email to convert to plain text.
|
||||
* @param boolean $remove_links Set to true if links should be removed from email
|
||||
* @return String pain text version
|
||||
*/
|
||||
public function convert_html_to_plain_txt($content, $remove_links=false){
|
||||
// TODO does not handle unsubscribe/manage subscription text very well.
|
||||
// Replace HTML line breaks with text line breaks
|
||||
$plain_text = str_ireplace(array("<br>","<br />"), "\n\r", $content);
|
||||
|
||||
// Remove the content between the tags that wouldn't normally get removed with the strip_tags function
|
||||
$plain_text = preg_replace(array('@<head[^>]*?>.*?</head>@siu',
|
||||
'@<style[^>]*?>.*?</style>@siu',
|
||||
'@<script[^>]*?.*?</script>@siu',
|
||||
'@<noscript[^>]*?.*?</noscript>@siu',
|
||||
), "", $plain_text); // Remove everything from between the tags that doesn't get removed with strip_tags function
|
||||
|
||||
// If the user has chosen to preserve the addresses from links
|
||||
if(!$remove_links){
|
||||
$plain_text = strip_tags(preg_replace('/<a href="(.*)">/', ' $1 ', $plain_text));
|
||||
}
|
||||
|
||||
// Remove HTML spaces
|
||||
$plain_text = str_replace(" ", "", $plain_text);
|
||||
|
||||
// Replace multiple line breaks with a single line break
|
||||
$plain_text = preg_replace("/(\s){3,}/","\r\n\r\n",trim($plain_text));
|
||||
|
||||
return $plain_text;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
|
||||
require_once(__DIR__ . "/queue.php");
|
||||
/**
|
||||
* Class that encapsulates everything that can be done with notifications
|
||||
*/
|
||||
|
||||
class Notification
|
||||
{
|
||||
|
||||
public $status_id = null;
|
||||
public $servicenames = "";
|
||||
public $serviceids = "";
|
||||
public $type = 0;
|
||||
public $time = 0;
|
||||
public $text = "";
|
||||
public $title = "";
|
||||
public $status = "";
|
||||
|
||||
/**
|
||||
* Generate an array of servicenames and service IDs affected by a given incident
|
||||
* @param int $status_id The incident to query
|
||||
* @return boolean
|
||||
*/
|
||||
public function populate_impacted_services($status_id)
|
||||
{
|
||||
global $mysqli;
|
||||
if (! empty($status_id)) {
|
||||
// Fetch services names for use in email
|
||||
$stmt = $mysqli->prepare("SELECT services.id, services.name FROM services INNER JOIN services_status on services.id = services_status.service_id WHERE services_status.status_id = ?");
|
||||
$stmt->bind_param("i", $status_id);
|
||||
$stmt->execute();
|
||||
$query = $stmt->get_result();
|
||||
$arrServicesNames = array();
|
||||
$arrServicesId = array();
|
||||
while ($result = $query->fetch_assoc()) {
|
||||
$arrServicesNames[] = $result['name'];
|
||||
$arrServicesId[] = (int) $result['id'];
|
||||
}
|
||||
$this->status_id = $status_id;
|
||||
$this->servicenames = implode(",", $arrServicesNames);
|
||||
$this->serviceids = implode(",", $arrServicesId);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop over the list of subscribers to notify depending on impacted service(s) and
|
||||
* call the differnet notification handles.
|
||||
* @return void
|
||||
*/
|
||||
public function notify_subscribers()
|
||||
{
|
||||
global $mysqli;
|
||||
// Fetch list of unique subscribers for given service
|
||||
// Direct inclusion of variable without using prepare justified by the fact that
|
||||
// this->serviceids are not user submitted
|
||||
$sql = "SELECT DISTINCT subscriberIDFK FROM services_subscriber WHERE serviceIDFK IN (" . $this->serviceids . ")";
|
||||
$query = $mysqli->query($sql);
|
||||
|
||||
// Create the queue tasks for email/telegram notifications
|
||||
$queue = new Queue();
|
||||
$queue->status = $queue->all_status['populating'];
|
||||
$queue->user_id = $_SESSION['user'];
|
||||
|
||||
$arr_data = array();
|
||||
if ( SUBSCRIBE_EMAIL ) {
|
||||
$arr_data = $this->prepare_email(); // Make up the base message and subject for email
|
||||
$queue->type_id = $queue->all_type_id['notify_email'];
|
||||
$queue->template_data1 = $arr_data['subject'];
|
||||
$queue->template_data2 = $arr_data['body'];
|
||||
$task_id_email = $queue->add_task();
|
||||
//syslog(1, "queue email: ". $task_id_email);
|
||||
$arr_email = array();
|
||||
}
|
||||
if ( SUBSCRIBE_TELEGRAM ) {
|
||||
$arr_data = $this->prepare_telegram();
|
||||
$queue->type_id = $queue->all_type_id['notify_telegram'];
|
||||
$queue->template_data1 = null;
|
||||
$queue->template_data2 = $arr_data['body'];
|
||||
$task_id_telegram = $queue->add_task();
|
||||
//syslog(1, "queue telegram: ". $task_id_telegram);
|
||||
$arr_telegram = array();
|
||||
}
|
||||
|
||||
while ($subscriber = $query->fetch_assoc()) {
|
||||
// Fetch list of subscriber details for already found subscriber IDs
|
||||
$stmt = $mysqli->prepare("SELECT typeID FROM subscribers WHERE subscriberID = ? AND active=1");
|
||||
$stmt->bind_param("i", $subscriber['subscriberIDFK']);
|
||||
$stmt->execute();
|
||||
$subscriberQuery = $stmt->get_result();
|
||||
|
||||
while ($subscriberData = $subscriberQuery->fetch_assoc()) {
|
||||
$typeID = $subscriberData['typeID']; // Telegram = 1, email = 2
|
||||
|
||||
// Handle telegram
|
||||
if ($typeID == 1 && SUBSCRIBE_TELEGRAM) {
|
||||
$arr_telegram[] = $subscriber['subscriberIDFK'];
|
||||
}
|
||||
// Handle email
|
||||
if ($typeID == 2 && SUBSCRIBE_EMAIL) {
|
||||
$arr_email[] = $subscriber['subscriberIDFK'];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( SUBSCRIBE_TELEGRAM) {
|
||||
$queue->task_id = $task_id_telegram;
|
||||
$queue->add_notification($arr_telegram); // Add array of Telegram users to the notification queue list
|
||||
}
|
||||
if ( SUBSCRIBE_EMAIL ) {
|
||||
$queue->task_id = $task_id_email;
|
||||
$queue->add_notification($arr_email); // Add array of Email users to the notification queue list
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends Telegram notification message using their web api.
|
||||
* @param string $userID The Telegram userid to send to
|
||||
* @param string $firstname The users firstname
|
||||
* @param string $msg Body of message
|
||||
* @return boolean true = Sent / False = failed
|
||||
*/
|
||||
public function submit_queue_telegram($userID, $firstname, $msg)
|
||||
{
|
||||
// TODO Handle limitations (Max 30 different subscribers per second)
|
||||
// TODO Error handling
|
||||
$msg = sprintf($msg, $firstname);
|
||||
|
||||
$tg_message = array('text' => $msg, 'chat_id' => $userID, 'parse_mode' => 'HTML');
|
||||
$json = @file_get_contents("https://api.telegram.org/bot" . TG_BOT_API_TOKEN . "/sendMessage?" . http_build_query($tg_message) );
|
||||
|
||||
$response = json_decode($json, true);
|
||||
|
||||
if (!is_array($response) || ! array_key_exists("ok", $response) || $response['ok'] != 1 ) {
|
||||
return false;
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends email notifications to a subscriber.
|
||||
* Function depends on Parsedown and Mailer class being loaded.
|
||||
* @param String $userID The email address to send to
|
||||
* @param String $uthkey Users token for managing subscription
|
||||
* @return void
|
||||
*/
|
||||
public function submit_queue_email($subscriber, $subject, $msg)
|
||||
{
|
||||
// TODO Error handling
|
||||
$mailer = new Mailer();
|
||||
if ( ! $mailer->send_mail($subscriber, $subject, $msg, true) ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function prepare_email(){
|
||||
|
||||
$Parsedown = new Parsedown();
|
||||
$str_mail = file_get_contents("../libs/templates/email_status_update.html");
|
||||
$str_mail = str_replace("%name%", NAME, $str_mail);
|
||||
// $smtp_mail = str_replace("%email%", $userID, $smtp_mail);
|
||||
$str_mail = str_replace("%url%", WEB_URL, $str_mail);
|
||||
$str_mail = str_replace("%service%", $this->servicenames, $str_mail);
|
||||
$str_mail = str_replace("%status%", $this->status, $str_mail);
|
||||
$str_mail = str_replace("%time%", date("c", $this->time), $str_mail);
|
||||
$str_mail = str_replace("%comment%", $Parsedown->setBreaksEnabled(true)->text($this->text), $str_mail);
|
||||
//$str_mail = str_replace("%token%", $token, $str_mail);
|
||||
|
||||
$str_mail = str_replace("%service_status_update_from%", _("Service status update from"), $str_mail);
|
||||
$str_mail = str_replace("%services_impacted%", _("Service(s) Impacted"), $str_mail);
|
||||
$str_mail = str_replace("%status_label%", _("Status"), $str_mail);
|
||||
$str_mail = str_replace("%time_label%", _("Time"), $str_mail);
|
||||
$str_mail = str_replace("%manage_subscription%", _("Manage subscription"), $str_mail);
|
||||
$str_mail = str_replace("%unsubscribe%", _("Unsubscribe"), $str_mail);
|
||||
$str_mail = str_replace("%powered_by%", _("Powered by"), $str_mail);
|
||||
|
||||
$subject = _('Status update from') . ' - ' . NAME . ' [ ' . $this->status . ' ]';
|
||||
|
||||
$val = array();
|
||||
$val['subject'] = $subject;
|
||||
$val['body'] = $str_mail;
|
||||
return $val;
|
||||
}
|
||||
|
||||
public function prepare_telegram(){
|
||||
$msg = _("Hi #s!\nThere is a status update for service(s): %s\nThe new status is: %s\nTitle: %s\n\n%s\n\n<a href='%s'>View online</a>");
|
||||
$val['body'] = sprintf($msg, $this->servicenames, $this->status, $this->title, $this->text, WEB_URL);
|
||||
return $val;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class for creating and managing the queue system
|
||||
*/
|
||||
class Queue
|
||||
{
|
||||
public $task_id;
|
||||
public $type_id;
|
||||
public $status;
|
||||
public $template_data1; // i.e. Subject for email
|
||||
public $template_data2; // i.e. HTML email body
|
||||
public $create_time;
|
||||
public $completed_time;
|
||||
public $num_errors;
|
||||
public $user_id;
|
||||
public $all_type_id = array('notify_telegram' => 1,
|
||||
'notify_email' => 2);
|
||||
|
||||
public $all_status = array('populating' => 1,
|
||||
'ready' => 2,
|
||||
'processing' => 3,
|
||||
'completed' => 4,
|
||||
'failed' => 5);
|
||||
|
||||
public function add_task() {
|
||||
global $mysqli;
|
||||
$stmt = $mysqli->prepare("INSERT INTO queue_task (type_id, status, template_data1, template_data2, created_time, user_id) VALUES (?,?,?,?,?,?)");
|
||||
if ( false===$stmt ) {
|
||||
//die('prepare() failed: ' . htmlspecialchars($mysqli->error));
|
||||
echo $mysqli->errno();
|
||||
|
||||
}
|
||||
#if ( false === $stmt ) { syslog(1, "Error :". $mysqli->error); }
|
||||
$now = time();
|
||||
$res = $stmt->bind_param("iissii", $this->type_id, $this->status, $this->template_data1, $this->template_data2, $now, $this->user_id);
|
||||
if ( false === $res ) {
|
||||
echo "error";
|
||||
die();
|
||||
}
|
||||
$stmt->execute();
|
||||
$query = $stmt->get_result();
|
||||
print $query;
|
||||
$this->task_id = $mysqli->insert_id;
|
||||
return $this->task_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove task from the queue
|
||||
* @return void
|
||||
*/
|
||||
public function delete_task($task_id){
|
||||
global $mysqli;
|
||||
$stmt = $mysqli->prepare("DELETE FROM queue_task WHERE id = ?");
|
||||
$stmt->bind_param("i", $task_id);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update status for given task
|
||||
* @param int $new_status The new current status of the task. Must be selected from the $all_status array.
|
||||
* @return void
|
||||
*/
|
||||
public function set_task_status($new_status) {
|
||||
global $mysqli;
|
||||
$stmt = $mysqli->prepare("UPDATE queue_task SET status = ? WHERE id = ?");
|
||||
$stmt->bind_param("ii", $new_status, $this->task_id);
|
||||
$stmt->execute();
|
||||
$this->status = $new_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add notification queue data for given task
|
||||
* @param array $arr_data Array filled with subscriber_id
|
||||
* @return void
|
||||
*/
|
||||
public function add_notification($arr_data) {
|
||||
global $mysqli;
|
||||
|
||||
//Default status = 1, retres = 0, task_id = $this->task_id
|
||||
|
||||
// Build query manually since mysqli doesn't cater well for multi insert..
|
||||
$count = count($arr_data); // Let's find number of elements
|
||||
$counter = 0;
|
||||
$query = '';
|
||||
$seperator = ',';
|
||||
$sub_query = '(%d, %d, %d ,%d)%s';
|
||||
|
||||
foreach ($arr_data as $value) {
|
||||
$counter++;
|
||||
if ($counter == $count) { $seperator = ''; } // Make sure last character for SQL query is correct
|
||||
$query .= sprintf($sub_query, $this->task_id, 1, $value, 0, $seperator);
|
||||
}
|
||||
$sql = "INSERT INTO queue_notify (task_id, status, subscriber_id, retries) VALUES ". $query;
|
||||
|
||||
$mysqli->query($sql);
|
||||
|
||||
$this->set_task_status($this->all_status['ready']); // Make task available for release
|
||||
}
|
||||
|
||||
public function update_notification_retries($task_id, $subscriber_id) {
|
||||
global $mysqli;
|
||||
$stmt = $mysqli->prepare("UPDATE queue_notify SET retries = retries+1 WHERE task_id = ? AND subscriber_id = ?");
|
||||
$stmt->bind_param("ii", $task_id, $subscriber_id);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
public function delete_notification($task_id, $subscriber_id) {
|
||||
global $mysqli;
|
||||
$stmt = $mysqli->prepare("DELETE FROM queue_notify WHERE task_id = ? AND subscriber_id = ?");
|
||||
$stmt->bind_param("ii", $task_id, $subscriber_id);
|
||||
$stmt->execute();
|
||||
|
||||
}
|
||||
|
||||
// TODO: Fix max attempts for notifications
|
||||
public function process_queue(){
|
||||
global $mysqli;
|
||||
$stmt = $mysqli->query("SELECT qn.id, qn.task_id, qn.status, qn.subscriber_id, qn.retries, sub.firstname, sub.userID, sub.token FROM queue_notify AS qn INNER JOIN subscribers AS sub ON qn.subscriber_id = sub.subscriberID WHERE qn.status NOT LIKE 2 AND sub.active=1");
|
||||
while ( $result = $stmt->fetch_assoc() ) {
|
||||
|
||||
$i = 2;
|
||||
$stmt2 = $mysqli->prepare("SELECT * FROM queue_task WHERE id = ? AND status = ?");
|
||||
$stmt2->bind_param("ii", $result['task_id'], $i);
|
||||
$stmt2->execute();
|
||||
$tmp = $stmt2->get_result();
|
||||
$result2 = $tmp->fetch_assoc();
|
||||
$typeID = $result2['type_id'];
|
||||
|
||||
// Handle telegram
|
||||
if ($typeID == 1) {
|
||||
$msg = str_replace("#s", $result['firstname'], $result2['template_data2']);
|
||||
if ( ! Notification::submit_queue_telegram($result['userID'], $result['firstname'], $msg) ) {
|
||||
Queue::update_notification_retries($result['task_id'], $result['subscriber_id']); // Sent
|
||||
} else {
|
||||
Queue::delete_notification($result['task_id'], $result['subscriber_id']); // Failed
|
||||
}
|
||||
}
|
||||
|
||||
// Handle email
|
||||
if ($typeID == 2) {
|
||||
$msg = str_replace("%token%", $result['token'], $result2['template_data2']);
|
||||
if ( ! Notification::submit_queue_email($result['userID'], $result2['template_data1'], $msg) ) {
|
||||
Queue::update_notification_retries($result['task_id'], $result['subscriber_id']); // Sent
|
||||
|
||||
} else {
|
||||
Queue::delete_notification($result['task_id'], $result['subscriber_id']); // Failed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if queue log is empty and if so delete the queue_task
|
||||
$stmt = $mysqli->query("SELECT id, (SELECT COUNT(*) FROM queue_notify AS qn WHERE qn.task_id = queue_task.id) AS count FROM queue_task");
|
||||
while ( $result = $stmt->fetch_assoc() ) {
|
||||
if ( $result['count'] == 0 ) {
|
||||
Queue::delete_task($result['id']);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
/**
|
||||
* Class for managing services
|
||||
*/
|
||||
class ServiceGroup
|
||||
{
|
||||
private $id;
|
||||
private $name;
|
||||
private $description;
|
||||
private $visibility_id;
|
||||
|
||||
/**
|
||||
* Constructs servicegroup from its data.
|
||||
* @param int $id service ID
|
||||
* @param String $name service name
|
||||
* @param String $description tooltip text
|
||||
* @param int $visibility_id how to display group items
|
||||
*/
|
||||
function __construct($id, $name, $description, $visibility_id)
|
||||
|
||||
{
|
||||
//TODO: Maybe get data from ID?
|
||||
$this->id = $id;
|
||||
$this->name = $name;
|
||||
$this->description = $description;
|
||||
$this->visibility_id = $visibility_id;
|
||||
$this->status = $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns id of this servicegroup
|
||||
* @return int id
|
||||
*/
|
||||
public function get_id()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of this servicegroup
|
||||
* @return String name
|
||||
*/
|
||||
public function get_name()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of this servicegroup
|
||||
* @return String description
|
||||
*/
|
||||
public function get_description()
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes submitted form and adds service unless problem is encountered,
|
||||
* calling this is possible only for admin or higher rank. Also checks requirements
|
||||
* for char limits.
|
||||
* @return void
|
||||
*/
|
||||
public static function add()
|
||||
{
|
||||
global $user, $message;
|
||||
if (strlen($_POST['group'])>50)
|
||||
{
|
||||
$message = _("Service group name is too long! Character limit is 50");
|
||||
return;
|
||||
}else if (strlen(trim($_POST['group']))==0){
|
||||
$message = _("Please enter name!");
|
||||
return;
|
||||
}
|
||||
|
||||
if ($user->get_rank()<=1)
|
||||
{
|
||||
global $mysqli;
|
||||
$name = $_POST["group"];
|
||||
$description = $_POST["description"];
|
||||
$visibility_id = $_POST["visibility_id"];
|
||||
$stmt = $mysqli->prepare("INSERT INTO services_groups VALUES(NULL,?,?,?)");
|
||||
$stmt->bind_param("ssi", $name, $description, $visibility_id);
|
||||
$stmt->execute();
|
||||
$stmt->get_result();
|
||||
header("Location: ".WEB_URL."/admin/?do=settings");
|
||||
}else
|
||||
{
|
||||
$message = _("You don't have the permission to do that!");
|
||||
}
|
||||
}
|
||||
|
||||
public static function edit()
|
||||
{
|
||||
global $user, $message;
|
||||
if (strlen($_POST['group'])>50)
|
||||
{
|
||||
$message = _("Service group name is too long! Character limit is 50");
|
||||
return;
|
||||
}else if (strlen(trim($_POST['group']))==0){
|
||||
$message = _("Please enter name!");
|
||||
return;
|
||||
}
|
||||
|
||||
if ($user->get_rank()<=1)
|
||||
{
|
||||
global $mysqli;
|
||||
$name = $_POST["group"];
|
||||
$description = $_POST["description"];
|
||||
$visibility_id = $_POST["visibility_id"];
|
||||
$group_id = $_POST["id"];
|
||||
$stmt = $mysqli->prepare("UPDATE services_groups SET name=?, description=?,visibility=? WHERE id LIKE ?");
|
||||
$stmt->bind_param("ssii", $name, $description, $visibility_id, $group_id);
|
||||
$stmt->execute();
|
||||
$stmt->get_result();
|
||||
header("Location: ".WEB_URL."/admin/?do=settings");
|
||||
}else
|
||||
{
|
||||
$message = _("You don't have the permission to do that!");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Deletes this service - first checks if user has permission to do that.
|
||||
* @return void
|
||||
*/
|
||||
public static function delete()
|
||||
{
|
||||
global $user, $message;
|
||||
if ($user->get_rank()<=1)
|
||||
{
|
||||
global $mysqli;
|
||||
$id = $_GET['delete'];
|
||||
|
||||
$stmt = $mysqli->prepare("DELETE FROM services_groups WHERE id = ?");
|
||||
$stmt->bind_param("i", $id);
|
||||
$stmt->execute();
|
||||
$query = $stmt->get_result();
|
||||
|
||||
$stmt = $mysqli->prepare("UPDATE services SET group_id = NULL WHERE group_id = ?");
|
||||
$stmt->bind_param("i", $id);
|
||||
$stmt->execute();
|
||||
$query = $stmt->get_result();
|
||||
|
||||
header("Location: ".WEB_URL."/admin/?do=settings");
|
||||
}
|
||||
else
|
||||
{
|
||||
$message = _("You don't have the permission to do that!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get list of services groups.
|
||||
* @return array $groups
|
||||
*/
|
||||
public function get_groups() {
|
||||
global $mysqli;
|
||||
$stmt = $mysqli->query("SELECT id, name FROM services_groups ORDER by name ASC");
|
||||
|
||||
$groups = array();
|
||||
$groups[0] = '';
|
||||
while ($res = $stmt->fetch_assoc()) {
|
||||
$groups[$res['id']] = $res['name'];
|
||||
}
|
||||
return $groups;
|
||||
}
|
||||
}
|
||||
+106
-15
@@ -6,19 +6,24 @@ class Service implements JsonSerializable
|
||||
{
|
||||
private $id;
|
||||
private $name;
|
||||
private $description;
|
||||
private $group_name;
|
||||
private $status;
|
||||
|
||||
/**
|
||||
* Constructs service from its data.
|
||||
* @param int $id service ID
|
||||
* @param String $name service name
|
||||
* @param String $descriotion service description for tooltip
|
||||
* @param int $status current service status
|
||||
*/
|
||||
function __construct($id, $name, $status=3)
|
||||
function __construct($id, $name, $description=null, $group_name='', $status=3)
|
||||
{
|
||||
//TODO: Maybe get data from ID?
|
||||
$this->id = $id;
|
||||
$this->name = $name;
|
||||
$this->description = $description;
|
||||
$this->group_name = $group_name;
|
||||
$this->status = $status;
|
||||
}
|
||||
|
||||
@@ -50,7 +55,16 @@ class Service implements JsonSerializable
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes submitted form and adds service unless problem is encountered,
|
||||
* Returns description of this service
|
||||
* @return String description
|
||||
*/
|
||||
public function get_description()
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes submitted form and adds service unless problem is encountered,
|
||||
* calling this is possible only for admin or higher rank. Also checks requirements
|
||||
* for char limits.
|
||||
* @return void
|
||||
@@ -70,9 +84,11 @@ class Service implements JsonSerializable
|
||||
if ($user->get_rank()<=1)
|
||||
{
|
||||
global $mysqli;
|
||||
$name = $_POST['service'];
|
||||
$stmt = $mysqli->prepare("INSERT INTO services VALUES(NULL,?)");
|
||||
$stmt->bind_param("s", $name);
|
||||
$name = htmlspecialchars($_POST['service']);
|
||||
$description = htmlspecialchars($_POST['description']);
|
||||
$group_id = $_POST['group_id'];
|
||||
$stmt = $mysqli->prepare("INSERT INTO services ( name, description, group_id ) VALUES ( ?, ?, ? )");
|
||||
$stmt->bind_param("ssi", $name, $description, $group_id);
|
||||
$stmt->execute();
|
||||
$stmt->get_result();
|
||||
header("Location: ".WEB_URL."/admin/?do=settings");
|
||||
@@ -81,6 +97,41 @@ class Service implements JsonSerializable
|
||||
$message = _("You don't have the permission to do that!");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Processes submitted form and adds service unless problem is encountered,
|
||||
* calling this is possible only for admin or higher rank. Also checks requirements
|
||||
* for char limits.
|
||||
* @return void
|
||||
*/
|
||||
public static function edit()
|
||||
{
|
||||
global $user, $message;
|
||||
if (strlen($_POST['service'])>50)
|
||||
{
|
||||
$message = _("Service name is too long! Character limit is 50");
|
||||
return;
|
||||
}else if (strlen(trim($_POST['service']))==0){
|
||||
$message = _("Please enter name!");
|
||||
return;
|
||||
}
|
||||
|
||||
if ($user->get_rank()<=1)
|
||||
{
|
||||
global $mysqli;
|
||||
$service_id = $_POST["id"];
|
||||
$name = htmlspecialchars($_POST['service']);
|
||||
$description = htmlspecialchars($_POST["description"]);
|
||||
$group_id = $_POST["group_id"];
|
||||
$stmt = $mysqli->prepare("UPDATE services SET name=?, description=?, group_id=? WHERE id = ?");
|
||||
$stmt->bind_param("ssii", $name, $description, $group_id, $service_id);
|
||||
$stmt->execute();
|
||||
$stmt->get_result();
|
||||
header("Location: ".WEB_URL."/admin/?do=settings");
|
||||
}else
|
||||
{
|
||||
$message = _("You don't have the permission to do that!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes this service - first checks if user has permission to do that.
|
||||
@@ -139,33 +190,72 @@ class Service implements JsonSerializable
|
||||
{
|
||||
$worst = $service->get_status();
|
||||
}
|
||||
$statuses[$service->get_status()]++;
|
||||
$statuses[$service->get_status()]++;
|
||||
}
|
||||
|
||||
echo '<div id="status-big" class="status '.$classes[$worst].'">';
|
||||
|
||||
|
||||
if ($statuses[$worst] == count($array))
|
||||
{
|
||||
echo _($all[$worst]);
|
||||
echo $all[$worst];
|
||||
}else{
|
||||
echo _($some[$worst]);
|
||||
echo $some[$worst];
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders this service.
|
||||
* @param $boolGroup set to true if the groups name is to be rendered
|
||||
* @return void
|
||||
*/
|
||||
public function render(){
|
||||
global $statuses;
|
||||
global $classes;
|
||||
?>
|
||||
<div class="item clearfix">
|
||||
<div class="service"><?php echo $this->name; ?></div>
|
||||
<?php if ($this->status!=-1){?><div class="status <?php echo $classes[$this->status];?>"><?php echo _($statuses[$this->status]);?></div><?php }?>
|
||||
</div>
|
||||
<?php
|
||||
static $arrCompletedGroups = array();
|
||||
//static $boolClosed;
|
||||
static $boolOpened;
|
||||
|
||||
// Check if previous ul has been opened, and if a empty/new group is being
|
||||
// render_header, close the UL first.
|
||||
if ( $boolOpened ) {
|
||||
if ( empty($this->group_name) || !in_array($this->group_name, $arrCompletedGroups) ) {
|
||||
echo '</ul>';
|
||||
$boolOpened = false;
|
||||
}
|
||||
}
|
||||
|
||||
// If no group exist or group is new, start a new UL
|
||||
if ( !empty($this->group_name) && !in_array($this->group_name, $arrCompletedGroups)) {
|
||||
echo '<ul class="list-group components">';
|
||||
//echo '<ul class="platforms list-group mb-2">';
|
||||
// Render the group status if it exists
|
||||
echo '<li class="list-group-item list-group-item-success group-name"><span><i class="glyphicon glyphicon-plus"></i></span> ' . $this->group_name .'<div class="status '. $classes[$this->status] .'">'. _($statuses[$this->status]).'</div></li>';
|
||||
//echo '<li class="cist-group-item d-flex flex-row justify-content-between platform list-group-item-action py-0 expanded" role="button">' . $this->group_name .'<div class="status '. $classes[$this->status] .'"'. _($statuses[$this->status]).'</div></li>';
|
||||
$arrCompletedGroups[] = $this->group_name;
|
||||
$boolOpened = true;
|
||||
}
|
||||
|
||||
if ( empty($this->group_name)) {
|
||||
echo '<ul class="list-group components">';
|
||||
|
||||
// echo '<ul class="platforms list-group mb-2">';
|
||||
$boolFinish = true;
|
||||
}
|
||||
|
||||
// Render the service status
|
||||
echo '<li class="list-group-item sub-component"><strong>' . $this->name .'</strong>';
|
||||
//echo '<li class="list-group-item d-flex flex-columns justify-content-between><span>+</span><h3 class="py-2 my-0 flex-fill expanded">' . $this->name . '</h3>';
|
||||
if(!empty($this->description)) {
|
||||
echo '<a class="desc-tool-tip" data-toggle="tooltip" data-placement="top" title="'.$this->description.'"> <span><i class="glyphicon glyphicon-question-sign"></i></span></a>';
|
||||
}
|
||||
if ($this->status!=-1){?><div class="status pull-right <?php echo $classes[$this->status];?>"><?php echo _($statuses[$this->status]);?></div>
|
||||
<?php
|
||||
}
|
||||
echo '</li>';
|
||||
if ( isset($boolFinish) && $boolFinish) {
|
||||
echo '</ul>';
|
||||
}
|
||||
}
|
||||
|
||||
public function jsonSerialize() {
|
||||
@@ -173,6 +263,7 @@ class Service implements JsonSerializable
|
||||
return [
|
||||
"id" => $this->id,
|
||||
"name" => $this->name,
|
||||
"description" => $this->description,
|
||||
"status" => $this->status,
|
||||
"status_string" => $statuses[$this->status]
|
||||
];
|
||||
|
||||
@@ -0,0 +1,329 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Subscriber class
|
||||
*
|
||||
*/
|
||||
Class Subscriber
|
||||
{
|
||||
public $id = null;
|
||||
public $firstname = null;
|
||||
public $lastname = null;
|
||||
public $userID = ""; // Holds email, telegram id etc
|
||||
public $token = null;
|
||||
public $active = 0;
|
||||
public $typeID = null; // Holds subscription type ID
|
||||
|
||||
|
||||
function __construct() {
|
||||
$this->firstname = null;
|
||||
$this->lastname = null;
|
||||
$this->userID = "";
|
||||
$this->token = null;
|
||||
$this->active = 0;
|
||||
$this->typeID = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets authentcation token for specified subscriberID
|
||||
* @param Integer $subscriberID - specifies which subscriber we are looking up
|
||||
* @param Integer $typeID - specifies which type of subscription we are refering (1 = telegram, 2 = email)
|
||||
* @return String $token - 32 bytes HEX string
|
||||
*/
|
||||
public function get_token($subscriberID, $typeID)
|
||||
{
|
||||
global $mysqli;
|
||||
$stmt = $mysqli->prepare("SELECT token FROM subscribers WHERE subscriberID = ? and typeID=? and active = 1 LIMIT 1");
|
||||
$stmt->bind_param("ii", $subscriberID, $typeID);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
if ($result->num_rows > 0) {
|
||||
$row = $result->fetch_assoc();
|
||||
$this->token = $row['token'];
|
||||
//$this->get_subscriber_by_token($this->token);
|
||||
return $row['token'];
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
public function get_subscriber_by_token($token)
|
||||
{
|
||||
global $mysqli;
|
||||
$stmt = $mysqli->prepare("SELECT subscriberID FROM subscribers WHERE token=? and typeID=?");
|
||||
$stmt->bind_param("si", $token, $this->typeID);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
if ($result->num_rows > 0) {
|
||||
$row = $result->fetch_assoc();
|
||||
$this->id = $row['subscriberID'];
|
||||
$this->populate(); //
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function get_subscriber_by_userid($create = false)
|
||||
{
|
||||
global $mysqli;
|
||||
$stmt = $mysqli->prepare("SELECT subscriberID FROM subscribers WHERE userID LIKE ? AND typeID = ? LIMIT 1");
|
||||
$stmt->bind_param("si", $this->userID, $this->typeID );
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
if ($result->num_rows > 0) {
|
||||
$row = $result->fetch_assoc();
|
||||
$this->id = $row['subscriberID'];
|
||||
$this->populate();
|
||||
return $row['subscriberID'];
|
||||
} else {
|
||||
// User is not registered in DB, so add if $create = true
|
||||
if ( $create ) {
|
||||
$subscriber_id = $this->add($this->typeID, $this->userID, $this->active, $this->firstname, $this->lastname);
|
||||
return $subscriber_id;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function populate()
|
||||
{
|
||||
global $mysqli;
|
||||
$stmt = $mysqli->prepare("SELECT typeID, userID, firstname, lastname, token, active FROM subscribers WHERE subscriberID = ?");
|
||||
$stmt->bind_param("i", $this->id);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
if ($result->num_rows > 0) {
|
||||
$row = $result->fetch_assoc();
|
||||
$this->userID = $row['userID'];
|
||||
$this->typeID = $row['typeID'];
|
||||
$this->firstname = $row['firstname'];
|
||||
$this->lastname = $row['lastname'];
|
||||
$this->token = $row['token'];
|
||||
$this->active = $row['active'];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function add($typeID, $userID, $active = null, $firstname = null, $lastname = null)
|
||||
{
|
||||
global $mysqli;
|
||||
$expireTime = strtotime("+2 hours");
|
||||
$updateTime = strtotime("now");
|
||||
$token = $this->generate_token();
|
||||
|
||||
$stmt = $mysqli->prepare("INSERT INTO subscribers (typeID, userID, firstname, lastname, token, active, expires, create_time) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->bind_param("issssiii", $typeID, $userID, $firstname, $lastname, $token, $active, $expireTime, $updateTime);
|
||||
$stmt->execute();
|
||||
//$query = $stmt->get_result();
|
||||
|
||||
$this->id = $mysqli->insert_id;
|
||||
$this->typeID = $typeID;
|
||||
$this->userID = $userID;
|
||||
$this->token = $token;
|
||||
$this->firstname = $firstname;
|
||||
$this->lastname = $lastname;
|
||||
$this->active = $active;
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function update($subscriberID)
|
||||
{
|
||||
global $mysqli;
|
||||
$updateTime = strtotime("now");
|
||||
$stmt = $mysqli->prepare("UPDATE subscribers SET update_time = ? WHERE subscriberID=?");
|
||||
$stmt->bind_param("ii", $updateTime, $subscriberID);
|
||||
$stmt->execute();
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public function activate($subscriberID)
|
||||
{
|
||||
global $mysqli;
|
||||
$updateTime = strtotime("now");
|
||||
|
||||
$stmt = $mysqli->prepare("UPDATE subscribers SET update_time = ?, expires = ? WHERE subscriberID = ?");
|
||||
$tmp = null;
|
||||
$stmt->bind_param("iii", $updateTime, $tmp, $subscriberID);
|
||||
$stmt->execute();
|
||||
return true;
|
||||
}
|
||||
|
||||
public function delete($subscriberID)
|
||||
{
|
||||
global $mysqli;
|
||||
|
||||
$stmt = $mysqli->prepare("DELETE FROM services_subscriber WHERE subscriberIDFK = ?");
|
||||
$stmt->bind_param("i", $subscriberID);
|
||||
$stmt->execute();
|
||||
//$query = $stmt->get_result();
|
||||
|
||||
$stmt = $mysqli->prepare("DELETE FROM subscribers WHERE subscriberID = ?");
|
||||
$stmt->bind_param("i", $subscriberID);
|
||||
$stmt->execute();
|
||||
//$query = $stmt->get_result();
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public function check_userid_exist()
|
||||
{
|
||||
global $mysqli;
|
||||
|
||||
$stmt = $mysqli->prepare("SELECT subscriberID, userID, token, active FROM subscribers WHERE typeID=? AND userID=? LIMIT 1");
|
||||
|
||||
$stmt->bind_param("is", $this->typeID, $this->userID);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
if($result->num_rows > 0) {
|
||||
$row = $result->fetch_assoc();
|
||||
$this->id = $row['subscriberID'];
|
||||
$this->populate();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function is_active_subscriber($token)
|
||||
{
|
||||
global $mysqli;
|
||||
|
||||
|
||||
$stmt = $mysqli->prepare("SELECT subscriberID, token, userID, active, expires FROM subscribers WHERE token LIKE ? LIMIT 1");
|
||||
$stmt->bind_param("s", $token );
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
if ($result->num_rows > 0) {
|
||||
$row = $result->fetch_assoc();
|
||||
} else {
|
||||
// No data found, fail gently...
|
||||
return false;
|
||||
}
|
||||
|
||||
// If account is not already active, check if we are within timeframe of exipre +2h
|
||||
// and active if so, otherwise,delete account and return falsev
|
||||
if ( $row['active'] <> 1 ) {
|
||||
|
||||
// Calculate time range for when subscription need to be validated
|
||||
$time_end = $row['expires'];
|
||||
$time_start = $time_end - (3600*2); // TODO - make this interval configurable via a config option
|
||||
$time_now = time();
|
||||
|
||||
if ( ($time_now > $time_start) && ($time_now < $time_end) ) {
|
||||
// Timefram is within range, active user..
|
||||
$stmt2 = $mysqli->prepare("UPDATE subscribers SET active=1, expires=null WHERE subscriberID = ?");
|
||||
$stmt2->bind_param("i", $row['subscriberID']);
|
||||
$stmt2->execute();
|
||||
$result = $stmt2->get_result();
|
||||
$this->active = 1;
|
||||
$this->id = $row['subscriberID'];
|
||||
$this->userID = $row['userID'];
|
||||
$this->token = $row['token'];
|
||||
return true;
|
||||
|
||||
} else {
|
||||
// Timeframe outside of given scope -> delete account
|
||||
$stmt2 = $mysqli->prepare("DELETE FROM subscribers WHERE subscriberID = ?");
|
||||
$stmt2->bind_param("i", $row['subscriberID']);
|
||||
$stmt2->execute();
|
||||
$result = $stmt2->get_result();
|
||||
$this->active = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// if we get here, account should already be active
|
||||
$this->active = 1;
|
||||
$this->id = $row['subscriberID'];
|
||||
$this->userID = $row['userID'];
|
||||
$this->token = $row['token'];
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new 64 byte token (32 bytes converted from bin2hex = 64 bytes)
|
||||
* @return string token
|
||||
*/
|
||||
public function generate_token()
|
||||
{
|
||||
global $mysqli;
|
||||
|
||||
if ( function_exists('openssl_random_pseudo_bytes') ) {
|
||||
$token = openssl_random_pseudo_bytes(32); //Generate a random string.
|
||||
$token = bin2hex($token); //Convert the binary data into hexadecimal representation.
|
||||
} else {
|
||||
// Use alternative token generator if openssl isn't available...
|
||||
$token = make_alt_token(32, 32);
|
||||
}
|
||||
|
||||
// Make sure token doesn't already exist in db
|
||||
$stmt = $mysqli->prepare("SELECT subscriberID FROM subscribers WHERE token LIKE ?");
|
||||
echo $mysqli->error;
|
||||
$stmt->bind_param("s", $token);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
if ($result->num_rows > 0 ) {
|
||||
// token already exists, call self again
|
||||
$token = $this->generate_token();
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alternative token generator if openssl_random_pseudo_bytes is not available
|
||||
* Original code by jsheets at shadonet dot com from http://php.net/manual/en/function.mt-rand.php
|
||||
* @params int min_length Minimum length of token
|
||||
* @params int max_length Maximum length of token
|
||||
* @return String token
|
||||
*/
|
||||
public function make_alt_token($min_length = 32, $max_length = 64)
|
||||
{
|
||||
$key = '';
|
||||
|
||||
// build range and shuffle range using ASCII table
|
||||
for ($i=0; $i<=255; $i++) {
|
||||
$range[] = chr($i);
|
||||
}
|
||||
|
||||
// shuffle our range 3 times
|
||||
for ($i=0; $i<=3; $i++) {
|
||||
shuffle($range);
|
||||
}
|
||||
|
||||
// loop for random number generation
|
||||
for ($i = 0; $i < mt_rand($min_length, $max_length); $i++) {
|
||||
$key .= $range[mt_rand(0, count($range)-1)];
|
||||
}
|
||||
|
||||
$return = bin2hex($key);
|
||||
|
||||
if (!empty($return)) {
|
||||
return $return;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public function set_logged_in()
|
||||
{
|
||||
$_SESSION['subscriber_valid'] = true;
|
||||
$_SESSION['subscriber_id'] = $this->id;
|
||||
$_SESSION['subscriber_userid'] = $this->userID;
|
||||
$_SESSION['subscriber_typeid'] = $this->typeID; //email
|
||||
$_SESSION['subscriber_token'] = $this->token;
|
||||
}
|
||||
|
||||
public function set_logged_off()
|
||||
{
|
||||
unset($_SESSION['subscriber_valid']);
|
||||
unset($_SESSION['subscriber_userid']);
|
||||
unset($_SESSION['subscriber_typeid']);
|
||||
unset($_SESSION['subscriber_id']);
|
||||
unset($_SESSION['subscriber_token']);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Subscriptions class
|
||||
*
|
||||
*/
|
||||
Class Subscriptions
|
||||
{
|
||||
public function add($userID, $service)
|
||||
{
|
||||
global $mysqli;
|
||||
|
||||
$stmt = $mysqli->prepare("INSERT INTO services_subscriber (subscriberIDFK, serviceIDFK) VALUES (?, ?)");
|
||||
$stmt->bind_param("ii", $userID, $service);
|
||||
$stmt->execute();
|
||||
//$query = $stmt->get_result();
|
||||
return true;
|
||||
}
|
||||
|
||||
public function remove($userID, $service)
|
||||
{
|
||||
global $mysqli;
|
||||
|
||||
$stmt = $mysqli->prepare("DELETE FROM services_subscriber WHERE subscriberIDFK = ? AND serviceIDFK = ?");
|
||||
$stmt->bind_param("ii", $userID, $service);
|
||||
$stmt->execute();
|
||||
//$query = $stmt->get_result();
|
||||
return true;
|
||||
}
|
||||
|
||||
function render_subscribed_services($typeID, $subscriberID, $userID, $token)
|
||||
{
|
||||
global $mysqli;
|
||||
$stmt = $mysqli->prepare("SELECT services.id, services.name, subscribers.subscriberID, subscribers.userID, subscribers.token
|
||||
FROM services
|
||||
LEFT JOIN services_subscriber ON services_subscriber.serviceIDFK = services.id
|
||||
LEFT JOIN subscribers ON services_subscriber.subscriberIDFK = subscribers.subscriberID
|
||||
WHERE subscribers.typeID = ? AND subscribers.subscriberID = ?");
|
||||
$stmt->bind_param("ii", $typeID, $subscriberID);
|
||||
$stmt->execute();
|
||||
$query = $stmt->get_result();
|
||||
|
||||
$strNotifyType = _('E-mail Notification subscription');
|
||||
if ( $typeID == 1 ) { $strNotifyType = _('Telegram Notification subscription'); }
|
||||
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-lg-offset-2 col-lg-8">
|
||||
<div class="text-center">
|
||||
<h3><?php echo $strNotifyType; ?></h3>
|
||||
<p><?php echo _("Manage notification subscription for"); echo " ". $userID; ?></p>
|
||||
<a onclick="if (confirm('<?php echo _("Are you sure you want to cancel you subscription?");?>')){return true;}else{event.stopPropagation(); event.preventDefault();};" class="confirmation" href="index.php?do=unsubscribe&type=<?php echo $typeID;?>&token=<?php echo $token;?>"><button class="btn btn-danger"><?php echo _("Cancel Subscription");?></button></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
|
||||
echo '<h1>' . _("Your subscriptions") . "</h1>";
|
||||
echo '<div class="list-group">';
|
||||
$subs = array(); // Will be used to hold IDs of services already selected
|
||||
|
||||
if ($query->num_rows){
|
||||
while($result = $query->fetch_assoc())
|
||||
{
|
||||
echo '<a href="'.WEB_URL.'/subscriptions.php?remove=' . $result['id'] .'" class="list-group-item"><span class="glyphicon glyphicon-remove text-danger"></span> ' . $result['name'] . '</a>';
|
||||
$subs[] = $result['id'];
|
||||
}
|
||||
|
||||
} else {
|
||||
echo '<div class="container"><summary>'._("You do not currently subscribe to any services. Please add services from the list below.").'</summary></div>';
|
||||
}
|
||||
echo "</div>";
|
||||
|
||||
echo '<h1>' . _("Add new subscription") . '</h1>';
|
||||
|
||||
// Prepare to query for unselect services. If none are selected, query for all
|
||||
$subsExp = null;
|
||||
if (count($subs) > 0 ) {
|
||||
$subsExp = 'NOT IN ('. implode(",", $subs) .')';
|
||||
}
|
||||
|
||||
$query = $mysqli->query("SELECT services.id, services.name from services WHERE services.id $subsExp");
|
||||
echo '<div class="list-group">';
|
||||
if ($query->num_rows){
|
||||
while($result = $query->fetch_assoc()){
|
||||
echo '<a href="'.WEB_URL.'/subscriptions.php?add=' . $result['id'] . '" class="list-group-item list-group-item-action"><span class="glyphicon glyphicon-plus text-success"></span> ' . $result['name'] . '</a>';
|
||||
}
|
||||
} else {
|
||||
echo '<div class="container"><summary>'._("No further services available for subscriptions.").'</summary></div>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
Class Telegram
|
||||
{
|
||||
|
||||
/**
|
||||
* Get telegram user data
|
||||
*
|
||||
* Gets telegram user data from cookie and save it to array
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @author Telegram
|
||||
*
|
||||
*
|
||||
* @since 0.1
|
||||
*/
|
||||
function getTelegramUserData() {
|
||||
if (isset($_COOKIE['tg_user'])) {
|
||||
$auth_data_json = urldecode($_COOKIE['tg_user']);
|
||||
$auth_data = json_decode($auth_data_json, true);
|
||||
return $auth_data;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Check if data is from telegram
|
||||
*
|
||||
* This checks if the data provides is from telegram. It includes a Fix for firefox
|
||||
*
|
||||
* @param mixed $auth_data The Authentication Data
|
||||
*
|
||||
* @return $auth_data
|
||||
*
|
||||
*/
|
||||
function checkTelegramAuthorization($auth_data) {
|
||||
$check_hash = $auth_data['hash'];
|
||||
unset($auth_data['hash']);
|
||||
$data_check_arr = [];
|
||||
foreach ($auth_data as $key => $value) {
|
||||
// $data_check_arr[] = $key . '=' . $value;
|
||||
$data_check_arr[] = $key . '=' . str_replace('https:/t', 'https://t', $value);
|
||||
}
|
||||
sort($data_check_arr);
|
||||
$data_check_string = implode("\n", $data_check_arr);
|
||||
$secret_key = hash('sha256', TG_BOT_API_TOKEN, true);
|
||||
$hash = hash_hmac('sha256', $data_check_string, $secret_key);
|
||||
if (strcmp($hash, $check_hash) !== 0) {
|
||||
throw new Exception('Data is NOT from Telegram');
|
||||
}
|
||||
if ((time() - $auth_data['auth_date']) > 86400) {
|
||||
throw new Exception('Data is outdated');
|
||||
}
|
||||
return $auth_data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Save telegram userdata
|
||||
*
|
||||
* Save the telegram user data in a cookie
|
||||
* @return void
|
||||
*/
|
||||
function saveTelegramUserData($auth_data) {
|
||||
$auth_data_json = json_encode($auth_data);
|
||||
setcookie('tg_user', $auth_data_json);
|
||||
}
|
||||
|
||||
function get_telegram_subscriberid($user)
|
||||
{
|
||||
global $mysqli;
|
||||
$stmt = $mysqli->prepare("SELECT subscriberID FROM subscribers WHERE typeID=1 AND userID LIKE ? LIMIT 1");
|
||||
$stmt->bind_param("s", $user);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
if ( $result->num_rows) {
|
||||
$row = $result->fetch_assoc();
|
||||
$subscriberID = $row['subscriberID'];
|
||||
return $subscriberID;
|
||||
}
|
||||
return null; // Return null on false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user