checkIfAuthenticated(true);
//If invalid settings are set, load setup
if (checkExtensionsInstalled()["result"] == RESULT_REQ_MISSING || !isGrocyApiSet()) {
header("Location: ./setup.php");
die();
}
//If mode was set with GET parameter
if (isset($_GET["mode"])) {
processModeChangeGetParameter($_GET["mode"]);
hideGetPostParameters();
}
//Called if a new product was created through the idea
if (isset($_GET["refreshbarcode"])) {
processRefreshedBarcode(sanitizeString($_GET["refreshbarcode"]));
hideGetPostParameters();
}
//If barcode was submitted with GET parameter "add"
if (isset($_GET["add"])) {
processNewBarcode(sanitizeString($_GET["add"], true));
if (!isset($_GET["showui"])) {
die("OK");
}
hideGetPostParameters();
}
//If barcode was submitted with GET parameter "text"
if (isset($_GET["text"])) {
processNewBarcode(sanitizeString($_GET["text"], true));
if (!isset($_GET["showui"])) {
die("OK");
}
hideGetPostParameters();
}
//After clicking on buttons on Federation popup
if (isset($_GET["fed"])) {
if ($_GET["fed"] == 1) {
BarcodeFederation::enableFederation();
} else {
BarcodeFederation::disableFederation();
}
hideGetPostParameters();
}
// If a button was pressed, we are processing everything here.
// Only one row can be processed at a time
processButtons();
$barcodes = DatabaseConnection::getInstance()->getStoredBarcodes();
if (sizeof($barcodes['known']) > 0 || sizeof($barcodes['unknown']) > 0 || sizeof($barcodes['tare']) > 0) {
$productinfo = API::getAllProductsInfo();
}
//Only pass refreshed cards to AJAX
if (isset($_GET["ajaxrefresh"])) {
$returnArray = array("f1" => getHtmlMainMenuTableKnown($barcodes),
"f2" => getHtmlMainMenuTableUnknown($barcodes),
"f3" => getHtmlLogTextArea(),
"f4" => getHtmlMainMenuReqActions($barcodes));
echo json_encode($returnArray, JSON_HEX_QUOT);
die();
}
$webUi = new WebUiGenerator(MENU_MAIN);
$webUi->addHeader('', true);
$link = (new MenuItemLink())
->setText("Delete all")
->setLink('window.location.href=\'' . $CONFIG->getPhpSelfWithBaseUrl() . '?delete=req_actions\'');
if (sizeof($barcodes['tare']) > 0) {
$webUi->addCard("Action required", getHtmlMainMenuReqActions($barcodes), $link);
}
$link = (new MenuItemLink())
->setText("Delete all")
->setLink('window.location.href=\'' . $CONFIG->getPhpSelfWithBaseUrl() . '?delete=known\'');
$webUi->addCard("New Barcodes", getHtmlMainMenuTableKnown($barcodes), $link);
$link = (new MenuItemLink())
->setText("Delete all")
->setLink('window.location.href=\'' . $CONFIG->getPhpSelfWithBaseUrl() . '?delete=unknown\'');
$webUi->addCard("Unknown Barcodes", getHtmlMainMenuTableUnknown($barcodes), $link);
$link = (new MenuItemLink())
->setText("Clear log")
->setLink('window.location.href=\'' . $CONFIG->getPhpSelfWithBaseUrl() . '?delete=log\'');
$webUi->addCard("Processed Barcodes", getHtmlLogTextArea(), $link);
$webUi->addScript("updateRedisCacheAndFederation(false)");
$webUi->addFooter();
displayFederationPopupHtml($webUi);
$webUi->printHtml();
function displayFederationPopupHtml(WebUiGenerator &$webUi): void {
$config = BBConfig::getInstance();
if (!$config["BBUDDY_SERVER_ENABLED"] && !$config["BBUDDY_SERVER_POPUPSHOWN"]) {
$webUi->addConfirmDialog("Do you want to enable Barcode Buddy Federation? This enables you to lookup " .
"barcodes that other users have already associated with a Grocy product before. It requires no signup, but " .
"is limited to 200 requests per day.
By using this method, you agree that all your Grocy product " .
"names that are associated with barcodes, the barcodes itself, your IP address and an unique ID will be " .
"sent and stored at a remote Barcode Buddy server located in Germany. No other data is sent or stored and " .
"no data will be used for commercial purposes. The sourcecode for the server application is available " .
"on Github and can be selfhosted as well." .
"
Would you like to enable Barcode Buddy Federation?",
"if (result) { location.href='./index.php?fed=1'; } else { location.href='./index.php?fed=0';}",
"Enable Barcode Buddy Federation", "Use Federation", "Cancel", "large");
}
}
//Check if a button on the web ui was pressed and process
function processButtons(): void {
global $CONFIG;
$db = DatabaseConnection::getInstance();
if (isset($_GET["delete"])) {
$db->deleteAll($_GET["delete"]);
//Hide get
header("Location: " . $CONFIG->getPhpSelfWithBaseUrl());
die();
}
if (isset($_POST["button_delete"])) {
$id = $_POST["button_delete"];
checkIfNumeric($id);
$db->deleteBarcode($id);
//Hide POST, so we can refresh
header("Location: " . $CONFIG->getPhpSelfWithBaseUrl());
die();
}
if (isset($_POST["button_submit"])) {
$id = $_POST["button_submit"];
checkIfNumeric($id);
if (isset($_POST["quantity_" . $id])) {
checkIfNumeric($_POST["quantity_" . $id]);
$barcode = $db->getBarcodeById($id);
if (changeWeightTareItem($barcode["barcode"], $_POST["quantity_" . $id]) == true) {
$db->deleteBarcode($id);
}
}
header("Location: " . $CONFIG->getPhpSelfWithBaseUrl());
}
if (isset($_POST["button_add_manual"])) {
if (isset($_POST["newbarcodes"]) && strlen(trim($_POST["newbarcodes"])) > 0) {
$barcodes = str_replace("\r", '', trim($_POST["newbarcodes"]));
$barcodes = explode("\n", $barcodes);
foreach ($barcodes as $barcode) {
$trimmedBarcode = trim(sanitizeString($barcode));
if (strlen($trimmedBarcode) > 0) {
processNewBarcode($trimmedBarcode);
}
}
}
//Hide POST, so we can refresh
header("Location: " . $CONFIG->getPhpSelfWithBaseUrl());
die();
}
if (isset($_POST["button_add"]) || isset($_POST["button_consume"])) {
if (isset($_POST["button_consume"])) {
$isConsume = true;
$id = $_POST["button_consume"];
} else {
$isConsume = false;
$id = $_POST["button_add"];
}
checkIfNumeric($id);
$gidSelected = $_POST["select_" . $id];
$gidSelected = checkIfNumeric($gidSelected);
if ($gidSelected != 0) {
$row = $db->getBarcodeById($id);
if ($row !== false) {
$barcode = sanitizeString($row["barcode"], true);
$amount = $row["amount"];
$name = $row["name"];
$amount = checkIfFloat($amount);
if (isset($_POST["tags"])) {
foreach ($_POST["tags"][$id] as $tag) {
TagManager::add(sanitizeString($tag), $gidSelected);
}
}
$product = API::getProductInfo($gidSelected);
if ($barcode == null)
die('Internal error: $barcode is null');
if (BBConfig::getInstance()["SAVE_BARCODE_NAME"] == "1")
API::addBarcode($gidSelected, $barcode, $name);
else
API::addBarcode($gidSelected, $barcode, null);
$log = new LogOutput("Associated barcode $barcode with " . $product->name, EVENT_TYPE_ASSOCIATE_PRODUCT);
$log->setVerbose()->dontSendWebsocket()->createLog();
$db->deleteBarcode($id);
QuantityManager::syncBarcodeToGrocy($barcode);
if ($product->isTare) {
if (!$db->isUnknownBarcodeAlreadyStored($barcode))
$db->insertActionRequiredBarcode($barcode, $row["bestBeforeInDays"], $row["price"]);
$log = new LogOutput("Action required: Enter weight for " . $product->name, EVENT_TYPE_ACTION_REQUIRED, $barcode);
$log->setVerbose()->dontSendWebsocket()->createLog();
} else {
if ($isConsume) {
if ($product->stockAmount < $amount)
$amount = $product->stockAmount;
if ($amount > 0) {
API::consumeProduct($gidSelected, $amount);
$log = new LogOutput("Consuming $amount " . $product->unit . " of " . $product->name, EVENT_TYPE_ADD_KNOWN_BARCODE);
} else {
$log = new LogOutput("None in stock, not consuming: " . $product->name, EVENT_TYPE_ADD_KNOWN_BARCODE);
}
$log->setVerbose()->dontSendWebsocket()->createLog();
} else {
$additionalLog = "";
if (!API::purchaseProduct($gidSelected, $amount, $row["bestBeforeInDays"], $row["price"])) {
$additionalLog = " [WARNING]: No default best before date set!";
}
$log = new LogOutput("Adding $amount " . $product->unit . " of " . $product->name . $additionalLog, EVENT_TYPE_ADD_KNOWN_BARCODE);
$log->setVerbose()->dontSendWebsocket()->createLog();
}
}
}
}
//Hide POST, so we can refresh
header("Location: " . $CONFIG->getPhpSelfWithBaseUrl());
die();
}
}
/**
* Generate the table with barcodes that require actions
* @param array $barcodes
* @return string
*/
function getHtmlMainMenuReqActions(array $barcodes): string {
$html = new UiEditor(true, null, "f4");
if (sizeof($barcodes['tare']) == 0) {
return "null";
} else {
$table = new TableGenerator(array(
"Name",
"Current Weight",
"Input",
"Action",
"Remove"
));
foreach ($barcodes['tare'] as $item) {
$product = API::getProductByBarcode($item['barcode']);
$totalWeight = $product->stockAmount + $product->tareWeight;
$table->startRow();
$table->addCell($product->name);
$table->addCell($totalWeight);
$table->addCell($html->buildEditField("quantity_" . $item['id'], "New weight (tare: " . intval($product->tareWeight) . ")", $totalWeight)
->type("number")
->setWidth('8em')
->minmax(array($product->tareWeight, null))
->generate(true));
$table->addCell($html->buildButton("button_submit", "Submit")
->setSubmit()
->setRaised()
->setIsAccent()
->setValue($item['id'])
->setId('button_submit_' . $item['id'])
->generate(true));
$table->addCell($html->buildButton("button_delete", "Remove")->setSubmit()->setValue($item['id'])->generate(true));
$table->endRow();
}
$html->addTableClass($table);
return $html->getHtml();
}
}
/**
* Generate the table with barcodes
* @param array $barcodes
* @return string
*/
function getHtmlMainMenuTableKnown(array $barcodes): string {
global $productinfo;
$html = new UiEditor(true, null, "f1");
if (sizeof($barcodes['known']) == 0) {
$html->addHtml("No known barcodes yet.");
return $html->getHtml();
} else {
$containsFederationName = false;
foreach ($barcodes['known'] as $item) {
if ($item['bbServerAltNames'] != null)
$containsFederationName = true;
}
$arrayTableEntries = array(
"Name",
"Barcode",
"Quantity",
"Product",
"Action",
"Tags",
"Create",
"Remove");
if ($containsFederationName)
array_splice($arrayTableEntries, 1, 0, array("Federation"));
$table = new TableGenerator($arrayTableEntries);
foreach ($barcodes['known'] as $item) {
$isDisabled = "disabled";
if ($item['match'] != 0) {
$isDisabled = "";
}
$itemId = $item['id'];
$table->startRow();
$bbServerButton = "";
if ($item['bbServerAltNames'] != null) {
if (sizeof($item['bbServerAltNames']) > 1)
$bbServerButton = getVoteButton($item) . getReportButton($item);
else
$bbServerButton = getReportButton($item);
}
$table->addCell(UiEditor::addTextWrap($item['name'], 15));
if ($containsFederationName)
$table->addCell($bbServerButton);
$table->addCell($item['barcode']);
$table->addCell($item['amount']);
$table->addCell('');
$table->addCell($html->buildButton("button_add", "Add")
->setDisabled($isDisabled)
->setSubmit()
->setRaised()
->setIsAccent()
->setValue($item['id'])
->setId('button_add_' . $item['id'])
->generate(true) . ' ' .
$html->buildButton("button_consume", "Consume")
->setDisabled($isDisabled)
->setSubmit()
->setRaised()
->setIsAccent()
->setValue($item['id'])
->setId('button_consume_' . $item['id'])
->generate(true));
$table->addCell(explodeWordsAndMakeCheckboxes($item['name'], $itemId));
$table->addCell($html->buildButton("button_createproduct", "Create Product")
->setOnClick(buildSafeOnClick('openNewTab', [
BBConfig::getInstance()["GROCY_BASE_URL"] . 'product/new?closeAfterCreation&flow=InplaceNewProductWithName&name=' . rawurlencode(html_entity_decode($item['name'], ENT_QUOTES, 'UTF-8')),
$item['barcode']
]))
->generate(true));
$table->addCell($html->buildButton("button_delete", "Remove")->setSubmit()->setValue($item['id'])->generate(true));
$table->endRow();
}
$html->addTableClass($table);
return $html->getHtml();
}
}
function getReportButton(array $item): string {
$onclick = buildSafeOnClick('showReportFederationName', [$item['barcode'], $item['name']]);
return '';
}
function getVoteButton(array $item): string {
$onclick = buildSafeOnClick('showMultipleFederationNames', [$item['barcode'], base64_encode(json_encode($item['bbServerAltNames']))]);
return ' ';
}
/**
* Generate the table with barcodes
* @param array $barcodes
* @return string
*/
function getHtmlMainMenuTableUnknown(array $barcodes): string {
global $productinfo;
global $CONFIG;
$html = new UiEditor(true, null, "f2");
if (sizeof($barcodes['unknown']) == 0) {
$html->addHtml("No unknown barcodes yet.");
return $html->getHtml();
} else {
$table = new TableGenerator(array(
"Barcode",
"Look up",
"Quantity",
"Product",
"Action",
"Create",
"Remove"
));
foreach ($barcodes['unknown'] as $item) {
$isDisabled = "disabled";
if ($item['match'] != 0) {
$isDisabled = "";
}
$itemId = $item['id'];
$table->startRow();
$table->addCell($item['barcode']);
$table->addCell('Search for barcode');
$table->addCell($item['amount']);
$table->addCell('');
$table->addCell($html->buildButton("button_add", "Add")
->setDisabled($isDisabled)
->setSubmit()
->setRaised()
->setIsAccent()
->setValue($item['id'])
->setId('button_add_' . $item['id'])
->generate(true) . ' ' .
$html->buildButton("button_consume", "Consume")
->setDisabled($isDisabled)
->setSubmit()
->setRaised()
->setIsAccent()
->setValue($item['id'])
->setId('button_consume_' . $item['id'])
->generate(true));
$table->addCell($html->buildButton("button_createproduct", "Create Product")
->setOnClick(buildSafeOnClick('openNewTab', [
BBConfig::getInstance()["GROCY_BASE_URL"] . 'product/new?closeAfterCreation&prefillbarcode=' . $item['barcode'],
$item['barcode']
]))
->generate(true));
$table->addCell($html->buildButton("button_delete", "Remove")->setSubmit()->setValue($item['id'])->generate(true));
$table->endRow();
}
$html->addTableClass($table);
return $html->getHtml();
}
}
/**
* Outputs stored logs to the textarea
* @return string
* @throws DbConnectionDuringEstablishException
*/
function getHtmlLogTextArea(): string {
$db = DatabaseConnection::getInstance();
$logs = $db->getLogs();
$html = new UiEditor(true, null, "f3");
if (sizeof($logs) == 0) {
$html->addHtml("No processed items yet.");
return $html->getHtml();
} else {
$html->addHtml('