Add translation system (#85)
This commit is contained in:
parent
d287f303f0
commit
d7366dcfb0
10
README.md
10
README.md
@ -17,6 +17,7 @@ Wallos: Open-Source Personal Subscription Tracker
|
||||
- [Docker-Compose](#docker-compose)
|
||||
- [Usage](#usage)
|
||||
- [Contributing](#contributing)
|
||||
- [Translations](#translations)
|
||||
- [Screenshots](#screenshots)
|
||||
- [License](#license)
|
||||
|
||||
@ -139,6 +140,15 @@ Feel free to open Pull requests with bug fixes and features. I'll do my best to
|
||||
Feel free to open issues with bug reports or feature requests. Bug fixes will take priority.
|
||||
I welcome contributions from the community and look forward to working with you to improve this project.
|
||||
|
||||
### Translations
|
||||
|
||||
If you want to contribute with a translation of wallos:
|
||||
- Add your language code to `includes/i18n/languages.php` in the format `"en" => "English"`. Please use the original language name and not the english translation.
|
||||
- Create a copy of the file `includes/i18n/en.php` and rename it to the language code you used above. Example: pt.php for "pt" => "Português".
|
||||
- Translate all the values on the language file to the new language. (Incomplete translations will not be accepted).
|
||||
- Create a copy of the file `scripts/i18n/en.php` and rename it to the language code you used above.
|
||||
- Translate all the values on the language file to the new language. (Incomplete translations will not be accepted).
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the [GNU General Public License, Version 3](LICENSE.md) - see the [LICENSE.md](LICENSE.md) file for details.
|
||||
|
||||
26
about.php
26
about.php
@ -6,50 +6,50 @@
|
||||
|
||||
<section class="account-section">
|
||||
<header>
|
||||
<h2>About and Credits</h2>
|
||||
<h2><?= translate('about_and_credits', $i18n) ?></h2>
|
||||
</header>
|
||||
<div class="credits-list">
|
||||
<p>Wallos v1.1.0</p>
|
||||
<p>License:
|
||||
<p>Wallos v1.2.0</p>
|
||||
<p><?= translate('license', $i18n) ?>:
|
||||
<span>
|
||||
GPLv3
|
||||
<a href="https://www.gnu.org/licenses/gpl-3.0.en.html" target="_blank" title="Visit external url">
|
||||
<a href="https://www.gnu.org/licenses/gpl-3.0.en.html" target="_blank" title="<?= translate('external_url', $i18n) ?>">
|
||||
<i class="fa-solid fa-arrow-up-right-from-square"></i>
|
||||
</a>
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
Issues and Request:
|
||||
<?= translate('issues_and_requests', $i18n) ?>:
|
||||
<span>
|
||||
GitHub
|
||||
<a href="https://github.com/ellite/Wallos/issues" target="_blank" title="Visit external url">
|
||||
<a href="https://github.com/ellite/Wallos/issues" target="_blank" title="<?= translate('external_url', $i18n) ?>">
|
||||
<i class="fa-solid fa-arrow-up-right-from-square"></i>
|
||||
</a>
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
The author:
|
||||
<?= translate('the_author', $i18n) ?>:
|
||||
<span>
|
||||
https://henrique.pt
|
||||
<a href="https://henrique.pt/" target="_blank" title="Visit external url">
|
||||
<a href="https://henrique.pt/" target="_blank" title="<?= translate('external_url', $i18n) ?>">
|
||||
<i class="fa-solid fa-arrow-up-right-from-square"></i>
|
||||
</a>
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
Icons:
|
||||
<?= translate('icons', $i18n) ?>:
|
||||
<span>
|
||||
https://www.streamlinehq.com/freebies/plump-flat-free
|
||||
<a href="https://www.streamlinehq.com/freebies/plump-flat-free" target="_blank" title="Visit external url">
|
||||
<a href="https://www.streamlinehq.com/freebies/plump-flat-free" target="_blank" title="<?= translate('external_url', $i18n) ?>">
|
||||
<i class="fa-solid fa-arrow-up-right-from-square"></i>
|
||||
</a>
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
Payment Icons:
|
||||
<?= translate('payment_icons', $i18n) ?>:
|
||||
<span>
|
||||
https://www.figma.com/file/5IMW8JfoXfB5GRlPNdTyeg/Credit-Cards-and-Payment-Methods-Icons-(Community)
|
||||
<a href="https://www.figma.com/file/5IMW8JfoXfB5GRlPNdTyeg/Credit-Cards-and-Payment-Methods-Icons-(Community)" target="_blank" title="Visit external url">
|
||||
<a href="https://www.figma.com/file/5IMW8JfoXfB5GRlPNdTyeg/Credit-Cards-and-Payment-Methods-Icons-(Community)" target="_blank" title="<?= translate('external_url', $i18n) ?>">
|
||||
<i class="fa-solid fa-arrow-up-right-from-square"></i>
|
||||
</a>
|
||||
</span>
|
||||
@ -58,7 +58,7 @@
|
||||
Chart.js:
|
||||
<span>
|
||||
https://www.chartjs.org/
|
||||
<a href="https://www.chartjs.org/" target="_blank" title="Visit external url">
|
||||
<a href="https://www.chartjs.org/" target="_blank" title="<?= translate('external_url', $i18n) ?>">
|
||||
<i class="fa-solid fa-arrow-up-right-from-square"></i>
|
||||
</a>
|
||||
</span>
|
||||
|
||||
@ -19,7 +19,7 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"errorMessage" => "Failed to add category"
|
||||
"errorMessage" => translate('failed_add_category', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
}
|
||||
@ -35,20 +35,21 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
|
||||
|
||||
if ($result) {
|
||||
$response = [
|
||||
"success" => true
|
||||
"success" => true,
|
||||
"message" => translate('category_saved', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"errorMessage" => "Failed to edit category"
|
||||
"errorMessage" => translate('failed_edit_category', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
}
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"errorMessage" => "Please fill all the fields"
|
||||
"errorMessage" => translate('fill_all_fields', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
}
|
||||
@ -65,7 +66,7 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
|
||||
if ($count > 0) {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"errorMessage" => "Category is in use in subscriptions and can't be removed"
|
||||
"errorMessage" => translate('category_in_use', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
} else {
|
||||
@ -75,13 +76,14 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
|
||||
$result = $stmt->execute();
|
||||
if ($result) {
|
||||
$response = [
|
||||
"success" => true
|
||||
"success" => true,
|
||||
"message" => translate('category_removed', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"errorMessage" => "Failed to remove category"
|
||||
"errorMessage" => translate('failed_remove_category', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
}
|
||||
@ -89,15 +91,15 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"errorMessage" => "Failed to remove category"
|
||||
"errorMessage" => translate('failed_remove_category', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
}
|
||||
} else {
|
||||
echo "Error";
|
||||
echo translate('error', $i18n);
|
||||
}
|
||||
} else {
|
||||
echo "Error";
|
||||
echo translate('error', $i18n);
|
||||
}
|
||||
|
||||
?>
|
||||
@ -19,7 +19,7 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
|
||||
$currencyId = $db->lastInsertRowID();
|
||||
echo $currencyId;
|
||||
} else {
|
||||
echo "Error adding currency entry.";
|
||||
echo translate('error_adding_currency', $i18n);
|
||||
}
|
||||
} else if (isset($_GET['action']) && $_GET['action'] == "edit") {
|
||||
if (isset($_GET['currencyId']) && $_GET['currencyId'] != "" && isset($_GET['name']) && $_GET['name'] != "" && isset($_GET['symbol']) && $_GET['symbol'] != "") {
|
||||
@ -36,18 +36,22 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
|
||||
$result = $stmt->execute();
|
||||
|
||||
if ($result) {
|
||||
echo json_encode(["success" => true]);
|
||||
$response = [
|
||||
"success" => true,
|
||||
"message" => $name . " " . translate('currency_saved', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"message" => "Failed to store Currency on the Database"
|
||||
"message" => translate('failed_to_store_currency', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
}
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"message" => "Some fields are missing"
|
||||
"message" => translate('fields_missing', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
}
|
||||
@ -70,7 +74,7 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
|
||||
if ($count > 0) {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"message" => "Currency is in use in subscriptions and can't be deleted."
|
||||
"message" => translate('currency_in_use', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
@ -78,7 +82,7 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
|
||||
if ($currencyId == $mainCurrencyId) {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"message" => "Currency is set as main currency and can't be deleted."
|
||||
"message" => translate('currency_is_main', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
@ -88,11 +92,11 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
|
||||
$stmt->bindParam(':currencyId', $currencyId, SQLITE3_INTEGER);
|
||||
$result = $stmt->execute();
|
||||
if ($result) {
|
||||
echo json_encode(["success" => true]);
|
||||
echo json_encode(["success" => true, "message" => translate('currency_removed', $i18n)]);
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"message" => "Failed to remove currency from the Database"
|
||||
"message" => translate('failed_to_remove_currency', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
}
|
||||
@ -101,7 +105,7 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"message" => "Some fields are missing."
|
||||
"message" => translate('fields_missing', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
}
|
||||
@ -111,7 +115,7 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"message" => "Your session expired. Please login again"
|
||||
"message" => translate('session_expired', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
}
|
||||
|
||||
@ -17,21 +17,21 @@
|
||||
$stmt->bindParam(":api_key", $newApiKey, SQLITE3_TEXT);
|
||||
$result = $stmt->execute();
|
||||
if ($result) {
|
||||
echo json_encode(["success" => true]);
|
||||
echo json_encode(["success" => true, "message" => translate('api_key_saved', $i18n)]);
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"message" => "Failed to store API Key on the Database"
|
||||
"message" => translate('failed_to_store_api_key', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
}
|
||||
} else {
|
||||
echo json_encode(["success" => true]);
|
||||
echo json_encode(["success" => true, "message" => translate('apy_key_saved', $i18n)]);
|
||||
}
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"message" => "Invalid API Key"
|
||||
"message" => translate('invalid_api_key', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
}
|
||||
|
||||
@ -13,13 +13,13 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
|
||||
$householdId = $db->lastInsertRowID();
|
||||
$response = [
|
||||
"success" => true,
|
||||
"householdId" => $householdId
|
||||
"householdId" => $householdId,
|
||||
];
|
||||
echo json_encode($response);
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"errorMessage" => "Failed to add household member"
|
||||
"errorMessage" => translate('failed_add_household', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
}
|
||||
@ -35,20 +35,21 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
|
||||
|
||||
if ($result) {
|
||||
$response = [
|
||||
"success" => true
|
||||
"success" => true,
|
||||
"message" => translate('member_saved', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"errorMessage" => "Failed to edit household member"
|
||||
"errorMessage" => translate('failed_edit_household', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
}
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"errorMessage" => "Please fill all the fields"
|
||||
"errorMessage" => translate('fill_all_fields', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
}
|
||||
@ -65,7 +66,7 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
|
||||
if ($count > 0) {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"errorMessage" => "Household member is in use in subscriptions and can't be removed"
|
||||
"errorMessage" => translate('household_in_use', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
} else {
|
||||
@ -75,13 +76,14 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
|
||||
$result = $stmt->execute();
|
||||
if ($result) {
|
||||
$response = [
|
||||
"success" => true
|
||||
"success" => true,
|
||||
"message" => translate('member_removed', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"errorMessage" => "Failed to remove household member"
|
||||
"errorMessage" => translate('failed_remove_household', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
}
|
||||
@ -89,15 +91,15 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"errorMessage" => "Failed to remove household member"
|
||||
"errorMessage" => translate('failed_remove_household', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
}
|
||||
} else {
|
||||
echo "Error";
|
||||
echo translate('error', $i18n);
|
||||
}
|
||||
} else {
|
||||
echo "Error";
|
||||
echo translate('error', $i18n);
|
||||
}
|
||||
|
||||
?>
|
||||
@ -15,7 +15,7 @@
|
||||
) {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"errorMessage" => "Please fill all mandatory fields"
|
||||
"errorMessage" => translate('fill_mandatory_fields', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
} else {
|
||||
@ -33,7 +33,7 @@
|
||||
if ($result === false) {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"errorMessage" => "Error saving notifications data"
|
||||
"errorMessage" => translate('error_saving_notifications', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
} else {
|
||||
@ -57,13 +57,14 @@
|
||||
|
||||
if ($stmt->execute()) {
|
||||
$response = [
|
||||
"success" => true
|
||||
"success" => true,
|
||||
"message" => translate('notifications_settings_saved', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"errorMessage" => "Error saving notification data"
|
||||
"errorMessage" => translate('error_saving_notifications', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
) {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"errorMessage" => "Please fill all fields"
|
||||
"errorMessage" => translate('fill_all_fields', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
} else {
|
||||
@ -34,6 +34,7 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$fromEmail = $data["fromemail"] ?? "wallos@wallosapp.com";
|
||||
|
||||
$mail = new PHPMailer(true);
|
||||
$mail->CharSet="UTF-8";
|
||||
$mail->isSMTP();
|
||||
|
||||
$mail->Host = $smtpAddress;
|
||||
@ -51,18 +52,19 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$mail->setFrom($fromEmail, 'Wallos App');
|
||||
$mail->addAddress($email, $name);
|
||||
|
||||
$mail->Subject = 'Wallos Notification';
|
||||
$mail->Body = 'This is a test notification. If you\'re seeing this, the configuration is correct.';
|
||||
$mail->Subject = translate('wallos_notification', $i18n);
|
||||
$mail->Body = translate('test_notification', $i18n);
|
||||
|
||||
if ($mail->send()) {
|
||||
$response = [
|
||||
"success" => true,
|
||||
"message" => translate('notification_sent_successfuly', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"errorMessage" => "Error sending email." . $mail->ErrorInfo
|
||||
"errorMessage" => translate('email_error', $i18n) . $mail->ErrorInfo
|
||||
];
|
||||
echo json_encode($response);
|
||||
}
|
||||
|
||||
@ -4,14 +4,14 @@ session_start();
|
||||
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
|
||||
die(json_encode([
|
||||
"success" => false,
|
||||
"message" => "Your session expired. Please login again"
|
||||
"message" => translate('session_expired', $i18n)
|
||||
]));
|
||||
}
|
||||
|
||||
if (!isset($_GET['paymentId']) || !isset($_GET['enabled'])) {
|
||||
die(json_encode([
|
||||
"success" => false,
|
||||
"message" => "Some fields are missing."
|
||||
"message" => translate('fields_missing', $i18n)
|
||||
]));
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ $inUse = $db->querySingle('SELECT COUNT(*) as count FROM subscriptions WHERE pay
|
||||
if ($inUse) {
|
||||
die(json_encode([
|
||||
"success" => false,
|
||||
"message" => "Can't delete used payment method"
|
||||
"message" => translate('payment_in_use', $i18n)
|
||||
]));
|
||||
}
|
||||
|
||||
@ -33,13 +33,16 @@ $stmtUpdate->bindParam(':enabled', $enabled);
|
||||
$stmtUpdate->bindParam(':id', $paymentId);
|
||||
$resultUpdate = $stmtUpdate->execute();
|
||||
|
||||
$text = $enabled ? "enabled" : "disabled";
|
||||
|
||||
if ($resultUpdate) {
|
||||
die(json_encode([
|
||||
"success" => true
|
||||
"success" => true,
|
||||
"message" => translate($text, $i18n)
|
||||
]));
|
||||
}
|
||||
|
||||
die(json_encode([
|
||||
"success" => false,
|
||||
"message" => "Failed to update payment method in the database"
|
||||
"message" => tranlate('failed_update_payment', $i18n)
|
||||
]));
|
||||
|
||||
@ -25,13 +25,13 @@
|
||||
if (saveLogo($imageData, $uploadFile, $name)) {
|
||||
return $fileName;
|
||||
} else {
|
||||
echo "Error fetching image: " . curl_error($ch);
|
||||
echo translate('error_fetching_image', $i18n) . ": " . curl_error($ch);
|
||||
return "";
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
} else {
|
||||
echo "Error fetching image: " . curl_error($ch);
|
||||
echo translate('error_fetching_image', $i18n) . ": " . curl_error($ch);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@ -194,13 +194,13 @@
|
||||
if ($stmt->execute()) {
|
||||
$success['status'] = "Success";
|
||||
$text = $isEdit ? "updated" : "added";
|
||||
$success['message'] = "Subscription " . $text . " successfuly";
|
||||
$success['message'] = translate('subscription_' . $text . '_successfuly', $i18n);
|
||||
$json = json_encode($success);
|
||||
header('Content-Type: application/json');
|
||||
echo $json;
|
||||
exit();
|
||||
} else {
|
||||
echo "Error: " . $db->lastErrorMsg();
|
||||
echo translate('error', $i18n) . ": " . $db->lastErrorMsg();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,11 +12,11 @@
|
||||
http_response_code(204);
|
||||
} else {
|
||||
http_response_code(500);
|
||||
echo json_encode(array("message" => "Error deleting the subscription."));
|
||||
echo json_encode(array("message" => translate('error_deleting_subscription', $i18n)));
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(array("message" => "Invalid request method."));
|
||||
echo json_encode(array("message" => translate('invalid_request_method', $i18n)));
|
||||
}
|
||||
}
|
||||
$db->close();
|
||||
|
||||
@ -31,10 +31,10 @@
|
||||
header('Content-Type: application/json');
|
||||
echo $subscriptionJson;
|
||||
} else {
|
||||
echo "Error";
|
||||
echo translate('error', $i18n);
|
||||
}
|
||||
} else {
|
||||
echo "Error";
|
||||
echo translate('error', $i18n);
|
||||
}
|
||||
}
|
||||
$db->close();
|
||||
|
||||
@ -43,7 +43,7 @@
|
||||
$print[$id]['name']= $subscription['name'];
|
||||
$cycle = $subscription['cycle'];
|
||||
$frequency = $subscription['frequency'];
|
||||
$print[$id]['billing_cycle'] = getBillingCycle($cycle, $frequency);
|
||||
$print[$id]['billing_cycle'] = getBillingCycle($cycle, $frequency, $i18n);
|
||||
$paymentMethodId = $subscription['payment_method_id'];
|
||||
$print[$id]['currency_code'] = $currencies[$subscription['currency_id']]['code'];
|
||||
$currencyId = $subscription['currency_id'];
|
||||
@ -66,19 +66,19 @@
|
||||
}
|
||||
|
||||
if (isset($print)) {
|
||||
printSubscriptions($print, $sort, $categories, $members);
|
||||
printSubscriptions($print, $sort, $categories, $members, $i18n);
|
||||
}
|
||||
|
||||
if (count($subscriptions) == 0) {
|
||||
?>
|
||||
<div class="empty-page">
|
||||
<img src="images/siteimages/empty.png" alt="Empty page" />
|
||||
<img src="images/siteimages/empty.png" alt="<?= translate('empty_page', $i18n) ?>" />
|
||||
<p>
|
||||
You don't have any subscriptions yet
|
||||
<?= translate('no_subscriptions_yet', $i18n) ?>
|
||||
</p>
|
||||
<button class="button" onClick="addSubscription()">
|
||||
<img class="button-icon" src="images/siteicons/plusicon.png">
|
||||
Add First Subscription
|
||||
<?= translate('add_first_subscription', $i18n) ?>
|
||||
</button>
|
||||
</div>
|
||||
<?php
|
||||
|
||||
@ -76,6 +76,7 @@
|
||||
$email = $_POST['email'];
|
||||
$avatar = $_POST['avatar'];
|
||||
$main_currency = $_POST['main_currency'];
|
||||
$language = $_POST['language'];
|
||||
|
||||
if (isset($_POST['password']) && $_POST['password'] != "") {
|
||||
$password = $_POST['password'];
|
||||
@ -84,7 +85,7 @@
|
||||
if ($password != $confirm) {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"errorMessage" => "Passwords do not match"
|
||||
"errorMessage" => translate('passwords_dont_match', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
exit();
|
||||
@ -92,7 +93,7 @@
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"errorMessage" => "Passwords do not match"
|
||||
"errorMessage" => translate('passwords_dont_match', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
exit();
|
||||
@ -100,9 +101,9 @@
|
||||
}
|
||||
|
||||
if (isset($_POST['password']) && $_POST['password'] != "") {
|
||||
$sql = "UPDATE user SET avatar = :avatar, username = :username, email = :email, password = :password, main_currency = :main_currency WHERE id = 1";
|
||||
$sql = "UPDATE user SET avatar = :avatar, username = :username, email = :email, password = :password, main_currency = :main_currency, language = :language WHERE id = 1";
|
||||
} else {
|
||||
$sql = "UPDATE user SET avatar = :avatar, username = :username, email = :email, main_currency = :main_currency WHERE id = 1";
|
||||
$sql = "UPDATE user SET avatar = :avatar, username = :username, email = :email, main_currency = :main_currency, language = :language WHERE id = 1";
|
||||
}
|
||||
|
||||
$stmt = $db->prepare($sql);
|
||||
@ -110,6 +111,7 @@
|
||||
$stmt->bindParam(':username', $username, SQLITE3_TEXT);
|
||||
$stmt->bindParam(':email', $email, SQLITE3_TEXT);
|
||||
$stmt->bindParam(':main_currency', $main_currency, SQLITE3_INTEGER);
|
||||
$stmt->bindParam(':language', $language, SQLITE3_TEXT);
|
||||
|
||||
if (isset($_POST['password']) && $_POST['password'] != "") {
|
||||
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
|
||||
@ -119,12 +121,13 @@
|
||||
$result = $stmt->execute();
|
||||
|
||||
if ($result) {
|
||||
$cookieExpire = time() + (30 * 24 * 60 * 60);
|
||||
setcookie('language', $language, $cookieExpire, '/');
|
||||
if ($username != $oldUsername) {
|
||||
$_SESSION['username'] = $username;
|
||||
if (isset($_COOKIE['wallos_login'])) {
|
||||
$cookie = explode('|', $_COOKIE['wallos_login'], 2) ;
|
||||
$token = $cookie[1];
|
||||
$cookieExpire = time() + (30 * 24 * 60 * 60);
|
||||
$cookieValue = $username . "|" . $token . "|" . $main_currency;
|
||||
}
|
||||
}
|
||||
@ -137,12 +140,13 @@
|
||||
|
||||
$response = [
|
||||
"success" => true,
|
||||
"message" => translate('user_details_saved', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"errorMessage" => "Error updating user data"
|
||||
"errorMessage" => translate('error_updating_user_data', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
}
|
||||
@ -151,7 +155,7 @@
|
||||
} else {
|
||||
$response = [
|
||||
"success" => false,
|
||||
"errorMessage" => "Please fill all fields"
|
||||
"errorMessage" => translate('fill_all_fields', $i18n)
|
||||
];
|
||||
echo json_encode($response);
|
||||
exit();
|
||||
|
||||
@ -8,4 +8,8 @@ if (!$db) {
|
||||
die('Connection to the database failed.');
|
||||
}
|
||||
|
||||
require_once 'i18n/languages.php';
|
||||
require_once 'i18n/getlang.php';
|
||||
require_once 'i18n/' . $lang . '.php';
|
||||
|
||||
?>
|
||||
@ -4,7 +4,7 @@
|
||||
<div class="toast-content">
|
||||
<i class="fas fa-solid fa-x toast-icon error"></i>
|
||||
<div class="message">
|
||||
<span class="text text-1">Error</span>
|
||||
<span class="text text-1"><?= translate("error", $i18n) ?></span>
|
||||
<span class="text text-2 errorMessage"></span>
|
||||
</div>
|
||||
</div>
|
||||
@ -16,7 +16,7 @@
|
||||
<div class="toast-content">
|
||||
<i class="fas fa-solid fa-check toast-icon success"></i>
|
||||
<div class="message">
|
||||
<span class="text text-1">Success</span>
|
||||
<span class="text text-1"><?= translate("success", $i18n) ?></span>
|
||||
<span class="text text-2 successMessage"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -4,6 +4,10 @@
|
||||
require_once 'checksession.php';
|
||||
require_once 'currency_formatter.php';
|
||||
|
||||
require_once 'i18n/languages.php';
|
||||
require_once 'i18n/getlang.php';
|
||||
require_once 'i18n/' . $lang . '.php';
|
||||
|
||||
if ($userCount == 0) {
|
||||
$db->close();
|
||||
header("Location: registration.php");
|
||||
@ -32,7 +36,10 @@
|
||||
<script type="text/javascript" src="scripts/common.js"></script>
|
||||
<script type="text/javascript">
|
||||
window.theme = "<?= $theme ?>";
|
||||
window.lang = "<?=$lang ?>";
|
||||
</script>
|
||||
<script type="text/javascript" src="scripts/i18n/<?= $lang ?>.js"></script>
|
||||
<script type="text/javascript" src="scripts/i18n/getlang.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
@ -49,10 +56,11 @@
|
||||
<span id="user"><?= $username ?></span>
|
||||
</button>
|
||||
<div class="dropdown-content">
|
||||
<a href="stats.php"><i class="fa-solid fa-chart-simple"></i>Stats</a>
|
||||
<a href="settings.php"><i class="fa-solid fa-gear"></i>Settings</a>
|
||||
<a href="about.php"><i class="fa-solid fa-info-circle"></i>About</a>
|
||||
<a href="logout.php"><i class="fa-solid fa-arrow-right-from-bracket"></i>Logout</a>
|
||||
<a href="/"><i class="fa-solid fa-list"></i><?= translate('subscriptions', $i18n) ?></a>
|
||||
<a href="stats.php"><i class="fa-solid fa-chart-simple"></i><?= translate('stats', $i18n) ?></a>
|
||||
<a href="settings.php"><i class="fa-solid fa-gear"></i><?= translate('settings', $i18n) ?></a>
|
||||
<a href="about.php"><i class="fa-solid fa-info-circle"></i><?= translate('about', $i18n) ?></a>
|
||||
<a href="logout.php"><i class="fa-solid fa-arrow-right-from-bracket"></i><?= translate('logout', $i18n) ?></a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
195
includes/i18n/en.php
Normal file
195
includes/i18n/en.php
Normal file
@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
$i18n = [
|
||||
// Registration page
|
||||
"create_account" => "You need to create an account before you're able to login",
|
||||
'username' => "Username",
|
||||
'password' => "Password",
|
||||
"email" => "Email",
|
||||
"confirm_password" => "Confirm Password",
|
||||
"main_currency" => "Main Currency",
|
||||
"language" => "Language",
|
||||
"passwords_dont_match" => "Passwords do not match",
|
||||
"registration_failed" => "Registration failed, please try again.",
|
||||
"register" => "Register",
|
||||
// Login Page
|
||||
'please_login' => "Please login",
|
||||
'stay_logged_in' => "Stay logged in (30 days)",
|
||||
'login' => "Login",
|
||||
'login_failed' => "Login details are incorrect",
|
||||
// Header
|
||||
'subscriptions' => "Subscriptions",
|
||||
'stats' => "Statistics",
|
||||
'settings' => "Settings",
|
||||
'about' => "About",
|
||||
'logout' => "Logout",
|
||||
// Subscriptions page
|
||||
"subscription" => "Subscription",
|
||||
"no_subscriptions_yet" => "You don't have any subscriptions yet",
|
||||
"add_first_subscription" => "Add first subscription",
|
||||
'new_subscription' => "New Subscription",
|
||||
'sort' => "Sort",
|
||||
'name' => "Nome",
|
||||
'last_added' => "Last Added",
|
||||
'price' => "Price",
|
||||
'next_payment' => "Next Payment",
|
||||
'member' => "Member",
|
||||
'category' => "Category",
|
||||
'payment_method' => "Payment Method",
|
||||
"Daily" => "Daily",
|
||||
"Weekly" => "Weekly",
|
||||
"Monthly" => "Monthly",
|
||||
"Yearly" => "Yearly",
|
||||
"days" => "days",
|
||||
"weeks" => "weeks",
|
||||
"months" => "months",
|
||||
"years" => "years",
|
||||
"external_url" => "Visit Externarl URL",
|
||||
"empty_page" => "Empty Page",
|
||||
// Subscription form
|
||||
"add_subscription" => "Add subscription",
|
||||
"edit_subscription" => "Edit subscription",
|
||||
"subscription_name" => "Subscription name",
|
||||
"logo_preview" => "Logo Preview",
|
||||
"search_logo" => "Search logo on the web",
|
||||
"web_search" => "Web search",
|
||||
"currency" => "Currency",
|
||||
"billing_cycle" => "Billing Cycle",
|
||||
"frequency" => "Frequency",
|
||||
"cycle" => "Cycle",
|
||||
"next_payment" => "Next Payment",
|
||||
"payment_method" => "Payment Method",
|
||||
"no_category" => "No category",
|
||||
"paid_by" => "Paid by",
|
||||
"url" => "URL",
|
||||
"notes" => "Notes",
|
||||
"enable_notifications" => "Enable Notifications for this subscription",
|
||||
"delete" => "Delete",
|
||||
"cancel" => "Cancel",
|
||||
"upload_logo" => "Upload Logo",
|
||||
// Statistics page
|
||||
'general_statistics' => "General Statistics",
|
||||
'active_subscriptions' => "Active Subscriptions",
|
||||
'monthly_cost' => "Monthly Cost",
|
||||
'yearly_cost' => "Yearly Cost",
|
||||
'average_monthly' => "Average Monthly Subscription Cost",
|
||||
'most_expensive' => "Most Expensive Subscription Cost",
|
||||
'amount_due' => "Amount due this month",
|
||||
'split_views' => "Split Views",
|
||||
'category_split' => "Category Split",
|
||||
'household_split' => "Household Split",
|
||||
// About page
|
||||
'about_and_credits' => "About and Credits",
|
||||
'license' => "License",
|
||||
'issues_and_requests' => "Issues and Requests",
|
||||
'the_author' => "The author",
|
||||
'icons' => "Icons",
|
||||
'payment_icons' => "Payment Icons",
|
||||
// Settings page
|
||||
'user_details' => "User Details",
|
||||
"household" => "Household",
|
||||
"save_member" => "Save Member",
|
||||
"delete_member" => "Delete Member",
|
||||
"cant_delete_member" => "Can't delete main member",
|
||||
"cant_delete_member_in_use" => "Can't delete member in use in subscription",
|
||||
"notifications" => "Notifications",
|
||||
"enable_email_notifications" => "Enable email notifications",
|
||||
"notify_me" => "Notify me",
|
||||
"day_before" => "day before",
|
||||
"days_before" => "days before",
|
||||
"smtp_address" => "SMTP Address",
|
||||
"port" => "Port",
|
||||
"smtp_username" => "SMTP Username",
|
||||
"smtp_password" => "SMTP Password",
|
||||
"from_email" => "From email (Optional)",
|
||||
"smtp_info" => "SMTP Password is transmitted and stored in plaintext. For security, please create an account just for this.",
|
||||
"categories" => "Categories",
|
||||
"save_category" => "Save Category",
|
||||
"delete_category" => "Delete Category",
|
||||
"cant_delete_category_in_use" => "Can't delete category in use in subscription",
|
||||
"currencies" => "Currencies",
|
||||
"save_currency" => "Save currency",
|
||||
"delete_currency" => "Delete currency",
|
||||
"cant_delete_main_currency" => "Can't delete main currency",
|
||||
"cant_delete_currency_in_use" => "Can't delete currency in use in subscription",
|
||||
"exchange_update" => "Exchange rates last updated on",
|
||||
"currency_info" => "Find the supported currencies and correct currency codes on",
|
||||
"currency_performance" => "For improved performance keep only the currencies you use.",
|
||||
"fixer_api_key" => "Fixer API Key",
|
||||
"api_key" => "API Key",
|
||||
"fixer_info" => "If you use multiple currencies, and want accurate statistics and sorting on the subscriptions, a FREE API Key from Fixer is necessary.",
|
||||
"get_key" => "Get your key at",
|
||||
"display_settings" => "Display Settings",
|
||||
"switch_theme" => "Switch Light / Dark Theme",
|
||||
"calculate_monthly_price" => "Calculate and show monthly price for all subscriptions",
|
||||
"convert_prices" => "Always convert and show prices on my main currency (slower)",
|
||||
"experimental_settings" => "Experimental Settings",
|
||||
"remove_background" => "Attempt to remove background of logos from image search (experimental)",
|
||||
"experimental_info" => "Experimental settings will probably not work perfectly.",
|
||||
"payment_methods" => "Payment Methods",
|
||||
"payment_methods_info" => "Click a payment method to disable / enable it.",
|
||||
"cant_delete_payment_method_in_use" => "Can't disable used payment method",
|
||||
"disable" => "Disable",
|
||||
"enable" => "Enable",
|
||||
"test" => "Test",
|
||||
"add" => "Add",
|
||||
"save" => "Save",
|
||||
// Toast
|
||||
"success" => "Success",
|
||||
// Endpoint responses
|
||||
"session_expired" => "Your session expired. Please login again",
|
||||
"fields_missing" => "Some fields are missing",
|
||||
"fill_all_fields" => "Please fill all fields",
|
||||
"fill_mandatory_fields" => "Please fill all mandatory fields",
|
||||
"error" => "Error",
|
||||
// Category
|
||||
"failed_add_category" => "Failed to add category",
|
||||
"failed_edit_category" => "Failed to edit category",
|
||||
"category_in_use" => "Category is in use in subscriptions and can't be removed",
|
||||
"failed_remove_category" => "Failed to remove category",
|
||||
"category_saved" => "Category saved",
|
||||
"category_removed" => "Category removed",
|
||||
// Currency
|
||||
"currency_saved" => "was saved.",
|
||||
"error_adding_currency" => "Error adding currency entry.",
|
||||
"failed_to_store_currency" => "Failed to store Currency on the Database.",
|
||||
"currency_in_use" => "Currency is in use in subscriptions and can't be deleted.",
|
||||
"currency_is_main" => "Currency is set as main currency and can't be deleted.",
|
||||
"failed_to_remove_currency" => "Failed to remove currency from the Database.",
|
||||
"failed_to_store_api_key" => "Failed to store API Key on the Database.",
|
||||
"invalid_api_key" => "Invalid API Key.",
|
||||
"api_key_saved" => "API key saved successfully",
|
||||
"currency_removed" => "Currency removed",
|
||||
// Household
|
||||
"failed_add_household" => "Failed to add household member",
|
||||
"failed_edit_household" => "Failed to edit household member",
|
||||
"failed_remove_household" => "Failed to remove household member",
|
||||
"household_in_use" => "Household member is in use in subscriptions and can't be removed",
|
||||
"member_saved" => "Member saved",
|
||||
"member_removed" => "Member removed",
|
||||
// Notifications
|
||||
"error_saving_notifications" => "Error saving notifications data.",
|
||||
"wallos_notification" => "Wallos Notification",
|
||||
"test_notification" => "This is a test notification. If you\'re seeing this, the configuration is correct.",
|
||||
"email_error" => "Error sending email",
|
||||
"notification_sent_successfuly" => "Notification sent successfuly",
|
||||
"notifications_settings_saved" => "Notification settings saved successfully.",
|
||||
// Payments
|
||||
"payment_in_use" => "Can't disable used payment method",
|
||||
"failed_update_payment" => "Failed to update payment method in the database",
|
||||
"enabled" => "enabled",
|
||||
"disabled" => "disabled",
|
||||
// Subscription
|
||||
"error_fetching_image" => "Error fetching image",
|
||||
"subscription_updated_successfuly" => "Subscription updated successfuly",
|
||||
"subscription_added_successfuly" => "Subscription added successfuly",
|
||||
"error_deleting_subscription" => "Error deleting subscription.",
|
||||
"invalid_request_method" => "Invalid request method.",
|
||||
// User
|
||||
"error_updating_user_data" => "Error updating user data.",
|
||||
"user_details_saved" => "User details saved",
|
||||
|
||||
];
|
||||
|
||||
|
||||
?>
|
||||
25
includes/i18n/getlang.php
Normal file
25
includes/i18n/getlang.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
$lang = "en";
|
||||
if (isset($_COOKIE['language'])) {
|
||||
$selectedLanguage = $_COOKIE['language'];
|
||||
|
||||
if (array_key_exists($selectedLanguage, $languages)) {
|
||||
$lang = $selectedLanguage;
|
||||
}
|
||||
}
|
||||
|
||||
function translate($text, $translations) {
|
||||
if (array_key_exists($text, $translations)) {
|
||||
return $translations[$text];
|
||||
} else {
|
||||
require_once 'en.php';
|
||||
if (array_key_exists($text, $i18n)) {
|
||||
return $i18n[$text];
|
||||
} else {
|
||||
return "[i18n String Missing]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
9
includes/i18n/languages.php
Normal file
9
includes/i18n/languages.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
// File Name => Language Name
|
||||
|
||||
$languages = [
|
||||
"en" => "English",
|
||||
"pt" => "Português",
|
||||
]
|
||||
|
||||
?>
|
||||
194
includes/i18n/pt.php
Normal file
194
includes/i18n/pt.php
Normal file
@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
$i18n = [
|
||||
// Registration page
|
||||
"create_account" => "Tem que criar uma conta antes de poder iniciar sessão",
|
||||
'username' => "Nome de utilizador",
|
||||
'password' => "Password",
|
||||
"email" => "Email",
|
||||
"confirm_password" => "Confirmar Password",
|
||||
"main_currency" => "Moeda Principal",
|
||||
"language" => "Linguagem",
|
||||
"passwords_dont_match" => "As passwords não coincidem",
|
||||
"registration_failed" => "O registo falhou. Tente novamente",
|
||||
"register" => "Registar",
|
||||
// Login Page
|
||||
'please_login' => "Por favor inicie sessão",
|
||||
'stay_logged_in' => "Manter sessão (30 dias)",
|
||||
'login' => "Iniciar Sessão",
|
||||
'login_failed' => "Dados de autenticação incorrectos",
|
||||
// Header
|
||||
'subscriptions' => "Subscrições",
|
||||
'stats' => "Estatísticas",
|
||||
'settings' => "Definições",
|
||||
'about' => "Sobre",
|
||||
'logout' => "Terminar Sessão",
|
||||
// Subscriptions page
|
||||
"subscription" => "Subscrição",
|
||||
"no_subscriptions_yet" => "Ainda não tem subscrições",
|
||||
"add_first_subscription" => "Adicionar primeira subscrição",
|
||||
'new_subscription' => "Nova Subscrição",
|
||||
'sort' => "Ordenar",
|
||||
'name' => "Nome",
|
||||
'last_added' => "Última Adicionada",
|
||||
'price' => "Preço",
|
||||
'next_payment' => "Próximo Pagamento",
|
||||
'member' => "Membro",
|
||||
'category' => "Categoria",
|
||||
'payment_method' => "Metodo de Pagamento",
|
||||
"Daily" => "Diario",
|
||||
"Weekly" => "Semanal",
|
||||
"Monthly" => "Mensal",
|
||||
"Yearly" => "Anual",
|
||||
"days" => "dias",
|
||||
"weeks" => "semanas",
|
||||
"months" => "meses",
|
||||
"years" => "anos",
|
||||
"external_url" => "Visitar URL Externo",
|
||||
"empty_page" => "Página Vazia",
|
||||
// Subscription form
|
||||
"add_subscription" => "Adicionar subscrição",
|
||||
"edit_subscription" => "Modificar subscrição",
|
||||
"subscription_name" => "Nome da subscrição",
|
||||
"logo_preview" => "Pre-visualisação do logo",
|
||||
"search_logo" => "Pesquisar logo na internet",
|
||||
"web_search" => "Pesquisa online",
|
||||
"currency" => "Moeda",
|
||||
"billing_cycle" => "Ciclo de faturação",
|
||||
"frequency" => "Frequencia",
|
||||
"Cycle" => "Ciclo",
|
||||
"next_payment" => "Próximo Pagamento",
|
||||
"payment_method" => "Método de Pagamento",
|
||||
"no_category" => "Sem categoria",
|
||||
"paid_by" => "Pago por",
|
||||
"url" => "URL",
|
||||
"notes" => "Notas",
|
||||
"enable_notifications" => "Activar notificações para esta subscrição",
|
||||
"delete" => "Remover",
|
||||
"cancel" => "Cancelar",
|
||||
"upload_logo" => "Enviar Logo",
|
||||
// Statistics page
|
||||
'general_statistics' => "Estatísticas Gerais",
|
||||
'active_subscriptions' => "Subscrições Activas",
|
||||
'monthly_cost' => "Custo Mensal",
|
||||
'yearly_cost' => "Custo Anual",
|
||||
'average_monthly' => "Custo Mensal Médio das Subscrições",
|
||||
'most_expensive' => "Custo da Subscrição Mais Cara",
|
||||
'amount_due' => "Quantia em dívida este mês",
|
||||
'split_views' => "Vistas Divididas",
|
||||
'category_split' => "Por Categoria",
|
||||
'household_split' => "Por Membro",
|
||||
// About page
|
||||
'about_and_credits' => "Sobre e Créditos",
|
||||
'license' => "Licença",
|
||||
'issues_and_requests' => "Problemas e Pedidos",
|
||||
'the_author' => "O Autor",
|
||||
'icons' => "Ícones",
|
||||
'payment_icons' => "Ícones de Pagamentos",
|
||||
// Settings page
|
||||
'user_details' => "Detalhes do utilizador",
|
||||
"household" => "Agregado",
|
||||
"save_member" => "Guardar Membro",
|
||||
"delete_member" => "Apagar Membro",
|
||||
"cant_delete_member" => "Não pode apagar o membro principal",
|
||||
"cant_delete_member_in_use" => "Não pode apagar membro em uso em subscrição",
|
||||
"notifications" => "Notificações",
|
||||
"enable_email_notifications" => "Activar notificações por email",
|
||||
"notify_me" => "Notificar-me",
|
||||
"day_before" => "dia antes",
|
||||
"days_before" => "dias antes",
|
||||
"smtp_address" => "Endereço SMTP",
|
||||
"port" => "Porto",
|
||||
"smtp_username" => "Utilizador SMTP",
|
||||
"smtp_password" => "Password SMTP",
|
||||
"from_email" => "Email de envio (Opcional)",
|
||||
"smtp_info" => "A Password é armazenada e transmitida em texto. Por segurança, crie uma conta só para esta finalidade.",
|
||||
"categories" => "Categorias",
|
||||
"save_category" => "Guardar Categoria",
|
||||
"delete_category" => "Apagar Categoria",
|
||||
"cant_delete_category_in_use" => "Não pode apagar categoria em uso em subscrição",
|
||||
"currencies" => "Moedas",
|
||||
"save_currency" => "Guardar moeda",
|
||||
"delete_currency" => "Apagar moeda",
|
||||
"cant_delete_main_currency" => "Não pode apagar a moeda principal",
|
||||
"cant_delete_currency_in_use" => "Não pode apagar moeda em uso em subscrição",
|
||||
"exchange_update" => "Taxas de conversão actualizadas em",
|
||||
"currency_info" => "Encontre a lista de moedas e os respectivos códigos em",
|
||||
"currency_performance" => "Por motivos de desempenho mantenha apenas as moedas que usa.",
|
||||
"fixer_api_key" => "Fixer API Key",
|
||||
"api_key" => "API Key",
|
||||
"fixer_info" => "Se usa multiplas moedas e deseja estatísticas correctas é necessário uma API Key grátis do Fixer.",
|
||||
"get_key" => "Obtenha a sua API Key em",
|
||||
"display_settings" => "Definições de visualização",
|
||||
"switch_theme" => "Trocar Tema Claro / Escuro",
|
||||
"calculate_monthly_price" => "Calcular e mostrar preço mensal para todas as subscrições",
|
||||
"convert_prices" => "Converter e mostrar todas as subscrições na moeda principal (mais lento)",
|
||||
"experimental_settings" => "Definições Experimentais",
|
||||
"remove_background" => "Tentar remover o fundo dos logos na pesquisa de imagem (experimental)",
|
||||
"experimental_info" => "Definições experimentais provavelmente não funcionarão correctamente.",
|
||||
"payment_methods" => "Métodos de Pagamento",
|
||||
"payment_methods_info" => "Clique num método de pagamento para o activar / desactivar.",
|
||||
"cant_delete_payment_method_in_use" => "Não pode desactivar metodo de pagamento em uso",
|
||||
"disable" => "Desactivar",
|
||||
"enable" => "Activar",
|
||||
"test" => "Testar",
|
||||
"add" => "Adicionar",
|
||||
"save" => "Guardar",
|
||||
// Toast
|
||||
"success" => "Sucesso",
|
||||
// Endpoint responses
|
||||
"session_expired" => "A sessão expirou. Por favor autentique-se.",
|
||||
"fields_missing" => "Alguns campos em falta",
|
||||
"fill_all_fields" => "Por favor preencha todos os campos",
|
||||
"fill_mandatory_fields" => "Por favor preencha todos os campos obrigatórios",
|
||||
"error" => "Erro",
|
||||
// Category
|
||||
"failed_add_category" => "Erro ao adicionar categoria",
|
||||
"failed_edit_category" => "Erro ao modificar categoria",
|
||||
"category_in_use" => "Categoria em uso em subscrição e não pode ser removida",
|
||||
"failed_remove_category" => "Erro ao remover categoria",
|
||||
"category_saved" => "Categoria guardada",
|
||||
"category_removed" => "Categoria removida",
|
||||
// Currency
|
||||
"currency_saved" => "guardada.",
|
||||
"error_adding_currency" => "Erro ao adicionar moeda.",
|
||||
"failed_to_store_currency" => "Erro ao guardar a moeda na base de dados.",
|
||||
"currency_in_use" => "Moeda em uso em subscrição e não pode ser removida.",
|
||||
"currency_is_main" => "A Moeda principal não pode ser removida.",
|
||||
"failed_to_remove_currency" => "Erro ao remover a moeda da base de dados.",
|
||||
"failed_to_store_api_key" => "Erro ao guardar API Key na base de dados.",
|
||||
"invalid_api_key" => "API Key inválida.",
|
||||
"api_key_saved" => "API key guardada",
|
||||
"currency_removed" => "Moeda removida",
|
||||
// Household
|
||||
"failed_add_household" => "Erro ao adicionar membro",
|
||||
"failed_edit_household" => "Erro ao modificar membro",
|
||||
"failed_remove_household" => "Erro ao remover membro",
|
||||
"household_in_use" => "Membro está em uso em subscrição e não pode er removido",
|
||||
"member_saved" => "Membro guardado",
|
||||
"member_removed" => "Membro removido",
|
||||
// Notifications
|
||||
"error_saving_notifications" => "Erro ao guardar os dados das notificaçoes.",
|
||||
"wallos_notification" => "Notificação Wallos",
|
||||
"test_notification" => "Isto é uma notificação de teste. Se está a ver isto a configuração está correcta.",
|
||||
"email_error" => "Erro ao enviar email",
|
||||
"notification_sent_successfuly" => "Notificação enviada com sucesso",
|
||||
"notifications_settings_saved" => "Configuração de notificações guardada.",
|
||||
// Payments
|
||||
"payment_in_use" => "Não pode desactivar método de pagamento em uso",
|
||||
"failed_update_payment" => "Erro ao actualizar método de pagamento na base de dados",
|
||||
"enabled" => "activado",
|
||||
"disabled" => "descativado",
|
||||
// Subscription
|
||||
"error_fetching_image" => "Erro ao obter a imagem",
|
||||
"subscription_updated_successfuly" => "Subscrição actualizada com sucesso",
|
||||
"subscription_added_successfuly" => "Subscrição adicionada com sucesso",
|
||||
"error_deleting_subscription" => "Erro ao remover subscrição.",
|
||||
"invalid_request_method" => "Método invalido.",
|
||||
// User
|
||||
"error_updating_user_data" => "Erro ao actualizar dados do utilizador.",
|
||||
"user_details_saved" => "Dados do utiliador actualizados.",
|
||||
|
||||
];
|
||||
|
||||
?>
|
||||
@ -1,18 +1,20 @@
|
||||
<?php
|
||||
|
||||
function getBillingCycle($cycle, $frequency) {
|
||||
require_once 'i18n/getlang.php';
|
||||
|
||||
function getBillingCycle($cycle, $frequency, $i18n) {
|
||||
switch ($cycle) {
|
||||
case 1:
|
||||
return $frequency == 1 ? "Daily" : $frequency . " days";
|
||||
return $frequency == 1 ? translate('Daily', $i18n) : $frequency . " " . translate('days', $i18n);
|
||||
break;
|
||||
case 2:
|
||||
return $frequency == 1 ? "Weekly" : $frequency . " weeks";
|
||||
return $frequency == 1 ? translate('Weekly', $i18n) : $frequency . " " . translate('weeks', $i18n);
|
||||
break;
|
||||
case 3:
|
||||
return $frequency == 1 ? "Monthly" : $frequency . " months";
|
||||
return $frequency == 1 ? translate('Monthly', $i18n) : $frequency . " " . translate('months', $i18n);
|
||||
break;
|
||||
case 4:
|
||||
return $frequency == 1 ? "Yearly" : $frequency . " years";
|
||||
return $frequency == 1 ? translate('YEarly', $i18n) : $frequency . " " . translate('years', $i18n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -54,7 +56,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
function printSubscriptions($subscriptions, $sort, $categories, $members) {
|
||||
function printSubscriptions($subscriptions, $sort, $categories, $members, $i18n) {
|
||||
if ($sort === "price") {
|
||||
usort($subscriptions, function($a, $b) {
|
||||
return $a['price'] < $b['price'] ? 1 : -1;
|
||||
@ -97,19 +99,19 @@
|
||||
<span class="cycle"><?= $subscription['billing_cycle'] ?></span>
|
||||
<span class="next"><?= $subscription['next_payment'] ?></span>
|
||||
<span class="price">
|
||||
<img src="<?= $subscription['payment_method_icon'] ?>" title="Payment Method: <?= $subscription['payment_method_name'] ?>"/>
|
||||
<img src="<?= $subscription['payment_method_icon'] ?>" title="<?= translate('payment_method', $i18n) ?>: <?= $subscription['payment_method_name'] ?>"/>
|
||||
<?= CurrencyFormatter::format($subscription['price'], $subscription['currency_code']) ?>
|
||||
</span>
|
||||
<span class="actions">
|
||||
<button class="image-button medium" onClick="openEditSubscription(event, <?= $subscription['id'] ?>)" name="edit">
|
||||
<img src="images/siteicons/edit.png" title="Edit subscription">
|
||||
<img src="images/siteicons/edit.png" title="<?= translate('edit_subscription', $i18n) ?>">
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="subscription-secondary">
|
||||
<span class="name"><img src="images/siteicons/subscription.png" alt="Subscription" /><?= $subscription['name'] ?></span>
|
||||
<span class="payer_user" title="Paid By"><img src="images/siteicons/payment.png" alt="Paid By" /><?= $members[$subscription['payer_user_id']]['name'] ?></span>
|
||||
<span class="category" title="Category" ><img src="images/siteicons/category.png" alt="Category" /><?= $categories[$subscription['category_id']]['name'] ?></span>
|
||||
<span class="name"><img src="images/siteicons/subscription.png" alt="<?= translate('subscription', $i18n) ?>" /><?= $subscription['name'] ?></span>
|
||||
<span class="payer_user" title="<?= translate('paid_by', $i18n) ?>"><img src="images/siteicons/payment.png" alt="<?= translate('paid_by', $i18n) ?>" /><?= $members[$subscription['payer_user_id']]['name'] ?></span>
|
||||
<span class="category" title="<?= translate('category', $i18n) ?>" ><img src="images/siteicons/category.png" alt="<?= translate('category', $i18n) ?>" /><?= $categories[$subscription['category_id']]['name'] ?></span>
|
||||
<?php
|
||||
if ($subscription['url'] != "") {
|
||||
$url = $subscription['url'];
|
||||
@ -117,7 +119,7 @@
|
||||
$url = "https://" . $url;
|
||||
}
|
||||
?>
|
||||
<span class="url" title="External Url"><a href="<?= $url ?>" target="_blank"><img src="images/siteicons/web.png" alt="URL" /></a></span>
|
||||
<span class="url" title="<?= translate('external_url', $i18n) ?>"><a href="<?= $url ?>" target="_blank"><img src="images/siteicons/web.png" alt="<?= translate('url', $i18n) ?>" /></a></span>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
77
index.php
77
index.php
@ -39,25 +39,30 @@
|
||||
$headerClass = count($subscriptions) > 0 ? "main-actions" : "main-actions hidden";
|
||||
$defaultLogo = $theme == "light" ? "images/wallos.png" : "images/walloswhite.png";
|
||||
?>
|
||||
<style>
|
||||
.logo-preview:after {
|
||||
content: '<?= translate('upload_logo', $i18n) ?>';
|
||||
}
|
||||
</style>
|
||||
<section class="contain">
|
||||
<header class="<?= $headerClass ?>" id="main-actions">
|
||||
<button class="button" onClick="addSubscription()">
|
||||
<img class="button-icon" src="images/siteicons/plusicon.png">
|
||||
New Subscription
|
||||
<?= translate('new_subscription', $i18n) ?>
|
||||
</button>
|
||||
<div class="sort-container">
|
||||
<button class="button" value="Sort" onClick="toggleSortOptions()" id="sort-button">
|
||||
<img src="images/siteicons/sort.png" class="button-icon" /> Sort
|
||||
<img src="images/siteicons/sort.png" class="button-icon" /> <?= translate('sort', $i18n) ?>
|
||||
</button>
|
||||
<div class="sort-options" id="sort-options">
|
||||
<ul>
|
||||
<li <?= $sort == "name" ? 'class="selected"' : "" ?> onClick="setSortOption('name')" id="sort-name">Name</li>
|
||||
<li <?= $sort == "id" ? 'class="selected"' : "" ?> onClick="setSortOption('id')" id="sort-id">Last Added</li>
|
||||
<li <?= $sort == "price" ? 'class="selected"' : "" ?> onClick="setSortOption('price')" id="sort-price">Price</li>
|
||||
<li <?= $sort == "next_payment" ? 'class="selected"' : "" ?> onClick="setSortOption('next_payment')" id="sort-next_payment">Next payment</li>
|
||||
<li <?= $sort == "payer_user_id" ? 'class="selected"' : "" ?> onClick="setSortOption('payer_user_id')" id="sort-payer_user_id">Member</li>
|
||||
<li <?= $sort == "category_id" ? 'class="selected"' : "" ?> onClick="setSortOption('category_id')" id="sort-category_id">Category</li>
|
||||
<li <?= $sort == "payment_method_id" ? 'class="selected"' : "" ?> onClick="setSortOption('payment_method_id')" id="sort-payment_method_id">Payment Method</li>
|
||||
<li <?= $sort == "name" ? 'class="selected"' : "" ?> onClick="setSortOption('name')" id="sort-name"><?= translate('name', $i18n) ?></li>
|
||||
<li <?= $sort == "id" ? 'class="selected"' : "" ?> onClick="setSortOption('id')" id="sort-id"><?= translate('last_added', $i18n) ?></li>
|
||||
<li <?= $sort == "price" ? 'class="selected"' : "" ?> onClick="setSortOption('price')" id="sort-price"><?= translate('price', $i18n) ?></li>
|
||||
<li <?= $sort == "next_payment" ? 'class="selected"' : "" ?> onClick="setSortOption('next_payment')" id="sort-next_payment"><?= translate('next_payment', $i18n) ?></li>
|
||||
<li <?= $sort == "payer_user_id" ? 'class="selected"' : "" ?> onClick="setSortOption('payer_user_id')" id="sort-payer_user_id"><?= translate('member', $i18n) ?></li>
|
||||
<li <?= $sort == "category_id" ? 'class="selected"' : "" ?> onClick="setSortOption('category_id')" id="sort-category_id"><?= translate('category', $i18n) ?></li>
|
||||
<li <?= $sort == "payment_method_id" ? 'class="selected"' : "" ?> onClick="setSortOption('payment_method_id')" id="sort-payment_method_id"><?= translate('payment_method', $i18n) ?></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@ -71,7 +76,7 @@
|
||||
$print[$id]['name']= $subscription['name'];
|
||||
$cycle = $subscription['cycle'];
|
||||
$frequency = $subscription['frequency'];
|
||||
$print[$id]['billing_cycle'] = getBillingCycle($cycle, $frequency);
|
||||
$print[$id]['billing_cycle'] = getBillingCycle($cycle, $frequency, $i18n);
|
||||
$paymentMethodId = $subscription['payment_method_id'];
|
||||
$print[$id]['currency_code'] = $currencies[$subscription['currency_id']]['code'];
|
||||
$currencyId = $subscription['currency_id'];
|
||||
@ -94,20 +99,20 @@
|
||||
}
|
||||
|
||||
if (isset($print)) {
|
||||
printSubscriptions($print, $sort, $categories, $members);
|
||||
printSubscriptions($print, $sort, $categories, $members, $i18n);
|
||||
}
|
||||
$db->close();
|
||||
|
||||
if (count($subscriptions) == 0) {
|
||||
?>
|
||||
<div class="empty-page">
|
||||
<img src="images/siteimages/empty.png" alt="Empty page" />
|
||||
<img src="images/siteimages/empty.png" alt="<?= translate('empty_page', $i18n) ?>" />
|
||||
<p>
|
||||
You don't have any subscriptions yet
|
||||
<?= translate('no_subscriptions_yet', $i18n) ?>
|
||||
</p>
|
||||
<button class="button" onClick="addSubscription()">
|
||||
<img class="button-icon" src="images/siteicons/plusicon.png">
|
||||
Add First Subscription
|
||||
<?= translate('add_first_subscription', $i18n) ?>
|
||||
</button>
|
||||
</div>
|
||||
<?php
|
||||
@ -117,25 +122,25 @@
|
||||
|
||||
<section class="subscription-form" id="subscription-form">
|
||||
<header>
|
||||
<h3 id="form-title">Add subscription</h3>
|
||||
<h3 id="form-title"><?= translate('add_subscription', $i18n) ?></h3>
|
||||
<span class="fa-solid fa-xmark close-form" onClick="closeAddSubscription()"></span>
|
||||
</header>
|
||||
<form action="endpoints/subscription/add.php" method="post" id="subs-form">
|
||||
|
||||
<div class="form-group-inline">
|
||||
<input type="text" id="name" name="name" placeholder="Subscription name" onchange="setSearchButtonStatus()" onkeypress="this.onchange();" onpaste="this.onchange();" oninput="this.onchange();" required>
|
||||
<input type="text" id="name" name="name" placeholder="<?= translate('subscription_name', $i18n) ?>" onchange="setSearchButtonStatus()" onkeypress="this.onchange();" onpaste="this.onchange();" oninput="this.onchange();" required>
|
||||
<label for="logo" class="logo-preview">
|
||||
<img src="" alt="Logo Preview" id="form-logo">
|
||||
<img src="" alt="<?= translate('logo_preview', $i18n) ?>" id="form-logo">
|
||||
</label>
|
||||
<input type="file" id="logo" name="logo" accept="image/jpeg, image/png" onchange="handleFileSelect(event)" class="hidden-input">
|
||||
<input type="hidden" id="logo-url" name="logo-url">
|
||||
<div id="logo-search-button" class="image-button medium disabled" title="Search logo on the web" onClick="searchLogo()">
|
||||
<div id="logo-search-button" class="image-button medium disabled" title="<?= translate('search_logo', $i18n) ?>" onClick="searchLogo()">
|
||||
<img src="images/siteicons/websearch.png">
|
||||
</div>
|
||||
<input type="hidden" id="id" name="id">
|
||||
<div id="logo-search-results" class="logo-search">
|
||||
<header>
|
||||
Web search
|
||||
<?= translate('web_search', $i18n) ?>
|
||||
<span class="fa-solid fa-xmark close-logo-search" onClick="closeLogoSearch()"></span>
|
||||
</header>
|
||||
<div id="logo-search-images"></div>
|
||||
@ -143,8 +148,8 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group-inline">
|
||||
<input type="number" step="0.01" id="price" name="price" placeholder="Price" required>
|
||||
<select id="currency" name="currency_id" placeholder="Currency">
|
||||
<input type="number" step="0.01" id="price" name="price" placeholder="<?= translate('price', $i18n) ?>" required>
|
||||
<select id="currency" name="currency_id" placeholder="<?= translate('add_subscription', $i18n) ?>">
|
||||
<?php
|
||||
foreach ($currencies as $currency) {
|
||||
$selected = ($currency['id'] == $main_currency) ? 'selected' : '';
|
||||
@ -156,16 +161,12 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="inline">
|
||||
<div class="split66">
|
||||
<label for="cycle">Billing Cycle</label>
|
||||
<label for="cycle"><?= translate('billing_cycle', $i18n) ?></label>
|
||||
<div class="inline">
|
||||
<select id="frequency" name="frequency" placeholder="Frequency">
|
||||
<select id="frequency" name="frequency" placeholder="<?= translate('frequency', $i18n) ?>">
|
||||
<?php
|
||||
foreach ($frequencies as $frequency) {
|
||||
?>
|
||||
@ -178,7 +179,7 @@
|
||||
<?php
|
||||
foreach ($cycles as $cycle) {
|
||||
?>
|
||||
<option value="<?= $cycle['id'] ?>" <?= $cycle['id'] == 3 ? "selected" : "" ?>><?= $cycle['name'] ?></option>
|
||||
<option value="<?= $cycle['id'] ?>" <?= $cycle['id'] == 3 ? "selected" : "" ?>><?= translate($cycle['name'], $i18n) ?></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
@ -186,14 +187,14 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="split33">
|
||||
<label for="next_payment">Next Payment</label>
|
||||
<label for="next_payment"><?= translate('next_payment', $i18n) ?></label>
|
||||
<input type="date" id="next_payment" name="next_payment" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="payment_method">Payment Method</label>
|
||||
<label for="payment_method"><?= translate('payment_method', $i18n) ?></label>
|
||||
<select id="payment_method" name="payment_method_id">
|
||||
<?php
|
||||
foreach ($payment_methods as $payment) {
|
||||
@ -208,7 +209,7 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="category">Category</label>
|
||||
<label for="category"><?= translate('category', $i18n) ?></label>
|
||||
<select id="category" name="category_id">
|
||||
<?php
|
||||
foreach ($categories as $category) {
|
||||
@ -224,7 +225,7 @@
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="payer_user">Paid by</label>
|
||||
<label for="payer_user"><?= translate('paid_by', $i18n) ?></label>
|
||||
<select id="payer_user" name="payer_user_id">
|
||||
<?php
|
||||
foreach ($members as $member) {
|
||||
@ -237,11 +238,11 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<input type="text" id="url" name="url" placeholder="URL">
|
||||
<input type="text" id="url" name="url" placeholder="<?= translate('url', $i18n) ?>">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<input type="text" id="notes" name="notes" placeholder="Notes">
|
||||
<input type="text" id="notes" name="notes" placeholder="<?= translate('notes', $i18n) ?>">
|
||||
</div>
|
||||
|
||||
<?php
|
||||
@ -249,16 +250,16 @@
|
||||
?>
|
||||
<div class="form-group-inline">
|
||||
<input type="checkbox" id="notifications" name="notifications">
|
||||
<label for="notifications">Enable Notifications for this subscription</label>
|
||||
<label for="notifications"><?= translate('enable_notifications', $i18n) ?></label>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="buttons">
|
||||
<input type="button" value="Delete" class="warning-button left" id="deletesub" style="display: none">
|
||||
<input type="button" value="Cancel" class="secondary-button" onClick="closeAddSubscription()">
|
||||
<input type="submit" value="Save" id="save-button">
|
||||
<input type="button" value="<?= translate('delete', $i18n) ?>" class="warning-button left" id="deletesub" style="display: none">
|
||||
<input type="button" value="<?= translate('cancel', $i18n) ?>" class="secondary-button" onClick="closeAddSubscription()">
|
||||
<input type="submit" value="<?= translate('save', $i18n) ?>" id="save-button">
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
24
login.php
24
login.php
@ -2,6 +2,10 @@
|
||||
require_once 'includes/connect.php';
|
||||
require_once 'includes/checkuser.php';
|
||||
|
||||
require_once 'includes/i18n/languages.php';
|
||||
require_once 'includes/i18n/getlang.php';
|
||||
require_once 'includes/i18n/' . $lang . '.php';
|
||||
|
||||
if ($userCount == 0) {
|
||||
header("Location: registration.php");
|
||||
exit();
|
||||
@ -25,7 +29,7 @@ if (isset($_POST['username']) && isset($_POST['password'])) {
|
||||
$password = $_POST['password'];
|
||||
$rememberMe = isset($_POST['remember']) ? true : false;
|
||||
|
||||
$query = "SELECT id, password, main_currency FROM user WHERE username = :username";
|
||||
$query = "SELECT id, password, main_currency, language FROM user WHERE username = :username";
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->bindValue(':username', $username, SQLITE3_TEXT);
|
||||
$result = $stmt->execute();
|
||||
@ -35,10 +39,13 @@ if (isset($_POST['username']) && isset($_POST['password'])) {
|
||||
$hashedPasswordFromDb = $row['password'];
|
||||
$userId = $row['id'];
|
||||
$main_currency = $row['main_currency'];
|
||||
$language = $row['language'];
|
||||
if (password_verify($password, $hashedPasswordFromDb)) {
|
||||
$_SESSION['username'] = $username;
|
||||
$_SESSION['loggedin'] = true;
|
||||
$_SESSION['main_currency'] = $main_currency;
|
||||
$cookieExpire = time() + (30 * 24 * 60 * 60);
|
||||
setcookie('language', $language, $cookieExpire, '/');
|
||||
if ($rememberMe) {
|
||||
$token = bin2hex(random_bytes(32));
|
||||
$addLoginTokens = "INSERT INTO login_tokens (user_id, token) VALUES (?, ?)";
|
||||
@ -47,9 +54,8 @@ if (isset($_POST['username']) && isset($_POST['password'])) {
|
||||
$addLoginTokensStmt->bindValue(2, $token, SQLITE3_TEXT);
|
||||
$addLoginTokensStmt->execute();
|
||||
$_SESSION['token'] = $token;
|
||||
$cookieExpire = time() + (30 * 24 * 60 * 60);
|
||||
$cookieValue = $username . "|" . $token . "|" . $main_currency;
|
||||
setcookie('wallos_login', $cookieValue , $cookieExpire, '/');
|
||||
setcookie('wallos_login', $cookieValue, $cookieExpire, '/');
|
||||
}
|
||||
$db->close();
|
||||
header("Location: /");
|
||||
@ -87,33 +93,33 @@ if (isset($_POST['username']) && isset($_POST['password'])) {
|
||||
}
|
||||
?>
|
||||
<p>
|
||||
Please login.
|
||||
<?= translate('please_login', $i18n) ?>
|
||||
</p>
|
||||
</header>
|
||||
<form action="login.php" method="post">
|
||||
<div class="form-group">
|
||||
<label for="username">Username:</label>
|
||||
<label for="username"><?= translate('username', $i18n) ?>:</label>
|
||||
<input type="text" id="username" name="username" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password:</label>
|
||||
<label for="password"><?= translate('password', $i18n) ?>:</label>
|
||||
<input type="password" id="password" name="password" required>
|
||||
</div>
|
||||
<div class="form-group-inline">
|
||||
<input type="checkbox" id="remember" name="remember">
|
||||
<label for="remember">Stay logged in (30 days)</label>
|
||||
<label for="remember"><?= translate('stay_logged_in', $i18n) ?></label>
|
||||
</div>
|
||||
<?php
|
||||
if ($loginFailed) {
|
||||
?>
|
||||
<sup class="error">
|
||||
Login details are incorrect.
|
||||
<?= translate('login_failed', $i18n) ?>.
|
||||
</sup>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<div class="form-group">
|
||||
<input type="submit" value="Login">
|
||||
<input type="submit" value="<?= translate('login', $i18n) ?>">
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
11
migrations/000005.php
Normal file
11
migrations/000005.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
// This migration adds a "language" column to the user table and sets all values to english.
|
||||
|
||||
/** @noinspection PhpUndefinedVariableInspection */
|
||||
$columnQuery = $db->query("SELECT * FROM pragma_table_info('user') where name='language'");
|
||||
$columnRequired = $columnQuery->fetchArray(SQLITE3_ASSOC) === false;
|
||||
|
||||
if ($columnRequired) {
|
||||
$db->exec('ALTER TABLE user ADD COLUMN language TEXT DEFAULT "en"');
|
||||
$db->exec('UPDATE user SET language = "en"');
|
||||
}
|
||||
@ -2,6 +2,10 @@
|
||||
require_once 'includes/connect.php';
|
||||
require_once 'includes/checkuser.php';
|
||||
|
||||
require_once 'includes/i18n/languages.php';
|
||||
require_once 'includes/i18n/getlang.php';
|
||||
require_once 'includes/i18n/' . $lang . '.php';
|
||||
|
||||
if ($userCount > 0) {
|
||||
header("Location: login.php");
|
||||
exit();
|
||||
@ -28,12 +32,13 @@ if (isset($_POST['username'])) {
|
||||
$password = $_POST['password'];
|
||||
$confirm_password = $_POST['confirm_password'];
|
||||
$main_currency = $_POST['main_currency'];
|
||||
$language = $_POST['language'];
|
||||
$avatar = "0";
|
||||
|
||||
if ($password != $confirm_password) {
|
||||
$passwordMismatch = true;
|
||||
} else {
|
||||
$query = "INSERT INTO user (username, email, password, main_currency, avatar) VALUES (:username, :email, :password, :main_currency, :avatar)";
|
||||
$query = "INSERT INTO user (username, email, password, main_currency, avatar, language) VALUES (:username, :email, :password, :main_currency, :avatar, :language)";
|
||||
$stmt = $db->prepare($query);
|
||||
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
|
||||
$stmt->bindValue(':username', $username, SQLITE3_TEXT);
|
||||
@ -41,6 +46,7 @@ if (isset($_POST['username'])) {
|
||||
$stmt->bindValue(':password', $hashedPassword, SQLITE3_TEXT);
|
||||
$stmt->bindValue(':main_currency', $main_currency, SQLITE3_TEXT);
|
||||
$stmt->bindValue(':avatar', $avatar, SQLITE3_TEXT);
|
||||
$stmt->bindValue(':language', $language, SQLITE3_TEXT);
|
||||
$result = $stmt->execute();
|
||||
|
||||
if ($result) {
|
||||
@ -80,6 +86,7 @@ if (isset($_POST['username'])) {
|
||||
<link rel="manifest" href="images/icon/site.webmanifest">
|
||||
<link rel="stylesheet" href="styles/login.css">
|
||||
<link rel="stylesheet" href="styles/login-dark-theme.css" id="dark-theme" <?= $theme == "light" ? "disabled" : "" ?>>
|
||||
<script type="text/javascript" src="scripts/registration.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
@ -93,28 +100,28 @@ if (isset($_POST['username'])) {
|
||||
}
|
||||
?>
|
||||
<p>
|
||||
You need to create an account before you're able to login.
|
||||
<?= translate('create_account', $i18n) ?>
|
||||
</p>
|
||||
</header>
|
||||
<form action="registration.php" method="post">
|
||||
<div class="form-group">
|
||||
<label for="username">Username:</label>
|
||||
<label for="username"><?= translate('username', $i18n) ?>:</label>
|
||||
<input type="text" id="username" name="username" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email">Email:</label>
|
||||
<label for="email"><?= translate('email', $i18n) ?>:</label>
|
||||
<input type="email" id="email" name="email" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password:</label>
|
||||
<label for="password"><?= translate('password', $i18n) ?>:</label>
|
||||
<input type="password" id="password" name="password" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="confirm_password">Confirm Password:</label>
|
||||
<label for="confirm_password"><?= translate('confirm_password', $i18n) ?>:</label>
|
||||
<input type="password" id="confirm_password" name="confirm_password" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="currency">Main Currency:</label>
|
||||
<label for="currency"><?= translate('main_currency', $i18n) ?>:</label>
|
||||
<select id="currency" name="main_currency" placeholder="Currency">
|
||||
<?php
|
||||
foreach ($currencies as $currency) {
|
||||
@ -125,11 +132,24 @@ if (isset($_POST['username'])) {
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="language"><?= translate('language', $i18n) ?>:</label>
|
||||
<select id="language" name="language" placeholder="Language" onchange="changeLanguage(this.value)">
|
||||
<?php
|
||||
foreach ($languages as $code => $name) {
|
||||
$selected = ($code === $lang) ? 'selected' : '';
|
||||
?>
|
||||
<option value="<?= $code ?>" <?= $selected ?>><?= $name ?></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
<?php
|
||||
if ($passwordMismatch) {
|
||||
?>
|
||||
<sup class="error">
|
||||
Passwords do not match.
|
||||
<?= translate('passwords_dont_match', $i18n) ?>
|
||||
</sup>
|
||||
<?php
|
||||
}
|
||||
@ -138,13 +158,13 @@ if (isset($_POST['username'])) {
|
||||
if ($registrationFailed) {
|
||||
?>
|
||||
<sup class="error">
|
||||
Registration failed, please try again.
|
||||
<?= translate('registration_failed', $i18n) ?>
|
||||
</sup>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<div class="form-group">
|
||||
<input type="submit" value="Register">
|
||||
<input type="submit" value="<?= translate('register', $i18n) ?>">
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
@ -15,7 +15,7 @@ function resetForm() {
|
||||
const id = document.querySelector("#id");
|
||||
id.value = "";
|
||||
const formTitle = document.querySelector("#form-title");
|
||||
formTitle.textContent = "Add subscription";
|
||||
formTitle.textContent = translate('add_subscription');
|
||||
const logo = document.querySelector("#form-logo");
|
||||
logo.src = "";
|
||||
logo.style = 'display: none';
|
||||
@ -35,7 +35,7 @@ function resetForm() {
|
||||
|
||||
function fillEditFormFields(subscription) {
|
||||
const formTitle = document.querySelector("#form-title");
|
||||
formTitle.textContent = "Edit subscription";
|
||||
formTitle.textContent = translate('edit_subscription');
|
||||
const logo = document.querySelector("#form-logo");
|
||||
const defaultLogo = window.theme && window.theme == "light" ? "images/wallos.png" : "images/walloswhite.png";
|
||||
const logoFile = subscription.logo !== null ? "images/uploads/logos/" + subscription.logo : defaultLogo;
|
||||
@ -91,19 +91,19 @@ function openEditSubscription(event, id) {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
} else {
|
||||
showErrorMessage("Failed to load subscription");
|
||||
showErrorMessage(translate('failed_to_load_subscription'));
|
||||
}
|
||||
})
|
||||
.then((data) => {
|
||||
if (data.error || data === "Error") {
|
||||
showErrorMessage("Failed to load subscription");
|
||||
showErrorMessage(translate('failed_to_load_subscription'));
|
||||
} else {
|
||||
const subscription = data;
|
||||
fillEditFormFields(subscription);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
showErrorMessage("Failed to load subscription");
|
||||
showErrorMessage(translate('failed_to_load_subscription'));
|
||||
});
|
||||
}
|
||||
|
||||
@ -145,11 +145,11 @@ function deleteSubscription(id) {
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
showSuccessMessage("Subscription deleted");
|
||||
showSuccessMessage(translate('subscription_deleted'));
|
||||
fetchSubscriptions();
|
||||
closeAddSubscription();
|
||||
} else {
|
||||
alert("Error deleting the subscription");
|
||||
alert(translate('error_deleting_subscription'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
@ -188,7 +188,7 @@ function searchLogo() {
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error fetching image results:", error);
|
||||
console.error(translate('error_fetching_image_results'), error);
|
||||
});
|
||||
} else {
|
||||
nameInput.focus();
|
||||
@ -243,7 +243,7 @@ function fetchSubscriptions() {
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error reloading subscriptions:", error);
|
||||
console.error(translate('error_reloading_subscription'), error);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
34
scripts/i18n/en.js
Normal file
34
scripts/i18n/en.js
Normal file
@ -0,0 +1,34 @@
|
||||
let i18n = {
|
||||
// Dashboard
|
||||
'error_reloading_subscription': 'Error reloading subscription:',
|
||||
'error_fetching_image_results': 'Error fetching image results:',
|
||||
'subscription_deleted': 'Subscription deleted',
|
||||
'error_deleting_subscription': "Error deleting subscription",
|
||||
'failed_to_load_subscription': "Failed to load subscription",
|
||||
'edit_subscription': "Edit subscription",
|
||||
'add_subscription': "Add subscription",
|
||||
// Settings
|
||||
'network_response_error': "Network response was not ok",
|
||||
'failed_add_member': 'Failed to add member',
|
||||
'member': 'Member',
|
||||
'save_member': 'Save member',
|
||||
'delete_member': 'Delete member',
|
||||
'failed_remove_member': 'Failed to remove member',
|
||||
'failed_save_member': 'Failed to sabe member',
|
||||
'failed_add_category': 'Failed to add categpry',
|
||||
'category': 'Category',
|
||||
'save_category': 'Save category',
|
||||
'delete_category': 'Delete category',
|
||||
'failed_remove_category': 'Failed to remove category',
|
||||
'currency': 'Currency',
|
||||
'currency_code': 'Currency code',
|
||||
'save_currency': 'Save currency',
|
||||
'delete_currency': 'Delete currency',
|
||||
'failed_remove_currency': 'Failed to remove currency',
|
||||
'failed_save_currency': 'Failed to save currency',
|
||||
'cant_disable_payment_in_use': 'Can\'t disable payment in use',
|
||||
'failed_save_payment_method': 'Failed to sabe payment method',
|
||||
'unknown_error': 'Unknown error, please try again.',
|
||||
'error_saving_notification_data': 'Error saving notification data',
|
||||
'error_sending_notification': 'Error sending notification',
|
||||
};
|
||||
7
scripts/i18n/getlang.js
Normal file
7
scripts/i18n/getlang.js
Normal file
@ -0,0 +1,7 @@
|
||||
function translate(key) {
|
||||
if (i18n[key]) {
|
||||
return i18n[key];
|
||||
} else {
|
||||
return "[Translation Missing]";
|
||||
}
|
||||
}
|
||||
34
scripts/i18n/pt.js
Normal file
34
scripts/i18n/pt.js
Normal file
@ -0,0 +1,34 @@
|
||||
let i18n = {
|
||||
// Dashboard
|
||||
'error_reloading_subscription': 'Erro ao carregar a subscrição:',
|
||||
'error_fetching_image_results': 'Erro ao obter imagens:',
|
||||
'subscription_deleted': 'Subscrição eliminada',
|
||||
'error_deleting_subscription': 'Erro ao eliminar a subscrição',
|
||||
'failed_to_load_subscription': 'Falha ao carregar a subscrição',
|
||||
'edit_subscription': 'Editar subscrição',
|
||||
'add_subscription': 'Adicionar subscrição',
|
||||
// Settings
|
||||
'network_response_error': 'Erro de resposta de rede',
|
||||
'failed_add_member': 'Falha ao adicionar membro',
|
||||
'member': 'Membro',
|
||||
'save_member': 'Guardar membro',
|
||||
'delete_member': 'Remover membro',
|
||||
'failed_remove_member': 'Erro ao remover membro',
|
||||
'failed_save_member': 'Erro ao guardar membro',
|
||||
'failed_add_category': 'Erro ao adicionar categoria',
|
||||
'category': 'Categoria',
|
||||
'save_category': 'Guardar categoria',
|
||||
'delete_category': 'Remover categoria',
|
||||
'failed_remove_category': 'Erro ao remover categoria',
|
||||
'currency': 'Moeda',
|
||||
'currency_code': 'Código de moeda',
|
||||
'save_currency': 'Guardar moeda',
|
||||
'delete_currency': 'Remover moeda',
|
||||
'failed_remove_currency': 'Erro ao remover moeda',
|
||||
'failed_save_currency': 'Erro ao guardar moeda',
|
||||
'cant_disable_payment_in_use': 'Não é possível desativar pagamento em uso',
|
||||
'failed_save_payment_method': 'Erro ao guardar método de pagamento',
|
||||
'unknown_error': 'Erro desconhecido, por favor, tente novamente.',
|
||||
'error_saving_notification_data': 'Erro ao guardar dados de notificação',
|
||||
'error_sending_notification': 'Erro ao enviar notificação',
|
||||
};
|
||||
58
scripts/registration.js
Normal file
58
scripts/registration.js
Normal file
@ -0,0 +1,58 @@
|
||||
function setCookie(name, value, days) {
|
||||
var expires = "";
|
||||
if (days) {
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||
expires = "; expires=" + date.toUTCString();
|
||||
}
|
||||
document.cookie = name + "=" + value + expires + "; path=/";
|
||||
}
|
||||
|
||||
function storeFormFieldValue(fieldId) {
|
||||
var fieldElement = document.getElementById(fieldId);
|
||||
if (fieldElement) {
|
||||
localStorage.setItem(fieldId, fieldElement.value);
|
||||
}
|
||||
}
|
||||
|
||||
function storeFormFields() {
|
||||
storeFormFieldValue('username');
|
||||
storeFormFieldValue('email');
|
||||
storeFormFieldValue('password');
|
||||
storeFormFieldValue('confirm_password');
|
||||
storeFormFieldValue('currency');
|
||||
}
|
||||
|
||||
function restoreFormFieldValue(fieldId) {
|
||||
var fieldElement = document.getElementById(fieldId);
|
||||
if (fieldElement) {
|
||||
fieldElement.value = localStorage.getItem(fieldId) || '';
|
||||
}
|
||||
}
|
||||
|
||||
function restoreFormFields() {
|
||||
restoreFormFieldValue('username');
|
||||
restoreFormFieldValue('email');
|
||||
restoreFormFieldValue('password');
|
||||
restoreFormFieldValue('confirm_password');
|
||||
restoreFormFieldValue('currency');
|
||||
}
|
||||
|
||||
function removeFromStorage() {
|
||||
localStorage.removeItem('username');
|
||||
localStorage.removeItem('email');
|
||||
localStorage.removeItem('password');
|
||||
localStorage.removeItem('confirm_password');
|
||||
localStorage.removeItem('currency');
|
||||
}
|
||||
|
||||
function changeLanguage(selectedLanguage) {
|
||||
storeFormFields();
|
||||
setCookie("language", selectedLanguage, 365);
|
||||
location.reload();
|
||||
}
|
||||
|
||||
window.onload = function () {
|
||||
restoreFormFields();
|
||||
removeFromStorage();
|
||||
};
|
||||
@ -24,8 +24,8 @@ function addMemberButton(memberId) {
|
||||
fetch(url)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
showErrorMessage("Failed to add member");
|
||||
throw new Error(translate('network_response_error'));
|
||||
showErrorMessage(translate('failed_add_member'));
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
@ -39,9 +39,9 @@ function addMemberButton(memberId) {
|
||||
|
||||
let input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.placeholder = "Member";
|
||||
input.placeholder = translate('member');
|
||||
input.name = "member";
|
||||
input.value = "Member";
|
||||
input.value = translate('member');
|
||||
|
||||
let editLink = document.createElement("button");
|
||||
editLink.className = "image-button medium"
|
||||
@ -52,7 +52,7 @@ function addMemberButton(memberId) {
|
||||
|
||||
let editImage = document.createElement("img");
|
||||
editImage.src = "images/siteicons/save.png";
|
||||
editImage.title = "Save Member";
|
||||
editImage.title = translate('save_member');
|
||||
|
||||
editLink.appendChild(editImage);
|
||||
|
||||
@ -65,7 +65,7 @@ function addMemberButton(memberId) {
|
||||
|
||||
let deleteImage = document.createElement("img");
|
||||
deleteImage.src = "images/siteicons/delete.png";
|
||||
deleteImage.title = "Delete Member";
|
||||
deleteImage.title = translate('delete_member');
|
||||
|
||||
deleteLink.appendChild(deleteImage);
|
||||
|
||||
@ -80,7 +80,7 @@ function addMemberButton(memberId) {
|
||||
document.getElementById("addMember").disabled = false;
|
||||
})
|
||||
.catch(error => {
|
||||
showErrorMessage("Failed to add member");
|
||||
showErrorMessage(translate('failed_add_member'));
|
||||
document.getElementById("addMember").disabled = false;
|
||||
});
|
||||
|
||||
@ -91,7 +91,7 @@ function removeMember(memberId) {
|
||||
fetch(url)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
throw new Error(translate('network_response_error'));
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
@ -101,13 +101,13 @@ function removeMember(memberId) {
|
||||
if (divToRemove) {
|
||||
divToRemove.parentNode.removeChild(divToRemove);
|
||||
}
|
||||
showSuccessMessage("Member removed");
|
||||
showSuccessMessage(responseData.message);
|
||||
} else {
|
||||
showErrorMessage(responseData.errorMessage || "Failed to remove member");
|
||||
showErrorMessage(responseData.errorMessage || translate('failed_remove_member'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showErrorMessage("Failed to remove member");
|
||||
showErrorMessage(translate('failed_remove_member'));
|
||||
});
|
||||
}
|
||||
|
||||
@ -124,19 +124,19 @@ function editMember(memberId) {
|
||||
.then(response => {
|
||||
saveButton.classList.remove("disabled");
|
||||
if (!response.ok) {
|
||||
showErrorMessage("Failed to save member");
|
||||
showErrorMessage(translate('failed_save_member'));
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(responseData => {
|
||||
if (responseData.success) {
|
||||
showSuccessMessage("Member saved");
|
||||
showSuccessMessage(responseData.message);
|
||||
} else {
|
||||
showErrorMessage(responseData.errorMessage || "Failed to save member");
|
||||
showErrorMessage(responseData.errorMessage || translate('failed_save_member'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showErrorMessage("Failed to save member");
|
||||
showErrorMessage(translate('failed_save_member'));
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -147,8 +147,8 @@ function addCategoryButton(categoryId) {
|
||||
fetch(url)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
showErrorMessage("Failed to add category");
|
||||
throw new Error(translate('network_response_error'));
|
||||
showErrorMessage(translate('failed_add_category'));
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
@ -162,9 +162,9 @@ function addCategoryButton(categoryId) {
|
||||
|
||||
let input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.placeholder = "Category";
|
||||
input.placeholder = translate('category');
|
||||
input.name = "category";
|
||||
input.value = "Category";
|
||||
input.value = translate('category');
|
||||
|
||||
let editLink = document.createElement("button");
|
||||
editLink.className = "image-button medium"
|
||||
@ -175,7 +175,7 @@ function addCategoryButton(categoryId) {
|
||||
|
||||
let editImage = document.createElement("img");
|
||||
editImage.src = "images/siteicons/save.png";
|
||||
editImage.title = "Save Category";
|
||||
editImage.title = translate('save_category');
|
||||
|
||||
editLink.appendChild(editImage);
|
||||
|
||||
@ -188,7 +188,7 @@ function addCategoryButton(categoryId) {
|
||||
|
||||
let deleteImage = document.createElement("img");
|
||||
deleteImage.src = "images/siteicons/delete.png";
|
||||
deleteImage.title = "Delete Category";
|
||||
deleteImage.title = translate('delete_category');
|
||||
|
||||
deleteLink.appendChild(deleteImage);
|
||||
|
||||
@ -203,7 +203,7 @@ function addCategoryButton(categoryId) {
|
||||
document.getElementById("addCategory").disabled = false;
|
||||
})
|
||||
.catch(error => {
|
||||
showErrorMessage("Failed to add category");
|
||||
showErrorMessage(translate('failed_add_category'));
|
||||
document.getElementById("addCategory").disabled = false;
|
||||
});
|
||||
|
||||
@ -214,7 +214,7 @@ function removeCategory(categoryId) {
|
||||
fetch(url)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
throw new Error(translate('network_response_error'));
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
@ -224,13 +224,13 @@ function removeCategory(categoryId) {
|
||||
if (divToRemove) {
|
||||
divToRemove.parentNode.removeChild(divToRemove);
|
||||
}
|
||||
showSuccessMessage("Category removed");
|
||||
showSuccessMessage(responseData.message);
|
||||
} else {
|
||||
showErrorMessage(responseData.errorMessage || "Failed to remove category");
|
||||
showErrorMessage(responseData.errorMessage || translate('failed_remove_category'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showErrorMessage("Failed to remove category");
|
||||
showErrorMessage(translate('failed_remove_category'));
|
||||
});
|
||||
}
|
||||
|
||||
@ -247,19 +247,19 @@ function editCategory(categoryId) {
|
||||
.then(response => {
|
||||
saveButton.classList.remove("disabled");
|
||||
if (!response.ok) {
|
||||
showErrorMessage("Failed to save category");
|
||||
showErrorMessage(translate('failed_save_category'));
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(responseData => {
|
||||
if (responseData.success) {
|
||||
showSuccessMessage("Category saved");
|
||||
showSuccessMessage(responseData.message);
|
||||
} else {
|
||||
showErrorMessage(responseData.errorMessage || "Failed to save category");
|
||||
showErrorMessage(responseData.errorMessage || translate('failed_save_category'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showErrorMessage("Failed to save category");
|
||||
showErrorMessage(translate('failed_save_category'));
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -270,7 +270,7 @@ function addCurrencyButton(currencyId) {
|
||||
fetch(url)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
throw new Error(translate('network_response_error'));
|
||||
showErrorMessage(response.text());
|
||||
}
|
||||
return response.text();
|
||||
@ -292,13 +292,13 @@ function addCurrencyButton(currencyId) {
|
||||
|
||||
let inputName = document.createElement("input");
|
||||
inputName.type = "text";
|
||||
inputName.placeholder = "Currency";
|
||||
inputName.placeholder = translate('currency');
|
||||
inputName.name = "currency";
|
||||
inputName.value = "Currency";
|
||||
inputName.value = translate('currency');
|
||||
|
||||
let inputCode = document.createElement("input");
|
||||
inputCode.type = "text";
|
||||
inputCode.placeholder = "Currency Code";
|
||||
inputCode.placeholder = translate('currency_code');
|
||||
inputCode.name = "code";
|
||||
inputCode.value = "CODE";
|
||||
|
||||
@ -311,7 +311,7 @@ function addCurrencyButton(currencyId) {
|
||||
|
||||
let editImage = document.createElement("img");
|
||||
editImage.src = "images/siteicons/save.png";
|
||||
editImage.title = "Save Currency";
|
||||
editImage.title = translate('save_currency');
|
||||
|
||||
editLink.appendChild(editImage);
|
||||
|
||||
@ -324,7 +324,7 @@ function addCurrencyButton(currencyId) {
|
||||
|
||||
let deleteImage = document.createElement("img");
|
||||
deleteImage.src = "images/siteicons/delete.png";
|
||||
deleteImage.title = "Delete Currency";
|
||||
deleteImage.title = translate('delete_currency');
|
||||
|
||||
deleteLink.appendChild(deleteImage);
|
||||
|
||||
@ -352,23 +352,23 @@ function removeCurrency(currencyId) {
|
||||
fetch(url)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error("There was an error removing the currency");
|
||||
throw new Error(translate('network_response_error'));
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showSuccessMessage("Currency removed");
|
||||
showSuccessMessage(data.message);
|
||||
let divToRemove = document.querySelector(`[data-currencyid="${currencyId}"]`);
|
||||
if (divToRemove) {
|
||||
divToRemove.parentNode.removeChild(divToRemove);
|
||||
}
|
||||
} else {
|
||||
showErrorMessage(data.message || "Failed to remove currency");
|
||||
showErrorMessage(data.message || translate('failed_remove_currency'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showErrorMessage(error.message || "There was an error removing the currency");
|
||||
showErrorMessage(error.message || translate('failed_remove_currency'));
|
||||
});
|
||||
}
|
||||
|
||||
@ -388,7 +388,7 @@ function editCurrency(currencyId) {
|
||||
fetch(url)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error("There was an error saving the currency");
|
||||
throw new Error(translate('network_response_error'));
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
@ -396,17 +396,17 @@ function editCurrency(currencyId) {
|
||||
if (data.success) {
|
||||
saveButton.classList.remove("disabled");
|
||||
saveButton.disabled = false;
|
||||
showSuccessMessage(currencyName + " was saved");
|
||||
showSuccessMessage(decodeURI(data.message));
|
||||
} else {
|
||||
saveButton.classList.remove("disabled");
|
||||
saveButton.disabled = false;
|
||||
showErrorMessage(data.message || "Failed to save currency");
|
||||
showErrorMessage(data.message || translate('failed_save_currency'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
saveButton.classList.remove("disabled");
|
||||
saveButton.disabled = false;
|
||||
showErrorMessage(error.message || "There was an error saving the currency");
|
||||
showErrorMessage(error.message || translate('failed_save_currency'));
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -415,7 +415,7 @@ function togglePayment(paymentId) {
|
||||
const element = document.querySelector(`div[data-paymentid="${paymentId}"]`);
|
||||
|
||||
if (element.dataset.inUse === 'yes') {
|
||||
return showErrorMessage('Can\'t delete used payment method');
|
||||
return showErrorMessage(translate(cant_disable_payment_in_use));
|
||||
}
|
||||
|
||||
const newEnabledState = element.dataset.enabled === '1' ? '0' : '1';
|
||||
@ -425,18 +425,18 @@ function togglePayment(paymentId) {
|
||||
|
||||
fetch(url).then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error("There was an error saving the payments method");
|
||||
throw new Error(translate('network_response_error'));
|
||||
}
|
||||
return response.json();
|
||||
}).then(data => {
|
||||
if (data.success) {
|
||||
element.dataset.enabled = newEnabledState;
|
||||
showSuccessMessage(`${paymentMethodName} was saved`);
|
||||
showSuccessMessage(`${paymentMethodName} ${data.message}`);
|
||||
} else {
|
||||
showErrorMessage(data.message || "Failed to save payments method");
|
||||
showErrorMessage(data.message || translate('failed_save_payment_method'));
|
||||
}
|
||||
}).catch(error => {
|
||||
showErrorMessage(error.message || "There was an error saving the payments method");
|
||||
showErrorMessage(error.message || translate('failed_save_payment_method'));
|
||||
});
|
||||
}
|
||||
|
||||
@ -457,14 +457,14 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
document.getElementById("avatar").src = "images/avatars/" + newAvatar + ".svg";
|
||||
var newUsername = document.getElementById("username").value;
|
||||
document.getElementById("user").textContent = newUsername;
|
||||
showSuccessMessage("User details saved");
|
||||
showSuccessMessage(data.message);
|
||||
} else {
|
||||
showErrorMessage(data.errorMessage);
|
||||
}
|
||||
document.getElementById("userSubmit").disabled = false;
|
||||
})
|
||||
.catch(error => {
|
||||
showErrorMessage("Unknown error, please try again");
|
||||
showErrorMessage(translate('unknown_error'));
|
||||
});
|
||||
});
|
||||
|
||||
@ -484,7 +484,7 @@ function addFixerKeyButton() {
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showSuccessMessage("API key saved successfully");
|
||||
showSuccessMessage(data.message);
|
||||
document.getElementById("addFixerKey").disabled = false;
|
||||
} else {
|
||||
showErrorMessage(data.message);
|
||||
@ -529,14 +529,14 @@ function saveNotificationsButton() {
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showSuccessMessage("Notification settings saved successfully.");
|
||||
showSuccessMessage(data.message);
|
||||
} else {
|
||||
showErrorMessage(data.errorMessage);
|
||||
}
|
||||
button.disabled = false;
|
||||
})
|
||||
.catch(error => {
|
||||
showErrorMessage("Error saving notification data");
|
||||
showErrorMessage(translate('error_saving_notification_data'));
|
||||
button.disabled = false;
|
||||
});
|
||||
}
|
||||
@ -569,14 +569,14 @@ function testNotificationButton() {
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showSuccessMessage("Notification sent successfully.");
|
||||
showSuccessMessage(data.message);
|
||||
} else {
|
||||
showErrorMessage(data.errorMessage);
|
||||
}
|
||||
button.disabled = false;
|
||||
})
|
||||
.catch(error => {
|
||||
showErrorMessage("Error sending notification");
|
||||
showErrorMessage(translate('error_sending_notification'));
|
||||
button.disabled = false;
|
||||
});
|
||||
}
|
||||
|
||||
119
settings.php
119
settings.php
@ -4,7 +4,7 @@
|
||||
<section class="contain settings">
|
||||
<section class="account-section">
|
||||
<header>
|
||||
<h2>User details</h2>
|
||||
<h2><?= translate('user_details', $i18n) ?></h2>
|
||||
</header>
|
||||
<form action="endpoints/user/saveuser.php" method="post" id="userForm">
|
||||
<div class="user-form">
|
||||
@ -35,19 +35,19 @@
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="form-group">
|
||||
<label for="username">Username:</label>
|
||||
<label for="username"><?= translate('username', $i18n) ?>:</label>
|
||||
<input type="text" id="username" name="username" value="<?= $userData['username'] ?>" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email">Email:</label>
|
||||
<label for="email"><?= translate('email', $i18n) ?>:</label>
|
||||
<input type="email" id="email" name="email" value="<?= $userData['email'] ?>" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password:</label>
|
||||
<label for="password"><?= translate('password', $i18n) ?>:</label>
|
||||
<input type="password" id="password" name="password">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="confirm_password">Confirm Password:</label>
|
||||
<label for="confirm_password"><?= translate('confirm_password', $i18n) ?>:</label>
|
||||
<input type="password" id="confirm_password" name="confirm_password">
|
||||
</div>
|
||||
<?php
|
||||
@ -60,7 +60,7 @@
|
||||
}
|
||||
?>
|
||||
<div class="form-group">
|
||||
<label for="currency">Main Currency:</label>
|
||||
<label for="currency"><?= translate('main_currency', $i18n) ?>:</label>
|
||||
<select id="currency" name="main_currency" placeholder="Currency">
|
||||
<?php
|
||||
foreach ($currencies as $currency) {
|
||||
@ -72,10 +72,23 @@
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="language"><?= translate('language', $i18n) ?>:</label>
|
||||
<select id="language" name="language" placeholder="Language">
|
||||
<?php
|
||||
foreach ($languages as $code => $name) {
|
||||
$selected = ($code === $lang) ? 'selected' : '';
|
||||
?>
|
||||
<option value="<?= $code ?>" <?= $selected ?>><?= $name ?></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<input type="submit" value="Save" id="userSubmit"/>
|
||||
<input type="submit" value="<?= translate('save', $i18n) ?>" id="userSubmit"/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@ -96,7 +109,7 @@
|
||||
|
||||
<section class="account-section">
|
||||
<header>
|
||||
<h2>Household</h2>
|
||||
<h2><?= translate('household', $i18n) ?></h2>
|
||||
</header>
|
||||
<div class="account-members">
|
||||
<div id="householdMembers">
|
||||
@ -106,19 +119,19 @@
|
||||
<div class="form-group-inline" data-memberid="<?= $member['id'] ?>">
|
||||
<input type="text" name="member" value="<?= $member['name'] ?>" placeholder="Member">
|
||||
<button class="image-button medium" onClick="editMember(<?= $member['id'] ?>)" name="save">
|
||||
<img src="images/siteicons/save.png" title="Save Member">
|
||||
<img src="images/siteicons/save.png" title="<?= translate('save_member', $i18n) ?>">
|
||||
</button>
|
||||
<?php
|
||||
if ($member['id'] != 1) {
|
||||
?>
|
||||
<button class="image-button medium" onClick="removeMember(<?= $member['id'] ?>)">
|
||||
<img src="images/siteicons/delete.png" title="Delete Member">
|
||||
<img src="images/siteicons/delete.png" title="<?= translate('delete_member', $i18n) ?>">
|
||||
</button>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
<button class="image-button medium disabled">
|
||||
<img src="images/siteicons/delete.png" title="Can't delete main member">
|
||||
<img src="images/siteicons/delete.png" title="<?= translate('cant_delete_member', $i18n) ?>">
|
||||
</button>
|
||||
<?php
|
||||
}
|
||||
@ -129,7 +142,7 @@
|
||||
?>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<input type="submit" value="Add" id="addMember" onClick="addMemberButton()"/>
|
||||
<input type="submit" value="<?= translate('add', $i18n) ?>" id="addMember" onClick="addMemberButton()"/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@ -158,23 +171,23 @@
|
||||
|
||||
<section class="account-section">
|
||||
<header>
|
||||
<h2>Notifications</h2>
|
||||
<h2><?= translate('notifications', $i18n) ?></h2>
|
||||
</header>
|
||||
<div class="account-notifications">
|
||||
<div class="form-group-inline">
|
||||
<input type="checkbox" id="notifications" name="notifications" <?= $notifications['enabled'] ? "checked" : "" ?>>
|
||||
<label for="notifications">Enable email notifications</label>
|
||||
<label for="notifications"><?= translate('enable_email_notifications', $i18n) ?></label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="days">Notify me: </label>
|
||||
<label for="days"><?= translate('notify_me', $i18n) ?>:</label>
|
||||
<select name="days" id="days">
|
||||
<?php
|
||||
for ($i = 1; $i <= 7; $i++) {
|
||||
$dayText = $i > 1 ? "days" : "day";
|
||||
$dayText = $i > 1 ? translate('days_before', $i18n) : translate('day_before', $i18n);
|
||||
$selected = $i == $notifications['days'] ? "selected" : "";
|
||||
?>
|
||||
<option value="<?= $i ?>" <?= $selected ?>>
|
||||
<?= $i ?> <?= $dayText ?> before
|
||||
<?= $i ?> <?= $dayText ?>
|
||||
</option>
|
||||
<?php
|
||||
}
|
||||
@ -182,27 +195,26 @@
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group-inline">
|
||||
<input type="text" name="smtpaddress" id="smtpaddress" placeholder="SMTP Address" value="<?= $notifications['smtp_address'] ?>" />
|
||||
<input type="text" name="smtpport" id="smtpport" placeholder="Port" class="one-third" value="<?= $notifications['smtp_port'] ?>" />
|
||||
<input type="text" name="smtpaddress" id="smtpaddress" placeholder="<?= translate('smtp_address', $i18n) ?>" value="<?= $notifications['smtp_address'] ?>" />
|
||||
<input type="text" name="smtpport" id="smtpport" placeholder="<?= translate('port', $i18n) ?>" class="one-third" value="<?= $notifications['smtp_port'] ?>" />
|
||||
</div>
|
||||
<div class="form-group-inline">
|
||||
<input type="text" name="smtpusername" id="smtpusername" placeholder="SMTP Username" value="<?= $notifications['smtp_username'] ?>" />
|
||||
<input type="text" name="smtpusername" id="smtpusername" placeholder="<?= translate('smtp_username', $i18n) ?>" value="<?= $notifications['smtp_username'] ?>" />
|
||||
</div>
|
||||
<div class="form-group-inline">
|
||||
<input type="password" name="smtppassword" id="smtppassword" placeholder="SMTP Password" value="<?= $notifications['smtp_password'] ?>" />
|
||||
<input type="password" name="smtppassword" id="smtppassword" placeholder="<?= translate('smtp_password', $i18n) ?>" value="<?= $notifications['smtp_password'] ?>" />
|
||||
</div>
|
||||
<div class="form-group-inline">
|
||||
<input type="text" name="fromemail" id="fromemail" placeholder="From email (Optional)" value="<?= $notifications['from_email'] ?>" />
|
||||
</div>
|
||||
<div class="settings-notes">
|
||||
<p>
|
||||
<i class="fa-solid fa-circle-info"></i> SMTP Password is transmitted and stored in plaintext.
|
||||
For security, please create an account just for this.</p>
|
||||
<i class="fa-solid fa-circle-info"></i> <?= translate('smtp_info', $i18n) ?></p>
|
||||
<p>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<input type="button" class="secondary-button" value="Test" id="testNotifications" onClick="testNotificationButton()"/>
|
||||
<input type="submit" value="Save" id="saveNotifications" onClick="saveNotificationsButton()"/>
|
||||
<input type="button" class="secondary-button" value="<?= translate('test', $i18n) ?>" id="testNotifications" onClick="testNotificationButton()"/>
|
||||
<input type="submit" value="<?= translate('save', $i18n) ?>" id="saveNotifications" onClick="saveNotificationsButton()"/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@ -221,7 +233,7 @@
|
||||
|
||||
<section class="account-section">
|
||||
<header>
|
||||
<h2>Categories</h2>
|
||||
<h2><?= translate('categories', $i18n) ?></h2>
|
||||
</header>
|
||||
<div class="account-categories">
|
||||
<div id="categories">
|
||||
@ -244,19 +256,19 @@
|
||||
<div class="form-group-inline" data-categoryid="<?= $category['id'] ?>">
|
||||
<input type="text" name="category" value="<?= $category['name'] ?>" placeholder="Category">
|
||||
<button class="image-button medium" onClick="editCategory(<?= $category['id'] ?>)" name="save">
|
||||
<img src="images/siteicons/save.png" title="Save Category">
|
||||
<img src="images/siteicons/save.png" title="<?= translate('save_category', $i18n) ?>">
|
||||
</button>
|
||||
<?php
|
||||
if ($canDelete) {
|
||||
?>
|
||||
<button class="image-button medium" onClick="removeCategory(<?= $category['id'] ?>)">
|
||||
<img src="images/siteicons/delete.png" title="Delete Category">
|
||||
<img src="images/siteicons/delete.png" title="<?= translate('delete_category', $i18n) ?>">
|
||||
</button>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
<button class="image-button medium disabled">
|
||||
<img src="images/siteicons/delete.png" title="Can't delete category in use in subscription">
|
||||
<img src="images/siteicons/delete.png" title="<?= translate('cant_delete_category_in_use', $i18n) ?>">
|
||||
</button>
|
||||
<?php
|
||||
}
|
||||
@ -268,7 +280,7 @@
|
||||
?>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<input type="submit" value="Add" id="addCategory" onClick="addCategoryButton()"/>
|
||||
<input type="submit" value="<?= translate('add', $i18n) ?>" id="addCategory" onClick="addCategoryButton()"/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@ -309,7 +321,7 @@
|
||||
|
||||
<section class="account-section">
|
||||
<header>
|
||||
<h2>Currencies</h2>
|
||||
<h2><?= translate('currencies', $i18n) ?></h2>
|
||||
</header>
|
||||
<div class="account-currencies">
|
||||
<div id="currencies">
|
||||
@ -339,20 +351,20 @@
|
||||
<input type="text" name="currency" value="<?= $currency['name'] ?>" placeholder="Currency Name">
|
||||
<input type="text" name="code" value="<?= $currency['code'] ?>" placeholder="Currency Code">
|
||||
<button class="image-button medium" onClick="editCurrency(<?= $currency['id'] ?>)" name="save">
|
||||
<img src="images/siteicons/save.png" title="Save Currency">
|
||||
<img src="images/siteicons/save.png" title="<?= translate('save_currency', $i18n) ?>">
|
||||
</button>
|
||||
<?php
|
||||
if ($canDelete) {
|
||||
?>
|
||||
<button class="image-button medium" onClick="removeCurrency(<?= $currency['id'] ?>)">
|
||||
<img src="images/siteicons/delete.png" title="Delete Currency">
|
||||
<img src="images/siteicons/delete.png" title="<?= translate('delete_currency', $i18n) ?>">
|
||||
</button>
|
||||
<?php
|
||||
} else {
|
||||
$cantDeleteMessage = $isMainCurrency ? "main currency" : "used currency";
|
||||
$cantDeleteMessage = $isMainCurrency ? translate('cant_delete_main_currency', $i18n) : translate('cant_delete_currency_in_use', $i18n);
|
||||
?>
|
||||
<button class="image-button medium disabled">
|
||||
<img src="images/siteicons/delete.png" title="Can't delete <?= $cantDeleteMessage ?>">
|
||||
<img src="images/siteicons/delete.png" title="<?= $cantDeleteMessage ?>">
|
||||
</button>
|
||||
<?php
|
||||
}
|
||||
@ -364,19 +376,19 @@
|
||||
?>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<input type="submit" value="Add" id="addCurrency" onClick="addCurrencyButton()"/>
|
||||
<input type="submit" value="<?= translate('add', $i18n) ?>" id="addCurrency" onClick="addCurrencyButton()"/>
|
||||
</div>
|
||||
<div class="settings-notes">
|
||||
<p>
|
||||
<i class="fa-solid fa-circle-info"></i>
|
||||
Exchange rates last updated on
|
||||
<?= translate('exchange_update', $i18n) ?>
|
||||
<span>
|
||||
<?= $exchange_rates_last_updated ?>
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
<i class="fa-solid fa-circle-info"></i>
|
||||
Find the supported currencies and correct currency codes on
|
||||
<?= translate('currency_info', $i18n) ?>
|
||||
<span>
|
||||
fixer.io
|
||||
<a href="https://fixer.io/symbols" target="_blank" title="Currency codes">
|
||||
@ -385,7 +397,7 @@
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
For improved performance keep only the currencies you use.
|
||||
<?= translate('currency_performance', $i18n) ?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -409,12 +421,11 @@
|
||||
</header>
|
||||
<div class="account-fixer">
|
||||
<div class="form-group">
|
||||
<input type="text" name="fixer-key" id="fixerKey" value="<?= $apiKey ?>" placeholder="ApiKey">
|
||||
<input type="text" name="fixer-key" id="fixerKey" value="<?= $apiKey ?>" placeholder="<?= translate('api_key', $i18n) ?>">
|
||||
</div>
|
||||
<div class="settings-notes">
|
||||
<p><i class="fa-solid fa-circle-info"></i> If you use multiple currencies, and want accurate statistics and sorting on the subscriptions,
|
||||
a FREE API Key from Fixer is necessary.</p>
|
||||
<p>Get your key at:
|
||||
<p><i class="fa-solid fa-circle-info"></i><?= translate('fixer_info', $i18n) ?></p>
|
||||
<p><?= translate('get_key', $i18n) ?>:
|
||||
<span>
|
||||
https://fixer.io/
|
||||
<a href="https://fixer.io/#pricing_plan" title="Get free fixer api key" target="_blank">
|
||||
@ -424,18 +435,18 @@
|
||||
</p>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<input type="submit" value="Save" id="addFixerKey" onClick="addFixerKeyButton()"/>
|
||||
<input type="submit" value="<?= translate('save', $i18n) ?>" id="addFixerKey" onClick="addFixerKeyButton()"/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="account-section">
|
||||
<header>
|
||||
<h2>Display settings</h2>
|
||||
<h2><?= translate('display_settings', $i18n) ?></h2>
|
||||
</header>
|
||||
<div class="account-settings-list">
|
||||
<div>
|
||||
<input type="button" value="Switch Light / Dark Theme" onClick="switchTheme()">
|
||||
<input type="button" value="<?= translate('switch_theme', $i18n) ?>" onClick="switchTheme()">
|
||||
</div>
|
||||
<?php
|
||||
$monthlyprice = isset($_COOKIE['showMonthlyPrice']) && $_COOKIE['showMonthlyPrice'] === 'true';
|
||||
@ -445,13 +456,13 @@
|
||||
<div>
|
||||
<div class="form-group-inline">
|
||||
<input type="checkbox" id="monthlyprice" name="monthlyprice" onChange="setShowMonthlyPriceCookie()" <?php if ($monthlyprice) echo 'checked'; ?>>
|
||||
<label for="monthlyprice">Calculate and show monthly price for all subscriptions</label>
|
||||
<label for="monthlyprice"><?= translate('calculate_monthly_price', $i18n) ?></label>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="form-group-inline">
|
||||
<input type="checkbox" id="convertcurrency" name="convertcurrency" onChange="setConvertCurrencyCookie()" <?php if ($convertcurrency) echo 'checked'; ?>>
|
||||
<label for="convertcurrency">Always convert and show prices on my main currency (slower).</label>
|
||||
<label for="convertcurrency"><?= translate('convert_prices', $i18n) ?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -459,27 +470,27 @@
|
||||
|
||||
<section class="account-section">
|
||||
<header>
|
||||
<h2>Experimental settings</h2>
|
||||
<h2><?= translate('experimental_settings', $i18n) ?></h2>
|
||||
</header>
|
||||
<div class="account-settings-list">
|
||||
<div>
|
||||
<div class="form-group-inline">
|
||||
<input type="checkbox" id="removebackground" name="removebackground" onChange="setRemoveBackgroundCookie()" <?php if ($removebackground) echo 'checked'; ?>>
|
||||
<label for="removebackground">Attempt to remove background of logos from image search (experimental).</label>
|
||||
<label for="removebackground"><?= translate('remove_background', $i18n) ?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="settings-notes">
|
||||
<p>
|
||||
<i class="fa-solid fa-circle-info"></i>
|
||||
Experimental settings will probably not work perfectly.
|
||||
<?= translate('experimental_info', $i18n) ?>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="account-section">
|
||||
<header>
|
||||
<h2>Payment Methods</h2>
|
||||
<h2><?= translate('payment_methods', $i18n) ?></h2>
|
||||
</header>
|
||||
<div class="payments-list">
|
||||
<?php
|
||||
@ -496,7 +507,7 @@
|
||||
data-enabled="<?= $payment['enabled']; ?>"
|
||||
data-in-use="<?= $inUse ? 'yes' : 'no' ?>"
|
||||
data-paymentid="<?= $payment['id'] ?>"
|
||||
title="<?= $inUse ? 'Can\'t delete used payment method' : '' ?>"
|
||||
title="<?= $inUse ? translate('cant_delete_payment_method_in_use', $i18n) : ($payment['enabled'] ? translate('disable', $i18n) : translate('enable', $i18n)) ?>"
|
||||
onClick="togglePayment(<?= $payment['id'] ?>)">
|
||||
<img src="images/uploads/icons/<?= $payment['icon'] ?>" alt="Logo" />
|
||||
<span class="payment-name">
|
||||
@ -510,7 +521,7 @@
|
||||
<div class="settings-notes">
|
||||
<p>
|
||||
<i class="fa-solid fa-circle-info"></i>
|
||||
Click a payment method to disable / enable it.
|
||||
<?= translate('payment_methods_info', $i18n) ?>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
24
stats.php
24
stats.php
@ -144,31 +144,31 @@ if ($result) {
|
||||
|
||||
?>
|
||||
<section class="contain">
|
||||
<h2>General Statistics</h2>
|
||||
<h2><?= translate('general_statistics', $i18n) ?></h2>
|
||||
<div class="statistics">
|
||||
<div class="statistic">
|
||||
<span><?= $activeSubscriptions ?></span>
|
||||
<div class="title">Active Subscriptions</div>
|
||||
<div class="title"><?= translate('active_subscriptions', $i18n) ?></div>
|
||||
</div>
|
||||
<div class="statistic">
|
||||
<span><?= CurrencyFormatter::format($totalCostPerMonth, $code) ?></span>
|
||||
<div class="title">Monthly Cost</div>
|
||||
<div class="title"><?= translate('monthly_cost', $i18n) ?></div>
|
||||
</div>
|
||||
<div class="statistic">
|
||||
<span><?= CurrencyFormatter::format($totalCostPerYear, $code) ?></span>
|
||||
<div class="title">Yearly Cost</div>
|
||||
<div class="title"><?= translate('yearly_cost', $i18n) ?></div>
|
||||
</div>
|
||||
<div class="statistic">
|
||||
<span><?= CurrencyFormatter::format($averageSubscriptionCost, $code) ?></span>
|
||||
<div class="title">Average Monthly Subscription Cost</div>
|
||||
<div class="title"><?= translate('average_monthly', $i18n) ?></div>
|
||||
</div>
|
||||
<div class="statistic">
|
||||
<span><?= CurrencyFormatter::format($mostExpensiveSubscription, $code) ?></span>
|
||||
<div class="title">Most Expensive Subscription Cost</div>
|
||||
<div class="title"><?= translate('most_expensive', $i18n) ?></div>
|
||||
</div>
|
||||
<div class="statistic">
|
||||
<span><?= CurrencyFormatter::format($amountDueThisMonth, $code) ?></span>
|
||||
<div class="title">Amount due this month</div>
|
||||
<div class="title"><?= translate('amount_due', $i18n) ?></div>
|
||||
</div>
|
||||
<?php
|
||||
$numberOfElements = 6;
|
||||
@ -179,7 +179,7 @@ if ($result) {
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<h2>Split Views</h2>
|
||||
<h2><?= translate('split_views', $i18n) ?></h2>
|
||||
<div class="graphs">
|
||||
<?php
|
||||
$categoryDataPoints = [];
|
||||
@ -211,8 +211,8 @@ if ($result) {
|
||||
?>
|
||||
<section class="graph">
|
||||
<header>
|
||||
Household Split
|
||||
<div class="sub-header">(Monthly cost)</div>
|
||||
<?= translate('household_split', $i18n) ?>
|
||||
<div class="sub-header">(<?= translate('monthly_cost', $i18n) ?>)</div>
|
||||
</header>
|
||||
<canvas id="memberSplitChart"></canvas>
|
||||
</section>
|
||||
@ -223,8 +223,8 @@ if ($result) {
|
||||
?>
|
||||
<section class="graph">
|
||||
<header>
|
||||
Category Split
|
||||
<div class="sub-header">(Monthly cost)</div>
|
||||
<?= translate('category_split', $i18n) ?>
|
||||
<div class="sub-header">(<?= translate('monthly_cost', $i18n) ?>)</div>
|
||||
</header>
|
||||
<canvas id="categorySplitChart" style="height: 370px; width: 100%;"></canvas>
|
||||
</section>
|
||||
|
||||
@ -87,6 +87,7 @@ header .logo .logo-image {
|
||||
min-width: 130px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
z-index: 5;
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
.dropdown-content a {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user