Merge pull request #151 from TheSerapher/issue-144

Issue 144 : Add notification handler and settings
This commit is contained in:
Sebastian Grewe 2013-06-09 23:57:53 -07:00
commit bc485ec213
15 changed files with 324 additions and 34 deletions

View File

@ -61,10 +61,19 @@ if (! empty($users)) {
// Create transaction record
if ($transaction->addTransaction($aUserData['id'], $dBalance, 'Debit_AP', NULL, $aUserData['coin_address'], 0.1)) {
verbose("OK\n");
// Notify user via mail
$aMailData['email'] = $user->getUserEmail($user->getUserName($aUserData['id']));
$aMailData['subject'] = 'Auto Payout Completed';
$aMailData['amount'] = $dBalance;
if (!$notification->sendNotification($aUserData['id'], 'auto_payout', $aMailData)) {
verbose("NOTIFY FAILED\n");
} else {
verbose("OK\n");
}
} else {
verbose("FAILED\n");
}
} else {
verbose("SKIPPED\n");
}

View File

@ -112,6 +112,16 @@ foreach ($aAllBlocks as $iIndex => $aBlock) {
. $strStatus
. "\n"
);
// Notify users
$aAccounts = $notification->getNotificationAccountIdByType('new_block');
foreach ($aAccounts as $account_id) {
$aMailData['height'] = $aBlock['height'];
$aMailData['subject'] = 'New Block';
$aMailData['email'] = $user->getUserEmail($user->getUserName($account_id));
$aMailData['shares'] = $iRoundShares;
$notification->sendNotification($account_id, 'new_block', $aMailData);
}
}
}
?>

View File

