Commit 8897ee16 authored by Malcolm Blaney's avatar Malcolm Blaney

Groupwizard need to allow invite group to set organisation settings.

The last attendance check in the Invoice module has been deprecated
by the same functionality in the Report module. Old invoices are
now given the correct date. Report and Stock modules now only show
data for invite groups that a user created. The Roster module was
largely rewritten, roles now store the number of volunteers required,
this information is used to better display the current vacancies in
the roster.
parent 1e59b7bc
......@@ -522,8 +522,9 @@ class Groupwizard extends Base {
'</div>'.
'<div class="groupwizard-9 hidden"><b>Section 9:</b> '.
'Settings for the order email for the organisation.'.
$this->OrganisationEmail($organisation, $select_ignore_group).
'Settings for the order email for the organisation or host.'.
$this->OrganisationEmail($organisation, $invite_group,
$select_ignore_group).
'</div>'.
'<div class="groupwizard-10 hidden"><b>Section 10:</b> Stock settings.'.
......@@ -1094,16 +1095,27 @@ class Groupwizard extends Base {
'</div>';
}
private function OrganisationEmail($organisation, $select_ignore_group) {
if (!$organisation) {
return '<p>This section is used when your group is part of a larger '.
'organisation, an orders email is sent that collates the total orders '.
'for all groups.</p>';
private function OrganisationEmail($organisation,
$invite_group, $select_ignore_group) {
if (!$organisation && !$invite_group) {
return '<p>This section is used when your group is the host for an '.
'invite order, or part of a larger organisation. An orders email is '.
'sent that collates the total orders for all groups.</p>';
}
$organisation_format = array('', 'horizontal', 'vertical');
return '<p>As your group is part of a larger organisation, an orders '.
'email is sent that collates the total orders for all groups.</p>'.
$content = '';
if ($organisation) {
$content .= '<p>As your group is part of a larger organisation, an '.
'orders email is sent that collates the total orders for all groups.'.
'</p>';
}
else {
$content .= '<p>As your group is the host for an invite order, an '.
'email is sent that collates the total orders for all groups.</p>';
}
return $content.
'<div class="form-spacing">'.
'<label for="groupwizard-invoice-organisation-email">The address '.
'to send to:</label>'.
......
......@@ -125,16 +125,6 @@ class Invoice extends Base {
$this->Notification('invoice', 'Invoice', $description, 'system',
$group);
}
$result = $this->LastAttended();
if ($result['done'] !== true) {
// Add a notification for unsent attendance.
$description = 'Cron: '.$result['error'];
if ($group !== '') {
$description .= ' for '.ucfirst($group);
}
$this->Notification('invoice', 'Invoice', $description, 'system',
$group);
}
}
// Returns a list of groups where 'remove-orders' matches today's date.
......@@ -318,8 +308,6 @@ class Invoice extends Base {
'"invoice-group-subject","","Orders for next week"',
'"invoice-intro-no-orders","","'.$no_orders.'"',
'"invoice-intro-orders","","'.$orders.'"',
'"invoice-attendance-hello","","Hello,"',
'"invoice-attendance-subject","","Attendance check"',
'"invoice-group-attach-files","","true"',
'"invoice-group-format","","user"',
'"invoice-organisation-format","","vertical"',
......@@ -768,7 +756,9 @@ class Invoice extends Base {
}
for ($i = 0; $i < count($group_list); $i++) {
$group_name = $group_list[$i];
$cc = $this->Substitute('invoice-group-email', $group_name);
// The group orders are also sent to the host group, but a copy is
// sent to each participating group listing their own members.
$cc = $this->Substitute('invoice-group-email', '', '', $group_name);
$group_result = $this->ProcessNextOrder($group_data[$group_name],
$products, array(), false,
true, $cc);
......@@ -931,7 +921,8 @@ class Invoice extends Base {
private function FormatInvoice($user, $first, $last, $invoice_number,
$reference, $start, $end, $purchase_value,
$surcharge, $sold, $balance, $credit,
$pre_order_available, $pre_order_created) {
$pre_order_available, $pre_order_created,
$invoice_date = 0) {
$purchase = new Purchase($this->user, $this->owner);
$stock = new Stock($this->user, $this->owner);
$detail = new Detail($this->user, $this->owner);
......@@ -940,7 +931,8 @@ class Invoice extends Base {
$message = '<html><head><title>Invoice</title></head>'."\n".
'<body>'."\n";
// The start of the invoice is created via substitutions for the group.
$message .= $this->FormatInvoiceStart($first, $last, $invoice_number);
$message .= $this->FormatInvoiceStart($first, $last, $invoice_number,
$invoice_date);
$products = $stock->AllProducts(false);
$cell_style = 'style="background-color: #f0f0f0"';
// Next the table of purchases are displayed.
......@@ -1026,7 +1018,7 @@ class Invoice extends Base {
$total += (float)$data[$i]['total'];
$message .= '<tr><td>'.$name.'</td><td>'.
$data[$i]['quantity'].'</td><td>$'.$data[$i]['price'].'/'.
$products[$supplier][$name]['unit'].'</td><td>$'.
$products[$user][$name]['unit'].'</td><td>$'.
$data[$i]['total'].'</td></tr>'."\n";
}
// Only show the title if there's more than one product sold.
......@@ -1046,19 +1038,24 @@ class Invoice extends Base {
return wordwrap($message);
}
private function FormatInvoiceStart($first, $last, $invoice_number) {
private function FormatInvoiceStart($first, $last,
$invoice_number, $invoice_date) {
if ($invoice_date === 0) {
$invoice_date = time();
}
// If there are no purchases, an invoice number is not given.
if ($invoice_number == 0) {
return $this->Substitute('invoice-start-no-purchase',
array('/!date/', '/!first/', '/!last/'),
array(date('j F Y'), $first, $last));
array(date('j F Y', $invoice_date),
$first, $last));
}
else {
return $this->Substitute('invoice-start-purchase',
array('/!date/', '/!number/',
'/!first/', '/!last/'),
array(date('j F Y'), $invoice_number,
$first, $last));
array(date('j F Y', $invoice_date),
$invoice_number, $first, $last));
}
}
......@@ -1325,70 +1322,6 @@ class Invoice extends Base {
return array($message, $data);
}
private function LastAttended() {
// Don't do anything if an email address is not configured.
$email = $this->Substitute('invoice-attendance-email');
if ($email === '') {
return array('done' => true);
}
$detail = new Detail($this->user, $this->owner);
$user_details = $detail->AllUsers();
$purchase = new Purchase($this->user, $this->owner);
$recent = $purchase->AllMostRecent(strtotime('-2 months'));
if (count($recent) == 0) {
return array('done' => true);
}
$subject = $this->Substitute('invoice-attendance-subject');
$cell_style = 'style="background-color: #f0f0f0"';
$message = '<html><head><title>'.$subject.'</title>'.
'</head>'."\n".
'<body>'."\n".
'<p>'.$this->Substitute('invoice-attendance-hello')."</p>\n".
"<p>Here is the list of people who haven't purchased recently</p>\n".
'<table border="0" cellspacing="5">'."\n".
'<tr><td '.$cell_style.'>Username</td><td '.$cell_style.
'>Full Name</td><td '.$cell_style.'>Email</td>'.'<td '.$cell_style.
'>Phone</td><td '.$cell_style.'>Last Attended</td></tr>'."\n";
foreach ($recent as $user => $timestamp) {
$message .= '<tr><td>'.$user.'</td><td>'.
$user_details[$user]['first'].' '.$user_details[$user]['last'].
'</td><td>'.$user_details[$user]['email'].'</td><td>'.
$user_details[$user]['phone'].'</td><td>'.
date('j F Y', $timestamp).'</td></tr>'."\n";
}
$message .= '</table>'."\n".
'</body></html>';
$message = wordwrap($message);
$sender = $this->Substitute('invoice-sender', '/!host/',
$this->user->config->ServerName());
$sender_name = $this->Substitute('invoice-sender-name');
if ($sender_name == '') {
$sender_name = $sender;
}
else {
$sender_name .= ' <'.$sender.'>';
}
$cc = $this->Substitute('invoice-attendance-cc');
$bcc = $this->Substitute('invoice-attendance-bcc');
$headers = "MIME-Version: 1.0\r\n".
"Content-Type: text/html; charset=iso-8859-1\r\n".
'From: '.$sender_name."\r\n";
if ($cc != '') {
$headers .= 'Cc: '.$cc."\r\n";
}
if ($bcc != '') {
$headers .= 'Bcc: '.$bcc."\r\n";
}
if (!mail($email, $subject, $message, $headers, '-f '.$sender)) {
return array('error' => 'Email to '.$email.' not accepted for delivery.');
}
return array('done' => true);
}
private function RoundPacks($packs) {
// Rounding is a value between zero and one, used to decide if the
// number of packs should be rounded up or down.
......@@ -1638,6 +1571,7 @@ class Invoice extends Base {
$start -= (int)$this->Substitute('invoice-day-count') * 86400;
$purchase = new Purchase($this->user, $this->owner);
$purchase_value = $purchase->Total($username, $start, $end);
$invoice_date = strtotime($invoice_date.' 12:00');
// If an invoice number wasn't found earlier, only create a new invoice
// number now if there are purchases for this user.
if ($invoice_number === 0 && $purchase_value > 0.01) {
......@@ -1656,7 +1590,7 @@ class Invoice extends Base {
}
$invoice_number++;
$query = 'INSERT INTO invoice VALUES ("'.$username.'", '.
strtotime($invoice_date.' 12:00').', '.$invoice_number.')';
$invoice_date.', '.$invoice_number.')';
if (!$mysqli->query($query)) {
$this->Log('Invoice->SendOldInvoice 3: '.$mysqli->error);
}
......@@ -1669,7 +1603,7 @@ class Invoice extends Base {
$message = $this->FormatInvoice($username, $first, $last, $invoice_number,
$reference, $start, $end, $purchase_value,
$surcharge, $sold, $balance, 0, false,
false);
false, $invoice_date);
$subject = $this->Substitute('invoice-subject', '/!week/',
date('W', $start));
$sender = $this->Substitute('invoice-sender', '/!host/',
......
......@@ -83,11 +83,12 @@ class Purchase extends Base {
'<div class="order hidden">'.
'You are currently entering purchases for '.
'<span class="name"></span>.<br>'.
'<span class="roster-reminder hidden">They haven\'t volunteered '.
'recently, please ask them to put their name on the roster.<br>'.
'</span>'.
'Their current total is: $'.
'<span class="total">0.00</span>'.
'<div class="roster-reminder hidden">They haven\'t volunteered '.
'recently, please ask them to put their name on the roster.'.
'</div>'.
'<div class="roster-volunteer hidden">They have already volunteered.'.
'</div>'.
'Their current total is: $<span class="total">0.00</span>'.
'<div class="form-spacing'.$hide_next_week.'">'.
'<label for="purchase-next-week-input">'.$purchase_next_week.
'</label>'.
......@@ -1626,7 +1627,7 @@ class Purchase extends Base {
$mysqli = connect_db();
$query = 'SELECT purchase.user, MAX(timestamp) AS timestamp FROM purchase '.
'LEFT JOIN users ON purchase.user = users.user WHERE users.user '.
'NOT LIKE "buyer\_%" AND '.$group_query.
'NOT LIKE "buyer\_%" AND active = 1 AND '.$group_query.
' GROUP BY user ORDER BY timestamp DESC';
if ($result = $mysqli->query($query)) {
while ($purchase = $result->fetch_assoc()) {
......@@ -2804,7 +2805,7 @@ class Purchase extends Base {
// Need to subtract recent and future purchases from totals.
$query = 'SELECT SUM(quantity * price) AS total FROM purchase WHERE '.
'user = "'.$this->user->name.'" AND timestamp > '.strtotime('-24 hours');
'user = "'.$this->user->name.'" AND timestamp > '.strtotime('-4 hours');
if ($result = $mysqli->query($query)) {
if ($purchase = $result->fetch_assoc()) {
$purchase_total -= (float)$purchase['total'];
......
......@@ -87,14 +87,10 @@ class Report extends Base {
public function Content($id) {
$content = '';
// Default to showing reports for the current value of purchase-group if
// set, otherwise show reports for the organisation if there is one,
// if not show reports for the user's default group.
$invite_group = false;
$default_group = $this->user->group;
if (isset($_SESSION['purchase-group'])) {
$this->user->group = $_SESSION['purchase-group'];
}
$purchase_group = isset($_SESSION['purchase-group']) ?
$_SESSION['purchase-group'] : '';
// Show a select if the current user has created any invite groups, or if
// their default group is part of an organisation, in which case add the
// organisation too.
......@@ -102,6 +98,12 @@ class Report extends Base {
$organiser = new Organiser($this->user, $this->owner);
$siblings = $organiser->Siblings();
if (count($siblings) > 1) {
// Default to showing reports for the current value of purchase_group if
// it's part of the organisation, otherwise show reports for the whole
// organisation.
if (in_array($purchase_group, $siblings)) {
$this->user->group = $purchase_group;
}
$parent = $organiser->Parent();
// The organisation can use 'group-name' to add a descriptive name.
$parent_description = $this->Substitute('group-name', '', '', $parent);
......@@ -110,8 +112,7 @@ class Report extends Base {
'<option value="'.$parent.'">'.$parent_description.'</option>';
for ($i = 0; $i < count($siblings); $i++) {
$group = $siblings[$i];
if ($group == $this->user->group &&
isset($_SESSION['purchase-group'])) {
if ($group == $this->user->group && $purchase_group !== '') {
$content .= '<option selected="selected" value="'.$group.'">'.
$this->Substitute('group-name').'</option>';
}
......@@ -120,14 +121,17 @@ class Report extends Base {
$this->Substitute('group-name', '', '', $group).'</option>';
}
}
// If purchase-group isn't set, show report for the organisation.
if (!isset($_SESSION['purchase-group'])) {
// If purchase_group isn't set, show report for the organisation.
if ($purchase_group === '') {
$organisation = true;
}
}
$invite = new Invite($this->user, $this->owner);
$created = $invite->Created();
if (count($created) !== 0) {
if (in_array($purchase_group, $created)) {
$this->user->group = $purchase_group;
}
if ($content === '') {
$content .= 'Showing report for <select id="report-group-select">'.
'<option value="'.$default_group.'">'.
......
This diff is collapsed.
......@@ -98,13 +98,13 @@ class Stock extends Base {
$base = '';
$extra_pricing = '';
$default_group = $this->user->group;
$purchase_group = isset($_SESSION['purchase-group']) ?
$_SESSION['purchase-group'] : '';
// Display a group select if this user has created invite groups.
$invite = new Invite($this->user, $this->owner);
$created = $invite->Created();
if (count($created) !== 0) {
if (isset($_SESSION['purchase-group'])) {
$this->user->group = $_SESSION['purchase-group'];
}
if (in_array($purchase_group, $created)) {
$this->user->group = $purchase_group;
$content .= '<p class="stock-display-group">Displaying stock for '.
'<select id="stock-group-select">'.
'<option value="'.$default_group.'">'.
......@@ -915,9 +915,7 @@ class Stock extends Base {
private function AllData($new_available = array(),
$alternatives_only = false, $show_hidden = false) {
$default_group = $this->user->group;
if (isset($_SESSION['purchase-group'])) {
$this->user->group = $_SESSION['purchase-group'];
}
$this->SetPurchaseGroup();
$object = array();
$object['wholesalePercent'] =
(float)$this->Substitute('stock-wholesale-percent');
......@@ -1025,9 +1023,7 @@ class Stock extends Base {
private function EditProduct() {
$default_group = $this->user->group;
if (isset($_SESSION['purchase-group'])) {
$this->user->group = $_SESSION['purchase-group'];
}
$this->SetPurchaseGroup();
$mysqli = connect_db();
// Leave name unescaped to check for some characters that aren't allowed,
$us_name = $_POST['name'];
......@@ -1201,9 +1197,7 @@ class Stock extends Base {
private function EditMultiple() {
$default_group = $this->user->group;
if (isset($_SESSION['purchase-group'])) {
$this->user->group = $_SESSION['purchase-group'];
}
$this->SetPurchaseGroup();
$mysqli = connect_db();
$new_unit = $mysqli->escape_string($_POST['unit']);
$new_size = (float)$mysqli->escape_string($_POST['size']);
......@@ -1290,9 +1284,7 @@ class Stock extends Base {
private function RemoveProduct() {
$default_group = $this->user->group;
if (isset($_SESSION['purchase-group'])) {
$this->user->group = $_SESSION['purchase-group'];
}
$this->SetPurchaseGroup();
$object = array();
$mysqli = connect_db();
$name = $mysqli->escape_string($_POST['name']);
......@@ -1487,9 +1479,7 @@ class Stock extends Base {
private function ImportData() {
$default_group = $this->user->group;
if (isset($_SESSION['purchase-group'])) {
$this->user->group = $_SESSION['purchase-group'];
}
$this->SetPurchaseGroup();
$organiser = new Organiser($this->user, $this->owner);
$mysqli = connect_db();
$user = $mysqli->escape_string($_POST['supplier']);
......@@ -1713,9 +1703,7 @@ class Stock extends Base {
private function Export($available_only = false) {
$default_group = $this->user->group;
if (isset($_SESSION['purchase-group'])) {
$this->user->group = $_SESSION['purchase-group'];
}
$this->SetPurchaseGroup();
$organiser = new Organiser($this->user, $this->owner);
$object = array();
$mysqli = connect_db();
......@@ -1774,9 +1762,7 @@ class Stock extends Base {
private function ListAdjustments() {
$default_group = $this->user->group;
if (isset($_SESSION['purchase-group'])) {
$this->user->group = $_SESSION['purchase-group'];
}
$this->SetPurchaseGroup();
$mysqli = connect_db();
$name = $mysqli->escape_string($_POST['name']);
$user = $mysqli->escape_string($_POST['supplier']);
......@@ -1810,9 +1796,7 @@ class Stock extends Base {
private function ListAllAdjustments() {
$default_group = $this->user->group;
if (isset($_SESSION['purchase-group'])) {
$this->user->group = $_SESSION['purchase-group'];
}
$this->SetPurchaseGroup();
$object = array();
$mysqli = connect_db();
$organiser = new Organiser($this->user, $this->owner);
......@@ -1841,9 +1825,7 @@ class Stock extends Base {
private function SaveAdjustment() {
$default_group = $this->user->group;
if (isset($_SESSION['purchase-group'])) {
$this->user->group = $_SESSION['purchase-group'];
}
$this->SetPurchaseGroup();
$mysqli = connect_db();
$name = $mysqli->escape_string($_POST['name']);
$user = $mysqli->escape_string($_POST['supplier']);
......@@ -1922,9 +1904,7 @@ class Stock extends Base {
private function ExportAdjustments() {
$default_group = $this->user->group;
if (isset($_SESSION['purchase-group'])) {
$this->user->group = $_SESSION['purchase-group'];
}
$this->SetPurchaseGroup();
$object = array();
$mysqli = connect_db();
$name = $mysqli->escape_string($_POST['name']);
......@@ -1977,15 +1957,25 @@ class Stock extends Base {
}
private function UpdateGroup() {
if (isset($_SESSION['purchase-group'])) {
if (!isset($_SESSION['purchase-group'])) {
return array('done' => true);
}
$purchase_group = $_SESSION['purchase-group'];
if ($this->user->group === $purchase_group) {
return array('done' => true);
}
$invite = new Invite($this->user, $this->owner);
if (in_array($purchase_group, $invite->Created())) {
$mysqli = connect_db();
$user = $mysqli->escape_string($_POST['supplier']);
$query = 'UPDATE users SET '.
'system_group = "'.$_SESSION['purchase-group'].'" WHERE '.
$query = 'UPDATE users SET system_group = "'.$purchase_group.'" WHERE '.
'user = "'.$user.'"';
if (!$mysqli->query($query)) {
$this->Log('Stock->UpdateGroup: '.$mysqli->error);
}
$mysqli->close();
}
return array('done' => true);
}
......@@ -2004,6 +1994,18 @@ class Stock extends Base {
return array('error' => 'Group not found.');
}
private function SetPurchaseGroup() {
if (!isset($_SESSION['purchase-group'])) return;
$purchase_group = $_SESSION['purchase-group'];
if ($this->user->group === $purchase_group) return;
$invite = new Invite($this->user, $this->owner);
if (in_array($purchase_group, $invite->Created())) {
$this->user->group = $purchase_group;
}
}
}
?>
\ No newline at end of file
......@@ -89,7 +89,7 @@ class Summary extends Base {
$detail = new Detail($this->user, $this->owner);
$user_details = $detail->User();
$name = '';
if (is_array($user_details)) $name = $user_details['first'];
if (isset($user_details['first'])) $name = $user_details['first'];
// If the user hasn't filled out their details just display their username.
if ($name == '') {
$name = $this->user->name;
......
......@@ -85,7 +85,7 @@ if(activeEditController!==null){throw"SlickGrid.EditorLock.activate: an editCont
if(!editController.commitCurrentEdit){throw"SlickGrid.EditorLock.activate: editController must implement .commitCurrentEdit()";}
if(!editController.cancelCurrentEdit){throw"SlickGrid.EditorLock.activate: editController must implement .cancelCurrentEdit()";}
activeEditController=editController;};this.deactivate=function(editController){if(activeEditController!==editController){throw"SlickGrid.EditorLock.deactivate: specified editController is not the currently active one";}
activeEditController=null;};this.commitCurrentEdit=function(){return(activeEditController?activeEditController.commitCurrentEdit():true);};this.cancelCurrentEdit=function cancelCurrentEdit(){return(activeEditController?activeEditController.cancelCurrentEdit():true);};}})(jQuery);(function($){$.extend(true,window,{"Slick":{"Formatters":{"PercentComplete":PercentCompleteFormatter,"PercentCompleteBar":PercentCompleteBarFormatter,"YesNo":YesNoFormatter,"Checkmark":CheckmarkFormatter,"Timestamp":TimestampFormatter,"Units":Units,"Dollar":Dollar,"Quota":Quota}}});function PercentCompleteFormatter(row,cell,value,columnDef,dataContext){if(value==null||value===""){return"-";}else if(value<50){return"<span style='color:red;font-weight:bold;'>"+value+"%</span>";}else{return"<span style='color:green'>"+value+"%</span>";}}
activeEditController=null;};this.commitCurrentEdit=function(){return(activeEditController?activeEditController.commitCurrentEdit():true);};this.cancelCurrentEdit=function cancelCurrentEdit(){return(activeEditController?activeEditController.cancelCurrentEdit():true);};}})(jQuery);(function($){$.extend(true,window,{"Slick":{"Formatters":{"PercentComplete":PercentCompleteFormatter,"PercentCompleteBar":PercentCompleteBarFormatter,"YesNo":YesNoFormatter,"Checkmark":CheckmarkFormatter,"Timestamp":TimestampFormatter,"Units":Units,"Dollar":Dollar,"Quota":Quota,"Fullname":Fullname}}});function PercentCompleteFormatter(row,cell,value,columnDef,dataContext){if(value==null||value===""){return"-";}else if(value<50){return"<span style='color:red;font-weight:bold;'>"+value+"%</span>";}else{return"<span style='color:green'>"+value+"%</span>";}}
function PercentCompleteBarFormatter(row,cell,value,columnDef,dataContext){if(value==null||value===""){return"";}
var color;if(value<30){color="red";}else if(value<70){color="silver";}else{color="green";}
return"<span class='percent-complete-bar' style='background:"+color+";width:"+value+"%'></span>";}
......@@ -100,7 +100,8 @@ var percent=fraction/size*100;var color;if(percent<30){color='red';}
else if(percent<60){color='orange';}
else{color='green';}
return'<span class="report-quota-wrapper">'+'<span class="report-quota-bar" style="background-color:'+color+';'+'width:'+percent+'%"></span></span> '+'('+fraction+'/'+size+
unit+' in box <b>'+dataContext.count+'</b>)';}})(jQuery);(function($){$.extend(true,window,{"Slick":{"Editors":{"Text":TextEditor,"Number":NumberEditor,"Date":DateEditor,"YesNoSelect":YesNoSelectEditor,"Checkbox":CheckboxEditor,"PercentComplete":PercentCompleteEditor,"LongText":LongTextEditor}}});function TextEditor(args){var $input;var defaultValue;var scope=this;this.init=function(){$input=$("<INPUT type=text class='editor-text' />").appendTo(args.container).bind("keydown.nav",function(e){if(e.keyCode===$.ui.keyCode.LEFT||e.keyCode===$.ui.keyCode.RIGHT){e.stopImmediatePropagation();}}).focus().select();};this.destroy=function(){$input.remove();};this.focus=function(){$input.focus();};this.getValue=function(){return $input.val();};this.setValue=function(val){$input.val(val);};this.loadValue=function(item){defaultValue=item[args.column.field]||"";$input.val(defaultValue);$input[0].defaultValue=defaultValue;$input.select();};this.serializeValue=function(){return $input.val();};this.applyValue=function(item,state){item[args.column.field]=state;};this.isValueChanged=function(){return(!($input.val()==""&&defaultValue==null))&&($input.val()!=defaultValue);};this.validate=function(){if(args.column.validator){var validationResults=args.column.validator($input.val());if(!validationResults.valid){return validationResults;}}
unit+' in box <b>'+dataContext.count+'</b>)';}
function Fullname(row,cell,value,columnDef,dataContext){return dataContext.first+' '+dataContext.last;}})(jQuery);(function($){$.extend(true,window,{"Slick":{"Editors":{"Text":TextEditor,"Number":NumberEditor,"Date":DateEditor,"YesNoSelect":YesNoSelectEditor,"Checkbox":CheckboxEditor,"PercentComplete":PercentCompleteEditor,"LongText":LongTextEditor}}});function TextEditor(args){var $input;var defaultValue;var scope=this;this.init=function(){$input=$("<INPUT type=text class='editor-text' />").appendTo(args.container).bind("keydown.nav",function(e){if(e.keyCode===$.ui.keyCode.LEFT||e.keyCode===$.ui.keyCode.RIGHT){e.stopImmediatePropagation();}}).focus().select();};this.destroy=function(){$input.remove();};this.focus=function(){$input.focus();};this.getValue=function(){return $input.val();};this.setValue=function(val){$input.val(val);};this.loadValue=function(item){defaultValue=item[args.column.field]||"";$input.val(defaultValue);$input[0].defaultValue=defaultValue;$input.select();};this.serializeValue=function(){return $input.val();};this.applyValue=function(item,state){item[args.column.field]=state;};this.isValueChanged=function(){return(!($input.val()==""&&defaultValue==null))&&($input.val()!=defaultValue);};this.validate=function(){if(args.column.validator){var validationResults=args.column.validator($input.val());if(!validationResults.valid){return validationResults;}}
return{valid:true,msg:null};};this.init();}
function NumberEditor(args){var $input;var defaultValue;var scope=this;this.init=function(){$input=$("<INPUT type=text class='editor-text' />");$input.bind("keydown.nav",function(e){if(e.keyCode===$.ui.keyCode.LEFT||e.keyCode===$.ui.keyCode.RIGHT){e.stopImmediatePropagation();}});$input.appendTo(args.container);$input.focus().select();};this.destroy=function(){$input.remove();};this.focus=function(){$input.focus();};this.loadValue=function(item){defaultValue=item[args.column.field];$input.val(defaultValue);$input[0].defaultValue=defaultValue;$input.select();};this.serializeValue=function(){return parseFloat($input.val())||0;};this.applyValue=function(item,state){item[args.column.field]=state;};this.isValueChanged=function(){return(!($input.val()==""&&defaultValue==null))&&($input.val()!=defaultValue);};this.validate=function(){if(parseFloat($input.val())<0){return{valid:false,msg:"Please enter a positive number"};}
return{valid:true,msg:null};};this.init();}
......
......@@ -2,7 +2,7 @@
// @licstart The following is the entire license notice
// for the JavaScript code in this page.
//
// Copyright (C) 2014 Malcolm Blaney
// Copyright (C) 2015 Malcolm Blaney
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
......@@ -898,7 +898,8 @@ $special.draginit = $special.dragstart = $special.dragend = drag;
"Timestamp": TimestampFormatter,
"Units" : Units,
"Dollar" : Dollar,
"Quota" : Quota
"Quota" : Quota,
"Fullname" : Fullname
}
}
});
......@@ -981,6 +982,10 @@ $special.draginit = $special.dragstart = $special.dragend = drag;
'width:' + percent + '%"></span></span> ' + '('+ fraction + '/' + size +
unit + ' in box <b>' + dataContext.count + '</b>)';
}
function Fullname(row, cell, value, columnDef, dataContext) {
return dataContext.first + ' ' + dataContext.last;
}
})(jQuery);
// File: slick.editors.js ///////////////////////////////////////////////////
......
......@@ -74,8 +74,8 @@ else{purchase.data[user]=[];}
$(".warning-level").hide();$(".info-level").hide();if(!viewPurchases){viewPurchases=true;$("#purchase-form .view-all").button("option","label","View all");$(".purchase-all-available").hide();$(".purchase-all-users").hide();$(allAvailableGridId).hide();$(allUserGridId).hide();}
if($.inArray(user,purchase.nextWeek)===-1){$("#purchase-next-week-input").prop("checked",false);}
else{$("#purchase-next-week-input").prop("checked",true);}
var total=0;$.each(purchase.data[user],function(i,item){if(item.date<tomorrow){total+=parseFloat(item.total);}});$("#purchase-form .total").html(total.toFixed(2));$("#purchase-form .order").show();if($.inArray(user,purchase.roster)===-1){$(".roster-reminder").hide();}
else{$(".roster-reminder").show();}
var total=0;$.each(purchase.data[user],function(i,item){if(item.date<tomorrow){total+=parseFloat(item.total);}});$("#purchase-form .total").html(total.toFixed(2));$("#purchase-form .order").show();if($.inArray(user,purchase.roster)===-1){$(".roster-reminder").hide();$(".roster-volunteer").show();}
else{$(".roster-reminder").show();$(".roster-volunteer").hide();}
var details=purchase.details[user];if(details&&details.first&&details.last){$("#purchase-form .order .name").html(details.first+" "+details.last);}
else{$("#purchase-form .order .name").html(user);$(".purchase-details-user").html(user);$("#purchase-details-first-input").val("");$("#purchase-details-last-input").val("");$("#purchase-details-phone-input").val("");$("#purchase-details-info").html("");if(details){if(details.first){$("#purchase-details-first-input").val(dobrado.decode(details.first));}
if(details.last){$("#purchase-details-last-input").val(dobrado.decode(details.last));}
......@@ -179,7 +179,7 @@ if($.inArray(user,purchase.processed)===-1){purchase.processed.push(user);}
if(dobrado.localStorage()){localStorage.purchase=JSON.stringify(purchase);}
$(".purchase .save").button("option","disabled",false);}
function save(){dobrado.log("Saving...","info");$.post("/php/request.php",{request:"purchase",data:JSON.stringify(purchase.data),nextWeek:JSON.stringify(purchase.nextWeek),action:"save",timestamp:purchase.date,url:location.href,token:dobrado.token},function(response){if(dobrado.checkResponseError(response,"purchase save request")){return;}
$(".purchase .message .stored").html("");$(".purchase .save").button("option","disabled",true);$(".warning-level").hide();$(".info-level").hide();$(".roster-reminder").hide();$("#purchase-form .order").hide();$(purchaseGridId).hide();$("#purchase-name-input").val("");resetForm();var save=JSON.parse(response);if(save.order){$(".purchase-save-order-confirm").dialog({modal:true,width:400,position:{my:"top",at:"top+50",of:"body"},title:"Order Saved"});}
$(".purchase .message .stored").html("");$(".purchase .save").button("option","disabled",true);$(".warning-level").hide();$(".info-level").hide();$(".roster-reminder").hide();$(".roster-volunteer").hide();$("#purchase-form .order").hide();$(purchaseGridId).hide();$("#purchase-name-input").val("");resetForm();var save=JSON.parse(response);if(save.order){$(".purchase-save-order-confirm").dialog({modal:true,width:400,position:{my:"top",at:"top+50",of:"body"},title:"Order Saved"});}
$.each(purchase.processed,function(i,user){if(user in purchase.data){$.each(purchase.data[user],function(i,item){item.server=true;});}});purchase.processed=[];if(dobrado.localStorage()){localStorage.purchase=JSON.stringify(purchase);}
if(purchase.date<stockUpdate||purchase.date<yesterday||save.order){loadProducts();}});return false;}
function saveDetails(){var user=$("#purchase-name-input").val();if(user===""){return;}
......
......@@ -559,9 +559,11 @@ if (!this.dobrado.purchase) {
// Check the list of users who need reminding to volunteer.
if ($.inArray(user, purchase.roster) === -1) {
$(".roster-reminder").hide();
$(".roster-volunteer").show();
}
else {
$(".roster-reminder").show();
$(".roster-volunteer").hide();
}
// Make sure the user's details are filled out or ask them to do it.
var details = purchase.details[user];
......@@ -1185,6 +1187,7 @@ if (!this.dobrado.purchase) {
$(".warning-level").hide();
$(".info-level").hide();
$(".roster-reminder").hide();
$(".roster-volunteer").hide();
$("#purchase-form .order").hide();
$(purchaseGridId).hide();
$("#purchase-name-input").val("");
......
This diff is collapsed.
This diff is collapsed.
......@@ -26,7 +26,7 @@ function session_expired() {
if (!isset($_POST["token"])) return true;
return $_SESSION["token"] != $_POST["token"];
return $_SESSION["token"] !== $_POST["token"];
}
?>
\ No newline at end of file
......@@ -471,6 +471,7 @@ $site_style = array('"","body","margin","0"',
'"","img","max-width","100%"',
'"","img","height","auto"',
'"","img","border-style","none"',
'"","input[type=text]","height","25px"',
'"","label","float","left"',
'"","label","margin-right","0.3em"',
'"","label","text-align","right"',
......
......@@ -17,9 +17,9 @@
class User {
public $name = "";
public $group = "";
public $page = "";
public $name = '';
public $group = '';
public $page = '';
public $config = NULL;
public $active = true;
public $loggedIn = false;
......@@ -31,15 +31,15 @@ class User {
public $canCopyPage = false;
public $canViewPage = false;
private $password = "";
private $password = '';
public function __construct($user = "", $group = "") {
public function __construct($user = '', $group = '') {
$this->config = new Config();
if ($user !== "") {
if ($user !== '') {
$this->name = $user;
$this->config->SetUser($user);
if ($group === "") {
if ($group === '') {
$this->SetGroup();
}
else {
......@@ -49,23 +49,23 @@ class User {
}
// Check if a (possibly different) user is trying to log in.
if (isset($_POST["user"])) {
if (isset($_POST['user'])) {
$mysqli = connect_db();
$this->name = strtolower(trim($mysqli->escape_string($_POST["user"])));
$this->name = strtolower(trim($mysqli->escape_string($_POST['user'])));
if ($this->name == "guest" && $this->config->GuestAllowed()) {
if ($this->name == 'guest' && $this->config->GuestAllowed()) {
$this->Guest();
$mysqli->close();
return;
}
if (isset($_POST["password"])) {
$this->password = trim($mysqli->escape_string($_POST["password"]));
if (isset($_POST['password'])) {
$this->password = trim($mysqli->escape_string($_POST['password']));
}
$mysqli->close();
// Check if the user is requesting a password reset.
if (isset($_POST["email"])) {