Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V2 39 0 #647

Merged
merged 2 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 210 additions & 0 deletions api/subscriptions/get_ical_feed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
<?php
/*
This API Endpoint accepts both POST and GET requests.
It receives the following parameters:
- convert_currency: whether to convert to the main currency (boolean) default false.
- apiKey: the API key of the user.

It returns a downloadable VCAL file with the active subscriptions
*/

require_once '../../includes/connect_endpoint.php';

header('Content-Type: application/json, charset=UTF-8');

if ($_SERVER["REQUEST_METHOD"] === "POST" || $_SERVER["REQUEST_METHOD"] === "GET") {
// if the parameters are not set, return an error

if (!isset($_REQUEST['api_key'])) {
$response = [
"success" => false,
"title" => "Missing parameters"
];
echo json_encode($response);
exit;
}

function getPriceConverted($price, $currency, $database)
{
$query = "SELECT rate FROM currencies WHERE id = :currency";
$stmt = $database->prepare($query);
$stmt->bindParam(':currency', $currency, SQLITE3_INTEGER);
$result = $stmt->execute();

$exchangeRate = $result->fetchArray(SQLITE3_ASSOC);
if ($exchangeRate === false) {
return $price;
} else {
$fromRate = $exchangeRate['rate'];
return $price / $fromRate;
}
}

$apiKey = $_REQUEST['api_key'];

// Get user from API key
$sql = "SELECT * FROM user WHERE api_key = :apiKey";
$stmt = $db->prepare($sql);
$stmt->bindValue(':apiKey', $apiKey);
$result = $stmt->execute();
$user = $result->fetchArray(SQLITE3_ASSOC);

// If the user is not found, return an error
if (!$user) {
$response = [
"success" => false,
"title" => "Invalid API key"
];
echo json_encode($response);
exit;
}

$userId = $user['id'];
$userCurrencyId = $user['main_currency'];

// Get last exchange update date for user
$sql = "SELECT * FROM last_exchange_update WHERE user_id = :userId";
$stmt = $db->prepare($sql);
$stmt->bindValue(':userId', $userId);
$result = $stmt->execute();
$lastExchangeUpdate = $result->fetchArray(SQLITE3_ASSOC);

$canConvertCurrency = empty($lastExchangeUpdate['date']) ? false : true;

// Get currencies for user
$sql = "SELECT * FROM currencies WHERE user_id = :userId";
$stmt = $db->prepare($sql);
$stmt->bindValue(':userId', $userId);
$result = $stmt->execute();
$currencies = [];
while ($currency = $result->fetchArray(SQLITE3_ASSOC)) {
$currencies[$currency['id']] = $currency;
}

// Get categories for user
$sql = "SELECT * FROM categories WHERE user_id = :userId";
$stmt = $db->prepare($sql);
$stmt->bindValue(':userId', $userId);
$result = $stmt->execute();
$categories = [];
while ($category = $result->fetchArray(SQLITE3_ASSOC)) {
$categories[$category['id']] = $category['name'];
}

// Get members for user
$sql = "SELECT * FROM household WHERE user_id = :userId";
$stmt = $db->prepare($sql);
$stmt->bindValue(':userId', $userId);
$result = $stmt->execute();
$members = [];
while ($member = $result->fetchArray(SQLITE3_ASSOC)) {
$members[$member['id']] = $member['name'];
}

// Get payment methods for user
$sql = "SELECT * FROM payment_methods WHERE user_id = :userId";
$stmt = $db->prepare($sql);
$stmt->bindValue(':userId', $userId);
$result = $stmt->execute();
$paymentMethods = [];
while ($paymentMethod = $result->fetchArray(SQLITE3_ASSOC)) {
$paymentMethods[$paymentMethod['id']] = $paymentMethod['name'];
}

$sql = "SELECT * FROM subscriptions WHERE user_id = :userId ORDER BY next_payment ASC";

$stmt = $db->prepare($sql);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();

if ($result) {
$subscriptions = array();
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$subscriptions[] = $row;
}
}

$subscriptionsToReturn = array();

foreach ($subscriptions as $subscription) {
$subscriptionToReturn = $subscription;

if (isset($_REQUEST['convert_currency']) && $_REQUEST['convert_currency'] === 'true' && $canConvertCurrency && $subscription['currency_id'] != $userCurrencyId) {
$subscriptionToReturn['price'] = getPriceConverted($subscription['price'], $subscription['currency_id'], $db);
} else {
$subscriptionToReturn['price'] = $subscription['price'];
}

$subscriptionToReturn['category_name'] = $categories[$subscription['category_id']];
$subscriptionToReturn['payer_user_name'] = $members[$subscription['payer_user_id']];
$subscriptionToReturn['payment_method_name'] = $paymentMethods[$subscription['payment_method_id']];

$subscriptionsToReturn[] = $subscriptionToReturn;
}

$stmt->bindValue(':inactive', false, SQLITE3_INTEGER);
$result = $stmt->execute();

header('Content-Type: text/calendar; charset=utf-8');
header('Content-Disposition: attachment; filename="subscriptions.ics"');

