Prepare 4.1.1

This commit is contained in:
ivan 2017-11-30 18:06:09 -03:00
parent 49f80f642f
commit a81e7320b7
12 changed files with 655 additions and 7 deletions

View File

@ -84,6 +84,7 @@ class InitSettingsController extends Controller {
'user-system-enabled' => !!Controller::request('user-system-enabled'),
'last-stat-day' => date('YmdHi', strtotime(' -12 day ')),
'ticket-gap' => Hashing::generateRandomPrime(1000000, 9999999),
'ticket-first-number' => Hashing::generateRandomNumber(1000000, 9999999),
'file-gap' => Hashing::generateRandomPrime(1000000, 9999999),
'file-first-number' => Hashing::generateRandomNumber(1000000, 9999999),
'file-quantity' => 0,

View File

@ -26,8 +26,4 @@ class LinearCongruentialGenerator {
return ($this->first - $this->min + $offset * $this->gap) % ($this->max - $this->min + 1) + $this->min;
}
public function generateFirst() {
return Hashing::generateRandomNumber($this->min, $this->max);
}
}

View File

@ -78,10 +78,10 @@ class Ticket extends DataStore {
$ticketQuantity = Ticket::count();
if ($ticketQuantity === 0) {
$ticketNumber = $linearCongruentialGenerator->generateFirst();
$ticketNumber = Setting::getSetting('ticket-first-number');
} else {
$linearCongruentialGenerator->setGap(Setting::getSetting('ticket-gap')->value);
$linearCongruentialGenerator->setFirst(Ticket::getTicket(1)->ticketNumber);
$linearCongruentialGenerator->setFirst(Setting::getSetting('ticket-first-number')->value);
$ticketNumber = $linearCongruentialGenerator->generate($ticketQuantity);
}

View File

@ -16,7 +16,7 @@ class LinearCongruentialGeneratorTest extends TestCase {
$linearCongruentialGenerator = new LinearCongruentialGenerator();
$linearCongruentialGenerator->setRange($min, $max);
$linearCongruentialGenerator->setGap(Hashing::generateRandomPrime($min, $max));
$linearCongruentialGenerator->setFirst($linearCongruentialGenerator->generateFirst());
$linearCongruentialGenerator->setFirst(Hashing::generateRandomNumber());
$used = [];

View File

@ -0,0 +1,4 @@
<?php
require_once '../mysql_connect.php';
$mysql->query(file_get_contents('./4.1.0.sql'));

View File

@ -0,0 +1,97 @@
<?php
require_once '../mysql_connect.php';
require_once './libs/Hashing.php';
require_once './libs/LinearCongruentialGenerator.php';
require_once './libs/MailSender.php';
$ticketGap = Hashing::generateRandomPrime(1000000, 9999999);
$fileGap = Hashing::generateRandomPrime(1000000, 9999999);
$ticketFirstNumber = Hashing::generateRandomNumber(1000000, 9999999);
$fileFirstNumber = Hashing::generateRandomNumber(1000000, 9999999);
$mysql->query("UPDATE setting SET value='$ticketGap' WHERE name='ticket-gap'");
$mysql->query("UPDATE setting SET value='$fileGap' WHERE name='file-gap'");
$mysql->query("UPDATE setting SET value='$ticketFirstNumber' WHERE name='ticket-first-number'");
$mysql->query("UPDATE setting SET value='$fileFirstNumber' WHERE name='file-first-number'");
$smtpHost = $mysql->query("SELECT value FROM setting WHERE name='smtp-host'")->fetch_array(MYSQLI_ASSOC)['value'];
$smtpPort = $mysql->query("SELECT value FROM setting WHERE name='smtp-port'")->fetch_array(MYSQLI_ASSOC)['value'];
$smtpUser = $mysql->query("SELECT value FROM setting WHERE name='smtp-user'")->fetch_array(MYSQLI_ASSOC)['value'];
$smtpPassword = $mysql->query("SELECT value FROM setting WHERE name='smtp-pass'")->fetch_array(MYSQLI_ASSOC)['value'];
$noReplyEmail = $mysql->query("SELECT value FROM setting WHERE name='no-reply-email'")->fetch_array(MYSQLI_ASSOC)['value'];
$userSystemEnabled = $mysql->query("SELECT value FROM setting WHERE name='user-system-enabled'")->fetch_array(MYSQLI_ASSOC)['value'];
$url = $mysql->query("SELECT value FROM setting WHERE name='url'")->fetch_array(MYSQLI_ASSOC)['value'];
$mailSender = MailSender::getInstance();
$mailSender->setConnectionSettings(
$smtpHost, $smtpPort, $smtpUser, $smtpPassword, $noReplyEmail
);
function compileString($string, $config) {
$compiledString = $string;
foreach ($config as $configName => $configValue) {
$compiledString = str_replace("{{{$configName}}}", $configValue, $compiledString);
}
return $compiledString;
}
$migrationMail = file_get_contents('./libs/migration-mail.html');
if($tickets = $mysql->query("SELECT * FROM ticket ORDER BY id ASC")) {
$linearCongruentialGenerator = new LinearCongruentialGenerator();
$linearCongruentialGenerator->setGap($ticketGap);
$linearCongruentialGenerator->setFirst($ticketFirstNumber);
$offset = 0;
$emails = [];
while($ticket = $tickets->fetch_assoc()) {
$ticketId = $ticket['id'];
$ticketNumber = $linearCongruentialGenerator->generate($offset);
$mysql->query("UPDATE ticket SET ticket_number='$ticketNumber' WHERE id='$ticketId'");
if(array_key_exists('author_email', $ticket) && $ticket['author_email']) {
if(!array_key_exists($ticket['author_email'], $emails)) {
$emails[$ticket['author_email']] = [];
}
array_push(
$emails[$ticket['author_email']],
[
'old_number' => $ticket['ticket_number'],
'new_number' => $ticketNumber,
'title' => $ticket['title']
]
);
}
$offset++;
}
foreach($emails as $email => $emailTickets) {
$ticketString = '';
foreach($emailTickets as $ticket) {
$ticketString += '<p>'
$ticketString += $ticket['old_number'];
$ticketString += '(old) => ';
$ticketString += $ticket['new_number'];
$ticketString += '(new) ';
$ticketString += $ticket['title'];
$ticketString += '</p>';
}
$mailSender->setMailContent([
'to' => $email,
'subject' => 'Tickets have been updated',
'body' => compileString($migrationMail, [
'url' => $url,
'email' => $email,
'tickets' => $ticketString,
])
]);
}
}

View File

@ -0,0 +1,44 @@
<?php
class Hashing {
public static function hashPassword($password) {
return password_hash($password, PASSWORD_DEFAULT);
}
public static function verifyPassword($password, $hash) {
return password_verify($password, $hash);
}
public static function generateRandomToken() {
return md5(uniqid(rand()));
}
public static function generateRandomNumber($min, $max) {
return rand($min, $max);
}
public static function generateRandomPrime($min, $max) {
$number = Hashing::generateRandomNumber($min, $max);
while(!Hashing::isPrime($number)) {
$number = Hashing::generateRandomNumber($min, $max);
}
return $number;
}
public static function isPrime($number) {
$sqrt = sqrt($number);
$prime = true;
if($number <= 1) return false;
for($i = 2; $i <= $sqrt; $i++) {
if($number % $i === 0) {
$prime = false;
break;
}
}
return $prime;
}
}

View File

@ -0,0 +1,29 @@
<?php
class LinearCongruentialGenerator {
private $gap;
private $first;
private $min = 100000;
private $max = 999999;
public function setRange($min, $max) {
$this->min = $min;
$this->max = $max;
}
public function setGap($gap) {
if(!Hashing::isPrime($gap)) throw new Exception('LinearCongruentialGenerator: gap must be prime');
$this->gap = $gap;
}
public function setFirst($first) {
$this->first = $first;
}
public function generate($offset) {
if(!$this->first) throw new Exception('LinearCongruentialGenerator: first is not set');
if(!$this->gap) throw new Exception('LinearCongruentialGenerator: gap is not set');
return ($this->first - $this->min + $offset * $this->gap) % ($this->max - $this->min + 1) + $this->min;
}
}

View File

@ -0,0 +1,80 @@
<?php
class MailSender {
private $mailOptions = [];
private $mailerInstance;
private static $instance = NULL;
public static function getInstance() {
if(MailSender::$instance === NULL) {
MailSender::$instance = new MailSender();
}
return MailSender::$instance;
}
private function __construct() {}
public function setConnectionSettings($host, $port, $user, $pass, $noReplyEmail) {
$this->mailOptions['from'] = $noReplyEmail;
$this->mailOptions['fromName'] = 'OpenSupports';
$this->mailOptions['smtp-host'] = $host;
$this->mailOptions['smtp-port'] = $port;
$this->mailOptions['smtp-user'] = $user;
$this->mailOptions['smtp-pass'] = $pass;
}
public function setMailContent($mailContent) {
$this->mailOptions = array_merge($this->mailOptions, $mailContent);
}
public function send() {
$mailerInstance = $this->getMailerInstance();
if( !array_key_exists('to', $this->mailOptions) ||
!array_key_exists('subject', $this->mailOptions) ||
!array_key_exists('body', $this->mailOptions) ) {
throw new Exception('Mail sending data not available');
}
$mailerInstance->addAddress($this->mailOptions['to']);
$mailerInstance->Subject = $this->mailOptions['subject'];
$mailerInstance->Body = $this->mailOptions['body'];
$mailerInstance->isHTML(true);
if ($this->isConnected()) {
$mailerInstance->send();
}
}
public function isConnected() {
return $this->getMailerInstance()->smtpConnect();
}
private function getMailerInstance() {
if(!($this->mailerInstance instanceof PHPMailer)) {
$this->mailerInstance = new PHPMailer();
$this->mailerInstance->From = $this->mailOptions['from'];
$this->mailerInstance->FromName = $this->mailOptions['fromName'];
$this->mailerInstance->isSMTP();
$this->mailerInstance->SMTPAuth = true;
$this->mailerInstance->Host = $this->mailOptions['smtp-host'];
$this->mailerInstance->Port = $this->mailOptions['smtp-port'];
$this->mailerInstance->Username = $this->mailOptions['smtp-user'];
$this->mailerInstance->Password = $this->mailOptions['smtp-pass'];
$this->mailerInstance->Timeout = 1000;
$this->mailerInstance->SMTPOptions = [
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true
]
];
}
return $this->mailerInstance;
}
}

View File

@ -0,0 +1,384 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Support Center</title>
<style type="text/css">
/* Take care of image borders and formatting, client hacks */
img { max-width: 600px; outline: none; text-decoration: none; -ms-interpolation-mode: bicubic;}
a img { border: none; }
table { border-collapse: collapse !important;}
#outlook a { padding:0; }
.ReadMsgBody { width: 100%; }
.ExternalClass { width: 100%; }
.backgroundTable { margin: 0 auto; padding: 0; width: 100% !important; }
table td { border-collapse: collapse; }
.ExternalClass * { line-height: 115%; }
.container-for-gmail-android { min-width: 600px; }
/* General styling */
* {
font-family: Helvetica, Arial, sans-serif;
}
body {
-webkit-font-smoothing: antialiased;
-webkit-text-size-adjust: none;
width: 100% !important;
margin: 0 !important;
height: 100%;
color: #676767;
}
td {
font-family: Helvetica, Arial, sans-serif;
font-size: 14px;
color: #777777;
text-align: center;
line-height: 21px;
}
a {
color: #676767;
text-decoration: none !important;
}
.pull-left {
text-align: left;
}
.pull-right {
text-align: right;
}
.header-lg,
.header-md,
.header-sm {
font-size: 32px;
font-weight: 700;
line-height: normal;
padding: 35px 0 0;
color: #4d4d4d;
}
.header-md {
font-size: 24px;
}
.header-sm {
padding: 5px 0;
font-size: 18px;
line-height: 1.3;
}
.content-padding {
padding: 20px 0 30px;
}
.mobile-header-padding-right {
width: 290px;
text-align: right;
padding-left: 10px;
}
.mobile-header-padding-left {
width: 290px;
text-align: left;
padding-left: 10px;
}
.free-text {
width: 100% !important;
padding: 10px 60px 0px;
}
.block-rounded {
border-radius: 5px;
border: 1px solid #e5e5e5;
vertical-align: top;
}
.button {
padding: 55px 0 0;
}
.info-block {
padding: 0 20px;
width: 260px;
}
.mini-block-container {
padding: 30px 50px;
width: 500px;
}
.mini-block {
background-color: #ffffff;
width: 498px;
border: 1px solid #cccccc;
border-radius: 5px;
padding: 60px 75px;
}
.block-rounded {
width: 260px;
}
.info-img {
width: 258px;
border-radius: 5px 5px 0 0;
}
.force-width-img {
width: 480px;
height: 1px !important;
}
.force-width-full {
width: 600px;
height: 1px !important;
}
.user-img img {
width: 82px;
border-radius: 5px;
border: 1px solid #cccccc;
}
.user-img {
width: 92px;
text-align: left;
}
.user-msg {
width: 236px;
font-size: 14px;
text-align: left;
font-style: italic;
}
.code-block {
padding: 10px 0;
border: 1px solid #cccccc;
color: #4d4d4d;
font-weight: bold;
font-size: 17px;
text-align: center;
}
.force-width-gmail {
min-width:600px;
height: 0px !important;
line-height: 1px !important;
font-size: 1px !important;
}
.button-width {
width: 228px;
}
</style>
<style type="text/css" media="screen">
@import url(http://fonts.googleapis.com/css?family=Oxygen:400,700);
</style>
<style type="text/css" media="screen">
@media screen {
/* Thanks Outlook 2013! */
* {
font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;
}
}
</style>
<style type="text/css" media="only screen and (max-width: 480px)">
/* Mobile styles */
@media only screen and (max-width: 480px) {
table[class*="container-for-gmail-android"] {
min-width: 290px !important;
width: 100% !important;
}
table[class="w320"] {
width: 320px !important;
}
img[class="force-width-gmail"] {
display: none !important;
width: 0 !important;
height: 0 !important;
}
a[class="button-width"],
a[class="button-mobile"] {
width: 248px !important;
}
td[class*="mobile-header-padding-left"] {
width: 160px !important;
padding-left: 0 !important;
}
td[class*="mobile-header-padding-right"] {
width: 160px !important;
padding-right: 0 !important;
}
td[class="header-lg"] {
font-size: 24px !important;
padding-bottom: 5px !important;
}
td[class="header-md"] {
font-size: 18px !important;
padding-bottom: 5px !important;
}
td[class="content-padding"] {
padding: 5px 0 30px !important;
}
td[class="button"] {
padding: 15px 0 5px !important;
}
td[class*="free-text"] {
padding: 10px 18px 30px !important;
}
img[class="force-width-img"],
img[class="force-width-full"] {
display: none !important;
}
td[class="info-block"] {
display: block !important;
width: 280px !important;
padding-bottom: 40px !important;
}
td[class="info-img"],
img[class="info-img"] {
width: 278px !important;
}
td[class="mini-block-container"] {
padding: 8px 20px !important;
width: 280px !important;
}
td[class="mini-block"] {
padding: 20px !important;
}
td[class="user-img"] {
display: block !important;
text-align: center !important;
width: 100% !important;
padding-bottom: 10px;
}
td[class="user-msg"] {
display: block !important;
padding-bottom: 20px !important;
}
}
</style>
</head>
<body bgcolor="#f7f7f7">
<table align="center" cellpadding="0" cellspacing="0" class="container-for-gmail-android" width="100%">
<tr>
<td align="left" valign="top" width="100%" style="background-color: #ffffff;">
<center>
<table cellspacing="0" cellpadding="0" width="100%" bgcolor="#ffffff" style="border-bottom: 1px solid #cccccc">
<tr>
<td width="100%" height="80" valign="top" style="text-align: center; vertical-align:middle;">
<center>
<table cellpadding="0" cellspacing="0" width="600" class="w320">
<tr>
<td style="vertical-align: middle;">
<a href="http://www.opensupports.com/" target="_blank"><img height="47" src="http://opensupports.com/logo.png" alt="logo"></a>
</td>
</tr>
</table>
</center>
<!--[if gte mso 9]>
</v:textbox>
</v:rect>
<![endif]-->
</td>
</tr>
</table>
</center>
</td>
</tr>
<tr>
<td align="center" valign="top" width="100%" style="background-color: #f7f7f7;" class="content-padding">
<center>
<table cellspacing="0" cellpadding="0" width="600" class="w320">
<tr>
<td class="header-lg">
Ticket numbers updated
</td>
</tr>
<tr>
<td class="free-text">
Hello, {{email}}. Due maintenance reasons, the following tickets have changed its number.
</td>
</tr>
<tr>
<td class="mini-block-container">
<table cellspacing="0" cellpadding="0" width="100%" style="border-collapse:separate !important;">
<tr>
<td class="mini-block">
<table cellpadding="0" cellspacing="0" width="100%">
<tr>
<td style="padding-bottom: 30px;">
{{tickets}}
</td>
</tr>
<tr>
<td class="button">
<div><!--[if mso]>
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="{{url}}/check-ticket/" style="height:45px;v-text-anchor:middle;width:155px;" arcsize="15%" strokecolor="#ffffff" fillcolor="#ff6f6f">
<w:anchorlock/>
<center style="color:#ffffff;font-family:Helvetica, Arial, sans-serif;font-size:14px;font-weight:regular;">Check Ticket</center>
</v:roundrect>
<![endif]--><a class="button-mobile" target="_blank" href="{{url}}/check-ticket/"
style="background-color:#ff6f6f;border-radius:5px;color:#ffffff;display:inline-block;font-family:'Cabin', Helvetica, Arial, sans-serif;font-size:14px;font-weight:regular;line-height:45px;text-align:center;text-decoration:none;width:155px;-webkit-text-size-adjust:none;mso-hide:all;">Check Tickets</a></div>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</center>
</td>
</tr>
<tr>
<td align="center" valign="top" width="100%" style="background-color: #ffffff; border-top: 1px solid #e5e5e5; border-bottom: 1px solid #e5e5e5; height: 100px;">
<center>
<table cellspacing="0" cellpadding="0" width="600" class="w320">
<tr>
<td style="padding: 25px 0 25px">
<strong>OpenSupports</strong><br />
Open source ticket system<br />
www.opensupports.com<br /><br />
</td>
</tr>
</table>
</center>
</td>
</tr>
</table>
</body>
</html>

View File

@ -0,0 +1,13 @@
<?php
$mysql_host = getenv('MYSQL_HOST');
$mysql_user = getenv('MYSQL_USER');
$mysql_password = getenv('MYSQL_PASSWORD');
$mysql_db = getenv('MYSQL_DB');
$mysql = new mysqli($mysql_host, $mysql_user, $mysql_password, $mysql_db);
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}