Commit 3dc7cf27 authored by Malcolm Blaney's avatar Malcolm Blaney

Add support for Micropub Media Endpoint to Browser module, and

Micropub query config support to auth.php. Improved parse_hcard
in SimplePie Parser to also cache photos in h-feeds, and
parse_hcard helper function in microformats.php will now add photos
from reposts to the nickname cache. Invoice and Purchase modules
now operate from the assumption that dates for a given day can have
timestamps anywhere between 00:00:00 and 23:59:59 on the day given.
Fixed a bug in Purchase module that was using a 12 hour window to
check purchases rather than following the new rule for timestamps.
Stock module bug made items that were available to purchase
unavailable from the supplier, which meant they no longer showed up
by default on the stock page. Other changes are small UI improvements.
parent 50633ad8
Pipeline #36803455 passed with stage
in 1 minute and 24 seconds
...@@ -78,9 +78,9 @@ class Browser extends Base { ...@@ -78,9 +78,9 @@ class Browser extends Base {
continue; continue;
} }
if (preg_match('/^(.+)\.(.+)$/', $filename, $matches)) { if (preg_match('/^(.+)\.(.+)$/', $filename, $match)) {
$name = $matches[1]; $name = $match[1];
$type = $matches[2]; $type = $match[2];
$thumbnail = ''; $thumbnail = '';
$updated = ''; $updated = '';
if (in_array(strtolower($type), ['gif', 'jpeg', 'jpg', 'png'])) { if (in_array(strtolower($type), ['gif', 'jpeg', 'jpg', 'png'])) {
...@@ -178,9 +178,9 @@ class Browser extends Base { ...@@ -178,9 +178,9 @@ class Browser extends Base {
private function RemoveFile() { private function RemoveFile() {
$regex = '/^([a-z0-9_-]{1,200})\.([a-z0-9]{1,10})/i'; $regex = '/^([a-z0-9_-]{1,200})\.([a-z0-9]{1,10})/i';
$us_file = basename($_POST['file']); $us_file = basename($_POST['file']);
if (preg_match($regex, $us_file, $matches)) { if (preg_match($regex, $us_file, $match)) {
$name = $matches[1]; $name = $match[1];
$type = $matches[2]; $type = $match[2];
unlink($this->PublicDirectory($name . '.' . $type)); unlink($this->PublicDirectory($name . '.' . $type));
if (in_array(strtolower($type), ['gif', 'jpeg', 'jpg', 'png'])) { if (in_array(strtolower($type), ['gif', 'jpeg', 'jpg', 'png'])) {
unlink($this->PublicDirectory($name . '_thumb.' . $type)); unlink($this->PublicDirectory($name . '_thumb.' . $type));
...@@ -195,10 +195,10 @@ class Browser extends Base { ...@@ -195,10 +195,10 @@ class Browser extends Base {
$handle = popen('/usr/bin/du -sm ' . $this->PublicDirectory(), 'r'); $handle = popen('/usr/bin/du -sm ' . $this->PublicDirectory(), 'r');
$size = fgets($handle); $size = fgets($handle);
pclose($handle); pclose($handle);
if (!preg_match('/^([0-9]+)/', $size, $matches)) { if (!preg_match('/^([0-9]+)/', $size, $match)) {
return ['error' => 'Could not check upload directory']; return ['error' => 'Could not check upload directory'];
} }
$size = $matches[1]; $size = $match[1];
if ((int)$size > $this->user->config->MaxUpload()) { if ((int)$size > $this->user->config->MaxUpload()) {
return ['error' => 'Upload directory full.']; return ['error' => 'Upload directory full.'];
} }
...@@ -211,18 +211,18 @@ class Browser extends Base { ...@@ -211,18 +211,18 @@ class Browser extends Base {
// Replace spaces in the uploaded file name. // Replace spaces in the uploaded file name.
$filename = preg_replace('/ /', '_', basename($_FILES['upload']['name'])); $filename = preg_replace('/ /', '_', basename($_FILES['upload']['name']));
$regex = '/^([a-z0-9_-]{1,200})\.([a-z0-9]{1,10})$/i'; $regex = '/^([a-z0-9_-]{1,200})\.([a-z0-9]{1,10})$/i';
if (!preg_match($regex, $filename, $matches)) { if (!preg_match($regex, $filename, $match)) {
return ['error' => 'Filename does not have correct format.']; return ['error' => 'Filename does not have correct format.'];
} }
$path = $this->PublicDirectory($filename); $path = $this->PublicDirectory($filename);
if (file_exists($path)) { if (file_exists($path)) {
return ['error' => 'A file with that name already exists.']; return ['error' => 'A file with that name already exists.'];
} }
$name = $matches[1]; $name = $match[1];
$type = $matches[2]; $type = $match[2];
// Whitelist all file extensions. // Whitelist all file extensions.
$allowed = ['gif', 'jpeg', 'jpg', 'png', 'ogg', 'mp3', 'ogv', 'webm', $allowed = ['gif', 'jpeg', 'jpg', 'png', 'aac', 'mpeg', 'oga', 'ogg', 'wav',
'mp4', 'pdf']; 'mp3', 'ogv', 'webm', 'mp4', 'mov', 'pdf'];
if (!in_array(strtolower($type), $allowed)) { if (!in_array(strtolower($type), $allowed)) {
return ['error' => 'File type not allowed.']; return ['error' => 'File type not allowed.'];
} }
...@@ -230,7 +230,14 @@ class Browser extends Base { ...@@ -230,7 +230,14 @@ class Browser extends Base {
if (!move_uploaded_file($tmp, $path)) { if (!move_uploaded_file($tmp, $path)) {
return ['error' => 'File: ' . $filename . ' was not uploaded.']; return ['error' => 'File: ' . $filename . ' was not uploaded.'];
} }
// If this user has a media endpoint configured transfer the file to it.
if (isset($this->user->settings['micropub']['config'])) {
$config = json_decode($this->user->settings['micropub']['config'], true);
if (isset($config['media-endpont'])) {
return $this->TransferMedia($path, $config['media-endpont']);
}
}
$name .= '_thumb.' . $type; $name .= '_thumb.' . $type;
$html_path = $this->user->name === 'admin' ? '/public/' : $html_path = $this->user->name === 'admin' ? '/public/' :
'/' . $this->user->name . '/public/'; '/' . $this->user->name . '/public/';
...@@ -277,16 +284,84 @@ class Browser extends Base { ...@@ -277,16 +284,84 @@ class Browser extends Base {
$this->ImageContent($thumbnail, $html_path . $filename)]; $this->ImageContent($thumbnail, $html_path . $filename)];
} }
private function ImageContent($thumbnail, $filename) { private function ImageContent($thumbnail, $filename, $local = true) {
// Display rotate and remove buttons on local thumbnails only.
$buttons = $local ?
'<button class="rotate-left hidden">rotate left</button>' .
'<button class="rotate-right hidden">rotate right</button>' .
'<button class="remove hidden">remove</button>' : '';
return '<span class="thumbnail hidden">' . $thumbnail . return '<span class="thumbnail hidden">' . $thumbnail .
'<span class="filename hidden">' . $filename . '</span><br>' . '<span class="filename hidden">' . $filename . '</span><br>' .
'<button class="select hidden">select</button>' . '<button class="select hidden">select</button>' . $buttons .
'<button class="rotate-left hidden">rotate left</button>' .
'<button class="rotate-right hidden">rotate right</button>' .
'<button class="remove hidden">remove</button>' .
'</span>'; '</span>';
} }
private function TransferMedia($path, $endpoint) {
$error = '';
$location = '';
$uid = uniqid();
$mime_type = strtolower(mime_content_type($path));
$curl_headers = ['Authorization: Bearer ' .
$this->user->settings['micropub']['token'],
'Content-Type: multipart/form-data; boundary=' . $uid];
$post_fields = '--' . $uid . "\r\n" .
'Content-Type: ' . $mime_type . "\r\n" .
"Content-Transfer-Encoding: binary\r\n" .
'Content-Disposition: form-data; name="file"; ' .
'filename="' . basename($path) . "\r\n\r\n" .
file_get_contents($path) . "\r\n" .
'--' . $uid . '--';
$ch = curl_init($endpoint);
curl_setopt($ch, CURLOPT_HTTPHEADER, $curl_headers);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 20);
curl_setopt($ch, CURLOPT_ENCODING, '');
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields);
curl_setopt($ch, CURLOPT_HEADERFUNCTION,
function($ch, $header) use(&$location) {
if (preg_match('/^Location:(.+)$/', $header, $match)) {
$location = $match[1];
}
return strlen($header);
});
$body = curl_exec($ch);
if (curl_errno($ch) === 0) {
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($http_code !== 201) {
$error = 'Error saving to media endpoint.';
$this->Log('Browser->TransferMedia 1: Error posting to ' . $endpoint .
"\nHTTP code: " . $http_code . "\nBody: " . $body);
}
}
else {
$error = 'Error connection to media endpoint.';
$this->Log('Browser->TransferMedia 2: Error connecting to ' . $endpoint .
"\nCurl error: " . curl_error($ch));
}
curl_close($ch);
unlink($path);
if ($error !== '') return ['error' => $error];
if ($location === '') {
return ['error' => 'Media endpoint did not return location.'];
}
if (strpos($location, 'http') !== 0) {
return ['error' => 'Media endpoint did not return a URL.'];
}
$thumbnail = '';
if (in_array($mime_type, ['image/gif', 'image/jpeg', 'image/png'])) {
$thumbnail = '<img title="' . $location . '" src="' . $location . '">';
}
else {
$thumbnail = '<span title="' . $location . '">' . $mime_type . '</span>';
}
return ['content' => $this->ImageContent($thumbnail, $location, false)];
}
private function ResizeImage($new_width, $new_height, private function ResizeImage($new_width, $new_height,
$old_width, $old_height, $old_width, $old_height,
$type, $new_path, $old_path = '') { $type, $new_path, $old_path = '') {
...@@ -332,9 +407,9 @@ class Browser extends Base { ...@@ -332,9 +407,9 @@ class Browser extends Base {
if ($_POST['direction'] === 'right') $angle = 270; if ($_POST['direction'] === 'right') $angle = 270;
$file = basename($_POST['file']); $file = basename($_POST['file']);
$regex = '/^([a-z0-9_-]{1,200})\.([a-z0-9]{1,10})/i'; $regex = '/^([a-z0-9_-]{1,200})\.([a-z0-9]{1,10})/i';
if (preg_match($regex, $file, $matches)) { if (preg_match($regex, $file, $match)) {
$name = $matches[1]; $name = $match[1];
$type = $matches[2]; $type = $match[2];
if (!in_array(strtolower($type), ['gif', 'jpeg', 'jpg', 'png'])) { if (!in_array(strtolower($type), ['gif', 'jpeg', 'jpg', 'png'])) {
return ['error' => 'Format not supported.']; return ['error' => 'Format not supported.'];
} }
......
...@@ -80,6 +80,7 @@ class Grid extends Base { ...@@ -80,6 +80,7 @@ class Grid extends Base {
'"",".slick-headerrow-column.ui-state-default","padding",' . '"",".slick-headerrow-column.ui-state-default","padding",' .
'"1px"', '"1px"',
'"",".slick-cell.selected","background-color","#fdffaf"', '"",".slick-cell.selected","background-color","#fdffaf"',
'"",".slick-cell > input[type=text]","padding","0"',
'"",".slick-cell > input[type=text]","border","none"', '"",".slick-cell > input[type=text]","border","none"',
'"",".slick-cell > input[type=text]","width","100%"', '"",".slick-cell > input[type=text]","width","100%"',
'"",".slick-cell > input[type=text]","height","100%"', '"",".slick-cell > input[type=text]","height","100%"',
......
...@@ -707,6 +707,8 @@ class Groupwizard extends Base { ...@@ -707,6 +707,8 @@ class Groupwizard extends Base {
'"", ".groupwizard textarea", "height", "100px"', '"", ".groupwizard textarea", "height", "100px"',
'"", "label[for=groupwizard-invoice-subject]", ' . '"", "label[for=groupwizard-invoice-subject]", ' .
'"width", "16em"', '"width", "16em"',
'"", "label[for=groupwizard-invoice-no-purchase-subject]", '.
'"width", "16em"',
'"", "label[for=groupwizard-invoice-sender]", ' . '"", "label[for=groupwizard-invoice-sender]", ' .
'"width", "16em"', '"width", "16em"',
'"", "label[for=groupwizard-invoice-sender-name]", ' . '"", "label[for=groupwizard-invoice-sender-name]", ' .
......
...@@ -728,12 +728,12 @@ class Invoice extends Base { ...@@ -728,12 +728,12 @@ class Invoice extends Base {
else if ($invoice_day_count === 1) { else if ($invoice_day_count === 1) {
// Special case if only one day is specified, set the start time to the // Special case if only one day is specified, set the start time to the
// start of today. // start of today.
$start = strtotime('0:00'); $start = strtotime('00:00:00');
$end = time(); $end = strtotime('23:59:59');
} }
else { else {
$start = strtotime('-' . $invoice_day_count . ' days'); $start = strtotime('-' . $invoice_day_count . ' days');
$end = time(); $end = strtotime('23:59:59');
} }
// TODO: Check if tax should be shown for the group and call AllTaxable. // TODO: Check if tax should be shown for the group and call AllTaxable.
$purchase_totals = $purchase->AllTotals($start, $end, $organisation); $purchase_totals = $purchase->AllTotals($start, $end, $organisation);
...@@ -924,12 +924,12 @@ class Invoice extends Base { ...@@ -924,12 +924,12 @@ class Invoice extends Base {
else if ($invoice_day_count === 1) { else if ($invoice_day_count === 1) {
// Special case if only one day is specified, set the start time to the // Special case if only one day is specified, set the start time to the
// start of today. // start of today.
$start = strtotime('0:00'); $start = strtotime('00:00:00');
$end = $invoice_date; $end = strtotime('23:59:59');
} }
else { else {
$start = strtotime('-' . $invoice_day_count . ' days'); $start = strtotime('-' . $invoice_day_count . ' days');
$end = $invoice_date; $end = strtotime('23:59:59');
} }
$message = $this->FormatInvoice($user, $first, $last, $invoice_number, $message = $this->FormatInvoice($user, $first, $last, $invoice_number,
$reference, $start, $end, $purchase_value, $reference, $start, $end, $purchase_value,
...@@ -1353,8 +1353,9 @@ class Invoice extends Base { ...@@ -1353,8 +1353,9 @@ class Invoice extends Base {
$xero->Factory('SendInvoice', $xml); $xero->Factory('SendInvoice', $xml);
} }
// Next display user info via substitutions for the group. // Next display user info via substitutions for the group.
$message .= $this->FormatInvoiceAfterPurchase($user, $end, $reference, $message .=
$invoice_number, $surcharge); $this->FormatInvoiceAfterPurchase($user, $invoice_date, $reference,
$invoice_number, $surcharge);
// Next pre order information is displayed, if in use. // Next pre order information is displayed, if in use.
if ($pre_order_available) { if ($pre_order_available) {
if ($purchase_value < 0.01) { if ($purchase_value < 0.01) {
...@@ -1969,8 +1970,8 @@ class Invoice extends Base { ...@@ -1969,8 +1970,8 @@ class Invoice extends Base {
$username = $mysqli->escape_string($_POST['selectedUser']); $username = $mysqli->escape_string($_POST['selectedUser']);
$invoice_date = $mysqli->escape_string($_POST['invoiceDate']); $invoice_date = $mysqli->escape_string($_POST['invoiceDate']);
$invoice_number = 0; $invoice_number = 0;
$start = strtotime($invoice_date . ' 0:00'); $start = strtotime($invoice_date . ' 00:00:00');
$end = strtotime($invoice_date . ' 24:00'); $end = strtotime($invoice_date . ' 23:59:59');
$query = 'SELECT invoice_number FROM invoice WHERE ' . $query = 'SELECT invoice_number FROM invoice WHERE ' .
'user = "' . $username . '" AND timestamp >= ' . $start . ' AND ' . 'user = "' . $username . '" AND timestamp >= ' . $start . ' AND ' .
'timestamp <= ' . $end; 'timestamp <= ' . $end;
......
...@@ -2075,7 +2075,7 @@ class Purchase extends Base { ...@@ -2075,7 +2075,7 @@ class Purchase extends Base {
$us_data = json_decode($_POST['data'], true); $us_data = json_decode($_POST['data'], true);
$us_user_index = json_decode($_POST['userIndex'], true); $us_user_index = json_decode($_POST['userIndex'], true);
$us_new_sales = json_decode($_POST['newSales'], true); $us_new_sales = json_decode($_POST['newSales'], true);
$start = strtotime(date('F j Y 00:00:00')); $start = strtotime('00:00:00');
$end = $start + 86399; $end = $start + 86399;
$current = $this->AllData($start, $end, false, false, false); $current = $this->AllData($start, $end, false, false, false);
$payment = new Payment($this->user, $this->owner); $payment = new Payment($this->user, $this->owner);
...@@ -2671,7 +2671,7 @@ class Purchase extends Base { ...@@ -2671,7 +2671,7 @@ class Purchase extends Base {
$object['products'] = $stock->AvailableProducts(); $object['products'] = $stock->AvailableProducts();
// Otherwise in normal purchasing mode, only show purchases from today's // Otherwise in normal purchasing mode, only show purchases from today's
// date onwards, as only these can be changed in UpdateData. // date onwards, as only these can be changed in UpdateData.
$start = strtotime(date('F j Y 00:00:00')); $start = strtotime('00:00:00');
$end = 0; $end = 0;
// An end timestamp is required when pre-order is true, otherwise // An end timestamp is required when pre-order is true, otherwise
// orders for next week will be shown when purchasing this week. // orders for next week will be shown when purchasing this week.
...@@ -2884,6 +2884,7 @@ class Purchase extends Base { ...@@ -2884,6 +2884,7 @@ class Purchase extends Base {
$stock = new Stock($this->user, $this->owner); $stock = new Stock($this->user, $this->owner);
$mysqli = connect_db(); $mysqli = connect_db();
$products = []; $products = [];
$elapsed = time() - strtotime('00:00:00');
$tomorrow = time() + 86400; $tomorrow = time() + 86400;
$values = ''; $values = '';
// Create a list of items that need updating. // Create a list of items that need updating.
...@@ -2923,8 +2924,8 @@ class Purchase extends Base { ...@@ -2923,8 +2924,8 @@ class Purchase extends Base {
$price . ', ' . $base_price . ', "' . $this->user->group . '", ' . $price . ', ' . $base_price . ', "' . $this->user->group . '", ' .
'"' . $this->user->name . '")'; '"' . $this->user->name . '")';
// Delete the old entry if it has a timestamp in the last 12 hours. // Delete the old entry if it has a timestamp from today.
if ($new_date > $old_date && $new_date - $old_date < 43200) { if ($new_date > $old_date && $new_date - $old_date < $elapsed) {
// Find the old quantity for this purchase to increase stock. // Find the old quantity for this purchase to increase stock.
// Need to look at a timestamp range due to rounding by 1ms. // Need to look at a timestamp range due to rounding by 1ms.
$query = 'SELECT quantity, timestamp FROM purchase WHERE ' . $query = 'SELECT quantity, timestamp FROM purchase WHERE ' .
......
...@@ -608,10 +608,14 @@ class Reader extends Base { ...@@ -608,10 +608,14 @@ class Reader extends Base {
$checked = $i === 0 ? ' checked="checked"' : ''; $checked = $i === 0 ? ' checked="checked"' : '';
$discovered_id = 'reader-discovered-feed-' . $i; $discovered_id = 'reader-discovered-feed-' . $i;
$discovered_url = $all_feeds[$i]->url; $discovered_url = $all_feeds[$i]->url;
$short_url = strlen($discovered_url) <= 50 ?
$discovered_url : substr($discovered_url, 0, 47) . '...';
$discovered_link = '<a href="' . $discovered_url . '">' . $short_url .
'</a>';
$discovered_title = $this->FeedTitle($all_feeds[$i]->body); $discovered_title = $this->FeedTitle($all_feeds[$i]->body);
$display = $discovered_title === '' ? $discovered_url : $display = $discovered_title === '' ? $discovered_link :
$discovered_title . ' <span class="reader-discovered-feed-url">[' . $discovered_title . ' <span class="reader-discovered-feed-url">[' .
$discovered_url . ']</span>'; $discovered_link . ']</span>';
$discovered .= '<div class="form-spacing">' . $discovered .= '<div class="form-spacing">' .
'<input type="checkbox" id="' . $discovered_id . '"' . '<input type="checkbox" id="' . $discovered_id . '"' .
'value="' . $discovered_url . '"' . $checked . '>' . 'value="' . $discovered_url . '"' . $checked . '>' .
......
...@@ -208,12 +208,12 @@ class Stock extends Base { ...@@ -208,12 +208,12 @@ class Stock extends Base {
$cart_column = ''; $cart_column = '';
if ($this->Substitute('stock-cart') === 'true') { if ($this->Substitute('stock-cart') === 'true') {
$cart = '<div class="form-spacing hidden">' . $cart = '<div class="form-spacing hidden">' .
'<label for="stock-cart-input">Copy to Cart:</label>' . '<label for="stock-cart-input">Sync to Cart:</label>' .
'<input id="stock-cart-input" type="checkbox">' . '<input id="stock-cart-input" type="checkbox">' .
'</div>'; '</div>';
$cart_column = '<input type="checkbox" id="stock-column-cart">' . $cart_column = '<input type="checkbox" id="stock-column-cart">' .
'<label for="stock-column-cart">Cart Status</label>'; '<label for="stock-column-cart">Sync to Cart</label>';
$checkbox_options .= '<option value="cart">Copy to Cart</option>'; $checkbox_options .= '<option value="cart">Sync to Cart</option>';
} }
$checkbox_options .= '<option value="hidden">Hidden</option>'; $checkbox_options .= '<option value="hidden">Hidden</option>';
...@@ -397,7 +397,7 @@ class Stock extends Base { ...@@ -397,7 +397,7 @@ class Stock extends Base {
$this->user = new User($this->owner, $group, $timezone); $this->user = new User($this->owner, $group, $timezone);
$category_query = ''; $category_query = '';
// Allow the update to be done for only a limited list of categories. // Allow the update to be done for only a limited list of categories.
// Note that templates are html encoded so need to decode here. // Note that templates are html encoded so decode them here.
$us_categories = $this->Substitute('stock-update-categories'); $us_categories = $this->Substitute('stock-update-categories');
if ($us_categories !== '') { if ($us_categories !== '') {
$us_category_list = $us_category_list =
...@@ -2269,7 +2269,7 @@ class Stock extends Base { ...@@ -2269,7 +2269,7 @@ class Stock extends Base {
$available_query = ''; $available_query = '';
$purchase = new Purchase($this->user, $this->owner); $purchase = new Purchase($this->user, $this->owner);
// Check if the user is choosing to change the product name. All previous // Check if the user is choosing to change the product name. All previous
// purchases also need to be updated in this case. // purchases are also updated in this case.
if ($field === 'name' && $product !== $value) { if ($field === 'name' && $product !== $value) {
$us_name = trim($_POST['value']); $us_name = trim($_POST['value']);
if ($value === '') { if ($value === '') {
...@@ -2503,7 +2503,7 @@ class Stock extends Base { ...@@ -2503,7 +2503,7 @@ class Stock extends Base {
} }
} }
$mysqli->close(); $mysqli->close();
// If the last group was removed, need to revert back to all groups. // If the last group was removed, revert back to all groups.
$this->CheckSupplyGroup($user); $this->CheckSupplyGroup($user);
} }
...@@ -2848,6 +2848,13 @@ class Stock extends Base { ...@@ -2848,6 +2848,13 @@ class Stock extends Base {
if (!$mysqli->query($query)) { if (!$mysqli->query($query)) {
$this->Log('Stock->ImportData 11: ' . $mysqli->error); $this->Log('Stock->ImportData 11: ' . $mysqli->error);
} }
// Make sure that items currently available to purchase aren't made
// unavailable from the supplier.
$query = 'UPDATE stock SET supplier_available = 1 WHERE ' .
'purchase_available = 1 AND user = "' . $user . '"';
if (!$mysqli->query($query)) {
$this->Log('Stock->ImportData 12: ' . $mysqli->error);
}
} }
$mysqli->close(); $mysqli->close();
$result = $this->AllData($new_available); $result = $this->AllData($new_available);
......
...@@ -59,8 +59,8 @@ return'<img src="'+value+'" style="height:20px;width:auto;">';} ...@@ -59,8 +59,8 @@ return'<img src="'+value+'" style="height:20px;width:auto;">';}
function filter(item,args){for(var columnId in args.columnFilters){if(columnId&&args.columnFilters[columnId]!==''){var column=args.columns[args.index(columnId)];var input=args.columnFilters[columnId].toLowerCase();var field=item[column.field].toString().toLowerCase();if(input.indexOf('*')===0){if(field.indexOf(input.substr(1))===-1){return false;}} function filter(item,args){for(var columnId in args.columnFilters){if(columnId&&args.columnFilters[columnId]!==''){var column=args.columns[args.index(columnId)];var input=args.columnFilters[columnId].toLowerCase();var field=item[column.field].toString().toLowerCase();if(input.indexOf('*')===0){if(field.indexOf(input.substr(1))===-1){return false;}}
else if(field.indexOf(input)!==0){return false;}}} else if(field.indexOf(input)!==0){return false;}}}
return true;} return true;}
function fixHeaderRow(){var currentScroll=$(this).scrollTop();if(previousScroll<=headerPosition&&currentScroll>headerPosition){headerRow.addClass('fixed-style');headerRow.css('width',$(stockGridId).width()+'px');if($('.control').css('position')==='absolute'){headerRow.css('top','0');}} function fixHeaderRow(){var currentScroll=$(this).scrollTop();if(previousScroll<=headerPosition&&currentScroll>headerPosition){headerRow.addClass('fixed-style');headerRow.css('width',$(stockGridId).width()+'px');if($('.control').css('position')==='static'){headerRow.css('top','0');}}
else if(previousScroll>headerPosition&&currentScroll<=headerPosition){headerRow.removeClass('fixed-style');headerRow.css('width','100%');if($('.control').css('position')==='absolute'){headerRow.css('top',headerTop);}} else if(previousScroll>headerPosition&&currentScroll<=headerPosition){headerRow.removeClass('fixed-style');headerRow.css('width','100%');if($('.control').css('position')==='static'){headerRow.css('top',headerTop);}}
previousScroll=currentScroll;} previousScroll=currentScroll;}
var priceName='Price';var availableName='Members';var availableToolTip='Available to Members';if(stock.orderAvailable){availableName='Order';availableToolTip='Available to Order';} var priceName='Price';var availableName='Members';var availableToolTip='Available to Members';if(stock.orderAvailable){availableName='Order';availableToolTip='Available to Order';}
if(stock.wholesalePercent||stock.retailPercent){priceName='Cost';} if(stock.wholesalePercent||stock.retailPercent){priceName='Cost';}
......
...@@ -546,7 +546,7 @@ if (!this.dobrado.stock) { ...@@ -546,7 +546,7 @@ if (!this.dobrado.stock) {
// When the control bar scrolls with the page, header row should be // When the control bar scrolls with the page, header row should be
// fixed at the top of the page, otherwise the default css rule will // fixed at the top of the page, otherwise the default css rule will
// position the header row under the control bar. // position the header row under the control bar.
if ($('.control').css('position') === 'absolute') { if ($('.control').css('position') === 'static') {
headerRow.css('top', '0'); headerRow.css('top', '0');
} }
} }
...@@ -556,7 +556,7 @@ if (!this.dobrado.stock) { ...@@ -556,7 +556,7 @@ if (!this.dobrado.stock) {
// Reset the original width rule so wide grid option can still be used. // Reset the original width rule so wide grid option can still be used.
headerRow.css('width', '100%'); headerRow.css('width', '100%');
// Only need to reset the css rule for top when it's applied above. // Only need to reset the css rule for top when it's applied above.
if ($('.control').css('position') === 'absolute') { if ($('.control').css('position') === 'static') {
headerRow.css('top', headerTop); headerRow.css('top', headerTop);
} }
} }
......
...@@ -71,7 +71,8 @@ else if(name==='east'){if(right!==$('.right').width()){difference=$('.right').wi ...@@ -71,7 +71,8 @@ else if(name==='east'){if(right!==$('.right').width()){difference=$('.right').wi
else if(name==='west'){if(left!==$('.left').width()){difference=$('.left').width()-left;left=$('.left').width();saveLayout('left');}}} else if(name==='west'){if(left!==$('.left').width()){difference=$('.left').width()-left;left=$('.left').width();saveLayout('left');}}}
if(dobrado.layoutMode){dobrado.layoutMode=false;$('.sortable > *').css('cursor','default');$('.sortable').sortable('option','disabled',true);$('.ui-layout-resizer').css('display','none');$('.ui-layout-toggler').css('display','none');} if(dobrado.layoutMode){dobrado.layoutMode=false;$('.sortable > *').css('cursor','default');$('.sortable').sortable('option','disabled',true);$('.ui-layout-resizer').css('display','none');$('.ui-layout-toggler').css('display','none');}
else{dobrado.layoutMode=true;$('.sortable > *').css('cursor','move');$('.sortable').sortable('option','disabled',false);$('.ui-layout-resizer').css('display','block');$('.ui-layout-toggler').css('display','block');if(!main){var height=$('.main').height();if(height<400){height=400;} else{dobrado.layoutMode=true;$('.sortable > *').css('cursor','move');$('.sortable').sortable('option','disabled',false);$('.ui-layout-resizer').css('display','block');$('.ui-layout-toggler').css('display','block');if(!main){var height=$('.main').height();if(height<400){height=400;}
$('.main').css('height',height);main=$('.main').layout({east:{size:right},west:{size:left},closable:false,onresize:resize});}}} $('.main').css('height',height);var width=$('.main').width();if(width<400){width=400;}
$('.main').css('width',width);main=$('.main').layout({east:{size:right},west:{size:left},closable:false,onresize:resize});}}}
function copy(){dobrado.hideOtherMenu('copy-menu-wrapper');dobrado.toggleMenu('copy-menu-wrapper');$('body').one('click',hideMenu);return false;} function copy(){dobrado.hideOtherMenu('copy-menu-wrapper');dobrado.toggleMenu('copy-menu-wrapper');$('body').one('click',hideMenu);return false;}
function tools(){if(dobrado.editorLoading||$('.extended').dialog('isOpen')===true){if($('#control-tools').is(':checked')){$('#control-tools').prop('checked',false);} function tools(){if(dobrado.editorLoading||$('.extended').dialog('isOpen')===true){if($('#control-tools').is(':checked')){$('#control-tools').prop('checked',false);}
else{$('#control-tools').prop('checked',true);} else{$('#control-tools').prop('checked',true);}
......
...@@ -479,12 +479,17 @@ if (!this.dobrado.control) { ...@@ -479,12 +479,17 @@ if (!this.dobrado.control) {
$('.ui-layout-resizer').css('display', 'block'); $('.ui-layout-resizer').css('display', 'block');
$('.ui-layout-toggler').css('display', 'block'); $('.ui-layout-toggler').css('display', 'block');
if (!main) { if (!main) {
// Make sure the main div has a minimum height, and set in css. // Make sure the main div has a minimum height and width, set in css.
var height = $('.main').height(); var height = $('.main').height();
if (height < 400) { if (height < 400) {
height = 400; height = 400;
} }
$('.main').css('height', height); $('.main').css('height', height);
var width = $('.main').width();
if (width < 400) {
width = 400;
}
$('.main').css('width', width);
main = $('.main').layout({ east: { size: right }, main = $('.main').layout({ east: { size: right },
west: { size: left }, west: { size: left },
closable: false, closable: false,
......
...@@ -17,6 +17,37 @@ ...@@ -17,6 +17,37 @@
session_start(); session_start();
function micropub_config($endpoint, $token) {
$config = '{}';
$endpoint .= strpos($endpoint, '?') === false ? '?q=config' : '&q=config';
$curl_headers = ['Authorization: Bearer ' . $token,
'Accept: application/json'];
$ch = curl_init($endpoint);
curl_setopt($ch, CURLOPT_HTTPHEADER, $curl_headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 20);
curl_setopt($ch, CURLOPT_ENCODING, '');
curl_setopt($ch, CURLOPT_HEADER, false);
$body = curl_exec($ch);
if (curl_errno($ch) === 0) {
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($http_code === 200) {
$config = $body;
}
else {
log_db('micropub_config 1: Error getting ' . $endpoint .
"\nHTTP code: " . $http_code . "\nBody: " . $body);
}
}
else {
log_db('micropub_config 2: Error connecting to ' . $endpoint .
"\nCurl error: " . curl_error($ch));
}
curl_close($ch);
return $config;
}
function verify_code($endpoint, $post_fields, $token = false) { function verify_code($endpoint, $post_fields, $token = false) {
$check_me = ''; $check_me = '';
$ch = curl_init($endpoint); $ch = curl_init($endpoint);
...@@ -44,21 +75,21 @@ function verify_code($endpoint, $post_fields, $token = false) { ...@@ -44,21 +75,21 @@ function verify_code($endpoint, $post_fields, $token = false) {
} }
} }
if ($check_me === '') { if ($check_me === '') {
log_db('auth.php 1: \'me\' property not found at ' . $endpoint . log_db('verify_code 1: \'me\' property not found at ' . $endpoint .
"\nBody: " . $body); "\nBody: " . $body);
} }
else if ($token && !isset($_SESSION['access-token'])) { else if ($token && !isset($_SESSION['access-token'])) {
log_db('auth.php 2: Access token not found at ' . $endpoint . log_db('verify_code 2: Access token not found at ' . $endpoint .
"\nBody: " . $body); "\nBody: " . $body);
} }
} }
else { else {
log_db('auth.php 3: Error posting to ' . $endpoint . log_db('verify_code 3: Error posting to ' . $endpoint .
"\nHTTP code: " . $http_code . "\nBody: " . $body); "\nHTTP code: " . $http_code . "\nBody: " . $body);
} }
} }
else { else {
log_db('auth.php 4: Error connecting to ' . $endpoint . log_db('verify_code 4: Error connecting to ' . $endpoint .
"\nCurl error: " . curl_error($ch)); "\nCurl error: " . curl_error($ch));
} }
curl_close($ch); curl_close($ch);
...@@ -155,7 +186,7 @@ if ($result = $mysqli->query($query)) { ...@@ -155,7 +186,7 @@ if ($result = $mysqli->query($query)) {
$result->close(); $result->close();
} }
else { else {
log_db('auth.php 5: ' . $mysqli->error); log_db('auth.php 1: ' . $mysqli->error);
} }
if (!$exists) { if (!$exists) {
$password = bin2hex(openssl_random_pseudo_bytes(8)); $password = bin2hex(openssl_random_pseudo_bytes(8));
...@@ -166,12 +197,12 @@ if (!$exists) { ...@@ -166,12 +197,12 @@ if (!$exists) {
'registration_time) VALUES ("' . $domain . '", "' . $system_group . '", ' . 'registration_time) VALUES ("' . $domain . '", "' . $system_group . '", ' .
'"' . $password_hash . '", 1, ' . time() . ')'; '"' . $password_hash . '", 1, ' . time() . ')';
if (!$mysqli->query($query)) { if (!$mysqli->query($query)) {
log_db('auth.php 6: ' . $mysqli->error); log_db('auth.php 2: ' . $mysqli->error);
} }
$query = 'INSERT INTO group_names VALUES ' . $query = 'INSERT INTO group_names VALUES ' .
'("admin", "' . $system_group . '", "' . $domain . '", 0)'; '("admin", "' . $system_group . '", "' . $domain . '", 0)';
if (!$mysqli->query($query)) { if (!$mysqli->query($query)) {
log_db('auth.php 7: ' . $mysqli->error); log_db('auth.php 3: ' . $mysqli->error);
} }
mkdir('../' . $domain . '/public', 0755, true); mkdir('../' . $domain . '/public', 0755, true);
} }
...@@ -183,7 +214,7 @@ if (isset($_SESSION['access-token'])) { ...@@ -183,7 +214,7 @@ if (isset($_SESSION['access-token'])) {
'"micropub", "token", "' . $token . '") ON DUPLICATE KEY UPDATE ' . '"micropub", "token", "' . $token . '") ON DUPLICATE KEY UPDATE ' .
'value = "' . $token . '"'; 'value = "' . $token . '"';
if (!$mysqli->query($query