if ($result === false) {
die("BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:NAME:\nEND:VCALENDAR");
}

$icsContent = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//Wallos//iCalendar//EN\nNAME:Wallos\nX-WR-CALNAME:Wallos\n";

while ($subscription = $result->fetchArray(SQLITE3_ASSOC)) {
$subscription['payer_user'] = $members[$subscription['payer_user_id']];
$subscription['category'] = $categories[$subscription['category_id']];
$subscription['payment_method'] = $paymentMethods[$subscription['payment_method_id']];
$subscription['currency'] = $currencies[$subscription['currency_id']]['symbol'];
$subscription['trigger'] = $subscription['notify_days_before'] ? $subscription['notify_days_before'] : 1;
$subscription['price'] = number_format($subscription['price'], 2);

$uid = uniqid();
$summary = "Wallos: " . $subscription['name'];
$description = "Price: {$subscription['currency']}{$subscription['price']}\\nCategory: {$subscription['category']}\\nPayment Method: {$subscription['payment_method']}\\nPayer: {$subscription['payer_user']}\\nNotes: {$subscription['notes']}";
$dtstart = (new DateTime($subscription['next_payment']))->format('Ymd\THis\Z');
$dtend = (new DateTime($subscription['next_payment']))->modify('+1 hour')->format('Ymd\THis\Z');
$location = isset($subscription['url']) ? $subscription['url'] : '';
$alarm_trigger = '-' . $subscription['trigger'] . 'D';

$icsContent .= <<<ICS
BEGIN:VEVENT
UID:$uid
SUMMARY:$summary
DESCRIPTION:$description
DTSTART:$dtstart
DTEND:$dtend
LOCATION:$location
STATUS:CONFIRMED
TRANSP:OPAQUE
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Reminder
TRIGGER:$alarm_trigger
END:VALARM
END:VEVENT

ICS;
}

$icsContent .= "END:VCALENDAR\n";
echo $icsContent;
$db->close();
exit;



} else {
$response = [
"success" => false,
"title" => "Invalid request method"
];
echo json_encode($response);
exit;
}


?>
58 changes: 0 additions & 58 deletions api/subscriptions/get_subscriptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -306,64 +306,6 @@ function getPriceConverted($price, $currency, $database)
$subscriptionsToReturn[] = $subscriptionToReturn;
}

if (isset($_REQUEST['type'])) {
$type = $_REQUEST['type'];
$stmt->bindValue(':inactive', false, SQLITE3_INTEGER);
$result = $stmt->execute();

if ($type == "iCalendar") {
header('Content-Type: text/calendar; charset=utf-8');
header('Content-Disposition: attachment; filename="subscriptions.ics"');

if ($result === false) {
die("BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:NAME:\nEND:VCALENDAR");
}

$icsContent = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//Wallos//$type//EN\nNAME:Wallos\nX-WR-CALNAME:Wallos\n";

while ($subscription = $result->fetchArray(SQLITE3_ASSOC)) {
$subscription['payer_user'] = $members[$subscription['payer_user_id']];
$subscription['category'] = $categories[$subscription['category_id']];
$subscription['payment_method'] = $paymentMethods[$subscription['payment_method_id']];
$subscription['currency'] = $currencies[$subscription['currency_id']]['symbol'];
$subscription['trigger'] = $subscription['notify_days_before'] ? $subscription['notify_days_before'] : 1;
$subscription['price'] = number_format($subscription['price'], 2);

$uid = uniqid();
$summary = "Wallos: " . $subscription['name'];
$description = "Price: {$subscription['currency']}{$subscription['price']}\\nCategory: {$subscription['category']}\\nPayment Method: {$subscription['payment_method']}\\nPayer: {$subscription['payer_user']}\\nNotes: {$subscription['notes']}";
$dtstart = (new DateTime($subscription['next_payment']))->format('Ymd\THis\Z');
$dtend = (new DateTime($subscription['next_payment']))->modify('+1 hour')->format('Ymd\THis\Z');
$location = isset($subscription['url']) ? $subscription['url'] : '';
$alarm_trigger = '-' . $subscription['trigger'] . 'D';

$icsContent .= <<<ICS
BEGIN:VEVENT
UID:$uid
SUMMARY:$summary
DESCRIPTION:$description
DTSTART:$dtstart
DTEND:$dtend
LOCATION:$location
STATUS:CONFIRMED
TRANSP:OPAQUE
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Reminder
TRIGGER:$alarm_trigger
END:VALARM
END:VEVENT

ICS;
}

$icsContent .= "END:VCALENDAR\n";
echo $icsContent;
$db->close();
exit;
}
}