@ -32,20 +32,13 @@ if (empty($aWorkers)) {
$aData['username'] = $user->getUserName($aWorker['account_id']);
$aData['subject'] = 'IDLE Worker : ' . $aWorker['username'];
$aData['email'] = $user->getUserEmail($aData['username']);
if ( $notification->isNotified($aData) ) {
verbose("Worker already notified\n");
continue;
}
if ($notification->addNotification('idle_worker', $aData) && $notification->sendMail($aData['email'], 'idle_worker', $aData)) {
verbose ("Notified " . $aData['email'] . " for IDLE worker " . $aWorker['username'] . "\n");
} else {
verbose("Unable to send notification: " . $notification->getError() . "\n");
}
if (!$notification->sendNotification($aWorker['account_id'], 'idle_worker', $aData))
verbose($notification->getError() . "\n");
}
}
// We notified, lets check which recovered
$aNotifications = $notification->getAllActive();
$aNotifications = $notification->getAllActive('idle_worker');
if (!empty($aNotifications)) {
foreach ($aNotifications as $aNotification) {
$aData = json_decode($aNotification['data'], true);

View File

@ -16,6 +16,9 @@ class Mail {
public function setSmarty($smarty) {
$this->smarty = $smarty;
}
public function setUser($user) {
$this->user = $user;
}
public function setConfig($config) {
$this->config = $config;
}
@ -35,14 +38,14 @@ class Mail {
return true;
}
public function sendMail($email, $template, $aData) {
public function sendMail($template, $aData) {
$this->smarty->assign('WEBSITENAME', $this->config['website']['name']);
$this->smarty->assign('SUBJECT', $aData['subject']);
$this->smarty->assign('DATA', $aData);
$headers = 'From: Website Administration <' . $this->config['website']['email'] . ">\n";
$headers .= "MIME-Version: 1.0\n";
$headers .= "Content-Type: text/html; charset=ISO-8859-1\r\n";
if (mail($email,
if (mail($aData['email'],
$this->smarty->fetch(BASEPATH . 'templates/mail/subject.tpl'),
$this->smarty->fetch(BASEPATH . 'templates/mail/' . $template . '.tpl'),
$headers)) {
@ -61,5 +64,4 @@ $mail->setDebug($debug);
$mail->setMysql($mysqli);
$mail->setSmarty($smarty);
$mail->setConfig($config);
?>

View File

@ -6,6 +6,7 @@ if (!defined('SECURITY'))
class Notification extends Mail {
var $table = 'notifications';
var $tableSettings = 'notification_settings';
public function setInactive($id) {
$field = array(
@ -22,9 +23,10 @@ class Notification extends Mail {
* @param field string Field to update
* @return bool
**/
private function updateSingle($id, $field) {
private function updateSingle($id, $field, $table='') {
if (empty($table)) $table = $this->table;
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("UPDATE $this->table SET " . $field['name'] . " = ? WHERE id = ? LIMIT 1");
$stmt = $this->mysqli->prepare("UPDATE $table SET " . $field['name'] . " = ? WHERE id = ? LIMIT 1");
if ($this->checkStmt($stmt) && $stmt->bind_param($field['type'].'i', $field['value'], $id) && $stmt->execute())
return true;
$this->debug->append("Unable to update " . $field['name'] . " with " . $field['value'] . " for ID $id");
@ -35,22 +37,23 @@ class Notification extends Mail {
* so we can avoid duplicate entries
**/
public function isNotified($aData) {
$this->debug->append("STA " . __METHOD__, 4);
$data = json_encode($aData);
$stmt = $this->mysqli->prepare("SELECT id FROM $this->table WHERE data = ? AND active = 1 LIMIT 1");
if ($stmt && $stmt->bind_param('s', $data) && $stmt->execute() && $stmt->store_result() && $stmt->num_rows == 1)
return true;
// Catchall
// Does not seem to have a notification set
$this->setErrorMessage("Unable to run query: " . $this->mysqli->error);
return false;
}
/**
* Get all active notifications
**/
public function getAllActive() {
$stmt =$this->mysqli->prepare("SELECT id, data FROM $this->table WHERE active = 1 LIMIT 1");
if ($stmt && $stmt->execute() && $result = $stmt->get_result())
public function getAllActive($strType) {
$this->debug->append("STA " . __METHOD__, 4);
$stmt =$this->mysqli->prepare("SELECT id, data FROM $this->table WHERE active = 1 AND type = ?");
if ($stmt && $stmt->bind_param('s', $strType) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC);
// Catchall
return false;
@ -61,14 +64,120 @@ class Notification extends Mail {
* @param type string Type of the notification
* @return bool
**/
public function addNotification($type, $data) {
public function addNotification($account_id, $type, $data) {
$this->debug->append("STA " . __METHOD__, 4);
// Store notification data as json
$data = json_encode($data);
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (type, data, active) VALUES (?,?,1)");
if ($stmt && $stmt->bind_param('ss', $type, $data) && $stmt->execute())
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, type, data, active) VALUES (?, ?,?,1)");
if ($stmt && $stmt->bind_param('iss', $account_id, $type, $data) && $stmt->execute())
return true;
$this->debug->append("Failed to add notification for $type with $data: " . $this->mysqli->error);
$this->setErrorMessage("Unable to add new notification");
$this->setErrorMessage("Unable to add new notification " . $this->mysqli->error);
return false;
}
/**
* Fetch notifications for a user account
* @param id int Account ID
* @return array Notification data
**/
public function getNofifications($account_id) {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE account_id = ? ORDER BY time DESC");
if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_all(MYSQLI_ASSOC);
// Catchall
return false;
}
/**
* Fetch notification settings for user account
* @param id int Account ID
* @return array Notification settings
**/
public function getNotificationSettings($account_id) {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("SELECT * FROM $this->tableSettings WHERE account_id = ?");
if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute() && $result = $stmt->get_result()) {
while ($row = $result->fetch_assoc()) {
$aData[$row['type']] = $row['active'];
}
return $aData;
}
// Catchall
return false;
}
/**
* Get all accounts that wish to receive a specific notification
* @param strType string Notification type
* @return data array User Accounts
**/
public function getNotificationAccountIdByType($strType) {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("SELECT account_id FROM $this->tableSettings WHERE type = ? AND active = 1");
if ($stmt && $stmt->bind_param('s', $strType) && $stmt->execute() && $result = $stmt->get_result()) {
return $result->fetch_all(MYSQLI_ASSOC);
}
// Catchall
return false;
}
/**
* Update accounts notification settings
* @param account_id int Account ID
* @param data array Data array
* @return bool
**/
public function updateSettings($account_id, $data) {
$this->debug->append("STA " . __METHOD__, 4);
$failed = $ok = 0;
foreach ($data as $type => $active) {
// Does an entry exist already
$stmt = $this->mysqli->prepare("SELECT * FROM $this->tableSettings WHERE account_id = ? AND type = ?");
if ($stmt && $stmt->bind_param('is', $account_id, $type) && $stmt->execute() && $stmt->store_result() && $stmt->num_rows() > 0) {
// We found a matching row
$stmt = $this->mysqli->prepare("UPDATE $this->tableSettings SET active = ? WHERE type = ? AND account_id = ?");
if ($stmt && $stmt->bind_param('isi', $active, $type, $account_id) && $stmt->execute() && $stmt->close()) {
$ok++;
} else {
$failed++;
}
} else {
$stmt = $this->mysqli->prepare("INSERT INTO $this->tableSettings (active, type, account_id) VALUES (?,?,?)");
if ($stmt && $stmt->bind_param('isi', $active, $type, $account_id) && $stmt->execute()) {
$ok++;
} else {
$failed++;
}
}
}
if ($failed > 0) {
$this->setErrorMessage('Failed to update ' . $failed . ' settings');
return false;
}
return true;
}
/**
* Send a specific notification setup in notification_settings
* @param type string Notification type
* @return bool
**/
public function sendNotification($account_id, $strType, $aMailData) {
// Check if we notified for this event already
if ( $this->isNotified($aMailData) ) {
$this->setErrorMessage('A notification for this event has been sent already');
return false;
}
// Check if this user wants strType notifications
$stmt = $this->mysqli->prepare("SELECT account_id FROM $this->tableSettings WHERE type = ? AND active = 1 AND account_id = ?");
if ($stmt && $stmt->bind_param('si', $strType, $account_id) && $stmt->execute() && $stmt->bind_result($id) && $stmt->fetch()) {
if ($stmt->close() && $this->sendMail('notifications/' . $strType, $aMailData) && $this->addNotification($account_id, $strType, $aMailData))
return true;
} else {
$this->setErrorMessage('User disabled ' . $strType . ' notifications');
}
return false;
}
}
@ -78,3 +187,5 @@ $notification->setDebug($debug);
$notification->setMysql($mysqli);
$notification->setSmarty($smarty);
$notification->setConfig($config);
?>

View File

@ -36,12 +36,17 @@ if ( ! $user->checkPin($_SESSION['USERDATA']['id'], $_POST['authPin']) && $_POST
}
} catch (BitcoinClientException $e) {
$_SESSION['POPUP'][] = array('CONTENT' => 'Failed to send LTC, please contact site support immidiately', 'TYPE' => 'errormsg');
$continue = false;
// $continue = false;
}
}
// Set balance to 0, add to paid out, insert to ledger
if ($continue == true && $transaction->addTransaction($_SESSION['USERDATA']['id'], $dBalance, 'Debit_MP', NULL, $sCoinAddress))
if ($continue == true && $transaction->addTransaction($_SESSION['USERDATA']['id'], $dBalance, 'Debit_MP', NULL, $sCoinAddress)) {
$_SESSION['POPUP'][] = array('CONTENT' => 'Transaction completed', 'TYPE' => 'success');
$aMailData['email'] = $user->getUserEmail($user->getUserName($_SESSION['USERDATA']['id']));
$aMailData['amount'] = $dBalance;
$aMailData['subject'] = 'Manual Payout Completed';
$notification->sendNotification($_SESSION['USERDATA']['id'], 'manual_payout', $aMailData);
}
} else {
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to litecoind RPC service', 'TYPE' => 'errormsg');
}

View File

@ -0,0 +1,25 @@
<?php
// Make sure we are called from index.php
if (!defined('SECURITY')) die('Hacking attempt');
if (!$_SESSION['AUTHENTICATED']) header('Location: index.php?page=home');
if ($_REQUEST['do'] == 'save') {
if ($notification->updateSettings($_SESSION['USERDATA']['id'], $_REQUEST['data'])) {
$_SESSION['POPUP'][] = array('CONTENT' => 'Updated notification settings');
} else {
$_SESSION['POPUP'][] = array('CONTENT' => 'Failed to update settings', 'TYPE' => 'errormsg');
}
}
// Fetch notifications
$aNotifications = $notification->getNofifications($_SESSION['USERDATA']['id']);
if (!$aNotifications) $_SESSION['POPUP'][] = array('CONTENT' => 'Could not find any notifications', 'TYPE' => 'errormsg');
// Fetch user notification settings
$aSettings = $notification->getNotificationSettings($_SESSION['USERDATA']['id']);
$smarty->assign('NOTIFICATIONS', $aNotifications);
$smarty->assign('SETTINGS', $aSettings);
$smarty->assign('CONTENT', 'default.tpl');
?>

View File

@ -0,0 +1,8 @@
<html>
<body>
<p>An automated payout completed.</p>
<p>Amount: {$DATA.amount}</p>
<br/>
<br/>
</body>
</html>

View File

@ -0,0 +1,8 @@
<html>
<body>
<p>An manual payout request completed.</p>
<p>Amount: {$DATA.amount}</p>
<br/>
<br/>
</body>
</html>

View File

@ -0,0 +1,7 @@
<html>
<body>
<p>A new block has been discovered!</p>
<br/>
<br/>
</body>
</html>

View File

@ -0,0 +1,80 @@
{include file="global/block_header.tpl" ALIGN="left" BLOCK_HEADER="Notification Settings"}
<form action="{$smarty.server.PHP_SELF}" method="POST">
<input type="hidden" name="page" value="{$smarty.request.page}">
<input type="hidden" name="action" value="{$smarty.request.action}">
<input type="hidden" name="do" value="save">
<table width="100%">
<tr>
<th class="left">Type</th>
<th class="center">Active</th>
</tr>
<tr>
<td class="left">IDLE Worker</td>
<td class="center">
<input type="hidden" name="data[idle_worker]" value="0" />
<input type="checkbox" name="data[idle_worker]" value="1"{if $SETTINGS['idle_worker']}checked{/if} />
</td>
</tr>
<tr>
<td class="left">New Blocks</td>
<td class="center">
<input type="hidden" name="data[new_block]" value="0" />
<input type="checkbox" name="data[new_block]" value="1"{if $SETTINGS['new_block']}checked{/if} />
</td>
</tr>
<tr>
<td class="left">Auto Payout</td>
<td class="center">
<input type="hidden" name="data[auto_payout]" value="0" />
<input type="checkbox" name="data[auto_payout]" value="1"{if $SETTINGS['auto_payout']}checked{/if} />
</td>
</tr>
<tr>
<td class="left">Manual Payout</td>
<td class="center">
<input type="hidden" name="data[manual_payout]" value="0" />
<input type="checkbox" name="data[manual_payout]" value="1"{if $SETTINGS['manual_payout']}checked{/if} />
</td>
</tr>
<tr>
<td colspan="2" class="center">
<input type="submit" class="submit small" value="Update">
</td>
</tr>
</table>
</form>
{include file="global/block_footer.tpl"}
{include file="global/block_header.tpl" ALIGN="right" BLOCK_HEADER="Notification History"}
<center>
{include file="global/pagination.tpl"}
<table width="100%" class="pagesort">
<thead style="font-size:13px;">
<tr>
<th class="center" style="cursor: pointer;">ID</th>
<th class="center" style="cursor: pointer;">Time</th>
<th class="center" style="cursor: pointer;">Type</th>
<th class="center" style="cursor: pointer;">Active</th>
</tr>
</thead>
<tbody style="font-size:12px;">
{section notification $NOTIFICATIONS}
<tr class="{cycle values="odd,even"}">
<td class="center">{$NOTIFICATIONS[notification].id}</td>
<td class="center">{$NOTIFICATIONS[notification].time}</td>
<td class="center">
{if $NOTIFICATIONS[notification].type == new_block}New Block
{else if $NOTIFICATIONS[notification].type == auto_payout}Auto Payout
{else if $NOTIFICATIONS[notification].type == idle_worker}IDLE Worker
{else if $NOTIFICATIONS[notification].type == manual_payout}Manual Payout
{/if}
</td>
<td class="center">
<img src="{$PATH}/images/{if $NOTIFICATIONS[notification].active}success{else}error{/if}.gif" />
</td>
</tr>
{/section}
</tbody>
</table>
</center>
{include file="global/block_footer.tpl"}

View File

@ -7,6 +7,7 @@
<li><a href="{$smarty.server.PHP_SELF}?page=account&action=workers">My Workers</a></li>
<li><a href="{$smarty.server.PHP_SELF}?page=statistics&action=user">My Graphs</a></li>
<li><a href="{$smarty.server.PHP_SELF}?page=account&action=transactions">Transactions</a></li>
<li><a href="{$smarty.server.PHP_SELF}?page=account&action=notifications">Notifications</a></li>
</ul>
</li>
{/if}

View File

@ -0,0 +1,17 @@
CREATE TABLE IF NOT EXISTS `notifications` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` varchar(25) NOT NULL,
`data` varchar(255) NOT NULL,
`active` tinyint(1) NOT NULL DEFAULT '1',
`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`account_id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `active` (`active`),
KEY `data` (`data`),
KEY `account_id` (`account_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `notification_settings` (
`type` varchar(15) NOT NULL,
`account_id` int(11) NOT NULL,
`active` tinyint(1) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

View File

@ -3,7 +3,7 @@
-- http://www.phpmyadmin.net
--
-- Host: localhost
-- Generation Time: Jun 07, 2013 at 03:39 PM
-- Generation Time: Jun 09, 2013 at 04:08 PM
-- Server version: 5.5.31-0ubuntu0.13.04.1
-- PHP Version: 5.4.9-4ubuntu2
@ -65,7 +65,7 @@ CREATE TABLE IF NOT EXISTS `blocks` (
PRIMARY KEY (`id`),
UNIQUE KEY `height` (`height`,`blockhash`),
KEY `time` (`time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Discovered blocks persisted from Litecoin Service';
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Discovered blocks persisted from Litecoin Service';
-- --------------------------------------------------------
@ -74,14 +74,28 @@ CREATE TABLE IF NOT EXISTS `blocks` (
--
CREATE TABLE IF NOT EXISTS `notifications` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`type` varchar(25) NOT NULL,
`data` varchar(255) NOT NULL,
`active` tinyint(1) NOT NULL DEFAULT '1',
`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`account_id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `active` (`active`),
KEY `data` (`data`)
KEY `data` (`data`),
KEY `account_id` (`account_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Table structure for table `notification_settings`
--
CREATE TABLE IF NOT EXISTS `notification_settings` (
`type` varchar(15) NOT NULL,
`account_id` int(11) NOT NULL,
`active` tinyint(1) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
@ -153,7 +167,7 @@ CREATE TABLE IF NOT EXISTS `shares_archive` (
PRIMARY KEY (`id`),
UNIQUE KEY `share_id` (`share_id`),
KEY `time` (`time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Archive shares for potential later debugging purposes';
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Archive shares for potential later debugging purposes';
-- --------------------------------------------------------
@ -170,7 +184,7 @@ CREATE TABLE IF NOT EXISTS `statistics_shares` (
PRIMARY KEY (`id`),
KEY `account_id` (`account_id`),
KEY `block_id` (`block_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
@ -190,7 +204,7 @@ CREATE TABLE IF NOT EXISTS `transactions` (
KEY `block_id` (`block_id`),
KEY `account_id` (`account_id`),
KEY `type` (`type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;