$response = [
"success" => true,
"title" => "subscriptions",
Expand Down
62 changes: 17 additions & 45 deletions calendar.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,51 +104,23 @@ function getPriceConverted($price, $currency, $database, $userId)
}
?>
<div class="split-header">
<h2>Calendar</h2>
<button class="button tiny" onClick="showExportPopup()" style="margin-right: auto"> <?= translate('subscriptions', $i18n) ?> </button>
<div id="subscriptions_calendar" class="subscription-modal">
<div class="modal-header">
<h3><?= translate('subscriptions', $i18n) ?></h3>
<span class="fa-solid fa-xmark close-modal" onclick="closePopup()"></span>
</div>
<div class="form-group-inline">
<input id="iCalendarUrl" type="text" value="" readonly>
<button onclick="copyToClipboard()" class="button tiny"> <?= translate('copy_to_clipboard', $i18n) ?> </button>
</div>
</div>

<script>
function showExportPopup() {
const host = window.location.origin;
const apiPath = "/api/subscriptions/get_subscriptions.php";
const apiKey = "<?= $userData['api_key'] ?>";
const queryParams = `?api_key=${apiKey}&type=iCalendar`;
const fullUrl = `${host}${apiPath}${queryParams}`;
document.getElementById('iCalendarUrl').value = fullUrl;

if (apiKey === "") {
showErrorMessage( "<?= translate('invalid_api_key', $i18n) ?>" );
return;
}
document.getElementById('subscriptions_calendar').classList.add('is-open');
}
function closePopup() {
document.getElementById('subscriptions_calendar').classList.remove('is-open');
}

function copyToClipboard() {
const urlField = document.getElementById('iCalendarUrl');
urlField.select();
urlField.setSelectionRange(0, 99999); // For mobile devices
navigator.clipboard.writeText(urlField.value)
.then(() => {
showSuccessMessage(translate('copied_to_clipboard'));
})
.catch(() => {
showErrorMessage(translate('unknown_error'));
});
}
</script>
<h2>
Calendar
<button class="button export-ical" onClick="showExportPopup()" title="<?= translate('export_icalendar', $i18n) ?>">
<?php require_once 'images/siteicons/svg/export_ical.php'; ?>
</button>
</h2>
<div id="subscriptions_calendar" class="subscription-modal">
<div class="modal-header">
<h3><?= translate('export_icalendar', $i18n) ?></h3>
<span class="fa-solid fa-xmark close-modal" onclick="closePopup()"></span>
</div>
<div class="form-group-inline">
<input id="iCalendarUrl" type="text" value="" readonly>
<input type="hidden" id="apiKey" value="<?= $userData['api_key'] ?>">
<button onclick="copyToClipboard()" class="button tiny"> <?= translate('copy_to_clipboard', $i18n) ?> </button>
</div>
</div>

<div class="calendar-nav">
<?php
Expand Down
4 changes: 4 additions & 0 deletions images/siteicons/svg/export_ical.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="mdi-calendar-export" width="24" height="24" viewBox="0 0 24 24">
<path d="M12,22L16,18H13V12H11V18H8M19,4H18V2H16V4H8V2H6V4H5A2,2 0 0,0 3,6V20A2,2 0 0,0 5,22H8V20H5V9H19V20H16V22H19A2,2 0 0,0 21,20V6A2,2 0 0,0 19,4Z" fill="currentColor"/>
</svg>
1 change: 1 addition & 0 deletions includes/i18n/de.php
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@
"month-11" => "November",
"month-12" => "Dezember",
"total_cost" => "Gesamtkosten",
"export_icalendar" => "iCalendar exportieren",
// TOTP Page
"insert_totp_code" => "Bitte geben Sie den TOTP-Code ein",

Expand Down
1 change: 1 addition & 0 deletions includes/i18n/el.php
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@
"month-11" => "Νοέμβριος",
"month-12" => "Δεκέμβριος",
"total_cost" => "Συνολικό κόστος",
"export_icalendar" => "Εξαγωγή iCalendar",
// TOTP Page
"insert_totp_code" => "Εισάγετε τον κωδικό TOTP",

Expand Down
1 change: 1 addition & 0 deletions includes/i18n/en.php
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@
"month-11" => "November",
"month-12" => "December",
"total_cost" => "Total Cost",
"export_icalendar" => "Export iCalendar",
// TOTP Page
"insert_totp_code" => "Insert TOTP code",

Expand Down
1 change: 1 addition & 0 deletions includes/i18n/es.php
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@
"month-11" => "Noviembre",
"month-12" => "Diciembre",
"total_cost" => "Costo Total",
"export_icalendar" => "Exportar iCalendar",
// TOTP Page
"insert_totp_code" => "Introduce el código TOTP",

Expand Down
1 change: 1 addition & 0 deletions includes/i18n/fr.php
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@
"month-11" => "Novembre",
"month-12" => "Décembre",
"total_cost" => "Coût total",
"export_icalendar" => "Exporter en iCalendar",
// TOTP Page
"insert_totp_code" => "Veuillez insérer le code TOTP",

Expand Down
1 change: 1 addition & 0 deletions includes/i18n/it.php
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@
"month-11" => "Novembre",
"month-12" => "Dicembre",
"total_cost" => "Costo totale",
"export_icalendar" => "Esporta iCal",

// TOTP Page
"insert_totp_code" => "Inserisci il codice TOTP",
Expand Down
Loading
Loading