Compare commits

...

47 Commits

Author SHA1 Message Date
09d0c71569 (Minimum delta): Add support for the X-WebAuth-User header for authentication 2024-06-10 14:07:22 +01:00
github-actions[bot]
c835a62462
chore(main): release 2.4.1 (#382)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-09 22:53:35 +02:00
Miguel Ribeiro
b0b4b7a65c
fix: cronjob exchange update would not work with apilayer (#381) 2024-06-09 22:41:33 +02:00
github-actions[bot]
d410d4804a
chore(main): release 2.4.0 (#379)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-08 00:06:52 +02:00
Miguel Ribeiro
092be22183
feat: add hability to disable login (#378) 2024-06-08 00:04:50 +02:00
github-actions[bot]
f79a3f9957
chore(main): release 2.3.0 (#376)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-05 22:11:05 +02:00
Miguel Ribeiro
65edf0963b
feat: add ntfy as notification method (#377) 2024-06-05 22:08:36 +02:00
Miguel Ribeiro
7217088bb0
fix: custom headers for webhook notifications (#375) 2024-06-05 09:01:17 +02:00
rvelasq
25a445e88d
fix custom headers not being applied on webhook (#374) 2024-06-05 08:01:42 +02:00
github-actions[bot]
4342787ace
chore(main): release 2.2.0 (#373)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-05 00:17:07 +02:00
Miguel Ribeiro
fa99a735cd
feat: frequency is now up to 366
feat: change filename of backup file
fix: translate: "no category"
fix: trim fixer api key
fix: add webp support to gd on the container
fix: update slovanian translations
2024-06-05 00:15:31 +02:00
thehijacker
76186d45d5
Updated Slovenian translation (#357)
Co-authored-by: Andrej Kralj <andrej.kralj@gmail.com>
2024-05-27 22:15:10 +02:00
github-actions[bot]
db18cbaaea
chore(main): release 2.1.0 (#354) 2024-05-27 19:29:20 +02:00
Miguel Ribeiro
03ceb8a6e6
feat: add slovenian translation
fix: currency conversion failing on the statistics page
2024-05-27 19:17:05 +02:00
Devin Buhl
cd9c75db37
push to GHCR
Push docker image to GHCR
2024-05-27 19:01:11 +02:00
thehijacker
4dccfc1771
Added Slovenian translation (#346)
Co-authored-by: Andrej Kralj <andrej.kralj@gmail.com>
2024-05-27 18:59:28 +02:00
github-actions[bot]
b9e46ba278
chore(main): release 2.0.0 (#341)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-26 23:33:03 +02:00
Miguel Ribeiro
e1006e5823
feat!: allow registration of multiple users (#340)
feat: administration area
feat: add reset password functionality
2024-05-26 23:30:51 +02:00
Miguel Ribeiro
57b5c3deec
Create FUNDING.yml 2024-05-26 15:05:33 +02:00
github-actions[bot]
e2cae2a0db
chore(main): release 1.29.1 (#339)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-20 14:33:07 +02:00
Miguel Ribeiro
5050a28f0e
fix: calling htmlspecialchars_decode on null objects (#338) 2024-05-20 14:32:20 +02:00
github-actions[bot]
9e8b84b794
chore(main): release 1.29.0 (#335)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-20 13:09:20 +02:00
Miguel Ribeiro
c7146dfd08
feat: subscriptions have personalized notification times (#334) 2024-05-20 13:07:40 +02:00
Miguel Ribeiro
f1815d7335
chore: add support for rtl languages (#331) 2024-05-18 17:41:01 +02:00
github-actions[bot]
38a2ba9804
chore(main): release 1.28.0 (#330)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-17 23:18:18 +02:00
Miguel Ribeiro
b622434ca0
feat: add monthly budget field and statistics (#329) 2024-05-17 23:14:17 +02:00
github-actions[bot]
e21500a719
chore(main): release 1.27.2 (#328) 2024-05-17 17:32:11 +02:00
Miguel Ribeiro
d44b40b0ce
refactor: update PHP to 8.2
fix: possible division by 0 on statistics page
fix: duplicated messages on discord notifications
docs: update readme
2024-05-17 17:03:46 +02:00
github-actions[bot]
b519376a27
chore(main): release 1.27.1 (#322)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-13 17:47:36 +02:00
Miguel Ribeiro
a524419e0a
fix: import of translations for cronjobs was missing (#321)
refactor: retrieval of webroot path
chore: add sqlite to docker image
2024-05-13 17:45:59 +02:00
github-actions[bot]
46f933ade7
chore(main): release 1.27.0 (#315)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-11 20:21:03 +02:00
Miguel Ribeiro
bc40320690
feat: add korean translation (#314) 2024-05-11 20:17:43 +02:00
Jun
b868b1067a
Korean translation (#313)
Korean translation for both server/client-side (PHP and JS).
2024-05-11 20:14:08 +02:00
Miguel Ribeiro
74d8e24e7a docs: update readme 2024-05-11 10:06:08 +02:00
Miguel Ribeiro
1d4c68e899 docs: update readme 2024-05-10 13:49:53 +02:00
github-actions[bot]
d065e74bfe
chore(main): release 1.26.2 (#310)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-09 17:09:59 +02:00
Miguel Ribeiro
8f890fc5d3
fix: russian translations (#309) 2024-05-09 17:08:37 +02:00
github-actions[bot]
98a6244836
chore(main): release 1.26.1 (#308)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-09 16:44:23 +02:00
Miguel Ribeiro
bb5ee2e64c
fix: background removal experimental setting (#307) 2024-05-09 16:43:00 +02:00
github-actions[bot]
36e64263bf
chore(main): release 1.26.0 (#306)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-08 21:46:44 +02:00
Miguel Ribeiro
ae04d50329
feat: add russian translation (#305)
chore: add build step for discord notification of new releases
2024-05-08 21:45:46 +02:00
github-actions[bot]
7ee8f5bb5f
chore(main): release 1.25.1 (#303)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-07 09:43:40 +02:00
Miguel Ribeiro
b435d6a5cf
fix: broken discord form (#302) 2024-05-07 09:42:22 +02:00
github-actions[bot]
6ccb90e8b8
chore(main): release 1.25.0 (#301) 2024-05-06 18:57:46 +02:00
Miguel Ribeiro
899482982e
feat: add discord and pushover as notification agents (#300)
fix: most error messages of the notifications endpoints would not reach the frontend
2024-05-06 18:55:10 +02:00
github-actions[bot]
4199d4c3e7
chore(main): release 1.24.0 (#296) 2024-05-06 00:20:33 +02:00
Miguel Ribeiro
a408031ef8
feat: add new notification methods (telegram, webhooks, gotify) (#295) 2024-05-06 00:15:44 +02:00
127 changed files with 8922 additions and 799 deletions

4
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,4 @@
# These are supported funding model platforms
github: [ellite]
custom: ['https://www.paypal.com/paypalme/miguelr']

View File

@ -34,6 +34,12 @@ jobs:
registry: ${{ env.REGISTRY }}
username: ${{ env.REGISTRY_USERNAME }}
password: ${{ env.REGISTRY_PASSWORD }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Prepare tags for Docker meta
id: tags
env:
@ -58,7 +64,9 @@ jobs:
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
ghcr.io/ellite/wallos
tags: ${{ steps.tags.outputs.tags }}
# necessary for multi-platform images
- name: Set up QEMU
@ -76,3 +84,10 @@ jobs:
platforms: linux/amd64,linux/arm64,linux/arm/v7
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Send notification to Discord
if: github.event_name != 'pull_request'
uses: Ilshidur/action-discord@master
with:
args: "A new release has been created: ${{ steps.meta.outputs.tags }}"
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}

4
.tmp/.gitignore vendored
View File

@ -1,2 +1,2 @@
*
!.gitignore
*
!.gitignore

View File

@ -1,5 +1,162 @@
# Changelog
## [2.4.1](https://github.com/ellite/Wallos/compare/v2.4.0...v2.4.1) (2024-06-09)
### Bug Fixes
* cronjob exchange update would not work with apilayer ([#381](https://github.com/ellite/Wallos/issues/381)) ([b0b4b7a](https://github.com/ellite/Wallos/commit/b0b4b7a65cd479e7532e72e826d3c01aead403c3))
## [2.4.0](https://github.com/ellite/Wallos/compare/v2.3.0...v2.4.0) (2024-06-07)
### Features
* add hability to disable login ([#378](https://github.com/ellite/Wallos/issues/378)) ([092be22](https://github.com/ellite/Wallos/commit/092be22183359f714fc9638d9013b742da828ed6))
## [2.3.0](https://github.com/ellite/Wallos/compare/v2.2.0...v2.3.0) (2024-06-05)
### Features
* add ntfy as notification method ([#377](https://github.com/ellite/Wallos/issues/377)) ([65edf09](https://github.com/ellite/Wallos/commit/65edf0963b73deff0f0f7f04427e69ce335bd776))
### Bug Fixes
* custom headers for webhook notifications ([#375](https://github.com/ellite/Wallos/issues/375)) ([7217088](https://github.com/ellite/Wallos/commit/7217088bb0732735a65322bce136d7d556b1acf3))
## [2.2.0](https://github.com/ellite/Wallos/compare/v2.1.0...v2.2.0) (2024-06-04)
### Features
* change filename of backup file ([fa99a73](https://github.com/ellite/Wallos/commit/fa99a735cd23918bab95baaf13b7a3142946d4b2))
* frequency is now up to 366 ([fa99a73](https://github.com/ellite/Wallos/commit/fa99a735cd23918bab95baaf13b7a3142946d4b2))
### Bug Fixes
* add webp support to gd on the container ([fa99a73](https://github.com/ellite/Wallos/commit/fa99a735cd23918bab95baaf13b7a3142946d4b2))
* translate: "no category" ([fa99a73](https://github.com/ellite/Wallos/commit/fa99a735cd23918bab95baaf13b7a3142946d4b2))
* trim fixer api key ([fa99a73](https://github.com/ellite/Wallos/commit/fa99a735cd23918bab95baaf13b7a3142946d4b2))
* update slovanian translations ([fa99a73](https://github.com/ellite/Wallos/commit/fa99a735cd23918bab95baaf13b7a3142946d4b2))
## [2.1.0](https://github.com/ellite/Wallos/compare/v2.0.0...v2.1.0) (2024-05-27)
### Features
* add slovenian translation ([03ceb8a](https://github.com/ellite/Wallos/commit/03ceb8a6e64c8cd4deb4019668fbf98acb57c5fe))
### Bug Fixes
* currency conversion failing on the statistics page ([03ceb8a](https://github.com/ellite/Wallos/commit/03ceb8a6e64c8cd4deb4019668fbf98acb57c5fe))
## [2.0.0](https://github.com/ellite/Wallos/compare/v1.29.1...v2.0.0) (2024-05-26)
### ⚠ BREAKING CHANGES
* allow registration of multiple users ([#340](https://github.com/ellite/Wallos/issues/340))
### Features
* add reset password functionality ([e1006e5](https://github.com/ellite/Wallos/commit/e1006e582388a7fab204f25c100347607b863e4e))
* administration area ([e1006e5](https://github.com/ellite/Wallos/commit/e1006e582388a7fab204f25c100347607b863e4e))
* allow registration of multiple users ([#340](https://github.com/ellite/Wallos/issues/340)) ([e1006e5](https://github.com/ellite/Wallos/commit/e1006e582388a7fab204f25c100347607b863e4e))
## [1.29.1](https://github.com/ellite/Wallos/compare/v1.29.0...v1.29.1) (2024-05-20)
### Bug Fixes
* calling htmlspecialchars_decode on null objects ([#338](https://github.com/ellite/Wallos/issues/338)) ([5050a28](https://github.com/ellite/Wallos/commit/5050a28f0e64e8c1eefb4f7cca8f6f6e473177e3))
## [1.29.0](https://github.com/ellite/Wallos/compare/v1.28.0...v1.29.0) (2024-05-20)
### Features
* subscriptions have personalized notification times ([#334](https://github.com/ellite/Wallos/issues/334)) ([c7146df](https://github.com/ellite/Wallos/commit/c7146dfd08c2a60d4ff6f7ac1f7cf5830fe28d9c))
## [1.28.0](https://github.com/ellite/Wallos/compare/v1.27.2...v1.28.0) (2024-05-17)
### Features
* add monthly budget field and statistics ([#329](https://github.com/ellite/Wallos/issues/329)) ([b622434](https://github.com/ellite/Wallos/commit/b622434ca0791d5c8026d641e1b32f8a2f0f42b8))
## [1.27.2](https://github.com/ellite/Wallos/compare/v1.27.1...v1.27.2) (2024-05-17)
### Bug Fixes
* duplicated messages on discord notifications ([d44b40b](https://github.com/ellite/Wallos/commit/d44b40b0ce80e91821fe7441c85e0d8794680618))
* possible division by 0 on statistics page ([d44b40b](https://github.com/ellite/Wallos/commit/d44b40b0ce80e91821fe7441c85e0d8794680618))
## [1.27.1](https://github.com/ellite/Wallos/compare/v1.27.0...v1.27.1) (2024-05-13)
### Bug Fixes
* import of translations for cronjobs was missing ([#321](https://github.com/ellite/Wallos/issues/321)) ([a524419](https://github.com/ellite/Wallos/commit/a524419e0a468147a2094dba81689dd643a0108b))
## [1.27.0](https://github.com/ellite/Wallos/compare/v1.26.2...v1.27.0) (2024-05-11)
### Features
* add korean translation ([#314](https://github.com/ellite/Wallos/issues/314)) ([bc40320](https://github.com/ellite/Wallos/commit/bc403206905b39c3aa88f3eb51e59b41e2a5e24e))
## [1.26.2](https://github.com/ellite/Wallos/compare/v1.26.1...v1.26.2) (2024-05-09)
### Bug Fixes
* russian translations ([#309](https://github.com/ellite/Wallos/issues/309)) ([8f890fc](https://github.com/ellite/Wallos/commit/8f890fc5d3a62a91feec50564179b3241ed538bf))
## [1.26.1](https://github.com/ellite/Wallos/compare/v1.26.0...v1.26.1) (2024-05-09)
### Bug Fixes
* background removal experimental setting ([#307](https://github.com/ellite/Wallos/issues/307)) ([bb5ee2e](https://github.com/ellite/Wallos/commit/bb5ee2e64c11b1415da3aa50119dfaa3783be37f))
## [1.26.0](https://github.com/ellite/Wallos/compare/v1.25.1...v1.26.0) (2024-05-08)
### Features
* add russian translation ([#305](https://github.com/ellite/Wallos/issues/305)) ([ae04d50](https://github.com/ellite/Wallos/commit/ae04d50329c1fb0117e186f89fef38b495cbbe9c))
## [1.25.1](https://github.com/ellite/Wallos/compare/v1.25.0...v1.25.1) (2024-05-07)
### Bug Fixes
* broken discord form ([#302](https://github.com/ellite/Wallos/issues/302)) ([b435d6a](https://github.com/ellite/Wallos/commit/b435d6a5cf6f80404c487b519334b2854aab9713))
## [1.25.0](https://github.com/ellite/Wallos/compare/v1.24.0...v1.25.0) (2024-05-06)
### Features
* add discord and pushover as notification agents ([#300](https://github.com/ellite/Wallos/issues/300)) ([8994829](https://github.com/ellite/Wallos/commit/899482982e7e200f5a7081ed6285475e5cb2a37d))
### Bug Fixes
* most error messages of the notifications endpoints would not reach the frontend ([8994829](https://github.com/ellite/Wallos/commit/899482982e7e200f5a7081ed6285475e5cb2a37d))
## [1.24.0](https://github.com/ellite/Wallos/compare/v1.23.0...v1.24.0) (2024-05-05)
### Features
* add new notification methods (telegram, webhooks, gotify) ([#295](https://github.com/ellite/Wallos/issues/295)) ([a408031](https://github.com/ellite/Wallos/commit/a408031ef8711bf87e9f8db35f52c498f250b235))
## [1.23.0](https://github.com/ellite/Wallos/compare/v1.22.0...v1.23.0) (2024-04-26)

View File

@ -1,15 +1,15 @@
# Use the php:8.0.5-fpm-alpine base image
FROM php:8.0.5-fpm-alpine
FROM php:8.2-fpm-alpine
# Set working directory to /var/www/html
WORKDIR /var/www/html
# Update packages and install dependencies
RUN apk upgrade --no-cache && \
apk add --no-cache sqlite-dev libpng libpng-dev libjpeg-turbo libjpeg-turbo-dev freetype freetype-dev curl autoconf libgomp icu-dev nginx dcron tzdata imagemagick imagemagick-dev libzip-dev && \
apk add --no-cache sqlite-dev libpng libpng-dev libjpeg-turbo libjpeg-turbo-dev freetype freetype-dev curl autoconf libgomp icu-dev nginx dcron tzdata imagemagick imagemagick-dev libzip-dev sqlite libwebp-dev && \
docker-php-ext-install pdo pdo_sqlite && \
docker-php-ext-enable pdo pdo_sqlite && \
docker-php-ext-configure gd --with-freetype --with-jpeg && \
docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp && \
docker-php-ext-install -j$(nproc) gd intl zip && \
apk add --no-cache --virtual .build-deps $PHPIZE_DEPS && \
pecl install imagick && \

View File

@ -25,6 +25,7 @@ Wallos: Open-Source Personal Subscription Tracker
- [Translations](#translations)
- [Screenshots](#screenshots)
- [License](#license)
- [Links](#links)
## Introduction
@ -42,7 +43,7 @@ Wallos is a powerful, open-source, and self-hostable web application designed to
- Logo Search: Wallos can search the web for the logo of your subscriptions if you don't have them available for upload.
- Mobile view: Wallos on the go.
- Statistics: Another perspective into your spendings.
- Notifications: Get notified by email of an upcoming payment.
- Notifications: Wallos supports multiple notification methods (email, discord, pushover, telegram, gotify and webhooks). Get notified about your upcoming payments.
- Multi Language support.
## Getting Started
@ -54,7 +55,7 @@ See instructions to run Wallos below.
#### Baremetal
- NGINX or APACHE websever running
- PHP 7.4 or 8.0 with the following modules enabled:
- PHP 8.2 with the following modules enabled:
- curl
- gd
- imagick
@ -80,9 +81,11 @@ See instructions to run Wallos below.
0 1 * * * php /var/www/html/endpoints/cronjobs/updatenextpayment.php >> /var/log/cron/updatenextpayment.log 2>&1
0 2 * * * php /var/www/html/endpoints/cronjobs/updateexchange.php >> /var/log/cron/updateexchange.log 2>&1
0 9 * * * php /var/www/html/endpoints/cronjobs/sendnotifications.php >> /var/log/cron/sendnotifications.log 2>&1
*/2 * * * * php /var/www/html/endpoints/cronjobs/sendverificationemails.php >> /var/log/cron/sendverificationemail.log 2>&1
*/2 * * * * php /var/www/html/endpoints/cronjobs/sendresetpasswordemails.php >> /var/log/cron/sendresetpasswordemails.log 2>&1
```
5. If your web root is not `/var/www/html/` adjust both the cronjobs above and `/endpoints/cronjobs/conf.php` accordingly.
5. If your web root is not `/var/www/html/` adjust the cronjobs above accordingly.
#### Updating
@ -148,7 +151,7 @@ I welcome contributions from the community and look forward to working with you
### Contributors
<a href="https://github.com/ellite/wallos/graphs/contributors">
<img src="https://contri-graphy.yourselfhosted.com/graph?repo=ellite/wallos&format=svg" />
<img src="https://contrib.rocks/image?repo=ellite/wallos" />
</a>
### Translations
@ -169,3 +172,9 @@ This project is licensed under the [GNU General Public License, Version 3](LICEN
I chose the GNU General Public License version 3 (GPLv3) for this project because it ensures that the software remains open source and freely available to the community. GPLv3 mandates that any derivative works or modifications must also be released under the same license, promoting the principles of software freedom.
I strongly believe in the importance of open source software and the collaborative nature of development, and I invite contributors to help improve this project.
## Links
- The author: [henrique.pt](https://henrique.pt)
- Wallos Landinpage: [wallosapp.com](https://wallosapp.com)
- Join the conversation: [Discord Server](https://discord.gg/anex9GUrPW)

226
admin.php Normal file
View File

@ -0,0 +1,226 @@
<?php
require_once 'includes/header.php';
if ($isAdmin != 1) {
header('Location: index.php');
exit;
}
// get admin settings from admin table
$stmt = $db->prepare('SELECT * FROM admin');
$result = $stmt->execute();
$settings = $result->fetchArray(SQLITE3_ASSOC);
// get user accounts
$stmt = $db->prepare('SELECT id, username, email FROM user ORDER BY id ASC');
$result = $stmt->execute();
$users = [];
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$users[] = $row;
}
$userCount = is_array($users) ? count($users) : 0;
$loginDisabledAllowed = $userCount == 1 && $settings['registrations_open'] == 0;
?>
<section class="contain settings">
<section class="account-section">
<header>
<h2><?= translate('registrations', $i18n) ?></h2>
</header>
<div class="admin-form">
<div class="form-group-inline">
<input type="checkbox" id="registrations" <?= $settings['registrations_open'] ? 'checked' : '' ?> />
<label for="registrations"><?= translate('enable_user_registrations', $i18n) ?></label>
</div>
<div class="form-group">
<label for="maxUsers"><?= translate('maximum_number_users', $i18n) ?></label>
<input type="number" id="maxUsers" value="<?= $settings['max_users'] ?>" />
</div>
<div class="settings-notes">
<p>
<i class="fa-solid fa-circle-info"></i>
<?= translate('max_users_info', $i18n) ?>
</p><p>
<i class="fa-solid fa-circle-info"></i>
By enabling user registrations, the setting to disable login will be unavailable.
</p>
</div>
<div class="form-group-inline">
<input type="checkbox" id="requireEmail" <?= $settings['require_email_verification'] ? 'checked' : '' ?> <?= empty($settings['smtp_address']) ? 'disabled' : '' ?>/>
<label for="requireEmail">
<?= translate('require_email_verification', $i18n) ?>
</label>
</div>
<?php
if (empty($settings['smtp_address'])) {
?>
<div class="settings-notes">
<p>
<i class="fa-solid fa-circle-info"></i>
<?= translate('configure_smtp_settings_to_enable', $i18n) ?>
</p>
</div>
<?php
}
?>
<div class="form-group">
<label for="serverUrl"><?= translate('server_url', $i18n) ?></label>
<input type="text" id="serverUrl" value="<?= $settings['server_url'] ?>" />
</div>
<div class="settings-notes">
<p>
<i class="fa-solid fa-circle-info"></i>
<?= translate('server_url_info', $i18n) ?>
</p>
<p>
<i class="fa-solid fa-circle-info"></i>
<?= translate('server_url_password_reset', $i18n) ?>
</p>
</div>
<hr>
<div class="form-group-inline">
<input type="checkbox" id="disableLogin" <?= $settings['login_disabled'] ? 'checked' : '' ?> <?= $loginDisabledAllowed ? '' : 'disabled' ?> />
<label for="disableLogin"><?= translate('disable_login', $i18n) ?></label>
</div>
<div class="settings-notes">
<p>
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i>
<?= translate('disable_login_info', $i18n) ?>
</p>
<p>
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i>
<?= translate('disable_login_info2', $i18n) ?>
</p>
</div>
<div class="buttons">
<input type="submit" class="thin" value="<?= translate('save', $i18n) ?>" id="saveAccountRegistrations" onClick="saveAccountRegistrationsButton()"/>
</div>
</div>
</section>
<?php
if ($userCount >= 0) {
?>
<section class="account-section">
<header>
<h2><?= translate('user_management', $i18n) ?></h2>
</header>
<div class="user-list">
<?php
foreach ($users as $user) {
$userIcon = $user['id'] == 1 ? 'fa-user-tie' : 'fa-id-badge';
?>
<div class="form-group-inline" data-userid="<?= $user['id'] ?>">
<div class="user-list-row">
<div title="<?= translate('username', $i18n) ?>"><i class="fa-solid <?= $userIcon ?>"></i><?= $user['username'] ?></div>
<div title="<?= translate('email', $i18n) ?>"><i class="fa-solid fa-envelope"></i>
<a href="mailto:<?= $user['email']?>"><?= $user['email']?></a>
</div>
</div>
<div>
<?php
if ($user['id'] != 1) {
?>
<button class="image-button medium" onClick="removeUser(<?= $user['id'] ?>)">
<img src="images/siteicons/<?= $colorTheme ?>/delete.png" title="<?= translate('delete_user', $i18n) ?>">
</button>
<?php
} else {
?>
<button class="image-button medium disabled" disabled>
<img src="images/siteicons/<?= $colorTheme ?>/delete.png" title="<?= translate('delete_user', $i18n) ?>">
</button>
<?php
}
?>
</div>
</div>
<?php
}
?>
</div>
<div class="settings-notes">
<p>
<i class="fa-solid fa-circle-info"></i>
<?= translate('delete_user_info', $i18n) ?>
</p>
</div>
</section>
<?php
}
?>
<section class="account-section">
<header>
<h2><?= translate('smtp_settings', $i18n) ?></h2>
</header>
<div class="admin-form">
<div class="form-group-inline">
<input type="text" name="smtpaddress" id="smtpaddress" placeholder="<?= translate('smtp_address', $i18n) ?>" value="<?= $settings['smtp_address'] ?>" />
<input type="text" name="smtpport" id="smtpport" placeholder="<?= translate('port', $i18n) ?>" class="one-third" value="<?= $settings['smtp_port'] ?>" />
</div>
<div class="form-group-inline">
<input type="radio" name="encryption" id="encryptiontls" value="tls" <?= empty($settings['encryption']) || $settings['encryption'] == "tls" ? "checked" : "" ?> />
<label for="encryptiontls"><?= translate('tls', $i18n) ?></label>
<input type="radio" name="encryption" id="encryptionssl" value="ssl" <?= $settings['encryption'] == "ssl" ? "checked" : "" ?> />
<label for="encryptionssl"><?= translate('ssl', $i18n) ?></label>
</div>
<div class="form-group-inline">
<input type="text" name="smtpusername" id="smtpusername" placeholder="<?= translate('smtp_username', $i18n) ?>" value="<?= $settings['smtp_username'] ?>" />
</div>
<div class="form-group-inline">
<input type="password" name="smtppassword" id="smtppassword" placeholder="<?= translate('smtp_password', $i18n) ?>" value="<?= $settings['smtp_password'] ?>" />
</div>
<div class="form-group-inline">
<input type="text" name="fromemail" id="fromemail" placeholder="<?= translate('from_email', $i18n) ?>" value="<?= $settings['from_email'] ?>" />
</div>
<div class="settings-notes">
<p>
<i class="fa-solid fa-circle-info"></i> <?= translate('smtp_info', $i18n) ?>
</p>
<p>
<i class="fa-solid fa-circle-info"></i>
<?= translate('smtp_usage_info', $i18n) ?>
</p>
</div>
<div class="buttons">
<input type="button" class="secondary-button thin" value="<?= translate('test', $i18n) ?>" id="testSmtpSettingsButton" onClick="testSmtpSettingsButton()"/>
<input type="submit" class="thin" value="<?= translate('save', $i18n) ?>" id="saveSmtpSettingsButton" onClick="saveSmtpSettingsButton()"/>
</div>
</div>
</section>
<section class="account-section">
<header>
<h2><?= translate('backup_and_restore', $i18n) ?></h2>
</header>
<div class="form-group-inline">
<div>
<input type="button" class="button thin" value="<?= translate('backup', $i18n) ?>" id="backupDB" onClick="backupDB()"/>
</div>
<div>
<input type="button" class="secondary-button thin" value="<?= translate('restore', $i18n) ?>" id="restoreDB" onClick="openRestoreDBFileSelect()" />
<input type="file" name="restoreDBFile" id="restoreDBFile" style="display: none;" onChange="restoreDB()" accept=".zip">
</div>
</div>
<div class="settings-notes">
<p>
<i class="fa-solid fa-circle-info"></i>
<?= translate('restore_info', $i18n) ?>
</p>
</div>
</section>
</section>
<script src="scripts/admin.js?<?= $version ?>"></script>
<?php
require_once 'includes/footer.php';
?>

View File

@ -2,3 +2,5 @@
0 1 * * * /usr/local/bin/php /var/www/html/endpoints/cronjobs/updatenextpayment.php >> /var/log/cron/updatenextpayment.log 2>&1
0 2 * * * /usr/local/bin/php /var/www/html/endpoints/cronjobs/updateexchange.php >> /var/log/cron/updateexchange.log 2>&1
0 9 * * * /usr/local/bin/php /var/www/html/endpoints/cronjobs/sendnotifications.php >> /var/log/cron/sendnotifications.log 2>&1
*/2 * * * * /usr/local/bin/php /var/www/html/endpoints/cronjobs/sendverificationemails.php >> /var/log/cron/sendverificationemails.log 2>&1
*/2 * * * * /usr/local/bin/php /var/www/html/endpoints/cronjobs/sendresetpasswordemails.php >> /var/log/cron/sendresetpasswordemails.log 2>&1

View File

@ -0,0 +1,132 @@
<?php
require_once '../../includes/connect_endpoint.php';
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
"message" => translate('session_expired', $i18n)
]));
}
// Check that user is an admin
if ($userId !== 1) {
die(json_encode([
"success" => false,
"message" => translate('error', $i18n)
]));
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$postData = file_get_contents("php://input");
$data = json_decode($postData, true);
$userId = $data['userId'];
if ($userId == 1) {
die(json_encode([
"success" => false,
"message" => translate('error', $i18n)
]));
} else {
// Delete user
$stmt = $db->prepare('DELETE FROM user WHERE id = :id');
$stmt->bindValue(':id', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
// Delete subscriptions
$stmt = $db->prepare('DELETE FROM subscriptions WHERE user_id = :id');
$stmt->bindValue(':id', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
// Delete settings
$stmt = $db->prepare('DELETE FROM settings WHERE user_id = :id');
$stmt->bindValue(':id', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
// Delete fixer
$stmt = $db->prepare('DELETE FROM fixer WHERE user_id = :id');
$stmt->bindValue(':id', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
// Delete custom colors
$stmt = $db->prepare('DELETE FROM custom_colors WHERE user_id = :id');
$stmt->bindValue(':id', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
// Delete currencies
$stmt = $db->prepare('DELETE FROM currencies WHERE user_id = :id');
$stmt->bindValue(':id', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
// Delete categories
$stmt = $db->prepare('DELETE FROM categories WHERE user_id = :id');
$stmt->bindValue(':id', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
// Delete household
$stmt = $db->prepare('DELETE FROM household WHERE user_id = :id');
$stmt->bindValue(':id', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
// Delete payment methods
$stmt = $db->prepare('DELETE FROM payment_methods WHERE user_id = :id');
$stmt->bindValue(':id', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
// Delete email notifications
$stmt = $db->prepare('DELETE FROM email_notifications WHERE user_id = :id');
$stmt->bindValue(':id', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
// Delete telegram notifications
$stmt = $db->prepare('DELETE FROM telegram_notifications WHERE user_id = :id');
$stmt->bindValue(':id', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
// Delete webhook notifications
$stmt = $db->prepare('DELETE FROM webhook_notifications WHERE user_id = :id');
$stmt->bindValue(':id', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
// Delete gotify notifications
$stmt = $db->prepare('DELETE FROM gotify_notifications WHERE user_id = :id');
$stmt->bindValue(':id', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
// Delete pushover notifications
$stmt = $db->prepare('DELETE FROM pushover_notifications WHERE user_id = :id');
$stmt->bindValue(':id', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
// Dele notification settings
$stmt = $db->prepare('DELETE FROM notification_settings WHERE user_id = :id');
$stmt->bindValue(':id', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
// Delete last exchange update
$stmt = $db->prepare('DELETE FROM last_exchange_update WHERE user_id = :id');
$stmt->bindValue(':id', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
// Delete email verification
$stmt = $db->prepare('DELETE FROM email_verification WHERE user_id = :id');
$stmt->bindValue(':id', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
die(json_encode([
"success" => true,
"message" => translate('success', $i18n)
]));
}
} else {
die(json_encode([
"success" => false,
"message" => translate('error', $i18n)
]));
}
?>

View File

@ -0,0 +1,85 @@
<?php
require_once '../../includes/connect_endpoint.php';
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
"message" => translate('session_expired', $i18n)
]));
}
// Check that user is an admin
if ($userId !== 1) {
die(json_encode([
"success" => false,
"message" => translate('error', $i18n)
]));
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$postData = file_get_contents("php://input");
$data = json_decode($postData, true);
$openRegistrations = $data['open_registrations'];
$maxUsers = $data['max_users'];
$requireEmailVerification = $data['require_email_validation'];
$serverUrl = $data['server_url'];
$disableLogin = $data['disable_login'];
if ($disableLogin == 1) {
if ($openRegistrations == 1) {
echo json_encode([
"success" => false,
"message" => translate('error', $i18n)
]);
die();
}
$sql = "SELECT COUNT(*) as userCount FROM user";
$stmt = $db->prepare($sql);
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
$userCount = $row['userCount'];
if ($userCount > 1) {
echo json_encode([
"success" => false,
"message" => translate('error', $i18n)
]);
die();
}
}
if ($requireEmailVerification == 1 && $serverUrl == "") {
echo json_encode([
"success" => false,
"message" => translate('fill_all_fields', $i18n)
]);
die();
}
$sql = "UPDATE admin SET registrations_open = :openRegistrations, max_users = :maxUsers, require_email_verification = :requireEmailVerification, server_url = :serverUrl, login_disabled = :disableLogin WHERE id = 1";
$stmt = $db->prepare($sql);
$stmt->bindParam(':openRegistrations', $openRegistrations, SQLITE3_INTEGER);
$stmt->bindParam(':maxUsers', $maxUsers, SQLITE3_INTEGER);
$stmt->bindParam(':requireEmailVerification', $requireEmailVerification, SQLITE3_INTEGER);
$stmt->bindParam(':serverUrl', $serverUrl, SQLITE3_TEXT);
$stmt->bindParam(':disableLogin', $disableLogin, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result) {
echo json_encode([
"success" => true,
"message" => translate('success', $i18n)
]);
} else {
echo json_encode([
"success" => false,
"message" => translate('error', $i18n)
]);
}
}
?>

View File

@ -0,0 +1,64 @@
<?php
require_once '../../includes/connect_endpoint.php';
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
"message" => translate('session_expired', $i18n)
]));
}
// Check that user is an admin
if ($userId !== 1) {
die(json_encode([
"success" => false,
"message" => translate('error', $i18n)
]));
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$postData = file_get_contents("php://input");
$data = json_decode($postData, true);
$smtpAddress = $data['smtpaddress'];
$smtpPort = $data['smtpport'];
$encryption = $data['encryption'];
$smtpUsername = $data['smtpusername'];
$smtpPassword = $data['smtppassword'];
$fromEmail = $data['fromemail'];
if (empty($smtpAddress) || empty($smtpPort)) {
die(json_encode([
"success" => false,
"message" => translate('fill_all_fields', $i18n)
]));
}
// Save settings
$stmt = $db->prepare('UPDATE admin SET smtp_address = :smtp_address, smtp_port = :smtp_port, encryption = :encryption, smtp_username = :smtp_username, smtp_password = :smtp_password, from_email = :from_email');
$stmt->bindValue(':smtp_address', $smtpAddress, SQLITE3_TEXT);
$stmt->bindValue(':smtp_port', $smtpPort, SQLITE3_TEXT);
$encryption = empty($data['encryption']) ? 'tls' : $data['encryption'];
$stmt->bindValue(':encryption', $encryption, SQLITE3_TEXT);
$stmt->bindValue(':smtp_username', $smtpUsername, SQLITE3_TEXT);
$stmt->bindValue(':smtp_password', $smtpPassword, SQLITE3_TEXT);
$stmt->bindValue(':from_email', $fromEmail, SQLITE3_TEXT);
$result = $stmt->execute();
if ($result) {
die(json_encode([
"success" => true,
"message" => translate('success', $i18n)
]));
} else {
die(json_encode([
"success" => false,
"message" => translate('error', $i18n)
]));
}
}
?>

View File

@ -2,11 +2,10 @@
require_once '../../includes/connect_endpoint.php';
require_once '../../includes/inputvalidation.php';
session_start();
if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
if (isset($_GET['action']) && $_GET['action'] == "add") {
$stmt = $db->prepare('SELECT MAX("order") as maxOrder FROM categories');
$stmt = $db->prepare('SELECT MAX("order") as maxOrder FROM categories WHERE user_id = :userId');
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
$maxOrder = $row['maxOrder'];
@ -18,10 +17,11 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
$order = $maxOrder + 1;
$categoryName = "Category";
$sqlInsert = 'INSERT INTO categories ("name", "order") VALUES (:name, :order)';
$sqlInsert = 'INSERT INTO categories ("name", "order", "user_id") VALUES (:name, :order, :userId)';
$stmtInsert = $db->prepare($sqlInsert);
$stmtInsert->bindParam(':name', $categoryName, SQLITE3_TEXT);
$stmtInsert->bindParam(':order', $order, SQLITE3_INTEGER);
$stmtInsert->bindParam(':userId', $userId, SQLITE3_INTEGER);
$resultInsert = $stmtInsert->execute();
if ($resultInsert) {
@ -42,10 +42,11 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
if (isset($_GET['categoryId']) && $_GET['categoryId'] != "" && isset($_GET['name']) && $_GET['name'] != "") {
$categoryId = $_GET['categoryId'];
$name = validate($_GET['name']);
$sql = "UPDATE categories SET name = :name WHERE id = :categoryId";
$sql = "UPDATE categories SET name = :name WHERE id = :categoryId AND user_id = :userId";
$stmt = $db->prepare($sql);
$stmt->bindParam(':name', $name, SQLITE3_TEXT);
$stmt->bindParam(':categoryId', $categoryId, SQLITE3_INTEGER);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result) {
@ -71,9 +72,10 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
} else if (isset($_GET['action']) && $_GET['action'] == "delete") {
if (isset($_GET['categoryId']) && $_GET['categoryId'] != "" && $_GET['categoryId'] != 1) {
$categoryId = $_GET['categoryId'];
$checkCategory = "SELECT COUNT(*) FROM subscriptions WHERE category_id = :categoryId";
$checkCategory = "SELECT COUNT(*) FROM subscriptions WHERE category_id = :categoryId AND user_id = :userId";
$checkStmt = $db->prepare($checkCategory);
$checkStmt->bindParam(':categoryId', $categoryId, SQLITE3_INTEGER);
$checkStmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$checkResult = $checkStmt->execute();
$row = $checkResult->fetchArray();
$count = $row[0];
@ -85,9 +87,10 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
];
echo json_encode($response);
} else {
$sql = "DELETE FROM categories WHERE id = :categoryId";
$sql = "DELETE FROM categories WHERE id = :categoryId AND user_id = :userId";
$stmt = $db->prepare($sql);
$stmt->bindParam(':categoryId', $categoryId, SQLITE3_INTEGER);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result) {
$response = [

View File

@ -2,17 +2,16 @@
require_once '../../includes/connect_endpoint.php';
session_start();
if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
$categories = $_POST['categoryIds'];
$order = 2;
foreach ($categories as $categoryId) {
$sql = "UPDATE categories SET `order` = :order WHERE id = :categoryId";
$sql = "UPDATE categories SET `order` = :order WHERE id = :categoryId AND user_id = :userId";
$stmt = $db->prepare($sql);
$stmt->bindParam(':order', $order, SQLITE3_INTEGER);
$stmt->bindParam(':categoryId', $categoryId, SQLITE3_INTEGER);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$order++;
}

View File

@ -1,6 +0,0 @@
<?php
#Webroot path
$webPath = "/var/www/html/";
?>

View File

@ -1,8 +1,6 @@
<?php
require_once 'conf.php';
$databaseFile = $webPath . 'db/wallos.db';
$databaseFile = __DIR__ . '/../../db/wallos.db';
if (!file_exists($databaseFile)) {
echo "Database does not exist. Creating it...\n";
@ -234,7 +232,6 @@ if (!file_exists($databaseFile)) {
} else {
echo "Database already exist. Checking for upgrades...\n";
$databaseFile = $webPath . 'db/wallos.db';
$db = new SQLite3($databaseFile);
$db->busyTimeout(5000);

View File

@ -3,106 +3,564 @@
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
require_once 'conf.php';
require_once $webPath . 'includes/connect_endpoint_crontabs.php';
require_once __DIR__ . '/../../includes/connect_endpoint_crontabs.php';
$query = "SELECT * FROM notifications WHERE id = 1";
$result = $db->query($query);
require __DIR__ . '/../../libs/PHPMailer/PHPMailer.php';
require __DIR__ . '/../../libs/PHPMailer/SMTP.php';
require __DIR__ . '/../../libs/PHPMailer/Exception.php';
if ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$notificationsEnabled = $row['enabled'];
$days = $row['days'];
$smtpAddress = $row["smtp_address"];
$smtpPort = $row["smtp_port"];
$encryption = $row["encryption"];
$smtpUsername = $row["smtp_username"];
$smtpPassword = $row["smtp_password"];
$fromEmail = $row["from_email"] ? $row["from_email"] : "wallos@wallosapp.com";
} else {
echo "Notifications are disabled. No need to run.";
}
// Get all user ids
$query = "SELECT id, username FROM user";
$stmt = $db->prepare($query);
$usersToNotify = $stmt->execute();
if ($notificationsEnabled) {
$currencies = array();
$query = "SELECT * FROM currencies";
$result = $db->query($query);
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$currencyId = $row['id'];
$currencies[$currencyId] = $row;
while ($userToNotify = $usersToNotify->fetchArray(SQLITE3_ASSOC)) {
$userId = $userToNotify['id'];
echo "For user: " . $userToNotify['username'] . "<br />";
$days = 1;
$emailNotificationsEnabled = false;
$gotifyNotificationsEnabled = false;
$telegramNotificationsEnabled = false;
$webhookNotificationsEnabled = false;
$pushoverNotificationsEnabled = false;
$discordNotificationsEnabled = false;
$ntfyNotificationsEnabled = false;
// Get notification settings (how many days before the subscription ends should the notification be sent)
$query = "SELECT days FROM notification_settings WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$days = $row['days'];
}
$stmt = $db->prepare('SELECT * FROM subscriptions WHERE notify = :notify AND inactive = :inactive ORDER BY payer_user_id ASC');
$stmt->bindValue(':notify', 1, SQLITE3_INTEGER);
$stmt->bindValue(':inactive', 0, SQLITE3_INTEGER);
$resultSubscriptions = $stmt->execute();
$notify = []; $i = 0;
$currentDate = new DateTime('now');
while ($rowSubscription = $resultSubscriptions->fetchArray(SQLITE3_ASSOC)) {
$nextPaymentDate = new DateTime($rowSubscription['next_payment']);
$difference = $currentDate->diff($nextPaymentDate)->days + 1;
if ($difference === $days) {
$notify[$rowSubscription['payer_user_id']][$i]['name'] = $rowSubscription['name'];
$notify[$rowSubscription['payer_user_id']][$i]['price'] = $rowSubscription['price'] . $currencies[$rowSubscription['currency_id']]['symbol'];
$i++;
// Check if email notifications are enabled and get the settings
$query = "SELECT * FROM email_notifications WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$emailNotificationsEnabled = $row['enabled'];
$email['smtpAddress'] = $row["smtp_address"];
$email['smtpPort'] = $row["smtp_port"];
$email['encryption'] = $row["encryption"];
$email['smtpUsername'] = $row["smtp_username"];
$email['smtpPassword'] = $row["smtp_password"];
$email['fromEmail'] = $row["from_email"] ? $row["from_email"] : "wallos@wallosapp.com";
}
// Check if Discord notifications are enabled and get the settings
$query = "SELECT * FROM discord_notifications WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$discordNotificationsEnabled = $row['enabled'];
$discord['webhook_url'] = $row["webhook_url"];
$discord['bot_username'] = $row["bot_username"];
$discord['bot_avatar_url'] = $row["bot_avatar_url"];
}
// Check if Gotify notifications are enabled and get the settings
$query = "SELECT * FROM gotify_notifications WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$gotifyNotificationsEnabled = $row['enabled'];
$gotify['serverUrl'] = $row["url"];
$gotify['appToken'] = $row["token"];
}
// Check if Telegram notifications are enabled and get the settings
$query = "SELECT * FROM telegram_notifications WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$telegramNotificationsEnabled = $row['enabled'];
$telegram['botToken'] = $row["bot_token"];
$telegram['chatId'] = $row["chat_id"];
}
// Check if Pushover notifications are enabled and get the settings
$query = "SELECT * FROM pushover_notifications WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$pushoverNotificationsEnabled = $row['enabled'];
$pushover['user_key'] = $row["user_key"];
$pushover['token'] = $row["token"];
}
// Check if Nrfy notifications are enabled and get the settings
$query = "SELECT * FROM ntfy_notifications WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$ntfyNotificationsEnabled = $row['enabled'];
$ntfy['host'] = $row["host"];
$ntfy['topic'] = $row["topic"];
$ntfy['headers'] = $row["headers"];
}
// Check if Webhook notifications are enabled and get the settings
$query = "SELECT * FROM webhook_notifications WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$webhookNotificationsEnabled = $row['enabled'];
$webhook['url'] = $row["url"];
$webhook['request_method'] = $row["request_method"];
$webhook['headers'] = $row["headers"];
$webhook['payload'] = $row["payload"];
$webhook['iterator'] = $row["iterator"];
if ($webhook['iterator'] === "") {
$webhook['iterator'] = "subscriptions";
}
}
if (!empty($notify)) {
$notificationsEnabled = $emailNotificationsEnabled || $gotifyNotificationsEnabled || $telegramNotificationsEnabled ||
$webhookNotificationsEnabled || $pushoverNotificationsEnabled || $discordNotificationsEnabled ||
$ntfyNotificationsEnabled;
require $webPath . 'libs/PHPMailer/PHPMailer.php';
require $webPath . 'libs/PHPMailer/SMTP.php';
require $webPath . 'libs/PHPMailer/Exception.php';
$stmt = $db->prepare('SELECT * FROM user WHERE id = :id');
$stmt->bindValue(':id', 1, SQLITE3_INTEGER);
$result = $stmt->execute();
$defaultUser = $result->fetchArray(SQLITE3_ASSOC);
$defaultEmail = $defaultUser['email'];
$defaultName = $defaultUser['username'];
foreach ($notify as $userId => $perUser) {
$dayText = $days == 1 ? "tomorrow" : "in " . $days . " days";
$message = "The following subscriptions are up for renewal " . $dayText . ":\n";
foreach ($perUser as $subscription) {
$message .= $subscription['name'] . " for " . $subscription['price'] . "\n";
}
$mail = new PHPMailer(true);
$mail->CharSet="UTF-8";
$mail->isSMTP();
$mail->Host = $smtpAddress;
$mail->SMTPAuth = true;
$mail->Username = $smtpUsername;
$mail->Password = $smtpPassword;
$mail->SMTPSecure = $encryption;
$mail->Port = $smtpPort;
$stmt = $db->prepare('SELECT * FROM household WHERE id = :userId');
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$user = $result->fetchArray(SQLITE3_ASSOC);
$email = !empty($user['email']) ? $user['email'] : $defaultEmail;
$name = !empty($user['name']) ? $user['name'] : $defaultName;
$mail->setFrom($fromEmail, 'Wallos App');
$mail->addAddress($email, $name);
$mail->Subject = 'Wallos Notification';
$mail->Body = $message;
if ($mail->send()) {
echo "Notifications sent";
} else {
echo "Error sending notifications: " . $mail->ErrorInfo;
}
}
// If no notifications are enabled, no need to run
if (!$notificationsEnabled) {
echo "Notifications are disabled. No need to run.<br />";
continue;
} else {
echo "Nothing to notify.";
// Get all currencies
$currencies = array();
$query = "SELECT * FROM currencies WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$currencies[$row['id']] = $row;
}
// Get all household members
$query = "SELECT * FROM household WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$resultHousehold = $stmt->execute();
$household = [];
while ($rowHousehold = $resultHousehold->fetchArray(SQLITE3_ASSOC)) {
$household[$rowHousehold['id']] = $rowHousehold;
}
// Get all categories
$query = "SELECT * FROM categories WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$resultCategories = $stmt->execute();
$categories = [];
while ($rowCategory = $resultCategories->fetchArray(SQLITE3_ASSOC)) {
$categories[$rowCategory['id']] = $rowCategory;
}
$query = "SELECT * FROM subscriptions WHERE user_id = :user_id AND notify = :notify AND inactive = :inactive ORDER BY payer_user_id ASC";
$stmt = $db->prepare($query);
$stmt->bindValue(':user_id', $userId, SQLITE3_INTEGER);
$stmt->bindValue(':notify', 1, SQLITE3_INTEGER);
$stmt->bindValue(':inactive', 0, SQLITE3_INTEGER);
$resultSubscriptions = $stmt->execute();
$notify = []; $i = 0;
$currentDate = new DateTime('now');
while ($rowSubscription = $resultSubscriptions->fetchArray(SQLITE3_ASSOC)) {
if ($rowSubscription['notify_days_before'] !== 0) {
$daysToCompare = $rowSubscription['notify_days_before'];
} else {
$daysToCompare = $days;
}
$nextPaymentDate = new DateTime($rowSubscription['next_payment']);
$difference = $currentDate->diff($nextPaymentDate)->days + 1;
if ($difference === $daysToCompare) {
$notify[$rowSubscription['payer_user_id']][$i]['name'] = $rowSubscription['name'];
$notify[$rowSubscription['payer_user_id']][$i]['price'] = $rowSubscription['price'] . $currencies[$rowSubscription['currency_id']]['symbol'];
$notify[$rowSubscription['payer_user_id']][$i]['currency'] = $currencies[$rowSubscription['currency_id']]['name'];
$notify[$rowSubscription['payer_user_id']][$i]['category'] = $categories[$rowSubscription['category_id']]['name'];
$notify[$rowSubscription['payer_user_id']][$i]['payer'] = $household[$rowSubscription['payer_user_id']]['name'];
$notify[$rowSubscription['payer_user_id']][$i]['date'] = $rowSubscription['next_payment'];
$notify[$rowSubscription['payer_user_id']][$i]['days'] = $daysToCompare;
$i++;
}
}
if (!empty($notify)) {
// Email notifications if enabled
if ($emailNotificationsEnabled) {
$stmt = $db->prepare('SELECT * FROM user WHERE id = :user_id');
$stmt->bindValue(':user_id', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$defaultUser = $result->fetchArray(SQLITE3_ASSOC);
$defaultEmail = $defaultUser['email'];
$defaultName = $defaultUser['username'];
foreach ($notify as $userId => $perUser) {
$message = "The following subscriptions are up for renewal:\n";
foreach ($perUser as $subscription) {
$dayText = $subscription['days'] == 1 ? "Tomorrow" : "In " . $subscription['days'] . " days";
$message .= $subscription['name'] . " for " . $subscription['price'] . " (" . $dayText . ")\n";
}
$mail = new PHPMailer(true);
$mail->CharSet="UTF-8";
$mail->isSMTP();
$mail->Host = $email['smtpAddress'];
$mail->SMTPAuth = true;
$mail->Username = $email['smtpUsername'];
$mail->Password = $email['smtpPassword'];
$mail->SMTPSecure = $email['encryption'];
$mail->Port = $email['smtpPort'];
$stmt = $db->prepare('SELECT * FROM household WHERE id = :userId');
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$user = $result->fetchArray(SQLITE3_ASSOC);
$emailaddress = !empty($user['email']) ? $user['email'] : $defaultEmail;
$name = !empty($user['name']) ? $user['name'] : $defaultName;
$mail->setFrom($email['fromEmail'], 'Wallos App');
$mail->addAddress($emailaddress, $name);
$mail->Subject = 'Wallos Notification';
$mail->Body = $message;
if ($mail->send()) {
echo "Email Notifications sent<br />";
} else {
echo "Error sending notifications: " . $mail->ErrorInfo . "<br />";
}
}
}
// Discord notifications if enabled
if ($discordNotificationsEnabled) {
foreach ($notify as $userId => $perUser) {
// Get name of user from household table
$stmt = $db->prepare('SELECT * FROM household WHERE id = :userId');
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$user = $result->fetchArray(SQLITE3_ASSOC);
$title = translate('wallos_notification', $i18n);
if ($user['name']) {
$message = $user['name'] . ", the following subscriptions are up for renewal:\n";
} else {
$message = "The following subscriptions are up for renewal:\n";
}
foreach ($perUser as $subscription) {
$dayText = $subscription['days'] == 1 ? "Tomorrow" : "In " . $subscription['days'] . " days";
$message .= $subscription['name'] . " for " . $subscription['price'] . " (" . $dayText . ")\n";
}
$postfields = [
'content' => $message
];
if (!empty($discord['bot_username'])) {
$postfields['username'] = $discord['bot_username'];
}
if (!empty($discord['bot_avatar_url'])) {
$postfields['avatar_url'] = $discord['bot_avatar_url'];
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $discord['webhook_url']);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($postfields));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
if ($result === false) {
echo "Error sending notifications: " . curl_error($ch) . "<br />";
} else {
echo "Discord Notifications sent<br />";
}
}
}
// Gotify notifications if enabled
if ($gotifyNotificationsEnabled) {
foreach ($notify as $userId => $perUser) {
// Get name of user from household table
$stmt = $db->prepare('SELECT * FROM household WHERE id = :userId');
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$user = $result->fetchArray(SQLITE3_ASSOC);
if ($user['name']) {
$message = $user['name'] . ", the following subscriptions are up for renewal:\n";
} else {
$message = "The following subscriptions are up for renewal:\n";
}
foreach ($perUser as $subscription) {
$dayText = $subscription['days'] == 1 ? "Tomorrow" : "In " . $subscription['days'] . " days";
$message .= $subscription['name'] . " for " . $subscription['price'] . " (" . $dayText . ")\n";
}
$data = array(
'message' => $message,
'priority' => 5
);
$data_string = json_encode($data);
$ch = curl_init($gotify['serverUrl'] . '/message?token=' . $gotify['appToken']);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($data_string))
);
$result = curl_exec($ch);
if ($result === false) {
echo "Error sending notifications: " . curl_error($ch) . "<br />";
} else {
echo "Gotify Notifications sent<br />";
}
}
}
// Telegram notifications if enabled
if ($telegramNotificationsEnabled) {
foreach ($notify as $userId => $perUser) {
// Get name of user from household table
$stmt = $db->prepare('SELECT * FROM household WHERE id = :userId');
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$user = $result->fetchArray(SQLITE3_ASSOC);
if ($user['name']) {
$message = $user['name'] . ", the following subscriptions are up for renewal:\n";
} else {
$message = "The following subscriptions are up for renewal:\n";
}
foreach ($perUser as $subscription) {
$dayText = $subscription['days'] == 1 ? "Tomorrow" : "In " . $subscription['days'] . " days";
$message .= $subscription['name'] . " for " . $subscription['price'] . " (" . $dayText . ")\n";
}
$data = array(
'chat_id' => $telegram['chatId'],
'text' => $message
);
$data_string = json_encode($data);
$ch = curl_init('https://api.telegram.org/bot' . $telegram['botToken'] . '/sendMessage');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($data_string))
);
$result = curl_exec($ch);
if ($result === false) {
echo "Error sending notifications: " . curl_error($ch) . "<br />";
} else {
echo "Telegram Notifications sent<br />";
}
}
}
// Pushover notifications if enabled
if ($pushoverNotificationsEnabled) {
foreach ($notify as $userId => $perUser) {
// Get name of user from household table
$stmt = $db->prepare('SELECT * FROM household WHERE id = :userId');
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$user = $result->fetchArray(SQLITE3_ASSOC);
if ($user['name']) {
$message = $user['name'] . ", the following subscriptions are up for renewal:\n";
} else {
$message = "The following subscriptions are up for renewal:\n";
}
foreach ($perUser as $subscription) {
$dayText = $subscription['days'] == 1 ? "Tomorrow" : "In " . $subscription['days'] . " days";
$message .= $subscription['name'] . " for " . $subscription['price'] . " (" . $dayText . ")\n";
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api.pushover.net/1/messages.json");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'token' => $pushover['token'],
'user' => $pushover['user_key'],
'message' => $message,
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
if ($result === false) {
echo "Error sending notifications: " . curl_error($ch) . "<br />";
} else {
echo "Pushover Notifications sent<br />";
}
}
}
// Ntfy notifications if enabled
if ($ntfyNotificationsEnabled) {
foreach ($notify as $userId => $perUser) {
// Get name of user from household table
$stmt = $db->prepare('SELECT * FROM household WHERE id = :userId');
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$user = $result->fetchArray(SQLITE3_ASSOC);
if ($user['name']) {
$message = $user['name'] . ", the following subscriptions are up for renewal:\n";
} else {
$message = "The following subscriptions are up for renewal:\n";
}
foreach ($perUser as $subscription) {
$dayText = $subscription['days'] == 1 ? "Tomorrow" : "In " . $subscription['days'] . " days";
$message .= $subscription['name'] . " for " . $subscription['price'] . " (" . $dayText . ")\n";
}
$headers = json_decode($ntfy["headers"], true);
$customheaders = array_map(function($key, $value) {
return "$key: $value";
}, array_keys($headers), $headers);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $ntfy['host'] . '/' . $ntfy['topic']);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $message);
curl_setopt($ch, CURLOPT_HTTPHEADER, $customheaders);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
if ($response === false) {
echo "Error sending notifications: " . curl_error($ch) . "<br />";
} else {
echo "Ntfy Notifications sent<br />";
}
}
}
// Webhook notifications if enabled
if ($webhookNotificationsEnabled) {
// Get webhook payload and turn it into a json object
$payload = str_replace("{{days_until}}", $days, $webhook['payload']); // The default value for all subscriptions
$payload_json = json_decode($payload, true);
$subscription_template = $payload_json["{{subscriptions}}"];
$subscriptions = [];
foreach ($notify as $userId => $perUser) {
// Get name of user from household table
$stmt = $db->prepare('SELECT * FROM household WHERE id = :userId');
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$user = $result->fetchArray(SQLITE3_ASSOC);
if ($user['name']) {
$payer = $user['name'];
}
foreach ($perUser as $k => $subscription) {
$temp_subscription = $subscription_template[0];
foreach ($temp_subscription as $key => $value) {
if (is_string($value)) {
$temp_subscription[$key] = str_replace("{{subscription_name}}", $subscription['name'], $value);
$temp_subscription[$key] = str_replace("{{subscription_price}}", $subscription['price'], $temp_subscription[$key]);
$temp_subscription[$key] = str_replace("{{subscription_currency}}", $subscription['currency'], $temp_subscription[$key]);
$temp_subscription[$key] = str_replace("{{subscription_category}}", $subscription['category'], $temp_subscription[$key]);
$temp_subscription[$key] = str_replace("{{subscription_payer}}", $subscription['payer'], $temp_subscription[$key]);
$temp_subscription[$key] = str_replace("{{subscription_date}}", $subscription['date'], $temp_subscription[$key]);
$temp_subscription[$key] = str_replace("{{subscription_days_until_payment}}", $subscription['days'], $temp_subscription[$key]); // The de facto value for this subscription
}
}
$subscriptions[] = $temp_subscription;
}
}
$payload_json["{{subscriptions}}"] = $subscriptions;
$payload_json[$webhook['iterator']] = $subscriptions;
unset($payload_json["{{subscriptions}}"]);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $webhook['url']);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $webhook['request_method']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload_json));
if (!empty($webhook['headers'])) {
$customheaders = preg_split("/\r\n|\n|\r/", $webhook['headers']);
curl_setopt($ch, CURLOPT_HTTPHEADER, $customheaders);
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
if ($response === false) {
echo "Error sending notifications: " . curl_error($ch) . "<br />";
} else {
echo "Webhook Notifications sent<br />";
}
}
} else {
echo "Nothing to notify.<br />";
}
}
}
?>

View File

@ -0,0 +1,82 @@
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
require_once __DIR__ . '/../../includes/connect_endpoint_crontabs.php';
$query = "SELECT * FROM admin";
$stmt = $db->prepare($query);
$result = $stmt->execute();
$admin = $result->fetchArray(SQLITE3_ASSOC);
$query = "SELECT * FROM password_resets WHERE email_sent = 0";
$stmt = $db->prepare($query);
$result = $stmt->execute();
$rows = [];
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$rows[] = $row;
}
if ($rows) {
if ($admin['smtp_address'] && $admin['smtp_port'] && $admin['smtp_username'] && $admin['smtp_password'] && $admin['encryption']) {
// There are SMTP settings
$smtpAddress = $admin['smtp_address'];
$smtpPort = $admin['smtp_port'];
$smtpUsername = $admin['smtp_username'];
$smtpPassword = $admin['smtp_password'];
$fromEmail = empty($admin['from_email']) ? 'wallos@wallosapp.com' : $admin['from_email'];
$encryption = $admin['encryption'];
$server_url = $admin['server_url'];
require __DIR__ . '/../../libs/PHPMailer/PHPMailer.php';
require __DIR__ . '/../../libs/PHPMailer/SMTP.php';
require __DIR__ . '/../../libs/PHPMailer/Exception.php';
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host = $smtpAddress;
$mail->SMTPAuth = true;
$mail->Username = $smtpUsername;
$mail->Password = $smtpPassword;
$mail->SMTPSecure = $encryption;
$mail->Port = $smtpPort;
$mail->setFrom($fromEmail);
try {
foreach ($rows as $user) {
$mail->addAddress($user['email']);
$mail->isHTML(true);
$mail->Subject = 'Wallos - Reset Password';
$mail->Body = '<img src="' . $server_url . '/images/siteicons/blue/wallos.png" alt="Logo" />
<br>
A password reset was requested for your account.
<br>
Please click the following link to reset your password: <a href="' . $server_url . '/passwordreset.php?email=' . $user['email'] . '&token=' . $user['token'] . '">Reset Password</a>';
$mail->send();
$query = "UPDATE password_resets SET email_sent = 1 WHERE id = :id";
$stmt = $db->prepare($query);
$stmt->bindParam(':id', $user['id'], SQLITE3_INTEGER);
$stmt->execute();
$mail->clearAddresses();
echo "Password reset email sent to " . $user['email'] . "<br>";
}
} catch (Exception $e) {
echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo} <br>";
}
} else {
// There are no SMTP settings
exit();
}
} else {
// There are no password reset emails to be sent
exit();
}
?>

View File

@ -0,0 +1,85 @@
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
require_once __DIR__ . '/../../includes/connect_endpoint_crontabs.php';
$query = "SELECT * FROM admin";
$stmt = $db->prepare($query);
$result = $stmt->execute();
$admin = $result->fetchArray(SQLITE3_ASSOC);
if ($admin['require_email_verification'] == 0) {
die("Email verification is not required.");
}
$query = "SELECT * FROM email_verification WHERE email_sent = 0";
$stmt = $db->prepare($query);
$result = $stmt->execute();
$rows = [];
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$rows[] = $row;
}
if ($rows) {
if ($admin['smtp_address'] && $admin['smtp_port'] && $admin['smtp_username'] && $admin['smtp_password'] && $admin['encryption']) {
// There are SMTP settings
$smtpAddress = $admin['smtp_address'];
$smtpPort = $admin['smtp_port'];
$smtpUsername = $admin['smtp_username'];
$smtpPassword = $admin['smtp_password'];
$fromEmail = empty($admin['from_email']) ? 'wallos@wallosapp.com' : $admin['from_email'];
$encryption = $admin['encryption'];
$server_url = $admin['server_url'];
require __DIR__ . '/../../libs/PHPMailer/PHPMailer.php';
require __DIR__ . '/../../libs/PHPMailer/SMTP.php';
require __DIR__ . '/../../libs/PHPMailer/Exception.php';
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host = $smtpAddress;
$mail->SMTPAuth = true;
$mail->Username = $smtpUsername;
$mail->Password = $smtpPassword;
$mail->SMTPSecure = $encryption;
$mail->Port = $smtpPort;
$mail->setFrom($fromEmail);
try {
foreach ($rows as $user) {
$mail->addAddress($user['email']);
$mail->isHTML(true);
$mail->Subject = 'Wallos - Email Verification';
$mail->Body = '<img src="' . $server_url . '/images/siteicons/blue/wallos.png" alt="Logo" />
<br>
Registration on Wallos was successful.
<br>
Please click the following link to verify your email: <a href="' . $server_url . '/verifyemail.php?email=' . $user['email'] . '&token=' . $user['token'] . '">Verify Email</a>';
$mail->send();
$query = "UPDATE email_verification SET email_sent = 1 WHERE id = :id";
$stmt = $db->prepare($query);
$stmt->bindParam(':id', $user['id'], SQLITE3_INTEGER);
$stmt->execute();
$mail->clearAddresses();
echo "Verification email sent to " . $user['email'] . "<br>";
}
} catch (Exception $e) {
echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}";
}
} else {
// There are no SMTP settings
exit();
}
} else {
// There are no verification emails to be sent
exit();
}
?>

View File

@ -1,74 +1,105 @@
<?php
require_once 'conf.php';
require_once $webPath . 'includes/connect_endpoint_crontabs.php';
require_once __DIR__ . '/../../includes/connect_endpoint_crontabs.php';
$query = "SELECT api_key FROM fixer";
$result = $db->query($query);
// Get all user ids
if ($result) {
$row = $result->fetchArray(SQLITE3_ASSOC);
if ($row) {
$apiKey = $row['api_key'];
$query = "SELECT id, username FROM user";
$stmt = $db->prepare($query);
$usersToUpdateExchange = $stmt->execute();
$codes = "";
$query = "SELECT id, name, symbol, code FROM currencies";
$result = $db->query($query);
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$codes .= $row['code'].",";
}
$codes = rtrim($codes, ',');
$query = "SELECT u.main_currency, c.code FROM user u LEFT JOIN currencies c ON u.main_currency = c.id WHERE u.id = 1";
$stmt = $db->prepare($query);
$result = $stmt->execute();
while ($userToUpdateExchange = $usersToUpdateExchange->fetchArray(SQLITE3_ASSOC)) {
$userId = $userToUpdateExchange['id'];
echo "For user: " . $userToUpdateExchange['username'] . "<br />";
$query = "SELECT api_key, provider FROM fixer WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result) {
$row = $result->fetchArray(SQLITE3_ASSOC);
$mainCurrencyCode = $row['code'];
$mainCurrencyId = $row['main_currency'];
if ($row) {
$apiKey = $row['api_key'];
$provider = $row['provider'];
$api_url = "http://data.fixer.io/api/latest?access_key=". $apiKey . "&base=EUR&symbols=" . $codes;
$response = file_get_contents($api_url);
$apiData = json_decode($response, true);
$mainCurrencyToEUR = $apiData['rates'][$mainCurrencyCode];
if ($apiData !== null && isset($apiData['rates'])) {
foreach ($apiData['rates'] as $currencyCode => $rate) {
if ($currencyCode === $mainCurrencyCode) {
$exchangeRate = 1.0;
} else {
$exchangeRate = $rate / $mainCurrencyToEUR;
}
$updateQuery = "UPDATE currencies SET rate = :rate WHERE code = :code";
$updateStmt = $db->prepare($updateQuery);
$updateStmt->bindParam(':rate', $exchangeRate, SQLITE3_TEXT);
$updateStmt->bindParam(':code', $currencyCode, SQLITE3_TEXT);
$updateResult = $updateStmt->execute();
if (!$updateResult) {
echo "Error updating rate for currency: $currencyCode";
}
}
$currentDate = new DateTime();
$formattedDate = $currentDate->format('Y-m-d');
$deleteQuery = "DELETE FROM last_exchange_update";
$deleteStmt = $db->prepare($deleteQuery);
$deleteResult = $deleteStmt->execute();
$query = "INSERT INTO last_exchange_update (date) VALUES (:formattedDate)";
$codes = "";
$query = "SELECT id, name, symbol, code FROM currencies WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindParam(':formattedDate', $formattedDate, SQLITE3_TEXT);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$codes .= $row['code'].",";
}
$codes = rtrim($codes, ',');
$query = "SELECT u.main_currency, c.code FROM user u LEFT JOIN currencies c ON u.main_currency = c.id WHERE u.id = :userId";
$stmt = $db->prepare($query);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
$mainCurrencyCode = $row['code'];
$mainCurrencyId = $row['main_currency'];
$db->close();
echo "Rates updated successfully!";
if ($provider === 1) {
$api_url = "https://api.apilayer.com/fixer/latest?base=EUR&symbols=" . $codes;
$context = stream_context_create([
'http' => [
'method' => 'GET',
'header' => 'apikey: ' . $apiKey,
]
]);
$response = file_get_contents($api_url, false, $context);
} else {
$api_url = "http://data.fixer.io/api/latest?access_key=". $apiKey . "&base=EUR&symbols=" . $codes;
$response = file_get_contents($api_url);
}
$apiData = json_decode($response, true);
$mainCurrencyToEUR = $apiData['rates'][$mainCurrencyCode];
if ($apiData !== null && isset($apiData['rates'])) {
foreach ($apiData['rates'] as $currencyCode => $rate) {
if ($currencyCode === $mainCurrencyCode) {
$exchangeRate = 1.0;
} else {
$exchangeRate = $rate / $mainCurrencyToEUR;
}
$updateQuery = "UPDATE currencies SET rate = :rate WHERE code = :code";
$updateStmt = $db->prepare($updateQuery);
$updateStmt->bindParam(':rate', $exchangeRate, SQLITE3_TEXT);
$updateStmt->bindParam(':code', $currencyCode, SQLITE3_TEXT);
$updateResult = $updateStmt->execute();
if (!$updateResult) {
echo "Error updating rate for currency: $currencyCode <br />";
}
}
$currentDate = new DateTime();
$formattedDate = $currentDate->format('Y-m-d');
$deleteQuery = "DELETE FROM last_exchange_update WHERE user_id = :userId";
$deleteStmt = $db->prepare($deleteQuery);
$deleteResult = $deleteStmt->execute();
$query = "INSERT INTO last_exchange_update (date, user_id) VALUES (:formattedDate, :userId)";
$stmt = $db->prepare($query);
$stmt->bindParam(':formattedDate', $formattedDate, SQLITE3_TEXT);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$db->close();
echo "Rates updated successfully!<br />";
}
} else {
echo "Exchange rates update skipped. No fixer.io api key provided<br />";
$apiKey = null;
}
} else {
echo "Exchange rates update skipped. No fixer.io api key provided";
echo "Exchange rates update skipped. No fixer.io api key provided<br />";
$apiKey = null;
}
} else {
echo "Exchange rates update skipped. No fixer.io api key provided";
$apiKey = null;
}
?>

View File

@ -1,6 +1,6 @@
<?php
require_once 'conf.php';
require_once $webPath . 'includes/connect_endpoint_crontabs.php';
require_once __DIR__ . '/../../includes/connect_endpoint_crontabs.php';
$currentDate = new DateTime();
$currentDateString = $currentDate->format('Y-m-d');

View File

@ -2,20 +2,19 @@
require_once '../../includes/connect_endpoint.php';
require_once '../../includes/inputvalidation.php';
session_start();
if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
if (isset($_GET['action']) && $_GET['action'] == "add") {
$currencyName = "Currency";
$currencySymbol = "$";
$currencyCode = "CODE";
$currencyRate = 1;
$sqlInsert = "INSERT INTO currencies (name, symbol, code, rate) VALUES (:name, :symbol, :code, :rate)";
$sqlInsert = "INSERT INTO currencies (name, symbol, code, rate, user_id) VALUES (:name, :symbol, :code, :rate, :userId)";
$stmtInsert = $db->prepare($sqlInsert);
$stmtInsert->bindParam(':name', $currencyName, SQLITE3_TEXT);
$stmtInsert->bindParam(':symbol', $currencySymbol, SQLITE3_TEXT);
$stmtInsert->bindParam(':code', $currencyCode, SQLITE3_TEXT);
$stmtInsert->bindParam(':rate', $currencyRate, SQLITE3_TEXT);
$stmtInsert->bindParam(':userId', $userId, SQLITE3_INTEGER);
$resultInsert = $stmtInsert->execute();
if ($resultInsert) {
@ -30,12 +29,13 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
$name = validate($_GET['name']);
$symbol = validate($_GET['symbol']);
$code = validate($_GET['code']);
$sql = "UPDATE currencies SET name = :name, symbol = :symbol, code = :code WHERE id = :currencyId";
$sql = "UPDATE currencies SET name = :name, symbol = :symbol, code = :code WHERE id = :currencyId AND user_id = :userId";
$stmt = $db->prepare($sql);
$stmt->bindParam(':name', $name, SQLITE3_TEXT);
$stmt->bindParam(':symbol', $symbol, SQLITE3_TEXT);
$stmt->bindParam(':code', $code, SQLITE3_TEXT);
$stmt->bindParam(':currencyId', $currencyId, SQLITE3_INTEGER);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result) {
@ -60,16 +60,18 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
}
} else if (isset($_GET['action']) && $_GET['action'] == "delete") {
if (isset($_GET['currencyId']) && $_GET['currencyId'] != "") {
$query = "SELECT main_currency FROM user WHERE id = 1";
$query = "SELECT main_currency FROM user WHERE id = :userId";
$stmt = $db->prepare($query);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
$mainCurrencyId = $row['main_currency'];
$currencyId = $_GET['currencyId'];
$checkQuery = "SELECT COUNT(*) FROM subscriptions WHERE currency_id = :currencyId";
$checkQuery = "SELECT COUNT(*) FROM subscriptions WHERE currency_id = :currencyId AND user_id = :userId";
$checkStmt = $db->prepare($checkQuery);
$checkStmt->bindParam(':currencyId', $currencyId, SQLITE3_INTEGER);
$checkStmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$checkResult = $checkStmt->execute();
$row = $checkResult->fetchArray();
$count = $row[0];
@ -90,9 +92,10 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
echo json_encode($response);
exit;
} else {
$sql = "DELETE FROM currencies WHERE id = :currencyId";
$sql = "DELETE FROM currencies WHERE id = :currencyId AND user_id = :userId";
$stmt = $db->prepare($sql);
$stmt->bindParam(':currencyId', $currencyId, SQLITE3_INTEGER);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result) {
echo json_encode(["success" => true, "message" => translate('currency_removed', $i18n)]);

View File

@ -1,14 +1,15 @@
<?php
require_once '../../includes/connect_endpoint.php';
session_start();
if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$newApiKey = isset($_POST["api_key"]) ? $_POST["api_key"] : "";
$newApiKey = isset($_POST["api_key"]) ? trim($_POST["api_key"]) : "";
$provider = isset($_POST["provider"]) ? $_POST["provider"] : 0;
$removeOldKey = "DELETE FROM fixer";
$db->exec($removeOldKey);
$removeOldKey = "DELETE FROM fixer WHERE user_id = :userId";
$stmt = $db->prepare($removeOldKey);
$stmt->bindParam(":userId", $userId, SQLITE3_INTEGER);
$stmt->execute();
if ($provider == 1) {
$testKeyUrl = "https://api.apilayer.com/fixer/latest?base=USD&symbols=EUR";
@ -27,10 +28,11 @@
$apiData = json_decode($response, true);
if ($apiData['success'] && $apiData['success'] == 1) {
if (!empty($newApiKey)) {
$insertNewKey = "INSERT INTO fixer (api_key, provider) VALUES (:api_key, :provider)";
$insertNewKey = "INSERT INTO fixer (api_key, provider, user_id) VALUES (:api_key, :provider, :userId)";
$stmt = $db->prepare($insertNewKey);
$stmt->bindParam(":api_key", $newApiKey, SQLITE3_TEXT);
$stmt->bindParam(":provider", $provider, SQLITE3_INTEGER);
$stmt->bindParam(":userId", $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result) {
echo json_encode(["success" => true, "message" => translate('api_key_saved', $i18n)]);

View File

@ -6,8 +6,10 @@ $shouldUpdate = true;
if (isset($_GET['force']) && $_GET['force'] === "true") {
$shouldUpdate = true;
} else {
$query = "SELECT date FROM last_exchange_update";
$result = $db->querySingle($query);
$query = "SELECT date FROM last_exchange_update WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result) {
$lastUpdateDate = new DateTime($result);
@ -34,14 +36,17 @@ if ($result) {
$provider = $row['provider'];
$codes = "";
$query = "SELECT id, name, symbol, code FROM currencies";
$result = $db->query($query);
$query = "SELECT id, name, symbol, code FROM currencies WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$codes .= $row['code'].",";
}
$codes = rtrim($codes, ',');
$query = "SELECT u.main_currency, c.code FROM user u LEFT JOIN currencies c ON u.main_currency = c.id WHERE u.id = 1";
$query = "SELECT u.main_currency, c.code FROM user u LEFT JOIN currencies c ON u.main_currency = c.id WHERE u.id = :userId";
$stmt = $db->prepare($query);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
$mainCurrencyCode = $row['code'];
@ -72,10 +77,11 @@ if ($result) {
} else {
$exchangeRate = $rate / $mainCurrencyToEUR;
}
$updateQuery = "UPDATE currencies SET rate = :rate WHERE code = :code";
$updateQuery = "UPDATE currencies SET rate = :rate WHERE code = :code AND user_id = :userId";
$updateStmt = $db->prepare($updateQuery);
$updateStmt->bindParam(':rate', $exchangeRate, SQLITE3_TEXT);
$updateStmt->bindParam(':code', $currencyCode, SQLITE3_TEXT);
$updateStmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$updateResult = $updateStmt->execute();
if (!$updateResult) {
@ -85,14 +91,11 @@ if ($result) {
$currentDate = new DateTime();
$formattedDate = $currentDate->format('Y-m-d');
$deleteQuery = "DELETE FROM last_exchange_update";
$deleteStmt = $db->prepare($deleteQuery);
$deleteResult = $deleteStmt->execute();
$query = "INSERT INTO last_exchange_update (date) VALUES (:formattedDate)";
$stmt = $db->prepare($query);
$stmt->bindParam(':formattedDate', $formattedDate, SQLITE3_TEXT);
$result = $stmt->execute();
$updateQuery = "UPDATE last_exchange_update SET date = :formattedDate WHERE user_id = :userId";
$updateStmt = $db->prepare($updateQuery);
$updateStmt->bindParam(':formattedDate', $formattedDate, SQLITE3_TEXT);
$updateStmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$updateResult = $updateStmt->execute();
$db->close();
echo "Rates updated successfully!";

View File

@ -1,6 +1,6 @@
<?php
require_once '../../includes/connect_endpoint.php';
session_start();
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,

View File

@ -1,6 +1,5 @@
<?php
require_once '../../includes/connect_endpoint.php';
session_start();
$result = $db->query("SELECT COUNT(*) as count FROM user");
$row = $result->fetchArray(SQLITE3_NUM);

View File

@ -33,6 +33,15 @@ $allMigrations = glob('migrations/*.php');
if (count($allMigrations) == 0) {
$allMigrations = glob('../../migrations/*.php');
}
$allMigrations = array_map(function($migration) {
return str_replace('../../', '', $migration);
}, $allMigrations);
$completedMigrations = array_map(function($migration) {
return str_replace('../../', '', $migration);
}, $completedMigrations);
$requiredMigrations = array_diff($allMigrations, $completedMigrations);
if (count($requiredMigrations) === 0) {
@ -40,6 +49,9 @@ if (count($requiredMigrations) === 0) {
}
foreach ($requiredMigrations as $migration) {
if (!file_exists($migration)) {
$migration = '../../' . $migration;
}
require_once $migration;
$stmtInsert = $db->prepare('INSERT INTO migrations (migration) VALUES (:migration)');

View File

@ -1,6 +1,6 @@
<?php
require_once '../../includes/connect_endpoint.php';
session_start();
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,

View File

@ -2,14 +2,13 @@
require_once '../../includes/connect_endpoint.php';
require_once '../../includes/inputvalidation.php';
session_start();
if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
if (isset($_GET['action']) && $_GET['action'] == "add") {
$householdName = "Member";
$sqlInsert = "INSERT INTO household (name) VALUES (:name)";
$sqlInsert = "INSERT INTO household (name, user_id) VALUES (:name, :userId)";
$stmtInsert = $db->prepare($sqlInsert);
$stmtInsert->bindParam(':name', $householdName, SQLITE3_TEXT);
$stmtInsert->bindParam(':userId', $userId, SQLITE3_INTEGER);
$resultInsert = $stmtInsert->execute();
if ($resultInsert) {
@ -32,11 +31,12 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
$name = validate($_GET['name']);
$email = $_GET['email'] ? $_GET['email'] : "";
$email = validate($email);
$sql = "UPDATE household SET name = :name, email = :email WHERE id = :memberId";
$sql = "UPDATE household SET name = :name, email = :email WHERE id = :memberId AND user_id = :userId";
$stmt = $db->prepare($sql);
$stmt->bindParam(':name', $name, SQLITE3_TEXT);
$stmt->bindParam(':email', $email, SQLITE3_TEXT);
$stmt->bindParam(':memberId', $memberId, SQLITE3_INTEGER);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result) {
@ -62,9 +62,10 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
} else if (isset($_GET['action']) && $_GET['action'] == "delete") {
if (isset($_GET['memberId']) && $_GET['memberId'] != "" && $_GET['memberId'] != 1) {
$memberId = $_GET['memberId'];
$checkMember = "SELECT COUNT(*) FROM subscriptions WHERE payer_user_id = :memberId";
$checkMember = "SELECT COUNT(*) FROM subscriptions WHERE payer_user_id = :memberId AND user_id = :userId";
$checkStmt = $db->prepare($checkMember);
$checkStmt->bindParam(':memberId', $memberId, SQLITE3_INTEGER);
$checkStmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$checkResult = $checkStmt->execute();
$row = $checkResult->fetchArray();
$count = $row[0];
@ -76,9 +77,10 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
];
echo json_encode($response);
} else {
$sql = "DELETE FROM household WHERE id = :memberId";
$sql = "DELETE FROM household WHERE id = :memberId and user_id = :userId";
$stmt = $db->prepare($sql);
$stmt->bindParam(':memberId', $memberId, SQLITE3_INTEGER);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result) {
$response = [

View File

@ -0,0 +1,77 @@
<?php
require_once '../../includes/connect_endpoint.php';
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
"message" => translate('session_expired', $i18n)
]));
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$postData = file_get_contents("php://input");
$data = json_decode($postData, true);
if (
!isset($data["url"]) || $data["url"] == ""
) {
$response = [
"success" => false,
"message" => translate('fill_mandatory_fields', $i18n)
];
echo json_encode($response);
} else {
$enabled = $data["enabled"];
$webhook_url = $data["url"];
$bot_username = $data["bot_username"];
$bot_avatar_url = $data["bot_avatar"];
$query = "SELECT COUNT(*) FROM discord_notifications WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindParam(":userId", $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result === false) {
$response = [
"success" => false,
"message" => translate('error_saving_notifications', $i18n)
];
echo json_encode($response);
} else {
$row = $result->fetchArray();
$count = $row[0];
if ($count == 0) {
$query = "INSERT INTO discord_notifications (enabled, webhook_url, bot_username, bot_avatar_url, user_id)
VALUES (:enabled, :webhook_url, :bot_username, :bot_avatar_url, :userId)";
} else {
$query = "UPDATE discord_notifications
SET enabled = :enabled, webhook_url = :webhook_url, bot_username = :bot_username, bot_avatar_url = :bot_avatar_url
WHERE user_id = :userId";
}
$stmt = $db->prepare($query);
$stmt->bindValue(':enabled', $enabled, SQLITE3_INTEGER);
$stmt->bindValue(':webhook_url', $webhook_url, SQLITE3_TEXT);
$stmt->bindValue(':bot_username', $bot_username, SQLITE3_TEXT);
$stmt->bindValue(':bot_avatar_url', $bot_avatar_url, SQLITE3_TEXT);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
if ($stmt->execute()) {
$response = [
"success" => true,
"message" => translate('notifications_settings_saved', $i18n)
];
echo json_encode($response);
} else {
$response = [
"success" => false,
"message" => translate('error_saving_notifications', $i18n)
];
echo json_encode($response);
}
}
}
}
?>

View File

@ -1,13 +1,18 @@
<?php
require_once '../../includes/connect_endpoint.php';
session_start();
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
"message" => translate('session_expired', $i18n)
]));
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$postData = file_get_contents("php://input");
$data = json_decode($postData, true);
if (
!isset($data["days"]) || $data['days'] == "" ||
!isset($data["smtpaddress"]) || $data["smtpaddress"] == "" ||
!isset($data["smtpport"]) || $data["smtpport"] == "" ||
!isset($data["smtpusername"]) || $data["smtpusername"] == "" ||
@ -15,12 +20,11 @@
) {
$response = [
"success" => false,
"errorMessage" => translate('fill_mandatory_fields', $i18n)
"message" => translate('fill_mandatory_fields', $i18n)
];
echo json_encode($response);
} else {
$enabled = $data["enabled"];
$days = $data["days"];
$smtpAddress = $data["smtpaddress"];
$smtpPort = $data["smtpport"];
$encryption = "tls";
@ -31,34 +35,38 @@
$smtpPassword = $data["smtppassword"];
$fromEmail = $data["fromemail"];
$query = "SELECT COUNT(*) FROM notifications";
$result = $db->querySingle($query);
$query = "SELECT COUNT(*) FROM email_notifications WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindParam(":userId", $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result === false) {
$response = [
"success" => false,
"errorMessage" => translate('error_saving_notifications', $i18n)
"message" => translate('error_saving_notifications', $i18n)
];
echo json_encode($response);
} else {
if ($result == 0) {
$query = "INSERT INTO notifications (enabled, days, smtp_address, smtp_port, smtp_username, smtp_password, from_email, encryption)
VALUES (:enabled, :days, :smtpAddress, :smtpPort, :smtpUsername, :smtpPassword, :fromEmail, :encryption)";
$row = $result->fetchArray();
$count = $row[0];
if ($count == 0) {
$query = "INSERT INTO email_notifications (enabled, smtp_address, smtp_port, smtp_username, smtp_password, from_email, encryption, user_id)
VALUES (:enabled, :smtpAddress, :smtpPort, :smtpUsername, :smtpPassword, :fromEmail, :encryption, :userId)";
} else {
$query = "UPDATE notifications
SET enabled = :enabled, days = :days, smtp_address = :smtpAddress, smtp_port = :smtpPort,
smtp_username = :smtpUsername, smtp_password = :smtpPassword, from_email = :fromEmail, encryption = :encryption";
$query = "UPDATE email_notifications
SET enabled = :enabled, smtp_address = :smtpAddress, smtp_port = :smtpPort,
smtp_username = :smtpUsername, smtp_password = :smtpPassword, from_email = :fromEmail, encryption = :encryption WHERE user_id = :userId";
}
$stmt = $db->prepare($query);
$stmt->bindValue(':enabled', $enabled, SQLITE3_INTEGER);
$stmt->bindValue(':days', $days, SQLITE3_INTEGER);
$stmt->bindValue(':smtpAddress', $smtpAddress, SQLITE3_TEXT);
$stmt->bindValue(':smtpPort', $smtpPort, SQLITE3_INTEGER);
$stmt->bindValue(':smtpUsername', $smtpUsername, SQLITE3_TEXT);
$stmt->bindValue(':smtpPassword', $smtpPassword, SQLITE3_TEXT);
$stmt->bindValue(':fromEmail', $fromEmail, SQLITE3_TEXT);
$stmt->bindValue(':encryption', $encryption, SQLITE3_TEXT);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
if ($stmt->execute()) {
$response = [
@ -69,7 +77,7 @@
} else {
$response = [
"success" => false,
"errorMessage" => translate('error_saving_notifications', $i18n)
"message" => translate('error_saving_notifications', $i18n)
];
echo json_encode($response);
}

View File

@ -0,0 +1,73 @@
<?php
require_once '../../includes/connect_endpoint.php';
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
"message" => translate('session_expired', $i18n)
]));
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$postData = file_get_contents("php://input");
$data = json_decode($postData, true);
if (
!isset($data["gotify_url"]) || $data["gotify_url"] == "" ||
!isset($data["token"]) || $data["token"] == ""
) {
$response = [
"success" => false,
"message" => translate('fill_mandatory_fields', $i18n)
];
echo json_encode($response);
} else {
$enabled = $data["enabled"];
$url = $data["gotify_url"];
$token = $data["token"];
$query = "SELECT COUNT(*) FROM gotify_notifications WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindParam(":userId", $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result === false) {
$response = [
"success" => false,
"message" => translate('error_saving_notifications', $i18n)
];
echo json_encode($response);
} else {
$row = $result->fetchArray();
$count = $row[0];
if ($count == 0) {
$query = "INSERT INTO gotify_notifications (enabled, url, token, user_id)
VALUES (:enabled, :url, :token, :userId)";
} else {
$query = "UPDATE gotify_notifications
SET enabled = :enabled, url = :url, token = :token WHERE user_id = :userId";
}
$stmt = $db->prepare($query);
$stmt->bindValue(':enabled', $enabled, SQLITE3_INTEGER);
$stmt->bindValue(':url', $url, SQLITE3_TEXT);
$stmt->bindValue(':token', $token, SQLITE3_TEXT);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
if ($stmt->execute()) {
$response = [
"success" => true,
"message" => translate('notifications_settings_saved', $i18n)
];
echo json_encode($response);
} else {
$response = [
"success" => false,
"message" => translate('error_saving_notifications', $i18n)
];
echo json_encode($response);
}
}
}
}
?>

View File

@ -0,0 +1,71 @@
<?php
require_once '../../includes/connect_endpoint.php';
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
"message" => translate('session_expired', $i18n)
]));
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$postData = file_get_contents("php://input");
$data = json_decode($postData, true);
if (!isset($data["days"]) || $data['days'] == "") {
$response = [
"success" => false,
"message" => translate('fill_mandatory_fields', $i18n)
];
echo json_encode($response);
} else {
$days = $data["days"];
$query = "SELECT COUNT(*) FROM notification_settings WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindParam(":userId", $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result === false) {
$response = [
"success" => false,
"message" => translate('error_saving_notifications', $i18n)
];
echo json_encode($response);
} else {
$row = $result->fetchArray();
$count = $row[0];
if ($count == 0) {
$query = "INSERT INTO notification_settings (days, user_id)
VALUES (:days, :userId)";
} else {
$query = "UPDATE notification_settings SET days = :days WHERE user_id = :userId";
}
$stmt = $db->prepare($query);
$stmt->bindValue(':days', $days, SQLITE3_INTEGER);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
if ($stmt->execute()) {
$response = [
"success" => true,
"message" => translate('notifications_settings_saved', $i18n)
];
echo json_encode($response);
} else {
$response = [
"success" => false,
"message" => translate('error_saving_notifications', $i18n)
];
echo json_encode($response);
}
}
}
} else {
$response = [
"success" => false,
"message" => "Invalid request method"
];
echo json_encode($response);
exit();
}

View File

@ -0,0 +1,84 @@
<?php
require_once '../../includes/connect_endpoint.php';
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
"message" => translate('session_expired', $i18n)
]));
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$postData = file_get_contents("php://input");
$data = json_decode($postData, true);
if (
!isset($data["topic"]) || $data["topic"] == "" ||
!isset($data["host"]) || $data["host"] == ""
) {
$response = [
"success" => false,
"message" => translate('fill_mandatory_fields', $i18n)
];
echo json_encode($response);
} else {
$enabled = $data["enabled"];
$host = $data["host"];
$topic = $data["topic"];
$headers = $data["headers"];
$query = "SELECT COUNT(*) FROM ntfy_notifications WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindParam(":userId", $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result === false) {
$response = [
"success" => false,
"message" => translate('error_saving_notifications', $i18n)
];
echo json_encode($response);
} else {
$row = $result->fetchArray();
$count = $row[0];
if ($count == 0) {
$query = "INSERT INTO ntfy_notifications (enabled, host, topic, headers, user_id)
VALUES (:enabled, :host, :topic, :headers, :userId)";
} else {
$query = "UPDATE ntfy_notifications
SET enabled = :enabled, host = :host, topic = :topic, headers = :headers WHERE user_id = :userId";
}
$stmt = $db->prepare($query);
$stmt->bindValue(':enabled', $enabled, SQLITE3_INTEGER);
$stmt->bindValue(':host', $host, SQLITE3_TEXT);
$stmt->bindValue(':topic', $topic, SQLITE3_TEXT);
$stmt->bindValue(':headers', $headers, SQLITE3_TEXT);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
if ($stmt->execute()) {
$response = [
"success" => true,
"message" => translate('notifications_settings_saved', $i18n)
];
echo json_encode($response);
} else {
$response = [
"success" => false,
"message" => translate('error_saving_notifications', $i18n)
];
echo json_encode($response);
}
}
}
} else {
$response = [
"success" => false,
"message" => translate('invalid_request_method', $i18n)
];
echo json_encode($response);
}
?>

View File

@ -0,0 +1,81 @@
<?php
require_once '../../includes/connect_endpoint.php';
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
"message" => translate('session_expired', $i18n)
]));
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$postData = file_get_contents("php://input");
$data = json_decode($postData, true);
if (
!isset($data["user_key"]) || $data["user_key"] == "" ||
!isset($data["token"]) || $data["token"] == ""
) {
$response = [
"success" => false,
"message" => translate('fill_mandatory_fields', $i18n)
];
echo json_encode($response);
} else {
$enabled = $data["enabled"];
$user_key = $data["user_key"];
$token = $data["token"];
$query = "SELECT COUNT(*) FROM pushover_notifications WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindParam(":userId", $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result === false) {
$response = [
"success" => false,
"message" => translate('error_saving_notifications', $i18n)
];
echo json_encode($response);
} else {
$row = $result->fetchArray();
$count = $row[0];
if ($count == 0) {
$query = "INSERT INTO pushover_notifications (enabled, user_key, token, user_id)
VALUES (:enabled, :user_key, :token, :userId)";
} else {
$query = "UPDATE pushover_notifications
SET enabled = :enabled, user_key = :user_key, token = :token, user_id = :userId";
}
$stmt = $db->prepare($query);
$stmt->bindValue(':enabled', $enabled, SQLITE3_INTEGER);
$stmt->bindValue(':user_key', $user_key, SQLITE3_TEXT);
$stmt->bindValue(':token', $token, SQLITE3_TEXT);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
if ($stmt->execute()) {
$response = [
"success" => true,
"message" => translate('notifications_settings_saved', $i18n)
];
echo json_encode($response);
} else {
$response = [
"success" => false,
"message" => translate('error_saving_notifications', $i18n)
];
echo json_encode($response);
}
}
}
} else {
$response = [
"success" => false,
"message" => translate('invalid_request_method', $i18n)
];
echo json_encode($response);
}
?>

View File

@ -0,0 +1,73 @@
<?php
require_once '../../includes/connect_endpoint.php';
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
"message" => translate('session_expired', $i18n)
]));
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$postData = file_get_contents("php://input");
$data = json_decode($postData, true);
if (
!isset($data["bot_token"]) || $data["bot_token"] == "" ||
!isset($data["chat_id"]) || $data["chat_id"] == ""
) {
$response = [
"success" => false,
"message" => translate('fill_mandatory_fields', $i18n)
];
echo json_encode($response);
} else {
$enabled = $data["enabled"];
$bot_token = $data["bot_token"];
$chat_id = $data["chat_id"];
$query = "SELECT COUNT(*) FROM telegram_notifications WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindParam(":userId", $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result === false) {
$response = [
"success" => false,
"message" => translate('error_saving_notifications', $i18n)
];
echo json_encode($response);
} else {
$row = $result->fetchArray();
$count = $row[0];
if ($count == 0) {
$query = "INSERT INTO telegram_notifications (enabled, bot_token, chat_id, user_id)
VALUES (:enabled, :bot_token, :chat_id, :userId)";
} else {
$query = "UPDATE telegram_notifications
SET enabled = :enabled, bot_token = :bot_token, chat_id = :chat_id WHERE user_id = :userId";
}
$stmt = $db->prepare($query);
$stmt->bindValue(':enabled', $enabled, SQLITE3_INTEGER);
$stmt->bindValue(':bot_token', $bot_token, SQLITE3_TEXT);
$stmt->bindValue(':chat_id', $chat_id, SQLITE3_TEXT);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
if ($stmt->execute()) {
$response = [
"success" => true,
"message" => translate('notifications_settings_saved', $i18n)
];
echo json_encode($response);
} else {
$response = [
"success" => false,
"message" => translate('error_saving_notifications', $i18n)
];
echo json_encode($response);
}
}
}
}
?>

View File

@ -0,0 +1,75 @@
<?php
require_once '../../includes/connect_endpoint.php';
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
"message" => translate('session_expired', $i18n)
]));
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$postData = file_get_contents("php://input");
$data = json_decode($postData, true);
if (
!isset($data["webhook_url"]) || $data["webhook_url"] == "" ||
!isset($data["payload"]) || $data["payload"] == ""
) {
$response = [
"success" => false,
"message" => translate('fill_mandatory_fields', $i18n)
];
echo json_encode($response);
} else {
$enabled = $data["enabled"];
$url = $data["webhook_url"];
$headers = $data["headers"];
$payload = $data["payload"];
$query = "SELECT COUNT(*) FROM webhook_notifications WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindParam(":userId", $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result === false) {
$response = [
"success" => false,
"message" => translate('error_saving_notifications', $i18n)
];
echo json_encode($response);
} else {
$row = $result->fetchArray();
$count = $row[0];
if ($count == 0) {
$query = "INSERT INTO webhook_notifications (enabled, url, headers, payload, user_id)
VALUES (:enabled, :url, :headers, :payload, :userId)";
} else {
$query = "UPDATE webhook_notifications
SET enabled = :enabled, url = :url, headers = :headers, payload = :payload WHERE user_id = :userId";
}
$stmt = $db->prepare($query);
$stmt->bindValue(':enabled', $enabled, SQLITE3_INTEGER);
$stmt->bindValue(':url', $url, SQLITE3_TEXT);
$stmt->bindValue(':headers', $headers, SQLITE3_TEXT);
$stmt->bindValue(':payload', $payload, SQLITE3_TEXT);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
if ($stmt->execute()) {
$response = [
"success" => true,
"message" => translate('notifications_settings_saved', $i18n)
];
echo json_encode($response);
} else {
$response = [
"success" => false,
"message" => translate('error_saving_notifications', $i18n)
];
echo json_encode($response);
}
}
}
}
?>

View File

@ -0,0 +1,89 @@
<?php
require_once '../../includes/connect_endpoint.php';
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
"message" => translate('session_expired', $i18n)
]));
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$postData = file_get_contents("php://input");
$data = json_decode($postData, true);
if (
!isset($data["url"]) || $data["url"] == ""
) {
$response = [
"success" => false,
"message" => translate('fill_mandatory_fields', $i18n)
];
echo json_encode($response);
} else {
// Set the message parameters
$title = translate('wallos_notification', $i18n);
$message = translate('test_notification', $i18n);
$webhook_url = $data["url"];
$bot_username = $data["bot_username"];
$bot_avatar_url = $data["bot_avatar"];
$postfields = [
'content' => $message,
'embeds' => [
[
'title' => $title,
'description' => $message,
'color' => hexdec("FF0000")
]
]
];
if (!empty($bot_username)) {
$postfields['username'] = $bot_username;
}
if (!empty($bot_avatar_url)) {
$postfields['avatar_url'] = $bot_avatar_url;
}
$ch = curl_init();
// Set the URL and other options
curl_setopt($ch, CURLOPT_URL, $webhook_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($postfields));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Execute the request
$response = curl_exec($ch);
// Close the cURL session
curl_close($ch);
// Check if the message was sent successfully
if ($response === false) {
die(json_encode([
"success" => false,
"message" => translate('notification_failed', $i18n)
]));
} else {
die(json_encode([
"success" => true,
"message" => translate('notification_sent_successfuly', $i18n)
]));
}
}
} else {
die(json_encode([
"success" => false,
"message" => translate("invalid_request_method", $i18n)
]));
}
?>

View File

@ -5,7 +5,13 @@ use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
require_once '../../includes/connect_endpoint.php';
session_start();
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
"message" => translate('session_expired', $i18n)
]));
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$postData = file_get_contents("php://input");
@ -19,16 +25,15 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
) {
$response = [
"success" => false,
"errorMessage" => translate('fill_all_fields', $i18n)
"message" => translate('fill_all_fields', $i18n)
];
echo json_encode($response);
die(json_encode($response));
} else {
$enxryption = "tls";
if (isset($data["encryption"])) {
$encryption = $data["encryption"];
}
require '../../libs/PHPMailer/PHPMailer.php';
require '../../libs/PHPMailer/SMTP.php';
require '../../libs/PHPMailer/Exception.php';
@ -66,13 +71,13 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
"success" => true,
"message" => translate('notification_sent_successfuly', $i18n)
];
echo json_encode($response);
die(json_encode($response));
} else {
$response = [
"success" => false,
"errorMessage" => translate('email_error', $i18n) . $mail->ErrorInfo
"message" => translate('email_error', $i18n) . $mail->ErrorInfo
];
echo json_encode($response);
die(json_encode($response));
}
}

View File

@ -0,0 +1,70 @@
<?php
require_once '../../includes/connect_endpoint.php';
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
"message" => translate('session_expired', $i18n)
]));
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$postData = file_get_contents("php://input");
$data = json_decode($postData, true);
if (
!isset($data["gotify_url"]) || $data["gotify_url"] == "" ||
!isset($data["token"]) || $data["token"] == ""
) {
$response = [
"success" => false,
"message" => translate('fill_mandatory_fields', $i18n)
];
die(json_encode($response));
} else {
// Set the message parameters
$title = translate('wallos_notification', $i18n);
$message = translate('test_notification', $i18n);
$priority = 5;
$url = $data["gotify_url"];
$token = $data["token"];
$ch = curl_init();
// Set the URL and other options
curl_setopt($ch, CURLOPT_URL, $url . "/message?token=" . $token);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'title' => $title,
'message' => $message,
'priority' => $priority,
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Execute the request
$response = curl_exec($ch);
// Close the cURL session
curl_close($ch);
// Check if the message was sent successfully
if ($response === false) {
die(json_encode([
"success" => false,
"message" => translate('notification_failed', $i18n)
]));
} else {
die(json_encode([
"success" => true,
"message" => translate('notification_sent_successfuly', $i18n)
]));
}
}
} else {
die(json_encode([
"success" => false,
"message" => translate("invalid_request_method", $i18n)
]));
}
?>

View File

@ -0,0 +1,70 @@
<?php
require_once '../../includes/connect_endpoint.php';
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
"message" => translate('session_expired', $i18n)
]));
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$postData = file_get_contents("php://input");
$data = json_decode($postData, true);
if (
!isset($data["host"]) || $data["host"] == "" ||
!isset($data["topic"]) || $data["topic"] == ""
) {
$response = [
"success" => false,
"message" => translate('fill_mandatory_fields', $i18n)
];
echo json_encode($response);
} else {
$host = $data["host"];
$topic = $data["topic"];
$headers = json_decode($data["headers"], true);
$customheaders = array_map(function($key, $value) {
return "$key: $value";
}, array_keys($headers), $headers);
$url = "$host/$topic";
// Set the message parameters
$message = translate('test_notification', $i18n);
$ch = curl_init();
// Set the URL and other options
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $message);
curl_setopt($ch, CURLOPT_HTTPHEADER, $customheaders);
// Execute the request
$response = curl_exec($ch);
// Close the cURL session
curl_close($ch);
// Check if the message was sent successfully
if ($response === false) {
die(json_encode([
"success" => false,
"message" => translate('notification_failed', $i18n)
]));
} else {
print_r($response);
}
die(json_encode([
"success" => true,
"message" => translate('notification_sent_successfuly', $i18n)
]));
}
}
?>

View File

@ -0,0 +1,70 @@
<?php
require_once '../../includes/connect_endpoint.php';
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
"message" => translate('session_expired', $i18n)
]));
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$postData = file_get_contents("php://input");
$data = json_decode($postData, true);
if (
!isset($data["user_key"]) || $data["user_key"] == "" ||
!isset($data["token"]) || $data["token"] == ""
) {
$response = [
"success" => false,
"message" => translate('fill_mandatory_fields', $i18n)
];
echo json_encode($response);
} else {
// Set the message parameters
$message = translate('test_notification', $i18n);
$user_key = $data["user_key"];
$token = $data["token"];
$ch = curl_init();
// Set the URL and other options
curl_setopt($ch, CURLOPT_URL, "https://api.pushover.net/1/messages.json");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'token' => $token,
'user' => $user_key,
'message' => $message,
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Execute the request
$response = curl_exec($ch);
// Close the cURL session
curl_close($ch);
// Check if the message was sent successfully
if ($response === false) {
die(json_encode([
"success" => false,
"message" => translate('notification_failed', $i18n)
]));
} else {
die(json_encode([
"success" => true,
"message" => translate('notification_sent_successfuly', $i18n)
]));
}
}
} else {
die(json_encode([
"success" => false,
"message" => translate("invalid_request_method", $i18n)
]));
}
?>

View File

@ -0,0 +1,68 @@
<?php
require_once '../../includes/connect_endpoint.php';
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
"message" => translate('session_expired', $i18n)
]));
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$postData = file_get_contents("php://input");
$data = json_decode($postData, true);
if (
!isset($data["bottoken"]) || $data["bottoken"] == "" ||
!isset($data["chatid"]) || $data["chatid"] == ""
) {
$response = [
"success" => false,
"message" => translate('fill_mandatory_fields', $i18n)
];
echo json_encode($response);
} else {
// Set the message parameters
$title = translate('wallos_notification', $i18n);
$message = translate('test_notification', $i18n);
$botToken = $data["bottoken"];
$chatId = $data["chatid"];
$ch = curl_init();
// Set the URL and other options
curl_setopt($ch, CURLOPT_URL, "https://api.telegram.org/bot" . $botToken . "/sendMessage");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'chat_id' => $chatId,
'text' => $message,
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Execute the request
$response = curl_exec($ch);
// Close the cURL session
curl_close($ch);
// Check if the message was sent successfully
if ($response === false) {
die(json_encode([
"success" => false,
"message" => translate('notification_failed', $i18n)
]));
} else {
die(json_encode([
"success" => true,
"message" => translate('notification_sent_successfuly', $i18n)
]));
}
}
} else {
die(json_encode([
"success" => false,
"message" => translate("invalid_request_method", $i18n)
]));
}
?>

View File

@ -0,0 +1,73 @@
<?php
require_once '../../includes/connect_endpoint.php';
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
"message" => translate('session_expired', $i18n)
]));
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$postData = file_get_contents("php://input");
$data = json_decode($postData, true);
if (
!isset($data["requestmethod"]) || $data["requestmethod"] == "" ||
!isset($data["url"]) || $data["url"] == "" ||
!isset($data["payload"]) || $data["payload"] == ""
) {
$response = [
"success" => false,
"message" => translate('fill_mandatory_fields', $i18n)
];
die(json_encode($response));
} else {
// Set the message parameters
$title = translate('wallos_notification', $i18n);
$message = translate('test_notification', $i18n);
$requestmethod = $data["requestmethod"];
$url = $data["url"];
$payload = $data["payload"];
$customheaders = json_decode($data["customheaders"], true);
$ch = curl_init();
// Set the URL and other options
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $requestmethod);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
if (!empty($customheaders)) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $customheaders);
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Execute the request
$response = curl_exec($ch);
// Close the cURL session
curl_close($ch);
// Check if the message was sent successfully
if ($response === false) {
die(json_encode([
"success" => false,
"message" => translate('notification_failed', $i18n)
]));
} else {
die(json_encode([
"success" => true,
"message" => translate('notification_sent_successfuly', $i18n)
]));
}
}
} else {
die(json_encode([
"success" => false,
"message" => translate("invalid_request_method", $i18n)
]));
}
?>

View File

@ -4,8 +4,6 @@
require_once '../../includes/inputvalidation.php';
require_once '../../includes/getsettings.php';
session_start();
function sanitizeFilename($filename) {
$filename = preg_replace("/[^a-zA-Z0-9\s]/", "", $filename);
$filename = str_replace(" ", "-", $filename);
@ -193,13 +191,14 @@
$newID = max($maxID + 1, 32);
// Insert the new record with the new ID
$sql = "INSERT INTO payment_methods (id, name, icon, enabled) VALUES (:id, :name, :icon, :enabled)";
$sql = "INSERT INTO payment_methods (id, name, icon, enabled, user_id) VALUES (:id, :name, :icon, :enabled, :userId)";
$stmt = $db->prepare($sql);
$stmt->bindParam(':id', $newID, SQLITE3_INTEGER);
$stmt->bindParam(':name', $name, SQLITE3_TEXT);
$stmt->bindParam(':icon', $icon, SQLITE3_TEXT);
$stmt->bindParam(':enabled', $enabled, SQLITE3_INTEGER);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
if ($stmt->execute()) {
$success['success'] = true;

View File

@ -1,13 +1,14 @@
<?php
require_once '../../includes/connect_endpoint.php';
session_start();
if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
if ($_SERVER["REQUEST_METHOD"] === "DELETE") {
$paymentMethodId = $_GET["id"];
$deleteQuery = "DELETE FROM payment_methods WHERE id = :paymentMethodId";
$deleteQuery = "DELETE FROM payment_methods WHERE id = :paymentMethodId and user_id = :userId";
$deleteStmt = $db->prepare($deleteQuery);
$deleteStmt->bindParam(':paymentMethodId', $paymentMethodId, SQLITE3_INTEGER);
$deleteStmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
if ($deleteStmt->execute()) {
$success['success'] = true;

View File

@ -1,18 +1,22 @@
<?php
require_once '../../includes/connect_endpoint.php';
session_start();
if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
$paymentsInUseQuery = $db->query('SELECT id FROM payment_methods WHERE id IN (SELECT DISTINCT payment_method_id FROM subscriptions)');
$paymentsInUseQuery = $db->prepare('SELECT id FROM payment_methods WHERE id IN (SELECT DISTINCT payment_method_id FROM subscriptions) AND user_id = :userId');
$paymentsInUseQuery->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $paymentsInUseQuery->execute();
$paymentsInUse = [];
while ($row = $paymentsInUseQuery->fetchArray(SQLITE3_ASSOC)) {
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$paymentsInUse[] = $row['id'];
}
$sql = "SELECT * FROM payment_methods";
$result = $db->query($sql);
$sql = "SELECT * FROM payment_methods WHERE user_id = :userId";
$stmt = $db->prepare($sql);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result) {
$payments = array();
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
@ -25,7 +29,7 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
}
foreach ($payments as $payment) {
$paymentIconFolder = $payment['id'] <= 31 ? 'images/uploads/icons/' : 'images/uploads/logos/';
$paymentIconFolder = (strpos($payment['icon'], 'images/uploads/icons/') !== false) ? "" : "images/uploads/logos/";
$inUse = in_array($payment['id'], $paymentsInUse);
?>
<div class="payments-payment"

View File

@ -1,6 +1,6 @@
<?php
require_once '../../includes/connect_endpoint.php';
session_start();
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
@ -17,8 +17,9 @@ if (!isset($_GET['paymentId']) || !isset($_GET['enabled'])) {
$paymentId = $_GET['paymentId'];
$stmt = $db->prepare('SELECT COUNT(*) as count FROM subscriptions WHERE payment_method_id=:paymentId');
$stmt = $db->prepare('SELECT COUNT(*) as count FROM subscriptions WHERE payment_method_id=:paymentId and user_id=:userId');
$stmt->bindValue(':paymentId', $paymentId, SQLITE3_INTEGER);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$row = $result->fetchArray();
$inUse = $row['count'] === 1;
@ -32,10 +33,11 @@ if ($inUse) {
$enabled = $_GET['enabled'];
$sqlUpdate = 'UPDATE payment_methods SET enabled=:enabled WHERE id=:id';
$sqlUpdate = 'UPDATE payment_methods SET enabled=:enabled WHERE id=:id and user_id=:userId';
$stmtUpdate = $db->prepare($sqlUpdate);
$stmtUpdate->bindParam(':enabled', $enabled);
$stmtUpdate->bindParam(':id', $paymentId);
$stmtUpdate->bindParam(':userId', $userId);
$resultUpdate = $stmtUpdate->execute();
$text = $enabled ? "enabled" : "disabled";

View File

@ -1,7 +1,6 @@
<?php
require_once '../../includes/connect_endpoint.php';
session_start();
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
@ -20,10 +19,11 @@ if (!isset($_POST['paymentId']) || !isset($_POST['name']) || $_POST['paymentId']
$paymentId = $_POST['paymentId'];
$name = $_POST['name'];
$sql = "UPDATE payment_methods SET name = :name WHERE id = :paymentId";
$sql = "UPDATE payment_methods SET name = :name WHERE id = :paymentId and user_id = :userId";
$stmt = $db->prepare($sql);
$stmt->bindParam(':name', $name, SQLITE3_TEXT);
$stmt->bindParam(':paymentId', $paymentId, SQLITE3_INTEGER);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result) {

View File

@ -2,17 +2,16 @@
require_once '../../includes/connect_endpoint.php';
session_start();
if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
$paymentMethods = $_POST['paymentMethodIds'];
$order = 1;
foreach ($paymentMethods as $paymentMethodId) {
$sql = "UPDATE payment_methods SET `order` = :order WHERE id = :paymentMethodId";
$sql = "UPDATE payment_methods SET `order` = :order WHERE id = :paymentMethodId and user_id = :userId";
$stmt = $db->prepare($sql);
$stmt->bindParam(':order', $order, SQLITE3_INTEGER);
$stmt->bindParam(':paymentMethodId', $paymentMethodId, SQLITE3_INTEGER);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$order++;
}

View File

@ -1,7 +1,7 @@
<?php
require_once '../../includes/connect_endpoint.php';
session_start();
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
@ -15,8 +15,9 @@
$color = $data['color'];
$stmt = $db->prepare('UPDATE settings SET color_theme = :color');
$stmt = $db->prepare('UPDATE settings SET color_theme = :color WHERE user_id = :userId');
$stmt->bindParam(':color', $color, SQLITE3_TEXT);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
if ($stmt->execute()) {
die(json_encode([

View File

@ -1,6 +1,6 @@
<?php
require_once '../../includes/connect_endpoint.php';
session_start();
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
@ -14,8 +14,9 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
$convert_currency = $data['value'];
$stmt = $db->prepare('UPDATE settings SET convert_currency = :convert_currency');
$stmt = $db->prepare('UPDATE settings SET convert_currency = :convert_currency WHERE user_id = :userId');
$stmt->bindParam(':convert_currency', $convert_currency, SQLITE3_INTEGER);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
if ($stmt->execute()) {
die(json_encode([

View File

@ -1,7 +1,7 @@
<?php
require_once '../../includes/connect_endpoint.php';
session_start();
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
@ -20,10 +20,11 @@
$stmt = $db->prepare('DELETE FROM custom_colors');
$stmt->execute();
$stmt = $db->prepare('INSERT INTO custom_colors (main_color, accent_color, hover_color) VALUES (:main_color, :accent_color, :hover_color)');
$stmt = $db->prepare('INSERT INTO custom_colors (main_color, accent_color, hover_color, user_id) VALUES (:main_color, :accent_color, :hover_color, :userId)');
$stmt->bindParam(':main_color', $main_color, SQLITE3_TEXT);
$stmt->bindParam(':accent_color', $accent_color, SQLITE3_TEXT);
$stmt->bindParam(':hover_color', $hover_color, SQLITE3_TEXT);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
if ($stmt->execute()) {
die(json_encode([

View File

@ -1,6 +1,6 @@
<?php
require_once '../../includes/connect_endpoint.php';
session_start();
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
@ -14,8 +14,9 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
$hide_disabled = $data['value'];
$stmt = $db->prepare('UPDATE settings SET hide_disabled = :hide_disabled');
$stmt = $db->prepare('UPDATE settings SET hide_disabled = :hide_disabled WHERE user_id = :userId');
$stmt->bindParam(':hide_disabled', $hide_disabled, SQLITE3_INTEGER);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
if ($stmt->execute()) {
die(json_encode([

View File

@ -1,6 +1,6 @@
<?php
require_once '../../includes/connect_endpoint.php';
session_start();
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
@ -14,8 +14,9 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
$monthly_price = $data['value'];
$stmt = $db->prepare('UPDATE settings SET monthly_price = :monthly_price');
$stmt = $db->prepare('UPDATE settings SET monthly_price = :monthly_price WHERE user_id = :userId');
$stmt->bindParam(':monthly_price', $monthly_price, SQLITE3_INTEGER);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
if ($stmt->execute()) {
die(json_encode([

View File

@ -1,6 +1,6 @@
<?php
require_once '../../includes/connect_endpoint.php';
session_start();
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
@ -14,8 +14,9 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
$remove_background = $data['value'];
$stmt = $db->prepare('UPDATE settings SET remove_background = :remove_background');
$stmt = $db->prepare('UPDATE settings SET remove_background = :remove_background WHERE user_id = :userId');
$stmt->bindParam(':remove_background', $remove_background, SQLITE3_INTEGER);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
if ($stmt->execute()) {
die(json_encode([

View File

@ -1,7 +1,7 @@
<?php
require_once '../../includes/connect_endpoint.php';
session_start();
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
@ -10,7 +10,8 @@
}
if ($_SERVER["REQUEST_METHOD"] === "DELETE") {
$stmt = $db->prepare('DELETE FROM custom_colors');
$stmt = $db->prepare('DELETE FROM custom_colors WHERE user_id = :userId');
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
if ($stmt->execute()) {
die(json_encode([

View File

@ -1,6 +1,6 @@
<?php
require_once '../../includes/connect_endpoint.php';
session_start();
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
@ -14,8 +14,9 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
$theme = $data['theme'];
$stmt = $db->prepare('UPDATE settings SET dark_theme = :theme');
$stmt = $db->prepare('UPDATE settings SET dark_theme = :theme WHERE user_id = :userId');
$stmt->bindParam(':theme', $theme, SQLITE3_INTEGER);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
if ($stmt->execute()) {
die(json_encode([

View File

@ -3,8 +3,6 @@
require_once '../../includes/connect_endpoint.php';
require_once '../../includes/inputvalidation.php';
require_once '../../includes/getsettings.php';
session_start();
function sanitizeFilename($filename) {
$filename = preg_replace("/[^a-zA-Z0-9\s]/", "", $filename);
@ -18,7 +16,7 @@
return in_array($fileExtension, $allowedExtensions);
}
function getLogoFromUrl($url, $uploadDir, $name) {
function getLogoFromUrl($url, $uploadDir, $name, $settings) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
@ -31,7 +29,7 @@
$uploadDir = '../../images/uploads/logos/';
$uploadFile = $uploadDir . $fileName;
if (saveLogo($imageData, $uploadFile, $name)) {
if (saveLogo($imageData, $uploadFile, $name, $settings)) {
return $fileName;
} else {
echo translate('error_fetching_image', $i18n) . ": " . curl_error($ch);
@ -45,7 +43,7 @@
}
}
function saveLogo($imageData, $uploadFile, $name) {
function saveLogo($imageData, $uploadFile, $name, $settings) {
$image = imagecreatefromstring($imageData);
$removeBackground = isset($settings['removeBackground']) && $settings['removeBackground'] === 'true';
if ($image !== false) {
@ -71,7 +69,7 @@
}
}
function resizeAndUploadLogo($uploadedFile, $uploadDir, $name) {
function resizeAndUploadLogo($uploadedFile, $uploadDir, $name, $settings) {
$targetWidth = 135;
$targetHeight = 42;
@ -166,10 +164,11 @@
$logoUrl = validate($_POST['logo-url']);
$logo = "";
$notify = isset($_POST['notifications']) ? true : false;
$notifyDaysBefore = $_POST['notify_days_before'];
$inactive = isset($_POST['inactive']) ? true : false;
if($logoUrl !== "") {
$logo = getLogoFromUrl($logoUrl, '../../images/uploads/logos/', $name);
$logo = getLogoFromUrl($logoUrl, '../../images/uploads/logos/', $name, $settings);
} else {
if (!empty($_FILES['logo']['name'])) {
$fileType = mime_content_type($_FILES['logo']['tmp_name']);
@ -177,21 +176,27 @@
echo translate("fill_all_fields", $i18n);
exit();
}
$logo = resizeAndUploadLogo($_FILES['logo'], '../../images/uploads/logos/', $name);
$logo = resizeAndUploadLogo($_FILES['logo'], '../../images/uploads/logos/', $name, $settings);
}
}
if (!$isEdit) {
$sql = "INSERT INTO subscriptions (name, logo, price, currency_id, next_payment, cycle, frequency, notes,
payment_method_id, payer_user_id, category_id, notify, inactive, url)
payment_method_id, payer_user_id, category_id, notify, inactive, url, notify_days_before, user_id)
VALUES (:name, :logo, :price, :currencyId, :nextPayment, :cycle, :frequency, :notes,
:paymentMethodId, :payerUserId, :categoryId, :notify, :inactive, :url)";
:paymentMethodId, :payerUserId, :categoryId, :notify, :inactive, :url, :notifyDaysBefore, :userId)";
} else {
$id = $_POST['id'];
if ($logo != "") {
$sql = "UPDATE subscriptions SET name = :name, logo = :logo, price = :price, currency_id = :currencyId, next_payment = :nextPayment, cycle = :cycle, frequency = :frequency, notes = :notes, payment_method_id = :paymentMethodId, payer_user_id = :payerUserId, category_id = :categoryId, notify = :notify, inactive = :inactive, url = :url WHERE id = :id";
$sql = "UPDATE subscriptions SET name = :name, logo = :logo, price = :price, currency_id = :currencyId,
next_payment = :nextPayment, cycle = :cycle, frequency = :frequency, notes = :notes, payment_method_id = :paymentMethodId,
payer_user_id = :payerUserId, category_id = :categoryId, notify = :notify, inactive = :inactive,
url = :url, notify_days_before = :notifyDaysBefore WHERE id = :id AND user_id = :userId";
} else {
$sql = "UPDATE subscriptions SET name = :name, price = :price, currency_id = :currencyId, next_payment = :nextPayment, cycle = :cycle, frequency = :frequency, notes = :notes, payment_method_id = :paymentMethodId, payer_user_id = :payerUserId, category_id = :categoryId, notify = :notify, inactive = :inactive, url = :url WHERE id = :id";
$sql = "UPDATE subscriptions SET name = :name, price = :price, currency_id = :currencyId, next_payment = :nextPayment,
cycle = :cycle, frequency = :frequency, notes = :notes, payment_method_id = :paymentMethodId, payer_user_id = :payerUserId,
category_id = :categoryId, notify = :notify, inactive = :inactive, url = :url,notify_days_before = :notifyDaysBefore
WHERE id = :id AND user_id = :userId";
}
}
@ -215,6 +220,8 @@
$stmt->bindParam(':notify', $notify, SQLITE3_INTEGER);
$stmt->bindParam(':inactive', $inactive, SQLITE3_INTEGER);
$stmt->bindParam(':url', $url, SQLITE3_TEXT);
$stmt->bindParam(':notifyDaysBefore', $notifyDaysBefore, SQLITE3_INTEGER);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
if ($stmt->execute()) {
$success['status'] = "Success";

View File

@ -1,12 +1,13 @@
<?php
require_once '../../includes/connect_endpoint.php';
session_start();
if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
if ($_SERVER["REQUEST_METHOD"] === "DELETE") {
$subscriptionId = $_GET["id"];
$deleteQuery = "DELETE FROM subscriptions WHERE id = :subscriptionId";
$deleteQuery = "DELETE FROM subscriptions WHERE id = :subscriptionId AND user_id = :userId";
$deleteStmt = $db->prepare($deleteQuery);
$deleteStmt->bindParam(':subscriptionId', $subscriptionId, SQLITE3_INTEGER);
$deleteStmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
if ($deleteStmt->execute()) {
http_response_code(204);

View File

@ -1,32 +1,34 @@
<?php
require_once '../../includes/connect_endpoint.php';
session_start();
if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
if (isset($_GET['id']) && $_GET['id'] != "") {
$subscriptionId = intval($_GET['id']);
$query = "SELECT * FROM subscriptions WHERE id = :subscriptionId";
$query = "SELECT * FROM subscriptions WHERE id = :subscriptionId AND user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindParam(':subscriptionId', $subscriptionId, SQLITE3_INTEGER);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$subscriptionData = array();
if ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$subscriptionData['id'] = $subscriptionId;
$subscriptionData['name'] = htmlspecialchars_decode($row['name']);
$subscriptionData['name'] = htmlspecialchars_decode($row['name'] ?? "");
$subscriptionData['logo'] = $row['logo'];
$subscriptionData['price'] = $row['price'];
$subscriptionData['currency_id'] = $row['currency_id'];
$subscriptionData['next_payment'] = $row['next_payment'];
$subscriptionData['frequency'] = $row['frequency'];
$subscriptionData['cycle'] = $row['cycle'];
$subscriptionData['notes'] = htmlspecialchars_decode($row['notes']);
$subscriptionData['notes'] = htmlspecialchars_decode($row['notes'] ?? "");
$subscriptionData['payment_method_id'] = $row['payment_method_id'];
$subscriptionData['payer_user_id'] = $row['payer_user_id'];
$subscriptionData['category_id'] = $row['category_id'];
$subscriptionData['notify'] = $row['notify'];
$subscriptionData['inactive'] = $row['inactive'];
$subscriptionData['url'] = htmlspecialchars_decode($row['url']);
$subscriptionData['url'] = htmlspecialchars_decode($row['url'] ?? "");
$subscriptionData['notify_days_before'] = $row['notify_days_before'];
$subscriptionJson = json_encode($subscriptionData);
header('Content-Type: application/json');

View File

@ -1,6 +1,5 @@
<?php
require_once '../../includes/connect_endpoint.php';
session_start();
require_once '../../includes/currency_formatter.php';
require_once '../../includes/getdbkeys.php';
@ -35,7 +34,7 @@
}
$params = array();
$sql = "SELECT * FROM subscriptions WHERE 1=1";
$sql = "SELECT * FROM subscriptions WHERE user_id = :userId";
if (isset($_GET['category']) && $_GET['category'] != "") {
$sql .= " AND category_id = :category";
@ -55,6 +54,7 @@
$sql .= " ORDER BY $sort $order, inactive ASC";
$stmt = $db->prepare($sql);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
foreach ($params as $key => $value) {
$stmt->bindValue($key, $value);
@ -76,7 +76,7 @@
$id = $subscription['id'];
$print[$id]['id'] = $id;
$print[$id]['logo'] = $subscription['logo'] != "" ? "images/uploads/logos/".$subscription['logo'] : $defaultLogo;
$print[$id]['name'] = htmlspecialchars_decode($subscription['name']);
$print[$id]['name'] = htmlspecialchars_decode($subscription['name'] ?? "");
$cycle = $subscription['cycle'];
$frequency = $subscription['frequency'];
$print[$id]['billing_cycle'] = getBillingCycle($cycle, $frequency, $i18n);
@ -84,7 +84,7 @@
$print[$id]['currency_code'] = $currencies[$subscription['currency_id']]['code'];
$currencyId = $subscription['currency_id'];
$print[$id]['next_payment'] = date('M d, Y', strtotime($subscription['next_payment']));
$paymentIconFolder = $paymentMethodId <= 31 ? 'images/uploads/icons/' : 'images/uploads/logos/';
$paymentIconFolder = (strpos($payment_methods[$paymentMethodId]['icon'], 'images/uploads/icons/') !== false) ? "" : "images/uploads/logos/";
$print[$id]['payment_method_icon'] = $paymentIconFolder . $payment_methods[$paymentMethodId]['icon'];
$print[$id]['payment_method_name'] = $payment_methods[$paymentMethodId]['name'];
$print[$id]['payment_method_id'] = $paymentMethodId;
@ -92,8 +92,8 @@
$print[$id]['payer_user_id'] = $subscription['payer_user_id'];
$print[$id]['price'] = floatval($subscription['price']);
$print[$id]['inactive'] = $subscription['inactive'];
$print[$id]['url'] = htmlspecialchars_decode($subscription['url']);
$print[$id]['notes'] = htmlspecialchars_decode($subscription['notes']);
$print[$id]['url'] = htmlspecialchars_decode($subscription['url'] ?? "");
$print[$id]['notes'] = htmlspecialchars_decode($subscription['notes'] ?? "");
if (isset($settings['convertCurrency']) && $settings['convertCurrency'] === 'true' && $currencyId != $mainCurrencyId) {
$print[$id]['price'] = getPriceConverted($print[$id]['price'], $currencyId, $db);

52
endpoints/user/budget.php Normal file
View File

@ -0,0 +1,52 @@
<?php
require_once '../../includes/connect_endpoint.php';
require_once '../../includes/inputvalidation.php';
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
"success" => false,
"message" => translate('session_expired', $i18n)
]));
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$postData = file_get_contents("php://input");
$data = json_decode($postData, true);
if (
!isset($data["budget"]) || $data["budget"] == ""
) {
$response = [
"success" => false,
"message" => translate('fill_mandatory_fields', $i18n)
];
echo json_encode($response);
} else {
$budget = $data["budget"];
$sql = "UPDATE user SET budget = :budget WHERE id = :userId";
$stmt = $db->prepare($sql);
$stmt->bindValue(':budget', $budget, SQLITE3_TEXT);
$stmt->bindValue(':userId', $userId, SQLITE3_TEXT);
$result = $stmt->execute();
if ($result) {
$response = [
"success" => true,
"message" => translate('user_details_saved', $i18n)
];
echo json_encode($response);
} else {
$response = [
"success" => false,
"message" => translate('error_updating_user_data', $i18n)
];
echo json_encode($response);
}
}
}
?>

View File

@ -1,8 +1,6 @@
<?php
require_once '../../includes/connect_endpoint.php';
session_start();
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
die(json_encode([
@ -14,8 +12,9 @@
$input = json_decode(file_get_contents('php://input'), true);
if (isset($input['avatar'])) {
$avatar = "images/uploads/logos/avatars/".$input['avatar'];
$sql = "SELECT avatar FROM user";
$sql = "SELECT avatar FROM user WHERE id = :userId";
$stmt = $db->prepare($sql);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$userAvatar = $result->fetchArray(SQLITE3_ASSOC)['avatar'];

View File

@ -1,12 +1,12 @@
<?php
require_once '../../includes/connect_endpoint.php';
require_once '../../includes/inputvalidation.php';
session_start();
function update_exchange_rate($db) {
$query = "SELECT api_key, provider FROM fixer";
$result = $db->query($query);
$query = "SELECT api_key, provider FROM fixer WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result) {
$row = $result->fetchArray(SQLITE3_ASSOC);
@ -23,8 +23,9 @@
}
$codes = rtrim($codes, ',');
$query = "SELECT u.main_currency, c.code FROM user u LEFT JOIN currencies c ON u.main_currency = c.id WHERE u.id = 1";
$query = "SELECT u.main_currency, c.code FROM user u LEFT JOIN currencies c ON u.main_currency = c.id WHERE u.id = :userId";
$stmt = $db->prepare($query);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
$mainCurrencyCode = $row['code'];
@ -55,23 +56,32 @@
} else {
$exchangeRate = $rate / $mainCurrencyToEUR;
}
$updateQuery = "UPDATE currencies SET rate = :rate WHERE code = :code";
$updateQuery = "UPDATE currencies SET rate = :rate WHERE code = :code AND user_id = :userId";
$updateStmt = $db->prepare($updateQuery);
$updateStmt->bindParam(':rate', $exchangeRate, SQLITE3_TEXT);
$updateStmt->bindParam(':code', $currencyCode, SQLITE3_TEXT);
$updateStmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$updateResult = $updateStmt->execute();
}
$currentDate = new DateTime();
$formattedDate = $currentDate->format('Y-m-d');
$deleteQuery = "DELETE FROM last_exchange_update";
$deleteStmt = $db->prepare($deleteQuery);
$deleteResult = $deleteStmt->execute();
$query = "SELECT * FROM last_exchange_update WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
if ($row) {
$query = "UPDATE last_exchange_update SET date = :formattedDate WHERE user_id = :userId";
} else {
$query = "INSERT INTO last_exchange_update (date, user_id) VALUES (:formattedDate, :userId)";
}
$query = "INSERT INTO last_exchange_update (date) VALUES (:formattedDate)";
$stmt = $db->prepare($query);
$stmt->bindParam(':formattedDate', $formattedDate, SQLITE3_TEXT);
$result = $stmt->execute();
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$resutl = $stmt->execute();
$db->close();
}
@ -79,8 +89,9 @@
}
}
$query = "SELECT main_currency FROM user WHERE id = 1";
$query = "SELECT main_currency FROM user WHERE id = :userId";
$stmt = $db->prepare($query);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
$mainCurrencyId = $row['main_currency'];
@ -174,10 +185,39 @@
return "";
}
if (isset($_SESSION['username']) && isset($_POST['username']) && isset($_POST['email']) && isset($_POST['avatar'])) {
$oldUsername = $_SESSION['username'];
$username = validate($_POST['username']);
if (isset($_SESSION['username']) && isset($_POST['email']) && $_POST['email'] !== ""
&& isset($_POST['avatar']) && $_POST['avatar'] !== ""
&& isset($_POST['main_currency']) && $_POST['main_currency'] !== ""
&& isset($_POST['language']) && $_POST['language'] !== "") {
$email = validate($_POST['email']);
$query = "SELECT email FROM user WHERE id = :user_id";
$stmt = $db->prepare($query);
$stmt->bindValue(':user_id', $userId, SQLITE3_TEXT);
$result = $stmt->execute();
$user = $result->fetchArray(SQLITE3_ASSOC);
$oldEmail = $user['email'];
if ($oldEmail != $email) {
$query = "SELECT email FROM user WHERE email = :email AND id != :userId";
$stmt = $db->prepare($query);
$stmt->bindValue(':email', $email, SQLITE3_TEXT);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$otherUser = $result->fetchArray(SQLITE3_ASSOC);
if ($otherUser) {
$response = [
"success" => false,
"errorMessage" => translate('email_exists', $i18n)
];
echo json_encode($response);
exit();
}
}
$avatar = $_POST['avatar'];
$main_currency = $_POST['main_currency'];
$language = $_POST['language'];
@ -221,17 +261,17 @@
}
if (isset($_POST['password']) && $_POST['password'] != "") {
$sql = "UPDATE user SET avatar = :avatar, username = :username, email = :email, password = :password, main_currency = :main_currency, language = :language WHERE id = 1";
$sql = "UPDATE user SET avatar = :avatar, email = :email, password = :password, main_currency = :main_currency, language = :language WHERE id = :userId";
} else {
$sql = "UPDATE user SET avatar = :avatar, username = :username, email = :email, main_currency = :main_currency, language = :language WHERE id = 1";
$sql = "UPDATE user SET avatar = :avatar, email = :email, main_currency = :main_currency, language = :language WHERE id = :userId";
}
$stmt = $db->prepare($sql);
$stmt->bindParam(':avatar', $avatar, SQLITE3_TEXT);
$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);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
if (isset($_POST['password']) && $_POST['password'] != "") {
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
@ -246,14 +286,6 @@
$root = str_replace('/endpoints/user', '', dirname($_SERVER['PHP_SELF']));
$root = $root == '' ? '/' : $root;
setcookie('language', $language, $cookieExpire, $root);
if ($username != $oldUsername) {
$_SESSION['username'] = $username;
if (isset($_COOKIE['wallos_login'])) {
$cookie = explode('|', $_COOKIE['wallos_login'], 2) ;
$token = $cookie[1];
$cookieValue = $username . "|" . $token . "|" . $main_currency;
}
}
$_SESSION['avatar'] = $avatar;
$_SESSION['main_currency'] = $main_currency;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 987 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 980 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 797 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -8,18 +8,40 @@
$stmt->bindValue(':username', $username, SQLITE3_TEXT);
$result = $stmt->execute();
$userData = $result->fetchArray(SQLITE3_ASSOC);
$userId = $userData['id'];
if ($userData === false) {
header('Location: logout.php');
exit();
} else {
$_SESSION['userId'] = $userData['id'];
}
if ($userData['avatar'] == "") {
$userData['avatar'] = "0";
}
} else {
// Read X-WebAuth-User header as option for login
if (isset($_SERVER['HTTP_X_WEBAUTH_USER'])) {
$username = $_SERVER['HTTP_X_WEBAUTH_USER'];
$query = "SELECT id, username, main_currency, language FROM user WHERE username = :username";
$stmt = $db->prepare($query);
$stmt->bindValue(':id', 1, SQLITE3_INTEGER);
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
if (isset($_COOKIE['wallos_login'])) {
if ($row) {
$_SESSION['username'] = $row['username'];
$_SESSION['loggedin'] = true;
$_SESSION['main_currency'] = $row['main_currency'];
$_SESSION['userId'] = $row['id'];
$_SESSION['language'] = $row['language'];
} else {
$db->close();
header("Location: logout.php");
exit();
}
} else if (isset($_COOKIE['wallos_login'])) {
$cookie = explode('|', $_COOKIE['wallos_login'], 3);
$username = $cookie[0];
$token = $cookie[1];
@ -29,7 +51,7 @@
$stmt = $db->prepare($sql);
$stmt->bindValue(':username', $username, SQLITE3_TEXT);
$result = $stmt->execute();
if ($result) {
$userData = $result->fetchArray(SQLITE3_ASSOC);
if (!isset($userData['id'])) {
@ -43,10 +65,20 @@
}
$userId = $userData['id'];
$main_currency = $userData['main_currency'];
$sql = "SELECT * FROM login_tokens WHERE user_id = ? AND token = ?";
$stmt = $db->prepare($sql);
$stmt->bindParam(1, $userId, SQLITE3_TEXT);
$stmt->bindParam(2, $token, SQLITE3_TEXT);
$adminQuery = "SELECT login_disabled FROM admin";
$adminResult = $db->query($adminQuery);
$adminRow = $adminResult->fetchArray(SQLITE3_ASSOC);
if ($adminRow['login_disabled'] == 1) {
$sql = "SELECT * FROM login_tokens WHERE user_id = :userId";
$stmt = $db->prepare($sql);
$stmt->bindParam(':userId', $userId, SQLITE3_TEXT);
} else {
$sql = "SELECT * FROM login_tokens WHERE user_id = :userId AND token = :token";
$stmt = $db->prepare($sql);
$stmt->bindParam(':userId', $userId, SQLITE3_TEXT);
$stmt->bindParam(':token', $token, SQLITE3_TEXT);
}
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
@ -55,6 +87,7 @@
$_SESSION['token'] = $token;
$_SESSION['loggedin'] = true;
$_SESSION['main_currency'] = $main_currency;
$_SESSION['userId'] = $userId;
} else {
$db->close();
header("Location: logout.php");
@ -65,8 +98,6 @@
header("Location: logout.php");
exit();
}
} else {
$db->close();
header("Location: login.php");

View File

@ -12,4 +12,12 @@ require_once 'i18n/languages.php';
require_once 'i18n/getlang.php';
require_once 'i18n/' . $lang . '.php';
session_start();
if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
$userId = $_SESSION['userId'];
} else {
$userId = 0;
}
?>

View File

@ -1,6 +1,6 @@
<?php
$databaseFile = '/var/www/html/db/wallos.db';
$databaseFile = __DIR__ . '/../db/wallos.db';
$db = new SQLite3($databaseFile);
$db->busyTimeout(5000);
@ -8,4 +8,8 @@ if (!$db) {
die('Connection to the database failed.');
}
require_once __DIR__ . '/../includes/i18n/languages.php';
require_once __DIR__ . '/../includes/i18n/getlang.php';
require_once __DIR__ . '/../includes/i18n/' . $lang . '.php';
?>

View File

@ -1,24 +1,29 @@
<?php
$currencies = array();
$query = "SELECT * FROM currencies";
$result = $db->query($query);
$query = "SELECT * FROM currencies WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$currencyId = $row['id'];
$currencies[$currencyId] = $row;
}
$members = array();
$query = "SELECT * FROM household";
$result = $db->query($query);
$query = "SELECT * FROM household WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$memberId = $row['id'];
$members[$memberId] = $row;
}
$payment_methods = array();
$query = $db->prepare("SELECT * FROM payment_methods WHERE enabled=:enabled");
$query = $db->prepare("SELECT * FROM payment_methods WHERE enabled=:enabled AND user_id = :userId");
$query->bindValue(':enabled', 1, SQLITE3_INTEGER);
$query->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $query->execute();
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$payment_methodId = $row['id'];
@ -26,8 +31,10 @@
}
$categories = array();
$query = "SELECT * FROM categories ORDER BY `order` ASC";
$result = $db->query($query);
$query = "SELECT * FROM categories WHERE user_id = :userId ORDER BY `order` ASC";
$stmt = $db->prepare($query);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$categoryId = $row['id'];
$categories[$categoryId] = $row;
@ -42,11 +49,8 @@
}
$frequencies = array();
$query = "SELECT * FROM frequencies";
$result = $db->query($query);
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$frequencyId = $row['id'];
$frequencies[$frequencyId] = $row;
for ($i = 1; $i <= 366; $i++) {
$frequencies[$i] = array('id' => $i, 'name' => $i);
}
?>

View File

@ -1,7 +1,10 @@
<?php
$query = "SELECT * FROM settings";
$result = $db->query($query);
$query = "SELECT * FROM settings WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$settings = $result->fetchArray(SQLITE3_ASSOC);
if ($settings) {
$cookieExpire = time() + (30 * 24 * 60 * 60);
@ -14,12 +17,22 @@ if ($settings) {
$settings['hideDisabledSubscriptions'] = $settings['hide_disabled'] ? 'true': 'false';
}
$query = "SELECT * FROM custom_colors";
$result = $db->query($query);
$query = "SELECT * FROM custom_colors WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$customColors = $result->fetchArray(SQLITE3_ASSOC);
if ($customColors) {
$settings['customColors'] = $customColors;
}
$query = "SELECT * FROM admin";
$result = $db->query($query);
$adminSettings = $result->fetchArray(SQLITE3_ASSOC);
if ($adminSettings) {
$settings['disableLogin'] = $adminSettings['login_disabled'];
}
?>

View File

@ -27,9 +27,12 @@
if (isset($settings['color_theme'])) {
$colorTheme = $settings['color_theme'];
}
$isAdmin = $_SESSION['userId'] == 1;
?>
<!DOCTYPE html>
<html>
<html dir="<?= $languages[$lang]['dir'] ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
@ -46,6 +49,7 @@
<link rel="stylesheet" href="styles/themes/yellow.css?<?= $version ?>" id="yellow-theme" <?= $colorTheme != "yellow" ? "disabled" : "" ?>>
<link rel="stylesheet" href="styles/barlow.css">
<link rel="stylesheet" href="styles/font-awesome.min.css">
<link rel="stylesheet" href="styles/brands.css">
<script type="text/javascript" src="scripts/all.js?<?= $version ?>"></script>
<script type="text/javascript" src="scripts/common.js?<?= $version ?>"></script>
<script type="text/javascript">
@ -75,7 +79,7 @@
<script type="text/javascript" src="scripts/i18n/<?= $lang ?>.js?<?= $version ?>"></script>
<script type="text/javascript" src="scripts/i18n/getlang.js?<?= $version ?>"></script>
</head>
<body class="<?= $theme ?>">
<body class="<?= $theme ?> <?= $languages[$lang]['dir'] ?>">
<header>
<div class="contain">
<div class="logo">
@ -93,8 +97,17 @@
<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>
<?php if ($isAdmin): ?>
<a href="admin.php"><i class="fa-solid fa-user-tie"></i><?= translate('admin', $i18n) ?></a>
<?php endif; ?>
<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>
<?php
if ($settings['disableLogin'] == 0) {
?>
<a href="logout.php"><i class="fa-solid fa-arrow-right-from-bracket"></i><?= translate('logout', $i18n) ?></a>
<?php
}
?>
</div>
</div>
</nav>

View File

@ -10,6 +10,8 @@ $i18n = [
"main_currency" => "Hauptwährung",
"language" => "Sprache",
"passwords_dont_match" => "Die Passwörter stimmen nicht überein",
"username_exists" => "Benutzername existiert bereits",
"email_exists" => "E-Mail existiert bereits",
"registration_failed" => "Registrierung fehlgeschlagen, bitte erneut versuchen.",
"register" => "Registrieren",
"restore_database" => "Datenbank wiederherstellen",
@ -18,10 +20,18 @@ $i18n = [
'stay_logged_in' => "Angemeldet bleiben (30 Tage)",
'login' => "Login",
'login_failed' => "Loginangaben sind nicht korrekt",
'registration_successful' => "Registrierung erfolgreich",
'user_email_waiting_verification' => "Ihre E-Mail muss noch verifiziert werden. Bitte überprüfen Sie Ihre E-Mail.",
// Password Reset Page
'forgot_password' => "Passwort vergessen?",
'reset_password' => "Passwort zurücksetzen",
'reset_sent_check_email' => "Passwort zurücksetzen E-Mail wurde gesendet. Bitte überprüfen Sie Ihr Postfach.",
'password_reset_successful' => "Passwort erfolgreich zurückgesetzt",
// Header
'subscriptions' => "Abonnements",
'stats' => "Statistiken",
'settings' => "Einstellungen",
'admin' => "Admin",
'about' => "Über",
'logout' => "Logout",
// Subscriptions page
@ -73,6 +83,7 @@ $i18n = [
"url" => "URL",
"notes" => "Notizen",
"enable_notifications" => "Benachrichtigungen für dieses Abonnement aktivieren",
"default_value_from_settings" => "Standardwert aus den Einstellungen",
"delete" => "Löschen",
"cancel" => "Abbrechen",
"upload_logo" => "Logo hochladen",
@ -85,7 +96,11 @@ $i18n = [
'average_monthly' => "Durchschnittliche monatliche Kosten",
'most_expensive' => "Kosten des teuersten Abonnements",
'amount_due' => "Diesen Monat fällige Summe",
'percentage_budget_used' => "Prozentualer Anteil des Budgets genutzt",
'budget_remaining' => "Verbleibendes Budget",
'amount_over_budget' => "Überzogenes Budget",
'monthly_savings' => "Monatliche Ersparnisse (bei inaktiven Abonnements)",
'yearly_savings' => "Jährliche Ersparnisse (bei inaktiven Abonnements)",
'split_views' => "Aufgeteilte Ansichten",
'category_split' => "Kategorien",
'household_split' => "Haushalt",
@ -101,7 +116,9 @@ $i18n = [
'upload_avatar' => "Avatar hochladen",
'file_type_error' => "Dateityp nicht unterstützt",
'user_details' => "Benutzerdetails",
"household" => "Haushalt",
"monthly_budget" => "Monatliches Budget",
"budget_info" => "Das monatliche Budget wird für die Berechnung der Statistiken verwendet.",
"household" => "Haushalt",
"save_member" => "Mitglied speichern",
"delete_member" => "Mitglied löschen",
"cant_delete_member" => "Hauptmitglied kann nicht gelöscht werden",
@ -120,6 +137,25 @@ $i18n = [
"smtp_password" => "SMTP Passwort",
"from_email" => "Absender E-Mail Adresse (optional)",
"smtp_info" => "Das SMTP Passwort wird in Klartext übermittelt und gespeichert. Aus Sicherheitsgründen erstelle bitte einen gesonderten Account nur zu diesem Zweck.",
"telegram" => "Telegram",
"telegram_bot_token" => "Telegram Bot Token",
"telegram_chat_id" => "Telegram Chat ID",
"webhook" => "Webhook",
"webhook_url" => "Webhook URL",
"request_method" => "Request Methode",
"custom_headers" => "Benutzerdefinierte Kopfzeilen",
"webhook_payload" => "Webhook Payload",
"webhook_iterator_key" => "Ersetze {{subscriptions}} durch den Schlüsselnamen",
"variables_available" => "Verfügbare Variablen",
"gotify" => "Gotify",
"token" => "Token",
"discord" => "Discord",
"discord_bot_username" => "Bot Benutzername",
"discord_bot_avatar_url" => "Bot Avatar URL",
"pushover" => "Pushover",
"pushover_user_key" => "Pushover User Key",
'host' => "Host",
'topic' => "Topic",
"categories" => "Kategorien",
"save_category" => "Kategorie speichern",
"delete_category" => "Kategorie löschen",
@ -215,6 +251,7 @@ $i18n = [
"email_error" => "E-Mail konnte nicht gesendet werden",
"notification_sent_successfuly" => "Benachrichtigung erfolgreich gesendet",
"notifications_settings_saved" => "Benachrichtigungseinstellungen erfolgreich gespeichert.",
"notification_failed" => "Benachrichtigung fehlgeschlagen",
// Payments
"payment_in_use" => "Genutzte Zahlungsmethoden können nicht deaktiviert werden",
"failed_update_payment" => "Zahlungsmethode in Datenbank konnte nicht aktualisiert werden",
@ -229,6 +266,27 @@ $i18n = [
// User
"error_updating_user_data" => "Benutzerangaben konnten nicht aktualisiert werden.",
"user_details_saved" => "Benutzerangaben gespeichert",
// Admin Page
"registrations" => "Registrierungen",
"enable_user_registrations" => "Benutzerregistrierungen aktivieren",
"maximum_number_users" => "Maximale Anzahl an Benutzern",
"require_email_verification" => "E-Mail Verifizierung erforderlich",
"configure_smtp_settings_to_enable" => "Konfiguriere SMTP Einstellungen um dies zu aktivieren",
"server_url" => "Server URL",
"server_url_info" => "Wird für die E-Mail-Überprüfung und die Passwortwiederherstellung verwendet. Muss eine gültige öffentliche URL sein.",
"server_url_password_reset" => "Wenn diese Option gesetzt ist, wird auch die Funktion zum Zurücksetzen des Passworts aktiviert.",
"disable_login" => "Login deaktivieren",
"disable_login_info" => "Anmeldung umgehen. Wenn Sie Ihren Server nur in einem lokalen Netzwerk betreiben, ohne Zugriff von außen, können Sie die Anmeldung deaktivieren. Dadurch wird automatisch der Admin-Benutzer angemeldet.",
"disable_login_info2" => "Sie können diese Einstellung nur aktivieren, wenn die Benutzerregistrierung ausgeschaltet ist und es nicht mehr als ein Admin-Benutzerkonto gibt.",
"max_users_info" => "0 für unbegrenzte Anzahl an Benutzern",
"user_management" => "Benutzerverwaltung",
"delete_user" => "Benutzer löschen",
"delete_user_info" => "Durch das Löschen eines Benutzers werden auch alle seine Abonnements und Einstellungen gelöscht.",
"smtp_settings" => "SMTP Einstellungen",
"smtp_usage_info" => "Wird für die Passwortwiederherstellung und andere System-E-Mails verwendet",
// Email Verification
"email_verified" => "E-Mail verifiziert",
"email_verification_failed" => "E-Mail konnte nicht verifiziert werden",
];

View File

@ -10,6 +10,8 @@ $i18n = [
"main_currency" => "Κύριο νόμισμα",
"language" => "Γλώσσα",
"passwords_dont_match" => "Οι κωδικοί πρόσβασης δεν ταιριάζουν",
"username_exists" => "Το όνομα χρήστη υπάρχει ήδη",
"email_exists" => "Το email υπάρχει ήδη",
"registration_failed" => "Η εγγραφή απέτυχε, παρακαλώ προσπάθησε ξανά.",
"register" => "Εγγραφή",
"restore_database" => "Επαναφορά βάσης δεδομένων",
@ -18,10 +20,18 @@ $i18n = [
'stay_logged_in' => "Μείνε συνδεδεμένος (30 ημέρες)",
'login' => "Σύνδεση",
'login_failed' => "Τα στοιχεία σύνδεσης είναι λανθασμένα",
'registration_successful' => "Επιτυχής Εγγραφή",
'user_email_waiting_verification' => "Το email σας πρέπει να επαληθευτεί. Παρακαλούμε ελέγξτε το email σας",
// Password Reset Page
'forgot_password' => "Ξέχασες τον κωδικό σου; Κάνε κλικ",
'reset_password' => "Επαναφορά κωδικού πρόσβασης",
'reset_sent_check_email' => "Ένα email με οδηγίες για την επαναφορά του κωδικού πρόσβασης σας έχει σταλεί. Παρακαλώ ελέγξτε το email σας.",
'password_reset_successful' => "Επιτυχής επαναφορά κωδικού πρόσβασης",
// Header
'subscriptions' => "Συνδρομές",
'stats' => "Στατιστικές",
'settings' => "Ρυθμίσεις",
'admin' => "Διαχείριση",
'about' => "Για εμάς",
'logout' => "Αποσύνδεση",
// Subscriptions page
@ -73,6 +83,7 @@ $i18n = [
"url" => "URL",
"notes" => "Σημειώσεις",
"enable_notifications" => "Ενεργοποίηση ειδοποιήσεων για αυτή τη συνδρομή",
"default_value_from_settings" => "Προεπιλεγμένη τιμή από τις ρυθμίσεις",
"delete" => "Διαγραφή",
"cancel" => "Ακύρωση",
"upload_logo" => "Φόρτωση λογότυπου",
@ -85,7 +96,11 @@ $i18n = [
'average_monthly' => "Μέσο μηνιαίο κόστος συνδρομής",
'most_expensive' => "Πιο ακριβό κόστος συνδρομής",
'amount_due' => "Ποσό που οφείλεται αυτόν τον μήνα",
'percentage_budget_used' => "Ποσοστό προϋπολογισμού που χρησιμοποιείται",
'budget_remaining' => "Υπόλοιπο προϋπολογισμού",
'amount_over_budget' => "Ποσό πάνω από τον προϋπολογισμό",
'monthly_savings' => "Μηνιαίες εξοικονομήσεις (σε ανενεργές συνδρομές)",
'yearly_savings' => "Ετήσιες εξοικονομήσεις (σε ανενεργές συνδρομές)",
'split_views' => "Διαχωρισμένες προβολές",
'category_split' => "Διαχωρισμός κατηγορίας",
'household_split' => "Διαχωρισμός νοικοκυριού",
@ -101,7 +116,9 @@ $i18n = [
'upload_avatar' => "μεταφόρτωση άβαταρ",
'file_type_error' => "Το αρχείο πρέπει να είναι τύπου jpg, jpeg, png, webp ή gif",
'user_details' => "Λεπτομέρειες χρήστη",
"household" => "Νοικοκυριό",
"monthly_budget" => "Μηνιαίος προϋπολογισμός",
"budget_info" => "Ο μηνιαίος προϋπολογισμός χρησιμοποιείται για τον υπολογισμό των στατιστικών",
"household" => "Νοικοκυριό",
"save_member" => "Αποθήκευση μέλους",
"delete_member" => "Διαγραφή μέλους",
"cant_delete_member" => "Δεν ειναι δυνατή η διαγραφή του βασικού μέλους",
@ -120,6 +137,25 @@ $i18n = [
"smtp_password" => "SMTP κωδικός",
"from_email" => "Από (Προαιρετικό)",
"smtp_info" => "Ο κωδικός πρόσβασης SMTP μεταδίδεται και αποθηκεύεται σε απλό κείμενο. Για λόγους ασφαλείας, παρακαλούμε δημιούργησε έναν λογαριασμό μόνο γι' αυτό το σκοπό.",
"telegram" => "Telegram",
"telegram_bot_token" => "Τηλεγραφήματα Bot Token",
"telegram_chat_id" => "Τηλεγραφήματα Chat ID",
"webhook" => "Webhook",
"webhook_url" => "Webhook URL",
"request_method" => "Μέθοδος αίτησης",
"custom_headers" => "Προσαρμοσμένες κεφαλίδες",
"webhook_payload" => "Webhook Payload",
"webhook_iterator_key" => "Αντικαταστήστε {{subscriptions}} με το όνομα του κλειδιού",
"variables_available" => "Διαθέσιμες μεταβλητές",
"gotify" => "Gotify",
"token" => "Token",
"discord" => "Discord",
"discord_bot_username" => "Discord Bot Username",
"discord_bot_avatar_url" => "Discord Bot Avatar URL",
"pushover" => "Pushover",
"pushover_user_key" => "Pushover User Key",
'host' => "Host",
'topic' => "Θέμα",
"categories" => "Κατηγορίες",
"save_category" => "Αποθήκευση κατηγορίας",
"delete_category" => "Διαγραφή κατηγορίας",
@ -215,6 +251,7 @@ $i18n = [
"email_error" => "Σφάλμα αποστολής email",
"notification_sent_successfuly" => "Η ειδοποίηση εστάλη επιτυχώς",
"notifications_settings_saved" => "Οι ρυθμίσεις ειδοποίησης αποθηκεύτηκαν με επιτυχία.",
"notification_failed" => "Η ειδοποίηση απέτυχε",
// Payments
"payment_in_use" => "Δεν είναι εφικτό να απενεργοποιηθεί η χρησιμοποιούμενη μέθοδο πληρωμής",
"failed_update_payment" => "Απέτυχε η ενημέρωση της μεθόδου πληρωμής στη βάση δεδομένων",
@ -229,6 +266,27 @@ $i18n = [
// User
"error_updating_user_data" => "Σφάλμα ενημέρωσης δεδομένων χρήστη.",
"user_details_saved" => "Αποθήκευση στοιχείων χρήστη",
// Admin Page
"registrations" => "Εγγραφές",
"enable_user_registrations" => "Ενεργοποίηση εγγραφών χρηστών",
"maximum_number_users" => "Μέγιστος αριθμός χρηστών",
"require_email_verification" => "Απαιτείται επιβεβαίωση email",
"configure_smtp_settings_to_enable" => "Διαμόρφωσε τις ρυθμίσεις SMTP για να ενεργοποιήσεις αυτή την επιλογή",
"server_url" => "Διεύθυνση URL διακομιστή",
"server_url_info" => "Χρησιμοποιείται για επαλήθευση email και ανάκτηση κωδικού πρόσβασης. Πρέπει να είναι ένα έγκυρο δημόσιο URL.",
"server_url_password_reset" => "Εάν οριστεί, θα ενεργοποιήσει επίσης τη λειτουργία επαναφοράς κωδικού πρόσβασης.",
"disable_login" => "Απενεργοποίηση σύνδεσης",
"disable_login_info" => "Παράκαμψη σύνδεσης. Εάν εκτελείτε το διακομιστή σας μόνο σε τοπικό δίκτυο, χωρίς εξωτερική πρόσβαση, μπορείτε να απενεργοποιήσετε τη σύνδεση. Αυτό θα πραγματοποιήσει αυτόματα την είσοδο του χρήστη διαχειριστή.",
"disable_login_info2" => "Μπορείτε να ενεργοποιήσετε αυτή τη ρύθμιση μόνο εάν η εγγραφή χρηστών είναι απενεργοποιημένη και δεν υπάρχουν περισσότεροι από το λογαριασμό χρήστη διαχειριστή.",
"max_users_info" => "Ο μέγιστος αριθμός χρηστών που μπορούν να εγγραφούν. Αν η τιμή είναι 0, δεν υπάρχει όριο.",
"user_management" => "Διαχείριση χρηστών",
"delete_user" => "Διαγραφή χρήστη",
"delete_user_info" => "Η διαγραφή ενός χρήστη θα διαγράψει επίσης όλες τις συνδρομές και τις ρυθμίσεις του.",
"smtp_settings" => "SMTP ρυθμίσεις",
"smtp_usage_info" => "Θα χρησιμοποιηθεί για ανάκτηση κωδικού πρόσβασης και άλλα μηνύματα ηλεκτρονικού ταχυδρομείου συστήματος.",
// Email Verification
"email_verified" => "Το email επιβεβαιώθηκε",
"email_verification_failed" => "Η επαλήθευση email απέτυχε",
];

View File

@ -10,6 +10,8 @@ $i18n = [
"main_currency" => "Main Currency",
"language" => "Language",
"passwords_dont_match" => "Passwords do not match",
"username_exists" => "Username already exists",
"email_exists" => "Email already exists",
"registration_failed" => "Registration failed, please try again.",
"register" => "Register",
"restore_database" => "Restore Database",
@ -18,10 +20,18 @@ $i18n = [
'stay_logged_in' => "Stay logged in (30 days)",
'login' => "Login",
'login_failed' => "Login details are incorrect",
'registration_successful' => "Registration successful",
'user_email_waiting_verification' => "Your email needs to be verified. Please check your email.",
// Password Reset Page
'forgot_password' => "Forgot Password",
'reset_password' => "Reset Password",
'reset_sent_check_email' => "Reset email sent. Please check your email.",
'password_reset_successful' => "Password reset successful",
// Header
'subscriptions' => "Subscriptions",
'stats' => "Statistics",
'settings' => "Settings",
'admin' => "Admin",
'about' => "About",
'logout' => "Logout",
// Subscriptions page
@ -73,6 +83,7 @@ $i18n = [
"url" => "URL",
"notes" => "Notes",
"enable_notifications" => "Enable Notifications for this subscription",
"default_value_from_settings" => "Default value from settings",
"delete" => "Delete",
"cancel" => "Cancel",
"upload_logo" => "Upload Logo",
@ -85,7 +96,11 @@ $i18n = [
'average_monthly' => "Average Monthly Subscription Cost",
'most_expensive' => "Most Expensive Subscription Cost",
'amount_due' => "Amount due this month",
'percentage_budget_used' => "Percentage of budget used",
'budget_remaining' => "Budget Remaining",
'amount_over_budget' => "Amount over budget",
'monthly_savings' => "Monthly Savings (on inactive subscriptions)",
'yearly_savings' => "Yearly Savings (on inactive subscriptions)",
'split_views' => "Split Views",
'category_split' => "Category Split",
'household_split' => "Household Split",
@ -101,7 +116,9 @@ $i18n = [
'upload_avatar' => "Upload Avatar",
'file_type_error' => "The file type supplied is not supported.",
'user_details' => "User Details",
"household" => "Household",
"monthly_budget" => "Monthly Budget",
"budget_info" => "Monthly budget is used to calculate statistics",
"household" => "Household",
"save_member" => "Save Member",
"delete_member" => "Delete Member",
"cant_delete_member" => "Can't delete main member",
@ -120,6 +137,25 @@ $i18n = [
"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.",
"telegram" => "Telegram",
"telegram_bot_token" => "Telegram Bot Token",
"telegram_chat_id" => "Telegram Chat ID",
"webhook" => "Webhook",
"webhook_url" => "Webhook URL",
"request_method" => "Request Method",
"custom_headers" => "Custom Headers",
"webhook_payload" => "Webhook Payload",
"webhook_iterator_key" => "Replace {{subscriptions}} with key name",
"variables_available" => "Variables available",
"gotify" => "Gotify",
"token" => "Token",
"discord" => "Discord",
"discord_bot_username" => "Discord Bot Username",
"discord_bot_avatar_url" => "Discord Bot Avatar URL",
"pushover" => "Pushover",
"pushover_user_key" => "Pushover User Key",
'host' => "Host",
'topic' => "Topic",
"categories" => "Categories",
"save_category" => "Save Category",
"delete_category" => "Delete Category",
@ -215,6 +251,7 @@ $i18n = [
"email_error" => "Error sending email",
"notification_sent_successfuly" => "Notification sent successfully",
"notifications_settings_saved" => "Notification settings saved successfully.",
"notification_failed" => "Notification failed",
// Payments
"payment_in_use" => "Can't disable used payment method",
"failed_update_payment" => "Failed to update payment method in the database",
@ -229,6 +266,28 @@ $i18n = [
// User
"error_updating_user_data" => "Error updating user data.",
"user_details_saved" => "User details saved",
// Admin Page
"registrations" => "Registrations",
"enable_user_registrations" => "Enable user registrations",
"maximum_number_users" => "Maximum number of users",
"require_email_verification" => "Require email verification",
"configure_smtp_settings_to_enable" => "Configure SMTP settings to enable",
"server_url" => "Server URL",
"server_url_info" => "Used for email verification and password recovery. Must be a valid public URL.",
"server_url_password_reset" => "If set will also enable password reset functionality.",
"disable_login" => "Disable login",
"disable_login_info" => "Bypass login. If you run your server on a local network only, without external access you can disable the login. This will automatically login the admin user.",
"disable_login_info2" => "You can only enable this setting if user registration is disabled and there are no more than the admin user account.",
"max_users_info" => "0 means unlimited",
"user_management" => "User Management",
"delete_user" => "Delete User",
"delete_user_info" => "Deleting a user will also delete all their subscriptions and settings.",
"smtp_settings" => "SMTP Settings",
"smtp_usage_info" => "Will be used for password recovery and other system emails.",
// Email Verification
"email_verified" => "Email verified successfully",
"email_verification_failed" => "Email verification failed",
];

View File

@ -10,6 +10,8 @@ $i18n = [
"main_currency" => "Moneda Principal",
"language" => "Idioma",
"passwords_dont_match" => "Las contraseñas no coinciden",
"username_exists" => "El nombre de usuario ya existe",
"email_exists" => "El correo electrónico ya está registrado",
"registration_failed" => "Error en el registro, por favor inténtalo de nuevo.",
"register" => "Registrar",
"restore_database" => "Restaurar Base de Datos",
@ -18,10 +20,18 @@ $i18n = [
'stay_logged_in' => "Mantener sesión iniciada (30 días)",
'login' => "Iniciar Sesión",
'login_failed' => "Los detalles de inicio de sesión son incorrectos",
'registration_successful' => "Registro efectuado con éxito",
'user_email_waiting_verification' => "Tu correo electrónico necesita ser verificado. Por favor, compruebe su correo electrónico",
// Password Reset Page
'forgot_password' => "¿Olvidaste tu contraseña?",
'reset_password' => "Restablecer Contraseña",
'reset_sent_check_email' => "Se ha enviado un correo electrónico con instrucciones para restablecer la contraseña. Por favor, compruebe su correo electrónico.",
'password_reset_successful' => "Contraseña restablecida con éxito",
// Header
'subscriptions' => "Suscripciones",
'stats' => "Estadísticas",
'settings' => "Configuración",
'admin' => "Admin",
'about' => "Acerca de",
'logout' => "Cerrar Sesión",
// Subscriptions page
@ -73,6 +83,7 @@ $i18n = [
"url" => "URL",
"notes" => "Notas",
"enable_notifications" => "Habilitar notificaciones para esta suscripción",
"default_value_from_settings" => "Valor predeterminado de la configuración",
"delete" => "Eliminar",
"cancel" => "Cancelar",
"upload_logo" => "Cargar Logotipo",
@ -85,7 +96,11 @@ $i18n = [
'average_monthly' => "Costo Promedio Mensual de Suscripción",
'most_expensive' => "Costo de Suscripción Más Caro",
'amount_due' => "Monto a pagar este mes",
'percentage_budget_used' => "Porcentaje del presupuesto utilizado",
'budget_remaining' => "Presupuesto Restante",
'amount_over_budget' => "Monto sobre el presupuesto",
'monthly_savings' => "Ahorro Mensual (en suscripciones inactivas)",
'yearly_savings' => "Ahorro Anual (en suscripciones inactivas)",
'split_views' => "Vistas Divididas",
'category_split' => "División por Categoría",
'household_split' => "División por Hogar",
@ -101,7 +116,9 @@ $i18n = [
'upload_avatar' => "Subir avatar",
'file_type_error' => "El archivo debe ser una imagen en formato PNG, JPG, WEBP o SVG",
'user_details' => "Detalles del Usuario",
"household" => "Hogar",
"monthly_budget" => "Presupuesto Mensual",
"budget_info" => "El presupuesto mensual se utiliza para calcular las estadísticas. Si no deseas utilizar esta función, déjalo en 0.",
"household" => "Hogar",
"save_member" => "Guardar Miembro",
"delete_member" => "Eliminar Miembro",
"cant_delete_member" => "No se puede eliminar el miembro principal",
@ -120,6 +137,25 @@ $i18n = [
"smtp_password" => "Contraseña SMTP",
"from_email" => "Correo electrónico de origen (Opcional)",
"smtp_info" => "La contraseña SMTP se transmite y almacena en texto plano. Por seguridad, crea una cuenta solo para esto.",
"telegram" => "Telegram",
"telegram_bot_token" => "Token del Bot de Telegram",
"telegram_chat_id" => "ID del Chat de Telegram",
"webhook" => "Webhook",
"webhook_url" => "URL del Webhook",
"request_method" => "Método de Solicitud",
"custom_headers" => "Cabeceras Personalizadas",
"webhook_payload" => "Carga del Webhook",
"webhook_iterator_key" => "Sustituye {{subscriptions}} por el nombre de la clave",
"variables_available" => "Variables disponibles",
"gotify" => "Gotify",
"token" => "Token",
"discord" => "Discord",
"discord_bot_username" => "Nombre de usuario del bot",
"discord_bot_avatar_url" => "URL del avatar del bot",
"pushover" => "Pushover",
"pushover_user_key" => "Clave de usuario",
'host' => "Host",
'topic' => "Topico",
"categories" => "Categorías",
"save_category" => "Guardar Categoría",
"delete_category" => "Eliminar Categoría",
@ -215,6 +251,7 @@ $i18n = [
"email_error" => "Error al enviar correo electrónico",
"notification_sent_successfuly" => "Notificación enviada con éxito",
"notifications_settings_saved" => "Configuración de notificaciones guardada con éxito.",
"notification_failed" => "Error al enviar la notificación",
// Payments
"payment_in_use" => "No se puede desactivar el método de pago utilizado",
"failed_update_payment" => "Error al actualizar el método de pago en la base de datos",
@ -229,6 +266,27 @@ $i18n = [
// User
"error_updating_user_data" => "Error al actualizar los datos del usuario.",
"user_details_saved" => "Detalles del usuario guardados",
// Admin Page
"registrations" => "Registro de Usuarios",
"enable_user_registrations" => "Habilitar registro de usuarios",
"maximum_number_users" => "Número máximo de usuarios",
"require_email_verification" => "Requerir verificación de correo electrónico",
"configure_smtp_settings_to_enable" => "Configura la configuración SMTP para habilitar",
"server_url" => "URL del Servidor",
"server_url_info" => "Se utiliza para verificar el correo electrónico y recuperar la contraseña. Debe ser una URL pública válida.",
"server_url_password_reset" => "Si se configura, también se habilitará la función de restablecimiento de contraseña.",
"disable_login" => "Deshabilitar Inicio de Sesión",
"disable_login_info" => "Omitir el inicio de sesión. Si ejecuta su servidor sólo en una red local, sin acceso externo, puede desactivar el inicio de sesión. Esto iniciará automáticamente la sesión del usuario administrador.",
"disable_login_info2" => "Sólo puede activar esta configuración si el registro de usuarios está desactivado y no hay más que la cuenta de usuario admin.",
"max_users_info" => "0 para ilimitado",
"user_management" => "Gestión de Usuarios",
"delete_user" => "Eliminar Usuario",
"delete_user_info" => "Al eliminar un usuario, también se eliminarán todas sus suscripciones y configuraciones.",
"smtp_settings" => "Configuración SMTP",
"smtp_usage_info" => "Se utilizará para recuperar contraseñas y otros correos electrónicos del sistema.",
// Email Verification
"email_verified" => "Correo electrónico verificado",
"email_verification_failed" => "Error al verificar el correo electrónico",
];

View File

@ -10,6 +10,8 @@ $i18n = [
"main_currency" => "Devise principale",
"language" => "Langue",
"passwords_dont_match" => "Les mots de passe ne correspondent pas",
"username_exists" => "Le nom d'utilisateur existe déjà",
"email_exists" => "L'adresse courriel existe déjà",
"registration_failed" => "L'inscription a échoué, veuillez réessayer.",
"register" => "S'inscrire",
"restore_database" => "Restaurer la base de données",
@ -18,10 +20,18 @@ $i18n = [
'stay_logged_in' => "Rester connecté (30 jours)",
'login' => "Se connecter",
'login_failed' => "Les détails de connexion sont incorrects",
'registration_successful' => "Inscription réussie",
'user_email_waiting_verification' => "Votre email doit être vérifié. Veuillez vérifier votre email",
// Password Reset Page
'forgot_password' => "Mot de passe oublié",
'reset_password' => "Réinitialiser le mot de passe",
'reset_sent_check_email' => "Un courriel a été envoyé à l'adresse fournie. Vérifiez votre boîte de réception.",
'password_reset_successful' => "Réinitialisation du mot de passe réussie",
// En-tête
'subscriptions' => "Abonnements",
'stats' => "Statistiques",
'settings' => "Paramètres",
'admin' => "Admin",
'about' => "À propos",
'logout' => "Déconnexion",
// Page d'abonnements
@ -73,6 +83,7 @@ $i18n = [
"url" => "URL",
"notes" => "Notes",
"enable_notifications" => "Activer les notifications pour cet abonnement",
"default_value_from_settings" => "Valeur par défaut des paramètres",
"delete" => "Supprimer",
"cancel" => "Annuler",
"upload_logo" => "Télécharger le logo",
@ -85,7 +96,11 @@ $i18n = [
'average_monthly' => "Coût moyen mensuel de l'abonnement",
'most_expensive' => "Coût d'abonnement le plus élevé",
'amount_due' => "Montant dû ce mois-ci",
'percentage_budget_used' => "Pourcentage du budget utilisé",
'budget_remaining' => "Budget restant",
'amount_over_budget' => "Montant dépassant le budget",
'monthly_savings' => "Économies mensuelles (sur les abonnements inactifs)",
'yearly_savings' => "Économies annuelles (sur les abonnements inactifs)",
'split_views' => "Vues partagées",
'category_split' => "Répartition par catégorie",
'household_split' => "Répartition du ménage",
@ -101,6 +116,8 @@ $i18n = [
'upload_avatar' => "Télécharger un Avatar",
'file_type_error' => "Le type de fichier n'est pas pris en charge",
'user_details' => "Détails de l'utilisateur",
"monthly_budget" => "Budget mensuel",
"budget_info" => "Le budget mensuel est utilisé pour calculer les statistiques. Laissez vide pour désactiver.",
"household" => "Ménage",
"save_member" => "Enregistrer le membre",
"delete_member" => "Supprimer le membre",
@ -120,6 +137,25 @@ $i18n = [
"smtp_password" => "Mot de passe SMTP",
"from_email" => "De l'adresse courriel (facultatif)",
"smtp_info" => "Le mot de passe SMTP est transmis et stocké en texte brut. Pour des raisons de sécurité, veuillez créer un compte uniquement à cette fin.",
"telegram" => "Telegram",
"telegram_bot_token" => "Jeton du bot Telegram",
"telegram_chat_id" => "ID de chat Telegram",
"webhook" => "Webhook",
"webhook_url" => "URL du webhook",
"request_method" => "Méthode de requête",
"custom_headers" => "En-têtes personnalisés",
"webhook_payload" => "Charge utile du webhook",
"webhook_iterator_key" => "Remplacer {{subscriptions}} par le nom de la clé",
"variables_available" => "Variables disponibles",
"gotify" => "Gotify",
"token" => "Jeton",
"discord" => "Discord",
"discord_bot_username" => "Nom d'utilisateur du bot Discord",
"discord_bot_avatar_url" => "URL de l'avatar du bot Discord",
"pushover" => "Pushover",
"pushover_user_key" => "Clé utilisateur Pushover",
'host' => "Hôte",
'topic' => "Sujet",
"categories" => "Catégories",
"save_category" => "Enregistrer la catégorie",
"delete_category" => "Supprimer la catégorie",
@ -215,6 +251,7 @@ $i18n = [
"email_error" => "Erreur dlors de l'envoi de courriel",
"notification_sent_successfuly" => "Notification envoyée avec succès",
"notifications_settings_saved" => "Paramètres de notifications enregistrés avec succès.",
"notification_failed" => "Échec de la notification",
// Paiements
"payment_in_use" => "Impossible de désactiver la méthode de paiement utilisée",
"failed_update_payment" => "Échec de la mise à jour de la méthode de paiement dans la base de données",
@ -229,6 +266,27 @@ $i18n = [
// Utilisateur
"error_updating_user_data" => "Erreur lors de la mise à jour des données utilisateur.",
"user_details_saved" => "Détails de l'utilisateur enregistrés",
// Admin Page
"registrations" => "Inscriptions",
"enable_user_registrations" => "Activer les inscriptions d'utilisateurs",
"maximum_number_users" => "Nombre maximum d'utilisateurs",
"require_email_verification" => "Exiger la vérification de l'adresse courriel",
"configure_smtp_settings_to_enable" => "Configurer les paramètres SMTP pour activer",
"server_url" => "URL du serveur",
"server_url_info" => "Utilisé pour la vérification du courrier électronique et la récupération du mot de passe. Il doit s'agir d'une URL publique valide.",
"server_url_password_reset" => "Si cette option est activée, la fonction de réinitialisation du mot de passe sera également activée.",
"disable_login" => "Désactiver la connexion",
"disable_login_info" => "Contourner le login. Si vous utilisez votre serveur sur un réseau local uniquement, sans accès externe, vous pouvez désactiver le login. L'utilisateur admin se connectera automatiquement.",
"disable_login_info2" => "Vous ne pouvez activer ce paramètre que si l'enregistrement des utilisateurs est désactivé et qu'il n'y a pas d'autre compte utilisateur que celui de l'administrateur.",
"max_users_info" => "0 signifie un nombre illimité d'utilisateurs",
"user_management" => "Gestion des utilisateurs",
"delete_user" => "Supprimer l'utilisateur",
"delete_user_info" => "La suppression d'un utilisateur supprimera également tous ses abonnements et paramètres.",
"smtp_settings" => "Paramètres SMTP",
"smtp_usage_info" => "Sera utilisé pour la récupération du mot de passe et d'autres e-mails système.",
// Email Verification
"email_verified" => "Votre adresse courriel a été vérifiée avec succès",
"email_verification_failed" => "La vérification de l'adresse courriel a échoué",
];

View File

@ -10,6 +10,8 @@ $i18n = [
'main_currency' => 'Valuta principale',
'language' => 'Lingua',
'passwords_dont_match' => 'Le password non corrispondono',
'username_exists' => 'Il nome utente esiste già',
'email_exists' => 'L\'indirizzo email esiste già',
'registration_failed' => 'Registrazione fallita, riprova.',
'register' => 'Registrati',
"restore_database" => 'Ripristina database',
@ -19,11 +21,20 @@ $i18n = [
'stay_logged_in' => 'Rimani connesso (30 giorni)',
'login' => 'Accedi',
'login_failed' => 'Le credenziali non sono corrette',
'registration_successful' => "L'account è stato creato con successo",
'user_email_waiting_verification' => "L'e-mail deve essere verificata. Controlla la tua email",
// Password Reset Page
'forgot_password' => "Hai dimenticato la password?",
'reset_password' => "Reimposta password",
'reset_sent_check_email' => "Un'email è stata inviata. Controlla la tua casella di posta",
'password_reset_successful' => "La password è stata reimpostata con successo",
// Header
'subscriptions' => 'Abbonamenti',
'stats' => 'Statistiche',
'settings' => 'Impostazioni',
'admin' => 'Amministrazione',
'about' => 'Informazioni',
'logout' => 'Esci',
@ -75,6 +86,7 @@ $i18n = [
'url' => 'URL',
'notes' => 'Note',
'enable_notifications' => 'Abilita notifiche per questo abbonamento',
'default_value_from_settings' => 'Valore predefinito dalle impostazioni',
'delete' => 'Cancella',
'cancel' => 'Annulla',
'upload_logo' => 'Carica logo',
@ -88,7 +100,11 @@ $i18n = [
'average_monthly' => "Costo medio mensile dell'abbonamento",
'most_expensive' => "Costo dell'abbonamento più elevato",
'amount_due' => 'Importo dovuto questo mese',
'percentage_budget_used' => 'Percentuale del budget utilizzata',
'budget_remaining' => 'Budget rimanente',
'amount_over_budget' => 'Importo oltre il budget',
'monthly_savings' => 'Risparmi mensili (su abbonamenti inattivi)',
'yearly_savings' => 'Risparmi annuali (su abbonamenti inattivi)',
'split_views' => 'Visualizzazioni con grafici',
'category_split' => 'Suddivisione per categoria',
'household_split' => 'Suddivisione per nucleo familiare',
@ -106,6 +122,8 @@ $i18n = [
'upload_avatar' => 'Carica avatar',
'file_type_error' => 'Il tipo di file fornito non è supportato.',
'user_details' => 'Dettagli utente',
"monthly_budget" => "Budget mensile",
"budget_info" => "Il budget mensile viene utilizzato per calcolare le statistiche. Se non si desidera utilizzare questa funzionalità, impostare il budget su 0.",
'household' => 'Nucleo familiare',
'save_member' => 'Salva membro',
'delete_member' => 'Elimina membro',
@ -125,6 +143,25 @@ $i18n = [
'smtp_password' => 'Password SMTP',
'from_email' => 'Da quale e-mail (Opzionale)',
'smtp_info' => 'La password SMTP viene memorizzata e trasmessa in chiaro. Per motivi di sicurezza, si prega di creare un account da utilizzare solo per questo.',
"telegram" => "Telegram",
"telegram_bot_token" => "Telegram Bot Token",
"telegram_chat_id" => "Telegram Chat ID",
"webhook" => "Webhook",
"webhook_url" => "Webhook URL",
"request_method" => "Metodo di richiesta",
"custom_headers" => "Intestazioni personalizzate",
"webhook_payload" => "Webhook Payload",
"webhook_iterator_key" => "Sostituisci {{subscriptions}} con il nome della chiave",
"variables_available" => "Variabili disponibili",
"gotify" => "Gotify",
"token" => "Token",
"discord" => "Discord",
"discord_bot_username" => "Nome utente del bot",
"discord_bot_avatar_url" => "URL dell'avatar del bot",
"pushover" => "Pushover",
"pushover_user_key" => "Chiave utente",
'host' => "Host",
'topic' => "Topic",
'categories' => 'Categorie',
'save_category' => 'Salva categoria',
'delete_category' => 'Elimina categoria',
@ -193,6 +230,80 @@ $i18n = [
// Category
'failed_add_category' => 'Impossibile aggiungere la categoria',
'failed_edit_category' => 'Impossibile modificare la categoria',
"category_in_use" => "La categoria è attualmente in uso da almeno un abbonamento",
"failed_remove_category" => "Impossibile rimuovere la categoria",
"category_saved" => "Categoria salvata",
"category_removed" => "Categoria rimossa",
"sort_order_saved" => "Ordine di visualizzazione salvato",
// Currency
"currency_saved" => "Valuta salvata con successo",
"error_adding_currency" => "Errore nell'aggiunta della valuta",
"failed_to_store_currency" => "Impossibile salvare la valuta nel Database",
"currency_in_use" => "La valuta è attualmente in uso da almeno un abbonamento",
"currency_is_main" => "Impossibile rimuovere la valuta principale",
"failed_to_remove_currency" => "Impossibile rimuovere la valuta",
"failed_to_store_api_key" => "Impossibile salvare la chiave API",
"invalid_api_key" => "Chiave API non valida",
"api_key_saved" => "Chiave API salvata",
"currency_removed" => "Valuta rimossa",
// Household
"failed_add_household" => "Impossibile aggiungere un membro del nucleo familiare",
"failed_edit_household" => "Impossibile modificare un membro del nucleo familiare",
"failed_remove_household" => "Impossibile rimuovere un membro del nucleo familiare",
"household_in_use" => "Il membro del nucleo familiare è attualmente in uso da almeno un abbonamento",
"member_saved" => "Membro salvato",
"member_removed" => "Membro rimosso",
// Notifications
"error_saving_notifications" => "Errore nel salvataggio delle notifiche",
"wallos_notification" => "Notifica Wallos",
"test_notification" => "Questa è una notifica di prova",
"email_error" => "Errore nell'invio dell'e-mail",
"notification_sent_successfuly" => "Notifica inviata con successo",
"notifications_settings_saved" => "Impostazioni delle notifiche salvate",
"notification_failed" => "Invio della notifica fallito",
// Payments
"payment_in_use" => "Questo metodo di pagamento è attualmente in uso da almeno un abbonamento",
"failed_update_payment" => "Aggiornamento del metodo di pagamento fallito",
"enabled" => "abilitato",
"disabled" => "disabilitato",
// Subscription
"error_fetching_image" => "Errore nel recupero dell'immagine",
"subscription_updated_successfuly" => "Abbonamento aggiornato con successo",
"subscription_added_successfuly" => "Abbonamento aggiunto con successo",
"error_deleting_subscription" => "Errore nell'eliminazione dell'abbonamento",
"invalid_request_method" => "Metodo di richiesta non valido",
// User
"error_updating_user_data" => "Errore nell'aggiornamento dei dati utente",
"user_details_saved" => "Dettagli utente salvati",
// Admin Page
"registrations" => "Registrazioni",
"enable_user_registrations" => "Abilita le registrazioni utente",
"maximum_number_users" => "Numero massimo di utenti",
"require_email_verification" => "Richiedi la verifica dell'e-mail",
"configure_smtp_settings_to_enable" => "Configura le impostazioni SMTP per abilitare",
"server_url" => "URL del server",
"server_url_info" => "Utilizzato per la verifica dell'e-mail e il recupero della password. Deve essere un URL pubblico valido.",
"server_url_password_reset" => "Se impostato, abilita anche la funzionalità di reimpostazione della password.",
"disable_login" => "Disabilita il login",
"disable_login_info" => "Bypassare il login. Se si gestisce il server solo su una rete locale, senza accesso esterno, è possibile disabilitare il login. In questo modo, l'utente amministratore effettuerà automaticamente il login.",
"disable_login_info2" => "Questa impostazione può essere attivata solo se la registrazione degli utenti è disattivata e non ci sono più account utente oltre a quello dell'amministratore.",
"max_users_info" => "Impostare a 0 per un numero illimitato di utenti",
"user_management" => "Gestione utenti",
"delete_user" => "Elimina utente",
"delete_user_info" => "L'eliminazione di un utente eliminerà anche tutte le sue iscrizioni e impostazioni.",
"smtp_settings" => "Impostazioni SMTP",
"smtp_usage_info" => "Verrà utilizzato per il recupero della password e altre e-mail di sistema.",
// Email Verification
"email_verified" => "L'indirizzo email è stato verificato con successo",
"email_verification_failed" => "La verifica dell'email è fallita",
];
?>

View File

@ -10,6 +10,8 @@ $i18n = [
"main_currency" => "主要通貨",
"language" => "言語",
"passwords_dont_match" => "パスワードが違います",
"username_exists" => "ユーザー名が既に存在します",
"email_exists" => "メールアドレスが既に存在します",
"registration_failed" => "登録に失敗しました。もう一度お試しください。",
"register" => "登録する",
"restore_database" => "データベースをリストア",
@ -18,10 +20,18 @@ $i18n = [
'stay_logged_in' => "ログインしたままにする (30日)",
'login' => "ログイン",
'login_failed' => "ログイン情報が間違っています",
'registration_successful' => "登録に成功",
'user_email_waiting_verification' => "Eメールの確認が必要です。メールを確認してください。",
// Password Reset Page
'forgot_password' => "パスワードを忘れた場合",
'reset_password' => "パスワードをリセット",
'reset_sent_check_email' => "パスワードリセットリンクが送信されました。メールを確認してください。",
'password_reset_successful' => "パスワードリセットに成功",
// Header
'subscriptions' => "定期購入",
'stats' => "統計",
'settings' => "設定",
'admin' => "管理者",
'about' => "About",
'logout' => "ログアウト",
// Subscriptions page
@ -73,6 +83,7 @@ $i18n = [
"url" => "URL",
"notes" => "注釈",
"enable_notifications" => "この定期購入の通知を有効にする",
"default_value_from_settings" => "設定からデフォルト値を使用",
"delete" => "削除",
"cancel" => "キャンセル",
"upload_logo" => "ロゴのアップロード",
@ -85,7 +96,11 @@ $i18n = [
'average_monthly' => "月額平均費用",
'most_expensive' => "最も高額な定期購入費用",
'amount_due' => "今月の支払額",
'percentage_budget_used' => "予算使用率",
'budget_remaining' => "予算残高",
'amount_over_budget' => "予算オーバー",
'monthly_savings' => "月間節約 (非アクティブな定期購入)",
'yearly_savings' => "年間節約 (非アクティブな定期購入)",
'split_views' => "分割表示",
'category_split' => "カテゴリ別",
'household_split' => "世帯別",
@ -101,7 +116,9 @@ $i18n = [
'upload_avatar' => "アバターをアップロードする",
'file_type_error' => "ファイルタイプが許可されていません",
'user_details' => "ユーザー詳細",
"household" => "世帯",
"monthly_budget" => "月間予算",
"budget_info" => "予算を設定すると、統計ページで予算と実際の支出を比較できます。",
"household" => "世帯",
"save_member" => "世帯員を保存",
"delete_member" => "世帯員を削除",
"cant_delete_member" => "世帯主は削除出ません",
@ -120,6 +137,25 @@ $i18n = [
"smtp_password" => "SMTPパスワード",
"from_email" => "送信元アドレス (オプション)",
"smtp_info" => "SMTPパスワードは平文で送信および保存されます。セキュリティのため専用のアカウントを作成してください。",
"telegram" => "Telegram",
"telegram_bot_token" => "Telegramボットトークン",
"telegram_chat_id" => "TelegramチャットID",
"webhook" => "Webhook",
"webhook_url" => "Webhook URL",
"request_method" => "リクエストメソッド",
"custom_headers" => "カスタムヘッダー",
"webhook_payload" => "Webhookペイロード",
"webhook_iterator_keu" => "{{subscriptions}}をキー名に置き換える」",
"variables_available" => "利用可能な変数",
"gotify" => "Gotify",
"token" => "トークン",
"discord" => "Discord",
"discord_bot_username" => "Discordボットユーザー名",
"discord_bot_avatar_url" => "DiscordボットアバターURL",
"pushover" => "Pushover",
"pushover_user_key" => "Pushoverユーザーキー",
'host' => "ホスト",
'topic' => "トピック",
"categories" => "カテゴリ",
"save_category" => "カテゴリを保存",
"delete_category" => "カテゴリを削除",
@ -215,6 +251,7 @@ $i18n = [
"email_error" => "電子メールの送信エラー",
"notification_sent_successfuly" => "通知の送信に成功しました",
"notifications_settings_saved" => "通知設定の保存に成功",
"notification_failed" => "通知の送信に失敗",
// Payments
"payment_in_use" => "使用中の支払い方法は削除できません",
"failed_update_payment" => "データーベースの支払い方法の更新に失敗しました",
@ -229,6 +266,27 @@ $i18n = [
// User
"error_updating_user_data" => "ユーザデータの更新エラー",
"user_details_saved" => "ユーザー詳細の保存",
// Admin Page
"registrations" => "登録",
"enable_user_registrations" => "ユーザー登録を有効にする",
"maximum_number_users" => "最大ユーザ数",
"require_email_verification" => "メール確認を必要とする",
"configure_smtp_settings_to_enable" => "SMTP設定を構成して有効にする",
"server_url" => "サーバーURL",
"server_url_info" => "電子メール認証とパスワード回復に使用される。有効な公開URLでなければなりません。",
"server_url_password_reset" => "設定すると、パスワードリセット機能も有効になる。",
"disable_login" => "ログインを無効にする",
"disable_login_info" => "ログインをバイパスします。サーバーをローカルネットワークのみで運用し、外部からのアクセスがない場合、ログインを無効にすることができます。これにより、管理者ユーザが自動的にログインします。",
"disable_login_info2" => "この設定を有効にできるのは、ユーザー登録がオフで、管理者以上のユーザーアカウントが存在しない場合のみです。",
"max_users_info" => "0に設定すると無制限になります",
"user_management" => "ユーザー管理",
"delete_user" => "ユーザーを削除",
"delete_user_info" => "ユーザーを削除すると、そのユーザーのサブスクリプションと設定もすべて削除されます。",
"smtp_settings" => "SMTP設定",
"smtp_usage_info" => "パスワードの回復やその他のシステム電子メールに使用されます。",
// Email Verification
"email_verified" => "メールアドレスが確認されました",
"email_verification_failed" => "メールアドレスの確認に失敗しました",
];

294
includes/i18n/ko.php Normal file
View File

@ -0,0 +1,294 @@
<?php
$i18n = [
// Registration page
"create_account" => "로그인 하기 전에 회원가입을 진행해야 합니다.",
'username' => "유저명",
'password' => "비밀번호",
"email" => "이메일",
"confirm_password" => "비밀번호 확인",
"main_currency" => "기본 통화",
"language" => "언어",
"passwords_dont_match" => "비밀번호가 일치하지 않습니다.",
"username_exists" => "이미 존재하는 유저명입니다.",
"email_exists" => "이미 존재하는 이메일입니다.",
"registration_failed" => "회원가입 실패. 다시 시도해 주세요.",
"register" => "회원가입",
"restore_database" => "데이터베이스 복구",
// Login Page
'please_login' => "로그인 해 주세요.",
'stay_logged_in' => "로그인 유지 (30일)",
'login' => "로그인",
'login_failed' => "로그인 정보가 부정확합니다.",
'registration_successful' => "등록 성공",
'user_email_waiting_verification' => "이메일을 인증해야 합니다. 이메일을 확인해 주세요.",
// Password Reset Page
'forgot_password' => "비밀번호를 잊으셨나요?",
'reset_password' => "비밀번호 재설정",
'reset_sent_check_email' => "비밀번호 재설정 이메일이 전송되었습니다. 이메일을 확인해 주세요.",
'password_reset_successful' => "비밀번호 재설정 성공",
// Header
'subscriptions' => "구독",
'stats' => "통계",
'settings' => "설정",
'admin' => "관리자",
'about' => "정보",
'logout' => "로그아웃",
// Subscriptions page
"subscription" => "구독",
"no_subscriptions_yet" => "아직 구독을 등록하지 않았습니다.",
"add_first_subscription" => "첫번째 구독을 추가하세요.",
'new_subscription' => "새 구독",
'search' => "검색",
'sort' => "정렬",
'name' => "이름",
'last_added' => "최근 등록",
'price' => "가격",
'next_payment' => "다음 결제일",
'inactive' => "구독 비활성화",
'member' => "구성원",
'category' => "카테고리",
'payment_method' => "지불 수단",
"Daily" => "일간 결제",
"Weekly" => "주간 결제",
"Monthly" => "월간 결제",
"Yearly" => "연간 결제",
"daily" => "",
"weekly" => "",
"monthly" => "",
"yearly" => "",
"days" => "매일",
"weeks" => "매주",
"months" => "매월",
"years" => "매년",
"external_url" => "외부 URL 방문",
"empty_page" => "빈 페이지",
"clear_filters" => "필터 제거",
"no_matching_subscriptions" => "해당하는 구독이 없습니다.",
// Subscription form
"add_subscription" => "구독 추가",
"edit_subscription" => "구독 편집",
"subscription_name" => "구독 이름",
"logo_preview" => "로고 미리보기",
"search_logo" => "웹에서 로고 검색하기",
"web_search" => "웹 검색",
"currency" => "통화",
"payment_every" => "지불 빈도",
"frequency" => "빈도",
"cycle" => "주기",
"next_payment" => "다음 결제",
"payment_method" => "결제 방법",
"no_category" => "카테고리 없음",
"paid_by" => "결제하는 사람",
"url" => "URL",
"notes" => "메모",
"enable_notifications" => "이 구독에 대한 알림을 활성화합니다.",
"default_value_from_settings" => "설정에서 기본값 사용",
"delete" => "삭제",
"cancel" => "취소",
"upload_logo" => "로고 업로드",
// Statistics page
'general_statistics' => "일반 통계",
'active_subscriptions' => "활성 구독",
'inactive_subscriptions' => "비활성 구독",
'monthly_cost' => "월간 지출",
'yearly_cost' => "연간 지출",
'average_monthly' => "월별 평균 구독 비용",
'most_expensive' => "최고가 구독 비용",
'amount_due' => "이달의 결제 비용",
'percentage_budget_used' => "예산 사용률",
'budget_remaining' => "남은 예산",
'amount_over_budget' => "예산 초과",
'monthly_savings' => "월간 절약 (비활성 구독)",
'yearly_savings' => "연간 절약 (비활성 구독)",
'split_views' => "분할 표시",
'category_split' => "카테고리별",
'household_split' => "가구별",
'payment_method_split' => "지불방법별",
// About page
'about_and_credits' => "개요 및 크레딧",
'license' => "라이선스",
'issues_and_requests' => "이슈 및 요청",
'the_author' => "제작자",
'icons' => "아이콘",
'payment_icons' => "지불 방식 아이콘",
// Settings page
'upload_avatar' => "아바타 업로드",
'file_type_error' => "제공된 파일이 지원하지 않는 타입입니다.",
'user_details' => "유저 상세",
"monthly_budget" => "월간 예산",
"budget_info" => "예산을 설정하면 통계 페이지에서 예산과 실제 지출을 비교할 수 있습니다.",
"household" => "가구",
"save_member" => "구성원 저장",
"delete_member" => "구성원 삭제",
"cant_delete_member" => "메인 구성원은 삭제할 수 없습니다",
"cant_delete_member_in_use" => "구독에서 사용 중인 구성원은 삭제할 수 없습니다",
'household_info' => "이메일 항목은 가구 구성원이 구독 만료에 대한 안내를 받기 위해 필요합니다.",
"notifications" => "알림",
"enable_email_notifications" => "이메일 알림 활성화",
"notify_me" => "알림 받기",
"day_before" => "일 전",
"days_before" => "일 전",
"smtp_address" => "SMTP 주소",
"port" => "포트",
"tls" => "TLS",
"ssl" => "SSL",
"smtp_username" => "SMTP 유저명",
"smtp_password" => "SMTP 비밀번호",
"from_email" => "발송 주소 (선택사항)",
"smtp_info" => "SMTP 비밀번호는 평문으로 저장되고 발송됩니다. 보안을 위해, 이 서비스를 위해서만 사용하는 계정을 생성해 주세요.",
"telegram" => "텔레그램",
"telegram_bot_token" => "텔레그램 봇 토큰",
"telegram_chat_id" => "텔레그램 채팅 ID",
"webhook" => "웹훅",
"webhook_url" => "웹훅 URL",
"request_method" => "요청 메서드",
"custom_headers" => "커스텀 헤더",
"webhook_payload" => "웹훅 페이로드",
"webhook_iterator_key" => "{{subscriptions}}을 키 이름으로 바꾸기",
"variables_available" => "사용 가능한 변수",
"gotify" => "Gotify",
"token" => "토큰",
"discord" => "디스코드",
"discord_bot_username" => "디스코드 봇 유저명",
"discord_bot_avatar_url" => "디스코드 봇 아바타 URL",
"pushover" => "Pushover",
"pushover_user_key" => "Pushover User Key",~
'host' => "호스트",
'topic' => "토픽",
"categories" => "카테고리",
"save_category" => "카테고리 저장",
"delete_category" => "카테고리 삭제",
"cant_delete_category_in_use" => "구독에서 사용 중인 카테고리는 삭제할 수 없습니다.",
"currencies" => "통화",
"save_currency" => "통화 저장",
"delete_currency" => "통화 삭제",
"cant_delete_main_currency" => "기본 통화는 삭제할 수 없습니다.",
"cant_delete_currency_in_use" => "구독에서 사용 중인 통화는 삭제할 수 없습니다.",
"exchange_update" => "환율 최종 갱신일",
"currency_info" => "지원하는 통화와 정확한 통화 코드 찾기",
"currency_performance" => "성능을 향상시키기 위해서는 사용할 통화들만 유지하세요.",
"fixer_api_key" => "Fixer API 키",
"api_key" => "API 키",
"provider" => "제공자",
"fixer_info" => "여러 통화를 사용하고, 정확한 통계와 구독별 정렬을 원하시느 경우에는, Fixer에서 발급받은 무료 API 키가 필요합니다.",
"get_key" => "키 얻기",
"get_free_fixer_api_key" => "무료 Fixer API 키 얻기",
"get_key_alternative" => "또는 다음 사이트에서 무료 Fixer api 키를 얻을 수 있습니다.",
"display_settings" => "디스플레이 설정",
"theme_settings" => "테마 설정",
"custom_colors" => "커스텀 색상",
"dark_theme" => "다크 테마",
"switch_theme" => "라이트/다크 테마 전환",
"calculate_monthly_price" => "모든 구독에 대한 월별 요금을 계산하고 표시",
"convert_prices" => "항상 기본 통화로 가격을 환산하고 표시 (느림)",
"hide_disabled_subscriptions" => "비활성화된 구독 숨기기",
"experimental_settings" => "실험적 설정",
"remove_background" => "로고 이미지 검색에서 배경 삭제 (실험적)",
"experimental_info" => "실험적 설정은 제대로 작동하지 않을 수 있습니다.",
"payment_methods" => "결제 수단",
"payment_methods_info" => "결제 수단을 클릭하여 활성화/비활성화 할 수 있습니다.",
"rename_payment_methods_info" => "결제 수단의 이름을 클릭해 이름을 변경합니다.",
"cant_delete_payment_method_in_use" => "사용중인 결제 수단을 비활성화 할 수 없습니다",
"add_custom_payment" => "커스텀 결제 수단 추가",
"payment_method_name" => "결제 수단 이름",
"payment_method_added_successfuly" => "결제 수단이 성공적으로 추가되었습니다",
"payment_method_removed" => "결제 수단이 제거되었습니다.",
"disable" => "비활성화",
"enable" => "활성화",
"rename_payment_method" => "결제 수단 이름 변경",
"payment_renamed" => "결제 수단 이름이 변경되었습니다.",
"payment_not_renamed" => "결제 수단 이름이 변경되지 않았습니다.",
"test" => "테스트",
"add" => "추가",
"save" => "저장",
"reset" => "리셋",
"backup_and_restore" => "백업 및 복구",
"backup" => "백업",
"restore" => "복구",
"restore_info" => "데이터베이스를 복구하면 현재 데이터를 모두 덮어씁니다. 복구 후 로그아웃됩니다.",
// Filters menu
"filter" => "필터",
"clear" => "초기화",
// Toast
"success" => "성공",
// Endpoint responses
"session_expired" => "세션이 만료되었습니다. 다시 로그인 해 주세요",
"fields_missing" => "일부 항목이 누락되었습니다",
"fill_all_fields" => "모든 항목을 채워 주세요",
"fill_mandatory_fields" => "모든 필수 항목을 채워 주세요",
"error" => "에러",
// Category
"failed_add_category" => "통화를 추가하는 데 실패했습니다",
"failed_edit_category" => "카테고리를 수정하는데 실패했습니다",
"category_in_use" => "카테고리가 구독에서 사용중이므로 제거할 수 없습니다",
"failed_remove_category" => "카테고리를 제거하는데 실패했습니다",
"category_saved" => "카테고리가 저장되었습니다",
"category_removed" => "카테고리가 삭제되었습니다",
"sort_order_saved" => "정렬 순서가 저장되었습니다",
// Currency
"currency_saved" => "통화 저장",
"error_adding_currency" => "통화 항목 추가 오류.",
"failed_to_store_currency" => "데이터베이스에 통화를 저장할 수 없습니다.",
"currency_in_use" => "구독에 사용 중인 통화는 삭제할 수 없습니다.",
"currency_is_main" => "기본 통화로 설정된 통화는 삭제할 수 없습니다.",
"failed_to_remove_currency" => "데이터베이스에서 통화를 삭제할 수 없습니다.",
"failed_to_store_api_key" => "데이터베이스에 API 키를 저장할 수 없습니다.",
"invalid_api_key" => "유효하지 않은 API 키.",
"api_key_saved" => "API 성공적으로 저장했습니다",
"currency_removed" => "통화 삭제",
// Household
"failed_add_household" => "가구 구성원을 추가하는데 실패했습니다",
"failed_edit_household" => "가구 구성원을 수정하는데 실패했습니다",
"failed_remove_household" => "가구 구성원을 삭제하는데 실패했습니다",
"household_in_use" => "구독에서 사용 중인 가구 구성원은 삭제할 수 없습니다",
"member_saved" => "구성원 저장",
"member_removed" => "구성원 삭제",
// Notifications
"error_saving_notifications" => "알림 데이터 저장 오류.",
"wallos_notification" => "Wallos 알림",
"test_notification" => "이 메세지는 테스트 알림입니다. 이 메세지를 보고 계시다면, 올바르게 설정된 상태입니다.",
"email_error" => "이메일 전송 오류",
"notification_sent_successfuly" => "알림 전송에 성공했습니다",
"notifications_settings_saved" => "알림 설정 저장 성공.",
"notification_failed" => "알림 전송 실패",
// Payments
"payment_in_use" => "사용 중인 결제 수단은 비활성화 할 수 없습니다",
"failed_update_payment" => "결제 수단을 데이터베이스에 업데이트 하지 못 했습니다",
"enabled" => "활성",
"disabled" => "비활성",
// Subscription
"error_fetching_image" => "이미지 가져오기 오류",
"subscription_updated_successfuly" => "구독이 성공적으로 수정되었습니다",
"subscription_added_successfuly" => "구독이 성공적으로 추가되었습니다",
"error_deleting_subscription" => "구독 삭제 에러.",
"invalid_request_method" => "잘못된 요청 메서드.",
// User
"error_updating_user_data" => "유저 데이터 갱신 실패.",
"user_details_saved" => "유저 세부정보 저장 성공",
// Admin Page
"registrations" => "회원가입",
"enable_user_registrations" => "유저 회원가입 활성화",
"maximum_number_users" => "최대 유저 수",
"require_email_verification" => "이메일 인증 필요",
"configure_smtp_settings_to_enable" => "SMTP 설정을 구성하여 이메일 인증을 활성화합니다.",
"server_url" => "서버 URL",
"server_url_info" => "이메일 인증 및 비밀번호 복구에 사용됩니다. 유효한 공개 URL이어야 합니다.",
"server_url_password_reset" => "설정하면 비밀번호 재설정 기능도 활성화됩니다.",
"disable_login" => "로그인 비활성화",
"disable_login_info" => "로그인 우회. 외부 액세스 없이 로컬 네트워크에서만 서버를 실행하는 경우 로그인을 비활성화할 수 있습니다. 그러면 관리자 사용자가 자동으로 로그인됩니다.",
"disable_login_info2" => "이 설정은 사용자 등록이 해제되어 있고 관리자 사용자 계정이 없는 경우에만 활성화할 수 있습니다.",
"max_users_info" => "0으로 설정하면 무제한으로 설정됩니다.",
"user_management" => "유저 관리",
"delete_user" => "유저 삭제",
"delete_user_info" => "사용자를 삭제하면 모든 구독 및 설정도 삭제됩니다.",
"smtp_settings" => "SMTP 설정",
"smtp_usage_info" => "비밀번호 복구 및 기타 시스템 이메일에 사용됩니다.",
// Email Verification
"email_verified" => "이메일 인증 완료",
"email_verification_failed" => "이메일 인증 실패",
];
?>

View File

@ -1,24 +1,26 @@
<?php
// File Name => Language Name
// File Name => Language Name
$languages = [
// English first
"en" => "English",
"en" => ["name" => "English", "dir" => "ltr"],
// Remaining sorted alphabetically by language code
"de" => "Deutsch",
"el" => "Ελληνικά",
"es" => "Español",
"fr" => "Français",
"it" => "Italiano",
"jp" => "日本語",
"pl" => "Polski",
"pt" => "Português",
"pt_br" => "Português Brasileiro",
"sr_lat" => "Srpski",
"sr" => "Српски",
"tr" => "Türkçe",
"zh_cn" => "简体中文",
"zh_tw" => "繁體中文",
"de" => ["name" => "Deutsch", "dir" => "ltr"],
"el" => ["name" => "Ελληνικά", "dir" => "ltr"],
"es" => ["name" => "Español", "dir" => "ltr"],
"fr" => ["name" => "Français", "dir" => "ltr"],
"it" => ["name" => "Italiano", "dir" => "ltr"],
"jp" => ["name" => "日本語", "dir" => "ltr"],
"ko" => ["name" => "한국어", "dir" => "ltr"],
"pl" => ["name" => "Polski", "dir" => "ltr"],
"pt" => ["name" => "Português", "dir" => "ltr"],
"pt_br" => ["name" => "Português Brasileiro", "dir" => "ltr"],
"ru" => ["name" => "Русский", "dir" => "ltr"],
"sl" => ["name" => "Slovenščina", "dir" => "ltr"],
"sr_lat" => ["name" => "Srpski", "dir" => "ltr"],
"sr" => ["name" => "Српски", "dir" => "ltr"],
"tr" => ["name" => "Türkçe", "dir" => "ltr"],
"zh_cn" => ["name" => "简体中文", "dir" => "ltr"],
"zh_tw" => ["name" => "繁體中文", "dir" => "ltr"],
]
?>
?>

View File

@ -10,6 +10,8 @@ $i18n = [
"main_currency" => "Główna waluta",
"language" => "Język",
"passwords_dont_match" => "Hasła nie pasują",
"username_exists" => "Nazwa użytkownika już istnieje",
"email_exists" => "E-mail już istnieje",
"registration_failed" => "Rejestracja nie powiodła się, spróbuj ponownie.",
"register" => "Rejestracja",
"restore_database" => "Przywróć bazę danych",
@ -18,10 +20,18 @@ $i18n = [
'stay_logged_in' => "Pozostań zalogowany (30 dni)",
'login' => "Zaloguj się",
'login_failed' => "Dane logowania są nieprawidłowe",
'registration_successful' => "Pomyślnie zarejestrowano",
'user_email_waiting_verification' => "Twój adres e-mail musi zostać zweryfikowany. Sprawdź swój adres e-mail",
// Password Reset Page
'forgot_password' => "Zapomniałeś hasła? Kliknij tutaj",
'reset_password' => "Zresetuj hasło",
'reset_sent_check_email' => "Link do zresetowania hasła został wysłany na Twój adres e-mail",
'password_reset_successful' => "Hasło zostało zresetowane pomyślnie",
// Header
'subscriptions' => "Subskrypcje",
'stats' => "Statystyki",
'settings' => "Ustawienia",
'admin' => "Admin",
'about' => "O aplikacji",
'logout' => "Wyloguj się",
// Subscriptions page
@ -73,6 +83,7 @@ $i18n = [
"url" => "URL",
"notes" => "Notatki",
"enable_notifications" => "Włącz powiadomienia dla tej subskrypcji",
"default_value_from_settings" => "Wartość domyślna z ustawień",
"delete" => "Usuń",
"cancel" => "Anuluj",
"upload_logo" => "Prześlij logo",
@ -85,7 +96,11 @@ $i18n = [
'average_monthly' => "Średni miesięczny koszt subskrypcji",
'most_expensive' => "Najdroższy koszt subskrypcji",
'amount_due' => "Kwota należna w tym miesiącu",
'percentage_budget_used' => "Procent wykorzystania budżetu",
'budget_remaining' => "Pozostały budżet",
'amount_over_budget' => "Kwota przekraczająca budżet",
'monthly_savings' => "Miesięczne oszczędności (w przypadku nieaktywnych subskrypcji)",
'yearly_savings' => "Roczne oszczędności (w przypadku nieaktywnych subskrypcji)",
'split_views' => "Podziel widoki",
'category_split' => "Podział kategorii",
'household_split' => "Podział gospodarstwa domowego",
@ -101,7 +116,9 @@ $i18n = [
'upload_avatar' => "Prześlij awatar",
'file_type_error' => "Podany typ pliku nie jest obsługiwany.",
'user_details' => "Szczegóły użytkownika",
"household" => "Gospodarstwo domowe",
"monthly_budget" => "Miesięczny budżet",
"budget_info" => "Jeśli ustawisz budżet, zobaczysz pasek postępu na stronie głównej.",
"household" => "Gospodarstwo domowe",
"save_member" => "Zapisz użytkownika",
"delete_member" => "Usuń użytkownika",
"cant_delete_member" => "Nie można usunąć głównego użytkownika",
@ -120,6 +137,25 @@ $i18n = [
"smtp_password" => "Hasło SMTP",
"from_email" => "Z adresu e-mail (opcjonalnie)",
"smtp_info" => "Hasło SMTP jest przesyłane i przechowywane w postaci zwykłego tekstu. Ze względów bezpieczeństwa utwórz konto tylko w tym celu.",
"telegram" => "Telegram",
"telegram_bot_token" => "Token bota",
"telegram_chat_id" => "ID czatu",
"webhook" => "Webhook",
"webhook_url" => "URL webhooka",
"request_method" => "Metoda żądania",
"custom_headers" => "Niestandardowe nagłówki",
"webhook_payload" => "Dane webhooka",
"webhook_iterator_key" => "Zastąp {{subscriptions}} nazwą klucza",
"variables_available" => "Dostępne zmienne",
"gotify" => "Gotify",
"token" => "Token",
"discord" => "Discord",
"discord_bot_username" => "Nazwa użytkownika bota",
"discord_bot_avatar_url" => "URL awatara bota",
"pushover" => "Pushover",
"pushover_user_key" => "Klucz użytkownika",
'host' => "Host",
'topic' => "Temat",
"categories" => "Kategorie",
"save_category" => "Zapisz kategorię",
"delete_category" => "Usuń kategorię",
@ -213,6 +249,7 @@ $i18n = [
"wallos_notification" => "Powiadomienie Wallos",
"test_notification" => "To jest powiadomienie testowe. Jeśli to widzisz, konfiguracja jest prawidłowa.",
"email_error" => "Błąd podczas wysyłania wiadomości e-mail",
"notification_failed" => "Powiadomienie nie powiodło się",
"notification_sent_successfuly" => "Powiadomienie wysłane pomyślnie",
"notifications_settings_saved" => "Ustawienia powiadomień zostały zapisane.",
// Payments
@ -229,6 +266,27 @@ $i18n = [
// User
"error_updating_user_data" => "Błąd podczas aktualizacji danych użytkownika.",
"user_details_saved" => "Dane użytkownika zostały zapisane",
// Admin Page
"registrations" => "Rejestracje",
"enable_user_registrations" => "Włącz rejestracje użytkowników",
"maximum_number_users" => "Maksymalna liczba użytkowników",
"require_email_verification" => "Wymagaj weryfikacji e-maila",
"configure_smtp_settings_to_enable" => "Skonfiguruj ustawienia SMTP, aby włączyć",
"server_url" => "Adres URL serwera",
"server_url_info" => "Służy do weryfikacji adresu e-mail i odzyskiwania hasła. Musi to być prawidłowy publiczny adres URL.",
"server_url_password_reset" => "Jeśli zostanie ustawiona, włączy również funkcję resetowania hasła.",
"disable_login" => "Wyłącz logowanie",
"disable_login_info" => "Obejście logowania. Jeśli serwer działa tylko w sieci lokalnej, bez dostępu z zewnątrz, można wyłączyć logowanie. Spowoduje to automatyczne zalogowanie użytkownika admin.",
"disable_login_info2" => "To ustawienie można włączyć tylko wtedy, gdy rejestracja użytkowników jest wyłączona i nie ma więcej niż konto administratora.",
"max_users_info" => "Jeśli ustawisz 0, nie będzie limitu użytkowników.",
"user_management" => "Zarządzanie użytkownikami",
"delete_user" => "Usuń użytkownika",
"delete_user_info" => "Usunięcie użytkownika spowoduje również usunięcie wszystkich jego subskrypcji i ustawień.",
"smtp_settings" => "Ustawienia SMTP",
"smtp_usage_info" => "Będzie używany do odzyskiwania hasła i innych e-maili systemowych.",
// Email Verification
"email_verified" => "E-mail został zweryfikowany",
"email_verification_failed" => "Weryfikacja e-maila nie powiodła się",
];

View File

@ -10,6 +10,8 @@ $i18n = [
"main_currency" => "Moeda Principal",
"language" => "Linguagem",
"passwords_dont_match" => "As passwords não coincidem",
"username_exists" => "Nome de utilizador já existe",
"email_exists" => "Email já existe",
"registration_failed" => "O registo falhou. Tente novamente",
"register" => "Registar",
"restore_database" => "Restaurar base de dados",
@ -18,10 +20,18 @@ $i18n = [
'stay_logged_in' => "Manter sessão (30 dias)",
'login' => "Iniciar Sessão",
'login_failed' => "Dados de autenticação incorrectos",
'registration_successful' => "Registo efectuado com sucesso.",
'user_email_waiting_verification' => "O seu e-mail precisa de ser validado. Verifique o seu correio eletrónico",
// Password Reset Page
'forgot_password' => "Esqueceu-se da password?",
'reset_password' => "Repor Password",
'reset_sent_check_email' => "Pedido de reposição de password enviado. Verifique o seu email.",
'password_reset_successful' => "Password reposta com sucesso",
// Header
'subscriptions' => "Subscrições",
'stats' => "Estatísticas",
'settings' => "Definições",
'admin' => "Administração",
'about' => "Sobre",
'logout' => "Terminar Sessão",
// Subscriptions page
@ -73,6 +83,7 @@ $i18n = [
"url" => "URL",
"notes" => "Notas",
"enable_notifications" => "Activar notificações para esta subscrição",
"default_value_from_settings" => "Valor por defeito das definições",
"delete" => "Remover",
"cancel" => "Cancelar",
"upload_logo" => "Enviar Logo",
@ -85,7 +96,11 @@ $i18n = [
'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",
'percentage_budget_used' => "Percentagem do orçamento usada",
'budget_remaining' => "Orçamento Restante",
'amount_over_budget' => "Quantia acima do orçamento",
'monthly_savings' => "Poupança Mensal (em subscrições inactivas)",
'yearly_savings' => "Poupança Anual (em subscrições inactivas)",
'split_views' => "Vistas Divididas",
'category_split' => "Por Categoria",
'household_split' => "Por Membro",
@ -101,7 +116,9 @@ $i18n = [
'upload_avatar' => "Enviar avatar",
'file_type_error' => "Tipo de ficheiro não permitido",
'user_details' => "Detalhes do utilizador",
"household" => "Agregado",
"monthly_budget" => "Orçamento Mensal",
"budget_info" => "Ao definir um orçamento pode comparar com os gastos reais na página de estatísticas.",
"household" => "Agregado",
"save_member" => "Guardar Membro",
"delete_member" => "Apagar Membro",
"cant_delete_member" => "Não pode apagar o membro principal",
@ -120,6 +137,25 @@ $i18n = [
"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.",
"telegram" => "Telegram",
"telegram_bot_token" => "Token do Bot Telegram",
"telegram_chat_id" => "ID do Chat Telegram",
"webhook" => "Webhook",
"webhook_url" => "URL do Webhook",
"request_method" => "Método de Pedido",
"custom_headers" => "Cabeçalhos Personalizados",
"webhook_payload" => "Payload do Webhook",
"webhook_iterator_key" => "Substituir {{subscriptions}} pelo nome da chave",
"variables_available" => "Variáveis Disponíveis",
"gotify" => "Gotify",
"token" => "Token",
"discord" => "Discord",
"discord_bot_username" => "Nome de Utilizador do Bot",
"discord_bot_avatar_url" => "URL do Avatar do Bot",
"pushover" => "Pushover",
"pushover_user_key" => "Chave de Utilizador Pushover",
'host' => "Host",
'topic' => "Tópico",
"categories" => "Categorias",
"save_category" => "Guardar Categoria",
"delete_category" => "Apagar Categoria",
@ -215,6 +251,7 @@ $i18n = [
"email_error" => "Erro ao enviar email",
"notification_sent_successfuly" => "Notificação enviada com sucesso",
"notifications_settings_saved" => "Configuração de notificações guardada.",
"notification_failed" => "Erro ao enviar notificação",
// 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",
@ -229,6 +266,27 @@ $i18n = [
// User
"error_updating_user_data" => "Erro ao actualizar dados do utilizador.",
"user_details_saved" => "Dados do utiliador actualizados.",
// Admin Page
"registrations" => "Registos",
"enable_user_registrations" => "Activar Registos de Utilizadores",
"maximum_number_users" => "Número Máximo de Utilizadores",
"require_email_verification" => "Requerer verificação de email",
"configure_smtp_settings_to_enable" => "Configure as definições SMTP para activar esta funcionalidade.",
"server_url" => "URL do Servidor",
"server_url_info" => "Usado para gerar links de verificação de email. Deve ser um URL público e válido.",
"server_url_password_reset" => "Se definido, também activará a funcionalidade de reposição da palavra-passe.",
"disable_login" => "Desactivar Inicio de Sessão",
"disable_login_info" => "Ultrapassar o início de sessão. Se o seu servidor funciona apenas numa rede local, sem acesso externo, pode desativar o início de sessão. Isto irá iniciar automaticamente a sessão do utilizador administrador.",
"disable_login_info2" => "Só pode ativar esta definição se o registo de utilizadores estiver desativado e se não houver mais do que a conta de utilizador administrador.",
"max_users_info" => "0 para ilimitado",
"user_management" => "Gestão de Utilizadores",
"delete_user" => "Apagar Utilizador",
"delete_user_info" => "Apagar utilizador irá remover todas as suas subscrições e dados associados.",
"smtp_settings" => "Definições SMTP",
"smtp_usage_info" => "Será usado para recuperações de password e outros emails do sistema.",
// Email Verification
"email_verified" => "Email verificado",
"email_verification_failed" => "Verificação de email falhou",
];

View File

@ -10,6 +10,8 @@ $i18n = [
"main_currency" => "Moeda principal",
"language" => "Idioma",
"passwords_dont_match" => "As senhas não são iguais",
"username_exists" => "O nome de usuário já existe",
"email_exists" => "O email já está em uso",
"registration_failed" => "O registro falhou. Por favor, tente novamente",
"register" => "Registrar",
"restore_database" => "Restaurar banco de dados",
@ -18,10 +20,18 @@ $i18n = [
'stay_logged_in' => "Me manter logado (30 dias)",
'login' => "Login",
'login_failed' => "As informações de login estão incorretas",
'registration_successful' => "Registro bem-sucedido",
'user_email_waiting_verification' => "Seu e-mail precisa ser validado. Por favor, verifique seu e-mail",
// Password Reset Page
'forgot_password' => "Esqueceu a senha?",
'reset_password' => "Redefinir senha",
'reset_sent_check_email' => "Redefinição de senha enviada. Por favor, verifique seu email",
'password_reset_successful' => "Senha redefinida com sucesso",
// Header
'subscriptions' => "Assinaturas",
'stats' => "Estatísticas",
'settings' => "Configurações",
'admin' => "Admin",
'about' => "Sobre",
'logout' => "Sair",
// Subscriptions page
@ -71,6 +81,7 @@ $i18n = [
"url" => "URL",
"notes" => "Anotações",
"enable_notifications" => "Ativar notificações para essa assinatura",
"default_value_from_settings" => "Valor padrão das configurações",
"delete" => "Excluir",
"cancel" => "Cancelar",
"upload_logo" => "Enviar Logo",
@ -83,7 +94,11 @@ $i18n = [
'average_monthly' => "Custom médio mensal",
'most_expensive' => "Assinatura mais cara",
'amount_due' => "Valor devido nesse mês",
'percentage_budget_used' => "Porcentagem do orçamento utilizado",
'budget_remaining' => "Orçamento restante",
'amount_over_budget' => "Valor acima do orçamento",
'monthly_savings' => "Economia mensal (em assinaturas inativas)",
'yearly_savings' => "Economia anual (em assinaturas inativas)",
'split_views' => "Visualizações",
'category_split' => "Por categoria",
'household_split' => "Por membro",
@ -99,7 +114,9 @@ $i18n = [
'upload_avatar' => "Carregar avatar",
'file_type_error' => "Tipo de arquivo não permitido",
'user_details' => "Informações do Usuário",
"household" => "Membros",
"monthly_budget" => "Orçamento mensal",
"budget_info" => "O orçamento mensal é usado para calcular estatísticas",
"household" => "Membros",
"save_member" => "Salvar membro",
"delete_member" => "Excluir membro",
"cant_delete_member" => "Não é possível excluir o membro principal",
@ -118,6 +135,25 @@ $i18n = [
"smtp_password" => "Senha SMTP",
"from_email" => "Email de envio (Opcional)",
"smtp_info" => "A senha do SMTP é transmitida em texto puro. Por segurança, crie uma conta só para esta finalidade.",
"telegram" => "Telegram",
"telegram_bot_token" => "Token do Bot",
"telegram_chat_id" => "Chat ID",
"webhook" => "Webhook",
"webhook_url" => "URL do Webhook",
"request_method" => "Método de requisição",
"custom_headers" => "Cabeçalhos personalizados",
"webhook_payload" => "Payload do Webhook",
"webhook_iterator_key" => "Substituir {{subscriptions}} pelo nome da chave",
"variables_available" => "Variáveis disponíveis",
"gotify" => "Gotify",
"token" => "Token",
"discord" => "Discord",
"discord_bot_username" => "Nome do Bot",
"discord_bot_avatar_url" => "URL do Avatar",
"pushover" => "Pushover",
"pushover_user_key" => "Chave do Usuário",
'host' => "Host",
'topic' => "Tópico",
"categories" => "Categorias",
"save_category" => "Salvar categoria",
"delete_category" => "Excluir categoria",
@ -213,6 +249,7 @@ $i18n = [
"email_error" => "Erro ao enviar email",
"notification_sent_successfuly" => "Notificação enviada com sucesso",
"notifications_settings_saved" => "Configurações de notificação salvas com sucesso",
"notification_failed" => "Falha ao enviar notificação",
// Payments
"payment_in_use" => "Não é possível desativar o método de pagamento",
"failed_update_payment" => "Erro ao atualizar o método de pagamento no banco de dados.",
@ -227,6 +264,27 @@ $i18n = [
// User
"error_updating_user_data" => "Erro ao atualizar os dados do usuário",
"user_details_saved" => "Dados do usuário salvos",
// Admin Page
"registrations" => "Registros",
"enable_user_registrations" => "Ativar registros de usuários",
"maximum_number_users" => "Número máximo de usuários",
"require_email_verification" => "Requerer verificação de email",
"configure_smtp_settings_to_enable" => "Configure as configurações SMTP para ativar o envio de email",
"server_url" => "URL do servidor",
"server_url_info" => "Será usado para gerar links de verificação de email, deve ser um endereço público e válido.",
"server_url_password_reset" => "Se definido, também ativará a funcionalidade de redefinição de senha.",
"disable_login" => "Desativar login",
"disable_login_info" => "Ignorar login. Se você executar o servidor somente em uma rede local, sem acesso externo, poderá desativar o login. Isso fará o login automático do usuário administrador.",
"disable_login_info2" => "Só é possível ativar essa configuração se o registro de usuário estiver desativado e não houver mais do que a conta de usuário administrador.",
"max_users_info" => "0 para ilimitado",
"user_management" => "Gerenciamento de usuários",
"delete_user" => "Excluir usuário",
"delete_user_info" => "Excluir um usuário também excluirá todas as assinaturas e dados associados",
"smtp_settings" => "Configurações SMTP",
"smtp_usage_info" => "Será usado para recuperação de senha e outros e-mails do sistema.",
// Email Verification
"email_verified" => "Email verificado",
"email_verification_failed" => "Falha na verificação do email",
];

294
includes/i18n/ru.php Normal file
View File

@ -0,0 +1,294 @@
<?php
$i18n = [
// Registration page
"create_account" => "Вам необходимо создать учетную запись, прежде чем вы сможете войти в систему",
'username' => "Имя пользователя",
'password' => "Пароль",
"email" => "E-mail",
"confirm_password" => "Подтвердите пароль",
"main_currency" => "Основная валюта",
"language" => "Язык",
"passwords_dont_match" => "Пароли не совпадают",
"username_exists" => "Имя пользователя уже существует",
"email_exists" => "E-mail уже существует",
"registration_failed" => "Регистрация не удалась, попробуйте еще раз.",
"register" => "Регистрация",
"restore_database" => "Восстановить базу данных",
// Login Page
'please_login' => "Пожалуйста, войдите",
'stay_logged_in' => "Оставаться в системе (30 дней)",
'login' => "Авторизоваться",
'login_failed' => "Данные для входа неверны",
'registration_successful' => "Регистрация прошла успешно",
'user_email_waiting_verification' => "Ваша электронная почта нуждается в проверке. Пожалуйста, проверьте свою электронную почту",
// Password Reset Page
'forgot_password' => "Забыли пароль?",
'reset_password' => "Сбросить пароль",
'reset_sent_check_email' => "Ссылка для сброса пароля отправлена на вашу электронную почту",
'password_reset_successful' => "Пароль успешно сброшен",
// Header
'subscriptions' => "Подписки",
'stats' => "Статистика",
'settings' => "Настройки",
'admin' => "Администратор",
'about' => "О программе",
'logout' => "Выйти",
// Subscriptions page
"subscription" => "Подписка",
"no_subscriptions_yet" => "У вас пока нет подписок",
"add_first_subscription" => "Добавить первую подписку",
'new_subscription' => "Новая подписка",
'search' => "Поиск",
'sort' => "Сортировка",
'name' => "Имя",
'last_added' => "Дата создания",
'price' => "Стоимость",
'next_payment' => "Следующий платеж",
'inactive' => "Отключить подписку",
'member' => "Член семьи",
'category' => "Категория",
'payment_method' => "Способ оплаты",
"Daily" => "День",
"Weekly" => "Неделя",
"Monthly" => "Месяц",
"Yearly" => "Год",
"daily" => "День",
"weekly" => "Неделя",
"monthly" => "Месяц",
"yearly" => "Год",
"days" => "дней",
"weeks" => "недель",
"months" => "месяцев",
"years" => "года",
"external_url" => "Посетите внешний URL-адрес",
"empty_page" => "Пустая страница",
"clear_filters" => "Очистить фильтры",
"no_matching_subscriptions" => "Нет подходящих подписок",
// Subscription form
"add_subscription" => "Добавить подписку",
"edit_subscription" => "Изменить подписку",
"subscription_name" => "Название подписки",
"logo_preview" => "Предварительный просмотр логотипа",
"search_logo" => "Поиск логотипа в Интернете",
"web_search" => "Веб-поиск",
"currency" => "Валюта",
"payment_every" => "Оплата каждые",
"frequency" => "Частота",
"cycle" => "Цикл",
"next_payment" => "Следующий платеж",
"payment_method" => "Способ оплаты",
"no_category" => "Нет категории",
"paid_by" => "Оплачивает",
"url" => "URL",
"notes" => "Примечания",
"enable_notifications" => "Включить уведомления для этой подписки",
"default_value_from_settings" => "Значение по умолчанию из настроек",
"delete" => "Удалить",
"cancel" => "Отмена",
"upload_logo" => "Загрузить логотип",
// Statistics page
'general_statistics' => "Общая статистика",
'active_subscriptions' => "Активные подписки",
'inactive_subscriptions' => "Неактивные подписки",
'monthly_cost' => "Ежемесячная стоимость",
'yearly_cost' => "Годовая стоимость",
'average_monthly' => "Средняя ежемесячная стоимость подписки",
'most_expensive' => "Самая дорогая стоимость подписки",
'amount_due' => "Сумма к оплате в этом месяце",
'percentage_budget_used' => "Процент использования бюджета",
'budget_remaining' => "Оставшийся бюджет",
'amount_over_budget' => "Сумма превышения бюджета",
'monthly_savings' => "Ежемесячная экономия (при неактивных подписках)",
'yearly_savings' => "Годовая экономия (при неактивных подписках)",
'split_views' => "Подробная статистика",
'category_split' => "По категориям",
'household_split' => "По членам семьи",
'payment_method_split' => "По способам оплаты",
// About page
'about_and_credits' => "О компании и авторах",
'license' => "Лицензия",
'issues_and_requests' => "Проблемы и вопросы",
'the_author' => "Автор",
'icons' => "Значки",
'payment_icons' => "Значки способов оплаты",
// Settings page
'upload_avatar' => "Загрузить аватар",
'file_type_error' => "Указанный тип файла не поддерживается.",
'user_details' => "Данные пользователя",
"monthly_budget" => "Ежемесячный бюджет",
"budget_info" => "Если вы укажете бюджет, Wallos будет отображать вашу текущую стоимость подписок в сравнении с вашим бюджетом.",
"household" => "Семья",
"save_member" => "Сохранить члена семьи",
"delete_member" => "Удалить члена семьи",
"cant_delete_member" => "Не могу удалить основного члена семьи",
"cant_delete_member_in_use" => "Невозможно удалить члена семьи, используемого в подписке.",
'household_info' => "Поле электронной почты позволяет членам семьи получать уведомления об истечении срока действия подписки.",
"notifications" => "Уведомления",
"enable_email_notifications" => "Включить уведомления по электронной почте",
"notify_me" => "Уведомить меня за",
"day_before" => "день до события",
"days_before" => "дня(дней) до события",
"smtp_address" => "SMTP-адрес",
"port" => "Порт",
"tls" => "TLS",
"ssl" => "SSL",
"smtp_username" => "Имя пользователя SMTP",
"smtp_password" => "Пароль SMTP",
"from_email" => "От кого E-Mail (необязательно)",
"smtp_info" => "Пароль SMTP передается и сохраняется в виде открытого текста. В целях безопасности создайте учетную запись только для Wallos.",
"telegram" => "Telegram",
"telegram_bot_token" => "Токен Telegram-бота",
"telegram_chat_id" => "Telegram Chat ID",
"webhook" => "Webhook",
"webhook_url" => "Webhook URL",
"request_method" => "Метод запроса",
"custom_headers" => "Пользовательские заголовки",
"webhook_payload" => "Webhook Payload",
"webhook_iterator_key" => "Заменить {{subscriptions}} именем ключа.",
"variables_available" => "Доступные переменные",
"gotify" => "Gotify",
"token" => "Токен",
"discord" => "Discord",
"discord_bot_username" => "Имя пользователя бота Discord",
"discord_bot_avatar_url" => "URL-адрес аватара бота Discord",
"pushover" => "Pushover",
"pushover_user_key" => "Ключ пользователя Pushover",
'host' => "Хост",
'topic' => "Тема",
"categories" => "Категории",
"save_category" => "Сохранить категорию",
"delete_category" => "Удалить категорию",
"cant_delete_category_in_use" => "Невозможно удалить категорию, используемую в подписке.",
"currencies" => "Валюты",
"save_currency" => "Сохранить валюту",
"delete_currency" => "Удалить валюту",
"cant_delete_main_currency" => "Не могу удалить основную валюту",
"cant_delete_currency_in_use" => "Невозможно удалить валюту, используемую в подписке.",
"exchange_update" => "Курсы валют последний раз обновлялись",
"currency_info" => "Найдите поддерживаемые валюты и правильные коды валют на",
"currency_performance" => "Для повышения производительности сохраняйте только те валюты, которые вы используете.",
"fixer_api_key" => "Ключ Fixer API",
"api_key" => "API ключ",
"provider" => "Провайдер",
"fixer_info" => "Если вы используете несколько валют и хотите получить точную статистику и сортировку подписок, вам необходим БЕСПЛАТНЫЙ ключ API от Fixer.",
"get_key" => "Получите ключ по адресу",
"get_free_fixer_api_key" => "Получите бесплатный ключ API Fixer",
"get_key_alternative" => "Кроме того, вы можете получить бесплатный ключ API Fixer на сайте",
"display_settings" => "Настройки отображения",
"theme_settings" => "Настройки темы",
"custom_colors" => "Пользовательские цвета",
"dark_theme" => "Темная тема",
"switch_theme" => "Переключить светлую/темную тему",
"calculate_monthly_price" => "Рассчитать и показать ежемесячную цену для всех подписок",
"convert_prices" => "Всегда конвертировать и показывать цены в моей основной валюте (медленнее)",
"hide_disabled_subscriptions" => "Скрыть отключенные подписки",
"experimental_settings" => "Экспериментальные настройки",
"remove_background" => "Попытка удалить фон логотипов из поиска изображений (экспериментально).",
"experimental_info" => "Экспериментальные настройки, вероятно, не будут работать идеально.",
"payment_methods" => "Способы оплаты",
"payment_methods_info" => "Нажмите на способ оплаты, чтобы отключить/включить его.",
"rename_payment_methods_info" => "Нажмите на название способа оплаты, чтобы переименовать его.",
"cant_delete_payment_method_in_use" => "Невозможно отключить используемый способ оплаты",
"add_custom_payment" => "Добавить собственный способ оплаты",
"payment_method_name" => "Название способа оплаты",
"payment_method_added_successfuly" => "Способ оплаты успешно добавлен",
"payment_method_removed" => "Способ оплаты удален.",
"disable" => "Отключить",
"enable" => "Включить",
"rename_payment_method" => "Переименовать способ оплаты",
"payment_renamed" => "Способ оплаты переименован",
"payment_not_renamed" => "Способ оплаты не переименован",
"test" => "Тест",
"add" => "Добавить",
"save" => "Сохранить",
"reset" => "Перезагрузить",
"backup_and_restore" => "Резервное копирование и восстановление",
"backup" => "Резервное копирование",
"restore" => "Восстановление",
"restore_info" => "Восстановление базы данных отменит все текущие данные. После восстановления вы выйдете из системы.",
// Filters menu
"filter" => "Фильтр",
"clear" => "Очистить",
// Toast
"success" => "Успешно",
// Endpoint responses
"session_expired" => "Срок действия вашей сессии истек. Пожалуйста, войдите снова",
"fields_missing" => "Некоторые поля отсутствуют",
"fill_all_fields" => "Пожалуйста заполните все поля",
"fill_mandatory_fields" => "Пожалуйста, заполните все обязательные поля",
"error" => "Ошибка",
// Category
"failed_add_category" => "Не удалось добавить категорию",
"failed_edit_category" => "Не удалось изменить категорию",
"category_in_use" => "Категория используется в подписках и не может быть удалена.",
"failed_remove_category" => "Не удалось удалить категорию",
"category_saved" => "Категория сохранена",
"category_removed" => "Категория удалена",
"sort_order_saved" => "Порядок сортировки сохранен.",
// Currency
"currency_saved" => "сохранено.",
"error_adding_currency" => "Ошибка добавления валюты.",
"failed_to_store_currency" => "Не удалось сохранить валюту в базе данных.",
"currency_in_use" => "Валюта используется в подписках и не может быть удалена.",
"currency_is_main" => "Валюта установлена ​​в качестве основной и не может быть удалена.",
"failed_to_remove_currency" => "Не удалось удалить валюту из базы данных.",
"failed_to_store_api_key" => "Не удалось сохранить ключ API в базе данных.",
"invalid_api_key" => "Неверный ключ API.",
"api_key_saved" => "Ключ API успешно сохранен",
"currency_removed" => "Валюта удалена",
// Household
"failed_add_household" => "Не удалось добавить члена семьи.",
"failed_edit_household" => "Не удалось изменить члена семьи.",
"failed_remove_household" => "Не удалось удалить члена семьи.",
"household_in_use" => "Член семьи используется в подписках и не может быть удален.",
"member_saved" => "Член семьи сохранен",
"member_removed" => "Член семьи удален",
// Notifications
"error_saving_notifications" => "Ошибка сохранения данных уведомлений.",
"wallos_notification" => "Уведомление от Wallos",
"test_notification" => "Это тестовое уведомление. Если вы видите это, значит, конфигурация правильная.",
"email_error" => "Ошибка отправки электронной почты",
"notification_sent_successfuly" => "Уведомление успешно отправлено",
"notifications_settings_saved" => "Настройки уведомлений успешно сохранены.",
"notification_failed" => "Уведомление не удалось",
// Payments
"payment_in_use" => "Невозможно отключить используемый способ оплаты",
"failed_update_payment" => "Не удалось обновить способ оплаты в базе данных.",
"enabled" => "включено",
"disabled" => "отключено",
// Subscription
"error_fetching_image" => "Ошибка при загрузке изображения.",
"subscription_updated_successfuly" => "Подписка успешно обновлена",
"subscription_added_successfuly" => "Подписка успешно добавлена",
"error_deleting_subscription" => "Ошибка удаления подписки.",
"invalid_request_method" => "Неверный метод запроса.",
// User
"error_updating_user_data" => "Ошибка обновления пользовательских данных.",
"user_details_saved" => "Данные пользователя сохранены.",
// Admin Page
"registrations" => "Регистрации",
"enable_user_registrations" => "Включить регистрацию пользователей",
"maximum_number_users" => "Максимальное количество пользователей",
"require_email_verification" => "Требовать подтверждение по электронной почте",
"configure_smtp_settings_to_enable" => "Настройте SMTP, чтобы включить эту функцию.",
"server_url" => "URL-адрес сервера",
"server_url_info" => "Используется для проверки электронной почты и восстановления пароля. Должен быть действительным публичным URL.",
"server_url_password_reset" => "Если этот параметр установлен, он также включает функцию сброса пароля.",
"disable_login" => "Отключить вход",
"disable_login_info" => "Обход входа в систему. Если вы используете свой сервер только в локальной сети, без доступа извне, вы можете отключить вход в систему. При этом будет автоматически входить пользователь admin.",
"disable_login_info2" => "Этот параметр можно включить только в том случае, если регистрация пользователей отключена и их количество не превышает учетную запись администратора.",
"max_users_info" => "Установите 0 для неограниченного количества пользователей.",
"user_management" => "Управление пользователями",
"delete_user" => "Удалить пользователя",
"delete_user_info" => "Удаление пользователя также приведет к удалению всех его подписок и настроек.",
"smtp_settings" => "Настройки SMTP",
"smtp_usage_info" => "Будет использоваться для восстановления пароля и других системных писем.",
// Email Verification
"email_verified" => "Ваш адрес электронной почты подтвержден. Теперь вы можете войти.",
"email_verification_failed" => "Не удалось подтвердить ваш адрес электронной почты.",
];
?>

294
includes/i18n/sl.php Normal file
View File

@ -0,0 +1,294 @@
<?php
$i18n = [
// Registration page
"create_account" => "Preden se lahko prijavite, morate ustvariti račun",
'username' => "Uporabniško ime",
'password' => "Geslo",
"email" => "E-pošta",
"confirm_password" => "Potrdite geslo",
"main_currency" => "Glavna valuta",
"language" => "Jezik",
"passwords_dont_match" => "Gesli se ne ujema",
"username_exists" => "Uporabniško ime že obstaja",
"email_exists" => "E-pošta že obstaja",
"registration_failed" => "Registracija ni uspela, poskusite znova.",
"register" => "Registrirajte se",
"restore_database" => "Obnovi bazo podatkov",
// Login Page
'please_login' => "Prosim prijavite se",
'stay_logged_in' => "Ostanite prijavljeni (30 dni)",
'login' => "Prijava",
'login_failed' => "Podatki za prijavo so napačni",
'registration_successful' => "Registracija uspešna",
'user_email_waiting_verification' => "Vaš e-poštni naslov je treba preveriti. Prosim, preglejte vašo e-pošto.",
// Password Reset Page
'forgot_password' => "Ste pozabili geslo",
'reset_password' => "Ponastavitev gesla",
'reset_sent_check_email' => "E-pošta ponastavitev gesla je bila poslana. Prosim, preglejte vašo e-pošto.",
'password_reset_successful' => "Ponastavitev gesla je uspela",
// Header
'subscriptions' => "Naročnine",
'stats' => "Statistika",
'settings' => "Nastavitve",
'admin' => "Skrbnik",
'about' => "O programu",
'logout' => "Odjava",
// Subscriptions page
"subscription" => "Naročnina",
"no_subscriptions_yet" => "Nimate še nobene naročnine",
"add_first_subscription" => "Dodaj prvo naročnino",
'new_subscription' => "Nova naročnina",
'search' => "Iskanje",
'sort' => "Razvrsti",
'name' => "Ime",
'last_added' => "Zadnje dodano",
'price' => "Cena",
'next_payment' => "Naslednje plačilo",
'inactive' => "Onemogoči naročnino",
'member' => "Član",
'category' => "Kategorija",
'payment_method' => "Način plačila",
"Daily" => "Dnevno",
"Weekly" => "Tedensko",
"Monthly" => "Mesečno",
"Yearly" => "Letno",
"daily" => "Dan (dni)",
"weekly" => "Teden (tednov)",
"monthly" => "Mesec (mesecev)",
"yearly" => "Leto (leta)",
"days" => "dnevi",
"weeks" => "tedni",
"months" => "meseci",
"years" => "leta",
"external_url" => "Obiščite zunanji URL",
"empty_page" => "Prazna stran",
"clear_filters" => "Počisti filter",
"no_matching_subscriptions" => "Ni ustreznih naročnin",
// Subscription form
"add_subscription" => "Dodaj naročnino",
"edit_subscription" => "Uredi naročnino",
"subscription_name" => "Ime naročnine",
"logo_preview" => "Predogled logotipa",
"search_logo" => "Poišči logotip v spletu",
"web_search" => "Spletno iskanje",
"currency" => "Valuta",
"payment_every" => "Plačilo vsakih",
"frequency" => "Ponavljanje",
"cycle" => "cikel",
"next_payment" => "Naslednje plačilo",
"payment_method" => "Način plačila",
"no_category" => "Brez kategorije",
"paid_by" => "Plačal",
"url" => "URL",
"notes" => "Opombe",
"enable_notifications" => "Omogoči obvestila za to naročnino",
"default_value_from_settings" => "Privzeta vrednost iz nastavitev",
"delete" => "Izbriši",
"cancel" => "Prekliči",
"upload_logo" => "Naloži logotip",
// Statistics page
'general_statistics' => "Splošna statistika",
'active_subscriptions' => "Aktivne naročnine",
'inactive_subscriptions' => "Neaktivne naročnine",
'monthly_cost' => "Mesečni stroški",
'yearly_cost' => "Letni stroški",
'average_monthly' => "Povprečni mesečni stroški naročnine",
'most_expensive' => "Najdražja cena naročnine",
'amount_due' => "Zapadli znesek ta mesec",
'percentage_budget_used' => "Odstotek porabljenega proračuna",
'budget_remaining' => "Preostali proračun",
'amount_over_budget' => "Znesek nad proračunom",
'monthly_savings' => "Mesečni prihranek (pri neaktivnih naročninah)",
'yearly_savings' => "Letni prihranki (pri neaktivnih naročninah)",
'split_views' => "Razdeljeni pogledi",
'category_split' => "Razdelitev kategorije",
'household_split' => "Razdelitev gospodinjstva",
'payment_method_split' => "Razdelitev načina plačila",
// About page
'about_and_credits' => "O programu in zahvale",
'license' => "Licenca",
'issues_and_requests' => "Težave in zahteve",
'the_author' => "Avtor",
'icons' => "Ikone",
'payment_icons' => "Ikone plačil",
// Settings page
'upload_avatar' => "Naloži avatar",
'file_type_error' => "Vrsta datoteke ni podprta.",
'user_details' => "Podrobnosti o uporabniku",
"monthly_budget" => "Mesečni proračun",
"budget_info" => "Mesečni proračun se uporablja za izračun statistike",
"household" => "Gospodinjstvo",
"save_member" => "Shrani člana",
"delete_member" => "Izbriši člana",
"cant_delete_member" => "Ne morem izbrisati glavnega člana",
"cant_delete_member_in_use" => "Ne morem izbrisati člana, ki je v uporabi v naročnini",
'household_info' => "E-poštno polje omogoča članom gospodinjstva, da so obveščeni o naročninah, ki bodo potekle.",
"notifications" => "Obvestila",
"enable_email_notifications" => "Omogoči e-poštna obvestila",
"notify_me" => "Obvesti me",
"day_before" => "dan prej",
"days_before" => "dni prej",
"smtp_address" => "SMTP naslov",
"port" => "vrata",
"tls" => "TLS",
"ssl" => "SSL",
"smtp_username" => "Uporabniško ime SMTP",
"smtp_password" => "Geslo SMTP",
"from_email" => "Iz e-pošte (izbirno)",
"smtp_info" => "Geslo SMTP se prenaša in shranjuje v navadnem besedilu. Zaradi varnosti ustvarite račun samo za to.",
"telegram" => "Telegram",
"telegram_bot_token" => "Telegram Bot žeton",
"telegram_chat_id" => "ID klepeta Telegrama",
"webhook" => "Webhook",
"webhook_url" => "Webhook URL",
"request_method" => "Metoda zahteve",
"custom_headers" => "Glave po meri",
"webhook_payload" => "Webhook Payload",
"webhook_iterator_key" => "Zamenjaj {{subscriptions}} z imenom ključa",
"variables_available" => "Spremenljivke, ki so na voljo",
"gotify" => "Gotify",
"token" => "Žeton",
"discord" => "Discord",
"discord_bot_username" => "Uporabniško ime za Discord Bot",
"discord_bot_avatar_url" => "URL avatarja Discordovega bota",
"pushover" => "Pushover",
"pushover_user_key" => "Uporabniški ključ Pushover",
'host' => "Gostitelj",
'topic' => "Tema",
"categories" => "Kategorije",
"save_category" => "Shrani kategorijo",
"delete_category" => "Izbriši kategorijo",
"cant_delete_category_in_use" => "Kategorija, ki se uporablja v naročnini, ni mogoče izbrisati",
"currencies" => "Valute",
"save_currency" => "Shrani valuto",
"delete_currency" => "Izbriši valuto",
"cant_delete_main_currency" => "Glavne valute ni mogoče izbrisati",
"cant_delete_currency_in_use" => "V naročnini ni mogoče izbrisati valute, ki je v uporabi",
"exchange_update" => "Menjalni tečaji so bili zadnjič posodobljeni dne",
"currency_info" => "Poiščite podprte valute in pravilne kode valut na",
"currency_performance" => "Za izboljšano delovanje obdržite samo valute, ki jih uporabljate.",
"fixer_api_key" => "API ključ za Fixer",
"api_key" => "Fixer API",
"provider" => "Ponudnik",
"fixer_info" => "Če uporabljate več valut in želite natančno statistiko in razvrščanje naročnin, potrebujete BREZPLAČNI API ključ od Fixerja.",
"get_key" => "Pridobite svoj ključ pri",
"get_free_fixer_api_key" => "Pridobite brezplačen ključ API Fixer",
"get_key_alternative" => "Lahko pa tudi dobite brezplačni Fixer API od",
"display_settings" => "Nastavitve zaslona",
"theme_settings" => "Nastavitve teme",
"custom_colors" => "Barve po meri",
"dark_theme" => "Temna tema",
"switch_theme" => "Preklop med svetlo/temno temo",
"calculate_monthly_price" => "Izračunaj in prikaži mesečno ceno za vse naročnine",
"convert_prices" => "Vedno pretvori in prikaži cene v moji glavni valuti (počasneje)",
"hide_disabled_subscriptions" => "Skrij onemogočene naročnine",
"experimental_settings" => "Eksperimentalne nastavitve",
"remove_background" => "Poskusi odstraniti ozadje logotipov iz iskanja slik (eksperimentalno)",
"experimental_info" => "Poskusne nastavitve verjetno ne bodo popolnoma delovale.",
"payment_methods" => "Načini plačila",
"payment_methods_info" => "Kliknite način plačila, da ga onemogočite/omogočite.",
"rename_payment_methods_info" => "Kliknite ime plačilnega sredstva, da ga preimenujete.",
"cant_delete_payment_method_in_use" => "Uporabljenega plačilnega sredstva ni mogoče onemogočiti",
"add_custom_payment" => "Dodaj način plačila po meri",
"payment_method_name" => "Ime plačilnega sredstva",
"payment_method_added_successfuly" => "Plačilno sredstvo je uspešno dodano",
"payment_method_removed" => "Plačilno sredstvo odstranjeno",
"disable" => "Onemogoči",
"enable" => "Omogoči",
"rename_payment_method" => "Preimenuj način plačila",
"payment_renamed" => "Plačilno sredstvo je preimenovano",
"payment_not_renamed" => "Način plačila ni preimenovan",
"test" => "Test",
"add" => "Dodaj",
"save" => "Shrani",
"reset" => "Ponastavi",
"backup_and_restore" => "Varnostno kopiranje in obnovitev",
"backup" => "Varnostna kopija",
"restore" => "Obnovitev",
"restore_info" => "Obnovitev baze podatkov bo prepisala vse trenutne podatke. Po obnovitvi boste odjavljeni.",
// Filters menu
"filter" => "Filter",
"clear" => "Počisti",
// Toast
"success" => "Uspeh",
// Endpoint responses
"session_expired" => "Vaša seja je potekla. Ponovno se prijavite",
"fields_missing" => "Nekatere polja niso izpoljnena",
"fill_all_fields" => "Prosim, izpolnite vsa polja",
"fill_mandatory_fields" => "Prosim, izpolnite vsa obvezna polja",
"error" => "Napaka",
// Category
"failed_add_category" => "Dodajanje kategorije ni uspelo",
"failed_edit_category" => "Urejanje kategorije ni uspelo",
"category_in_use" => "Kategorija je v uporabi v naročninah in je ni mogoče odstraniti",
"failed_remove_category" => "Odstranitev kategorije ni uspela",
"category_saved" => "Kategorija je shranjena",
"category_removed" => "Kategorija je odstranjena",
"sort_order_saved" => "Vrstni red je shranjen",
// Currency
"currency_saved" => "je bila shranjen.",
"error_adding_currency" => "Napaka pri dodajanju zapisa valute.",
"failed_to_store_currency" => "Shranjevanje valute v zbirko podatkov ni uspelo.",
"currency_in_use" => "Valuta je v uporabi v naročninah in je ni mogoče izbrisati.",
"currency_is_main" => "Valuta je nastavljena kot glavna valuta in je ni mogoče izbrisati.",
"failed_to_remove_currency" => "Odstranitev valute iz zbirke podatkov ni uspela.",
"failed_to_store_api_key" => "Ključa API ni bilo mogoče shraniti v zbirko podatkov.",
"invalid_api_key" => "Neveljaven API ključ.",
"api_key_saved" => "API ključ je uspešno shranjen",
"currency_removed" => "Valuta je odstranjena",
// Household
"failed_add_household" => "Dodajanje člana gospodinjstva ni uspelo",
"failed_edit_household" => "Urejanje člana gospodinjstva ni uspelo",
"failed_remove_household" => "Odstranitev člana gospodinjstva ni uspela",
"household_in_use" => "Član gospodinjstva je v uporabi v naročninah in ga ni mogoče odstraniti",
"member_saved" => "Član je shranjen",
"member_removed" => "Član je odstranjen",
// Notifications
"error_saving_notifications" => "Napaka pri shranjevanju podatkov obvestil.",
"wallos_notification" => "Wallosovo obvestilo",
"test_notification" => "To je preizkusno obvestilo. Če ga vidite, je konfiguracija pravilna.",
"email_error" => "Napaka pri pošiljanju e-pošte",
"notification_sent_successfuly" => "Obvestilo je bilo uspešno poslano",
"notifications_settings_saved" => "Nastavitve obvestil so uspešno shranjene.",
"notification_failed" => "Pošiljanje obvestila ni uspelo",
// Payments
"payment_in_use" => "Uporabljenega plačilnega sredstva ni mogoče onemogočiti",
"failed_update_payment" => "Posodobitev plačilnega sredstva v bazi podatkov ni uspela",
"enabled" => "omogočeno",
"disabled" => "onemogočeno",
// Subscription
"error_fetching_image" => "Napaka pri pridobivanju slike",
"subscription_updated_successfuly" => "Naročnina je bila uspešno posodobljena",
"subscription_added_successfuly" => "Naročnina je bila uspešno dodana",
"error_deleting_subscription" => "Napaka pri brisanju naročnine.",
"invalid_request_method" => "Neveljavna metoda zahteve.",
// User
"error_updating_user_data" => "Napaka pri posodabljanju uporabniških podatkov.",
"user_details_saved" => "Podrobnosti o uporabniku so shranjene",
// Admin Page
"registrations" => "Registracije",
"enable_user_registrations" => "Omogoči registracije uporabnikov",
"maximum_number_users" => "Največje število uporabnikov",
"require_email_verification" => "Zahtevaj preverjanje elektronske pošte",
"configure_smtp_settings_to_enable" => "Za omogočanje nastavitve SMTP nastavitve",
"server_url" => "URL strežnika",
"server_url_info" => "Uporablja se za preverjanje e-pošte in obnovitev gesla. Biti mora veljaven javni URL.",
"server_url_password_reset" => "Če je nastavljeno, bo omogočena tudi funkcija ponastavitve gesla.",
"disable_login" => "Onemogoči prijavo",
"disable_login_info" => "Obidite prijavo. Če strežnik uporabljate samo v lokalnem omrežju brez zunanjega dostopa, lahko onemogočite prijavo. Tako se bo samodejno prijavil uporabnik administrator.",
"disable_login_info2" => "To nastavitev lahko omogočite le, če je registracija uporabnikov izklopljena in če ni več uporabniških računov, razen uporabniškega računa upravitelja.",
"max_users_info" => "0 pomeni neomejeno",
"user_management" => "Upravljanje uporabnikov",
"delete_user" => "Izbriši uporabnika",
"delete_user_info" => "Če izbrišete uporabnika, boste izbrisali tudi vse njegove naročnine in nastavitve.",
"smtp_settings" => "Nastavitve SMTP",
"smtp_usage_info" => "Uporabljeno bo za obnovitev gesla in druge sistemske e-pošte.",
// Email Verification
"email_verified" => "E-pošta je bila uspešno preverjena",
"email_verification_failed" => "Preverjanje e-pošte ni uspelo",
];
?>

View File

@ -10,6 +10,8 @@ $i18n = [
"main_currency" => "Главна валута",
"language" => "Језик",
"passwords_dont_match" => "Лозинке се не поклапају",
"username_exists" => "Корисничко име већ постоји",
"email_exists" => "Е-пошта већ постоји",
"registration_failed" => "Регистрација није успела, покушајте поново.",
"register" => "Региструј се",
"restore_database" => "Врати базу података",
@ -18,10 +20,18 @@ $i18n = [
'stay_logged_in' => "Остани пријављен (30 дана)",
'login' => "Пријави се",
'login_failed' => "Подаци за пријаву нису исправни",
'registration_successful' => "Пријава успешна",
'user_email_waiting_verification' => "Ваша е-пошта треба да буде верификована. Молимо прегледајте Е-пошту",
// Password Reset Page
'forgot_password' => "Заборављена лозинка",
'reset_password' => "Ресетуј лозинку",
'reset_sent_check_email' => "Ресетовање лозинке је послато на вашу е-пошту",
'password_reset_successful' => "Ресетовање лозинке је успешно",
// Header
'subscriptions' => "Претплате",
'stats' => "Статистике",
'settings' => "Подешавања",
'admin' => "Админ",
'about' => "О апликацији",
'logout' => "Одјава",
// Страница са претплатама
@ -85,7 +95,11 @@ $i18n = [
'average_monthly' => "Просечни месечни трошак претплате",
'most_expensive' => "Најскупља претплата",
'amount_due' => "Износ за уплату овог месеца",
'percentage_budget_used' => "Проценат искоришћеног буџета",
'budget_remaining' => "Преостали буџет",
'amount_over_budget' => "Износ преко буџета",
'monthly_savings' => "Месечне уштеде (на неактивним претплатама)",
'yearly_savings' => "Годишње уштеде (на неактивним претплатама)",
'split_views' => "Подељени прикази",
'category_split' => "Подела по категоријама",
'household_split' => "Подела по домаћинству",
@ -101,7 +115,9 @@ $i18n = [
'upload_avatar' => "Постави аватар",
'file_type_error' => "Датотека није у подржаном формату.",
'user_details' => "Кориснички детаљи",
"household" => "Домаћинство",
"monthly_budget" => "Месечни буџет",
"budget_info" => "Унесите месечни буџет да бисте видели препоручену максималну цену претплате на почетној страници.",
"household" => "Домаћинство",
"save_member" => "Сачувај члана",
"delete_member" => "Обриши члана",
"cant_delete_member" => "Главни члан не може бити обрисан",
@ -120,6 +136,25 @@ $i18n = [
"smtp_password" => "SMTP лозинка",
"from_email" => "Од е-поште (Опционо)",
"smtp_info" => "SMTP лозинка се преноси и чува у обичном тексту. Из сигурносних разлога, молимо вас да направите налог само за ово.",
"telegram" => "Телеграм",
"telegram_bot_token" => "Телеграм бот токен",
"telegram_chat_id" => "Телеграм чет ID",
"webhook" => "Вебхук",
"webhook_url" => "Вебхук URL",
"request_method" => "Метод захтева",
"custom_headers" => "Прилагођени заглавља",
"webhook_payload" => "Вебхук Пајлоад",
"webhook_iterator_key" => "{{subscriptions}} замени кључним итератором",
"variables_available" => "Доступне променљиве",
"gotify" => "Готифи",
"token" => "Токен",
"discord" => "Дискорд",
"discord_bot_username" => "Дискорд бот корисничко име",
"discord_bot_avatar_url" => "Дискорд бот URL аватара",
"pushover" => "Пушовер",
"pushover_user_key" => "Пушовер кориснички кључ",
'host' => "Домаћин",
'topic' => "Тема",
"categories" => "Категорије",
"save_category" => "Сачувај категорију",
"delete_category" => "Избриши категорију",
@ -215,6 +250,7 @@ $i18n = [
"email_error" => "Грешка при слању е-поште",
"notification_sent_successfuly" => "Обавештење успешно послато",
"notifications_settings_saved" => "Подешавања обавештења успешно сачувана.",
"notification_failed" => "Обавештење није послато",
// Плаћања
"payment_in_use" => "Не може се онемогућити коришћени начин плаћања",
"failed_update_payment" => "Ажурирање начина плаћања у бази података није успело",
@ -229,6 +265,27 @@ $i18n = [
// Корисник
"error_updating_user_data" => "Грешка при ажурирању корисничких података.",
"user_details_saved" => "Кориснички подаци сачувани",
// Admin Page
"registrations" => "Регистрације",
"enable_user_registrations" => "Омогући регистрације корисника",
"maximum_number_users" => "Максималан број корисника",
"require_email_verification" => "Захтевај верификацију е-поште",
"configure_smtp_settings_to_enable" => "Конфигуришите SMTP подешавања да бисте омогућили ову опцију",
"server_url" => "URL сервера",
"server_url_info" => "Користи се за верификацију е-поште и опоравак лозинке. Мора да буде важећи јавни УРЛ.",
"server_url_password_reset" => "Ако је подешено, такође ће се омогућити функција ресетовања лозинке.",
"disable_login" => "Онемогући пријаву",
"disable_login_info" => "Заобиђите пријаву. Ако свој сервер покрећете само на локалној мрежи, без спољног приступа можете да онемогућите пријаву. Ово ће аутоматски пријавити корисника администратора.",
"disable_login_info2" => "Ово подешавање се може омогућити само ако је регистрација корисника затворена и број корисничких налога не прелази администраторске налоге.",
"max_users_info" => "Максималан број корисника који могу бити регистровани. 0 за неограничено.",
"user_management" => "Управљање корисницима",
"delete_user" => "Обриши корисника",
"delete_user_info" => "Брисање корисника ће такође обрисати све његове претплате и податке.",
"smtp_settings" => "SMTP подешавања",
"smtp_usage_info" => "SMTP се користи за слање е-поште за обавештења.",
// Email Verification
"email_verified" => "Е-пошта је верификована",
"email_verification_failed" => "Верификација е-поште није успела",
];

View File

@ -10,6 +10,8 @@ $i18n = [
"main_currency" => "Glavna valuta",
"language" => "Jezik",
"passwords_dont_match" => "Lozinke se ne poklapaju",
"username_exists" => "Korisničko ime već postoji",
"email_exists" => "E-pošta već postoji",
"registration_failed" => "Registracija nije uspela, pokušajte ponovo.",
"register" => "Registruj se",
"restore_database" => "Vrati bazu podataka",
@ -18,10 +20,18 @@ $i18n = [
'stay_logged_in' => "Ostani prijavljen (30 dana)",
'login' => "Prijavi se",
'login_failed' => "Podaci za prijavu nisu ispravni",
'registration_successful' => "Registracija uspešna",
'user_email_waiting_verification' => "Vaša e-pošta treba da bude verifikovana. Molimo pregledajte E-poštu",
// Password Reset Page
'forgot_password' => "Zaboravili ste lozinku?",
'reset_password' => "Resetuj lozinku",
'reset_sent_check_email' => "Poslali smo vam e-poštu sa uputstvima za resetovanje lozinke",
'password_reset_successful' => "Lozinka uspešno resetovana",
// Header
'subscriptions' => "Pretplate",
'stats' => "Statistike",
'settings' => "Podešavanja",
'admin' => "Admin",
'about' => "O aplikaciji",
'logout' => "Odjava",
// Stranica sa pretplatama
@ -73,6 +83,7 @@ $i18n = [
"url" => "URL",
"notes" => "Beleške",
"enable_notifications" => "Omogući obaveštenja za ovu pretplatu",
"default_value_from_settings" => "Podrazumevana vrednost iz podešavanja",
"delete" => "Izbriši",
"cancel" => "Otkaži",
"upload_logo" => "Učitaj logo",
@ -85,7 +96,11 @@ $i18n = [
'average_monthly' => "Prosečni mesečni trošak pretplate",
'most_expensive' => "Najskuplja pretplata",
'amount_due' => "Iznos za plaćanje ovog meseca",
'percentage_budget_used' => "Procenat budžeta iskorišćen",
'budget_remaining' => "Preostali budžet",
'amount_over_budget' => "Iznos preko budžeta",
'monthly_savings' => "Mesečne uštede (na neaktivnim pretplatama)",
'yearly_savings' => "Godišnje uštede (na neaktivnim pretplatama)",
'split_views' => "Podeljene statistike",
'category_split' => "Podela po kategorijama",
'household_split' => "Podela po domaćinstvima",
@ -101,7 +116,9 @@ $i18n = [
'upload_avatar' => "Učitaj avatar",
'file_type_error' => "Tip datoteke koji ste priložili nije podržan.",
'user_details' => "Detalji korisnika",
"household" => "Domaćinstvo",
"monthly_budget" => "Mesečni budžet",
"budget_info" => "Ovo je vaš mesečni budžet za sve pretplate. Ovo je samo informativno i ne ograničava vas.",
"household" => "Domaćinstvo",
"save_member" => "Sačuvaj člana",
"delete_member" => "Izbriši člana",
"cant_delete_member" => "Nemoguće brisanje glavnog člana",
@ -120,6 +137,25 @@ $i18n = [
"smtp_password" => "SMTP lozinka",
"from_email" => "Od e-pošte (Opciono)",
"smtp_info" => "SMTP lozinka se prenosi i čuva u običnom tekstu. Iz sigurnosnih razloga, molimo vas da napravite nalog samo za ovo.",
"telegram" => "Telegram",
"telegram_bot_token" => "Telegram bot token",
"telegram_chat_id" => "Telegram chat ID",
"webhook" => "Webhook",
"webhook_url" => "Webhook URL",
"request_method" => "Metod zahteva",
"custom_headers" => "Prilagođeni zaglavlja",
"webhook_payload" => "Webhook payload",
"webhook_iterator_key" => "Zameni {{subscriptions}} sa ključem",
"variables_available" => "Dostupne promenljive",
"gotify" => "Gotify",
"token" => "Token",
"discord" => "Discord",
"discord_bot_username" => "Discord bot korisničko ime",
"discord_bot_avatar_url" => "Discord bot URL avatara",
"pushover" => "Pushover",
"pushover_user_key" => "Pushover korisnički ključ",
'host' => "Host",
'topic' => "Tema",
"categories" => "Kategorije",
"save_category" => "Sačuvaj kategoriju",
"delete_category" => "Izbriši kategoriju",
@ -215,6 +251,7 @@ $i18n = [
"email_error" => "Greška pri slanju e-pošte",
"notification_sent_successfuly" => "Obaveštenje uspešno poslato",
"notifications_settings_saved" => "Podešavanja obaveštenja uspešno sačuvana.",
"notification_failed" => "Obaveštenje nije poslato",
// Plaćanja
"payment_in_use" => "Nije moguće onemogućiti korišćeni način plaćanja",
"failed_update_payment" => "Nije uspelo ažuriranje načina plaćanja u bazi podataka",
@ -229,6 +266,27 @@ $i18n = [
// Korisnik
"error_updating_user_data" => "Greška pri ažuriranju korisničkih podataka.",
"user_details_saved" => "Korisnički podaci sačuvani",
// Admin Page
"registrations" => "Registracije",
"enable_user_registrations" => "Omogući registracije korisnika",
"maximum_number_users" => "Maksimalan broj korisnika",
"require_email_verification" => "Zahtevaj verifikaciju e-pošte",
"configure_smtp_settings_to_enable" => "Konfigurišite SMTP podešavanja da biste omogućili ovu opciju",
"server_url" => "URL servera",
"server_url_info" => "Koristi se za verifikaciju e-pošte i oporavak lozinke. Mora da bude važeći javni URL.",
"server_url_password_reset" => "Ako je podešeno, takođe će se omogućiti funkcija resetovanja lozinke.",
"disable_login" => "Onemogući prijavu",
"disable_login_info" => "Zaobiđite prijavu. Ako svoj server pokrećete samo na lokalnoj mreži, bez spoljnog pristupa možete da onemogućite prijavu. Ovo će automatski prijaviti korisnika administratora.",
"disable_login_info2" => "Ovo podešavanje se može omogućiti samo ako je registracija korisnika zatvorena i broj korisničkih naloga ne prelazi administratorske naloge.",
"max_users_info" => "0 za neograničen broj korisnika",
"user_management" => "Upravljanje korisnicima",
"delete_user" => "Izbriši korisnika",
"delete_user_info" => "Brisanjem korisnika izbrisaće se i sve njegove pretplate i podešavanja.",
"smtp_settings" => "SMTP podešavanja",
"smtp_usage_info" => "Koristiće se za oporavak lozinke i druge sistemske e-poruke.",
// Email Verification
"email_verified" => "E-pošta je uspešno verifikovana",
"email_verification_failed" => "Verifikacija e-pošte nije uspela",
];

View File

@ -10,6 +10,8 @@ $i18n = [
"main_currency" => "Ana Para Birimi",
"language" => "Dil",
"passwords_dont_match" => "Şifreler eşleşmiyor",
"username_exists" => "Bu kullanıcı adı zaten mevcut",
"email_exists" => "Bu e-posta zaten mevcut",
"registration_failed" => "Kayıt başarısız, lütfen tekrar deneyin.",
"register" => "Kayıt Ol",
"restore_database" => "Veritabanını geri yükle",
@ -18,10 +20,18 @@ $i18n = [
'stay_logged_in' => "Oturumu açık tut (30 gün)",
'login' => "Giriş Yap",
'login_failed' => "Giriş bilgileri hatalı",
'registration_successful' => "Kayıt başarılı",
'user_email_waiting_verification' => "E-postanızın doğrulanması gerekiyor. Lütfen e-postanızı kontrol edin",
// Password Reset Page
'forgot_password' => "Şifremi Unuttum",
'reset_password' => "Şifreyi Sıfırla",
'reset_sent_check_email' => "Şifre sıfırlama bağlantısı e-posta adresinize gönderildi. Lütfen e-postanızı kontrol edin.",
'password_reset_successful' => "Şifre sıfırlama başarılı",
// Header
'subscriptions' => "Abonelikler",
'stats' => "İstatistikler",
'settings' => "Ayarlar",
'admin' => "Yönetici",
'about' => "Hakkında",
'logout' => "Çıkış Yap",
// Subscriptions page
@ -73,6 +83,7 @@ $i18n = [
"url" => "URL",
"notes" => "Notlar",
"enable_notifications" => "Bu abonelik için bildirimleri etkinleştir",
"default_value_from_settings" => "Ayarlar'dan varsayılan değeri al",
"delete" => "Sil",
"cancel" => "İptal",
"upload_logo" => "Logo Yükle",
@ -85,7 +96,11 @@ $i18n = [
'average_monthly' => "Ortalama Aylık Abonelik Maliyeti",
'most_expensive' => "En Pahalı Abonelik Maliyeti",
'amount_due' => "Bu ay ödenecek miktar",
'percentage_budget_used' => "Bütçe Kullanımı",
'budget_remaining' => "Kalan Bütçe",
'amount_over_budget' => "Bütçe Aşımı",
'monthly_savings' => "Aylık Tasarruf (aktif olmayan aboneliklerde)",
'yearly_savings' => "Yıllık Tasarruf (aktif olmayan aboneliklerde)",
'split_views' => "Bölünmüş Görünümler",
'category_split' => "Kategori Bölümü",
'household_split' => "Hane Bölümü",
@ -101,6 +116,8 @@ $i18n = [
'upload_avatar' => "Avatarı yükle",
'file_type_error' => "Dosya türü izin verilmiyor",
'user_details' => "Kullanıcı Detayları",
"monthly_budget" => "Aylık Bütçe",
"budget_info" => "Bir bütçe belirlemek, istatistik sayfasında bütçe ve gerçek harcamaları karşılaştırmanıza olanak tanır.",
"household" => "Hane",
"save_member" => "Üyeyi Kaydet",
"delete_member" => "Üyeyi Sil",
@ -120,6 +137,25 @@ $i18n = [
"smtp_password" => "SMTP Şifresi",
"from_email" => "Gönderen e-posta (İsteğe bağlı)",
"smtp_info" => "SMTP Şifresi düz metin olarak iletilir ve saklanır. Güvenlik için, lütfen bunun için özel bir hesap oluşturun.",
"telegram" => "Telegram",
"telegram_bot_token" => "Telegram Bot Token",
"telegram_chat_id" => "Telegram Chat ID",
"webhook" => "Webhook",
"webhook_url" => "Webhook URL",
"request_method" => "İstek Metodu",
"custom_headers" => "Özel Başlıklar",
"webhook_payload" => "Webhook Payload",
"webhook_iterator_key" => "{{subscriptions}}'yi anahtar olarak değiştir",
"variables_available" => "Kullanılabilir Değişkenler",
"gotify" => "Gotify",
"token" => "Token",
"discord" => "Discord",
"discord_bot_username" => "Discord Bot Kullanıcı Adı",
"discord_bot_avatar_url" => "Discord Bot Avatar URL",
"pushover" => "Pushover",
"pushover_user_key" => "Pushover Kullanıcı Anahtarı",
'host' => "Host",
'topic' => "Konu",
"categories" => "Kategoriler",
"save_category" => "Kategoriyi Kaydet",
"delete_category" => "Kategoriyi Sil",
@ -215,6 +251,7 @@ $i18n = [
"email_error" => "E-posta gönderilirken hata oluştu",
"notification_sent_successfuly" => "Bildirim başarıyla gönderildi",
"notifications_settings_saved" => "Bildirim ayarları başarıyla kaydedildi.",
"notification_failed" => "Bildirim gönderilemedi",
// Payments
"payment_in_use" => "Kullanımda olan ödeme yöntemi devre dışı bırakılamaz",
"failed_update_payment" => "Ödeme yöntemi veritabanında güncellenemedi",
@ -229,6 +266,28 @@ $i18n = [
// User
"error_updating_user_data" => "Kullanıcı verileri güncellenirken hata oluştu.",
"user_details_saved" => "Kullanıcı detayları kaydedildi",
// Admin Page
"registrations" => "Kayıtlar",
"enable_user_registrations" => "Kullanıcı kayıtlarını etkinleştir",
"maximum_number_users" => "Maksimum kullanıcı sayısı",
"require_email_verification" => "E-posta doğrulaması gerektir",
"configure_smtp_settings_to_enable" => "E-posta doğrulamasını etkinleştirmek için SMTP ayarlarını yapılandırın",
"server_url" => "Sunucu URL'si",
"server_url_info" => "E-posta doğrulama ve şifre kurtarma için kullanılır. Geçerli bir genel URL olmalıdır.",
"server_url_password_reset" => "Ayarlanırsa şifre sıfırlama işlevini de etkinleştirir.",
"disable_login" => "Girişi devre dışı bırak",
"disable_login_info" => "Girişi atlayın. Sunucunuzu yalnızca yerel bir ağ üzerinde, harici erişim olmadan çalıştırıyorsanız, oturum açmayı devre dışı bırakabilirsiniz. Bu, yönetici kullanıcıyı otomatik olarak oturum açacaktır.",
"disable_login_info2" => "Bu ayarı yalnızca kullanıcı kaydı kapalıysa ve yönetici kullanıcı hesabından başka kullanıcı yoksa etkinleştirebilirsiniz.",
"max_users_info" => "0 veya boş bırakıldığında sınırsız kullanıcı sayısı",
"user_management" => "Kullanıcı Yönetimi",
"delete_user" => "Kullanıcıyı Sil",
"delete_user_info" => "Bir kullanıcının silinmesi aynı zamanda tüm aboneliklerinin ve ayarlarının da silinmesine neden olur.",
"smtp_settings" => "SMTP Ayarları",
"smtp_usage_info" => "Şifre kurtarma ve diğer sistem e-postaları için kullanılacaktır.",
// Email Verification
"email_verified" => "E-posta doğrulandı",
"email_verification_failed" => "E-posta doğrulaması başarısız oldu",
];

View File

@ -10,6 +10,8 @@ $i18n = [
"main_currency" => "主要货币",
"language" => "语言",
"passwords_dont_match" => "密码不匹配",
"username_exists" => "用户名已存在",
"email_exists" => "电子邮箱已存在",
"registration_failed" => "注册失败,请重试。",
"register" => "注册",
"restore_database" => "恢复数据库",
@ -19,11 +21,20 @@ $i18n = [
'stay_logged_in' => "30 天内免登录",
'login' => "登录",
'login_failed' => "登录信息错误",
'registration_successful' => "注册成功",
'user_email_waiting_verification' => "您的电子邮件需要验证。请检查您的电子邮件",
// Password Reset Page
'forgot_password' => "忘记密码",
'reset_password' => "重置密码",
'reset_sent_check_email' => "重置密码链接已发送到您的电子邮箱",
'password_reset_successful' => "密码重置成功",
// 页眉
'subscriptions' => "订阅",
'stats' => "统计",
'settings' => "设置",
'admin' => "管理员",
'about' => "关于",
'logout' => "登出",
@ -77,6 +88,7 @@ $i18n = [
"url" => "链接",
"notes" => "备注",
"enable_notifications" => "为此订阅启用通知",
"default_value_from_settings" => "默认值从设置中获取",
"delete" => "删除",
"cancel" => "取消",
"upload_logo" => "上传 Logo",
@ -90,7 +102,11 @@ $i18n = [
'average_monthly' => "平均每月订阅费用",
'most_expensive' => "最昂贵订阅费用",
'amount_due' => "本月应付金额",
'percentage_budget_used' => "预算使用百分比",
'budget_remaining' => "剩余预算",
'amount_over_budget' => "超出预算",
'monthly_savings' => "每月节省",
'yearly_savings' => "每年节省",
'split_views' => "拆分视图",
'category_split' => "分类视图",
'household_split' => "家庭视图",
@ -108,6 +124,8 @@ $i18n = [
'upload_avatar' => "上传头像",
'file_type_error' => "文件类型不允许",
'user_details' => "用户详情",
"monthly_budget" => "每月预算",
"budget_info" => "设置预算后,您可以在统计页面上比较预算和实际支出。",
"household" => "家庭",
"save_member" => "保存成员",
"delete_member" => "删除成员",
@ -127,6 +145,25 @@ $i18n = [
"smtp_password" => "SMTP 密码",
"from_email" => "发件人邮箱(可选)",
"smtp_info" => "SMTP 密码以明文传输和存储。为安全起见,建议专门为 Wallos 创建一个账户。",
"telegram" => "Telegram",
"telegram_bot_token" => "Telegram 机器人令牌",
"telegram_chat_id" => "Telegram 聊天 ID",
"webhook" => "Webhook",
"webhook_url" => "Webhook URL",
"request_method" => "请求方法",
"custom_headers" => "自定义标头",
"webhook_payload" => "Webhook 负载",
"webhook_iterator_key" => "替换 {{subscriptions}} 为键名",
"variables_available" => "可用变量",
"gotify" => "Gotify",
"token" => "令牌",
"discord" => "Discord",
"discord_bot_username" => "Discord 机器人用户名",
"discord_bot_avatar_url" => "Discord 机器人头像 URL",
"pushover" => "Pushover",
"pushover_user_key" => "Pushover 用户密钥",
'host' => "主机",
'topic' => "主题",
"categories" => "分类",
"save_category" => "保存分类",
"delete_category" => "删除分类",
@ -226,6 +263,7 @@ $i18n = [
"wallos_notification" => "Wallos 通知",
"test_notification" => "这是一条测试通知。如果您看到此消息,说明 Wallos 通知邮件配置正确。",
"email_error" => "发送电子邮件时出错",
"notification_failed" => "通知发送失败",
"notification_sent_successfuly" => "通知已成功发送",
"notifications_settings_saved" => "通知设置已成功保存。",
@ -246,6 +284,29 @@ $i18n = [
"error_updating_user_data" => "更新用户数据时出错。",
"user_details_saved" => "用户详细信息已保存",
// Admin Page
"registrations" => "注册",
"enable_user_registrations" => "启用用户注册",
"maximum_number_users" => "最大用户数",
"require_email_verification" => "需要电子邮件验证",
"configure_smtp_settings_to_enable" => "要启用此功能,请配置 SMTP 设置。",
"server_url" => "服务器 URL",
"server_url_info" => "用于电子邮件验证和密码恢复。必须是有效的公共 URL。",
"server_url_password_reset" => "如果设置,还将启用密码重置功能。",
"disable_login" => "禁用登录",
"disable_login_info" => "旁路登录。如果服务器只在本地网络上运行,没有外部访问,则可以禁用登录。这会自动登录管理员用户。",
"disable_login_info2" => "只有在用户注册关闭且用户账户数不超过管理员账户时,才能启用此设置。",
"max_users_info" => "设置为 0 以无限制用户数",
"user_management" => "用户管理",
"delete_user" => "删除用户",
"delete_user_info" => "删除用户也会删除其所有订阅和设置。",
"smtp_settings" => "SMTP 设置",
"smtp_usage_info" => "将用于密码恢复和其他系统电子邮件。",
// Email Verification
"email_verified" => "电子邮件已验证",
"email_verification_failed" => "电子邮件验证失败",
];
?>

View File

@ -10,6 +10,8 @@ $i18n = [
"main_currency" => "主要貨幣單位",
"language" => "語言",
"passwords_dont_match" => "密碼不一致",
"username_exists" => "使用者名稱已存在",
"email_exists" => "電子信箱已存在",
"registration_failed" => "註冊失敗,請再試一次。",
"register" => "註冊",
"restore_database" => "還原資料庫",
@ -18,10 +20,18 @@ $i18n = [
'stay_logged_in' => "保持登入 30 天",
'login' => "登入",
'login_failed' => "登入資訊錯誤",
'registration_successful' => "註冊成功",
'user_email_waiting_verification' => "您的電子郵件需要驗證。 請查看你的信箱",
// Password Reset Page
'forgot_password' => "忘記密碼",
'reset_password' => "重設密碼",
'reset_sent_check_email' => "重設密碼的電子郵件已發送,請檢查您的電子郵件",
'password_reset_successful' => "密碼重設成功",
// 頁首
'subscriptions' => "訂閱",
'stats' => "統計",
'settings' => "設定",
'admin' => "管理員",
'about' => "關於",
'logout' => "登出",
// 訂閱頁面
@ -73,6 +83,7 @@ $i18n = [
"url" => "連結",
"notes" => "備註",
"enable_notifications" => "為該訂閱開啟通知",
"default_value_from_settings" => "從設定中取得預設值",
"delete" => "刪除",
"cancel" => "取消",
"upload_logo" => "上傳圖示",
@ -85,7 +96,11 @@ $i18n = [
'average_monthly' => "平均每月訂閱費用",
'most_expensive' => "最高的訂閱費用",
'amount_due' => "本月應付金額",
'percentage_budget_used' => "預算使用率",
'budget_remaining' => "剩餘預算",
'amount_over_budget' => "預算超支",
'monthly_savings' => "每月節省",
'yearly_savings' => "每年節省",
'split_views' => "分割檢視",
'category_split' => "類別分割",
'household_split' => "家庭分割",
@ -101,6 +116,8 @@ $i18n = [
'upload_avatar' => "上传头像",
'file_type_error' => "文件类型不允许",
'user_details' => "使用者詳細資訊",
"monthly_budget" => "每月預算",
"budget_info" => "設定預算後,您可以在統計頁面上比較預算和實際支出。",
"household" => "家庭",
"save_member" => "儲存成員",
"delete_member" => "刪除成員",
@ -120,6 +137,25 @@ $i18n = [
"smtp_password" => "SMTP 密碼",
"from_email" => "寄件人信箱(可選)",
"smtp_info" => "SMTP 密碼將以明文傳輸和儲存。為了安全起見,建議專門為 Wallos 建立一個帳戶。",
"telegram" => "Telegram",
"telegram_bot_token" => "Telegram 機器人令牌",
"telegram_chat_id" => "Telegram 聊天 ID",
"webhook" => "Webhook",
"webhook_url" => "Webhook URL",
"request_method" => "請求方法",
"custom_headers" => "自訂標頭",
"webhook_payload" => "Webhook 載荷",
"webhook_iterator_key" => "替換 {{subscriptions}} 為鍵名",
"variables_available" => "可用變數",
"gotify" => "Gotify",
"token" => "令牌",
"discord" => "Discord",
"discord_bot_username" => "Discord 機器人使用者名稱",
"discord_bot_avatar_url" => "Discord 機器人頭像 URL",
"pushover" => "Pushover",
"pushover_user_key" => "Pushover 使用者金鑰",
'host' => "主機",
'topic' => "主題",
"categories" => "分類",
"save_category" => "儲存分類",
"delete_category" => "刪除分類",
@ -215,6 +251,7 @@ $i18n = [
"email_error" => "傳送到電子信箱時發生錯誤",
"notification_sent_successfuly" => "通知已成功傳送",
"notifications_settings_saved" => "通知設定已成功儲存。",
"notification_failed" => "通知傳送失敗",
// Payments
"payment_in_use" => "無法停用正在使用的付款方式",
"failed_update_payment" => "更新資料庫中的付款方式失敗",
@ -229,6 +266,27 @@ $i18n = [
// User
"error_updating_user_data" => "更新使用者資訊時發生錯誤。",
"user_details_saved" => "使用者資訊已儲存",
// Admin Page
"registrations" => "註冊",
"enable_user_registrations" => "啟用使用者註冊",
"maximum_number_users" => "最大使用者數",
"require_email_verification" => "需要電子郵件驗證",
"configure_smtp_settings_to_enable" => "要啟用使用者註冊,請先設定 SMTP 設定。",
"server_url" => "伺服器 URL",
"server_url_info" => "用於電子郵件驗證和密碼恢復。 必須是有效的公共 URL。",
"server_url_password_reset" => "如果設置,還將啟用密碼重置功能。",
"disable_login" => "停用登入",
"disable_login_info" => "繞過登入。如果您僅在本機網路上執行伺服器,而沒有外部存取權限,則可以停用登入。這將自動登入管理員用戶。",
"disable_login_info2" => "只有在使用者註冊關閉且使用者帳戶數不超過管理員帳戶時,才能啟用此設定。",
"max_users_info" => "設定為 0 以無限制使用者數",
"user_management" => "使用者管理",
"delete_user" => "刪除使用者",
"delete_user_info" => "刪除用戶也會刪除其所有訂閱和設定。",
"smtp_settings" => "SMTP 設定",
"smtp_usage_info" => "將用於密碼恢復和其他系統電子郵件。",
// Email Verification
"email_verified" => "電子郵件已驗證",
"email_verification_failed" => "電子郵件驗證失敗",
];

View File

@ -147,8 +147,9 @@
}
}
$query = "SELECT main_currency FROM user WHERE id = 1";
$query = "SELECT main_currency FROM user WHERE id = :userId";
$stmt = $db->prepare($query);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
$mainCurrencyId = $row['main_currency'];

View File

@ -1,3 +1,3 @@
<?php
$version = "v1.23.0";
$version = "v2.4.1";
?>

View File

@ -5,7 +5,7 @@
include_once 'includes/list_subscriptions.php';
$sort = "next_payment";
$sql = "SELECT * FROM subscriptions ORDER BY next_payment ASC, inactive ASC";
$sql = "SELECT * FROM subscriptions WHERE user_id = :userId ORDER BY next_payment ASC, inactive ASC";
if (isset($_COOKIE['sortOrder']) && $_COOKIE['sortOrder'] != "") {
$sort = $_COOKIE['sortOrder'];
$allowedSortCriteria = ['name', 'id', 'next_payment', 'price', 'payer_user_id', 'category_id', 'payment_method_id'];
@ -14,11 +14,13 @@
$order = "DESC";
}
if (in_array($sort, $allowedSortCriteria)) {
$sql = "SELECT * FROM subscriptions ORDER BY $sort $order, inactive ASC";
$sql = "SELECT * FROM subscriptions WHERE user_id = :userId ORDER BY $sort $order, inactive ASC";
}
}
$result = $db->query($sql);
$stmt = $db->prepare($sql);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
if ($result) {
$subscriptions = array();
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
@ -26,16 +28,6 @@
}
}
$notificationsEnabled = false;
$query = "SELECT enabled FROM notifications WHERE id = 1";
$result = $db->query($query);
if ($result) {
$row = $result->fetchArray(SQLITE3_ASSOC);
if ($row) {
$notificationsEnabled = $row['enabled'];
}
}
$headerClass = count($subscriptions) > 0 ? "main-actions" : "main-actions hidden";
$defaultLogo = $theme == "light" ? "images/siteicons/" . $colorTheme . "/wallos.png" : "images/siteicons/" . $colorTheme . "/walloswhite.png";
?>
@ -47,7 +39,7 @@
<section class="contain">
<header class="<?= $headerClass ?>" id="main-actions">
<button class="button" onClick="addSubscription()">
<img class="button-icon" src="images/siteicons/plusicon.png">
<i class="fa-solid fa-circle-plus"></i>
<?= translate('new_subscription', $i18n) ?>
</button>
<div class="top-actions">
@ -57,7 +49,7 @@
</div>
<div class="filtermenu on-dashboard">
<button class="button" id="filtermenu-button" title="<?= translate("filter", $i18n) ?>">
<button class="button secondary-button" id="filtermenu-button" title="<?= translate("filter", $i18n) ?>">
<i class="fa-solid fa-filter"></i>
</button>
<div class="filtermenu-content">
@ -91,6 +83,9 @@
<div class="filtermenu-submenu-content" id="filter-category">
<?php
foreach ($categories as $category) {
if ($category['name'] == "No category") {
$category['name'] = translate("no_category", $i18n);
}
$selectedClass = '';
if (isset($_GET['category']) && $_GET['category'] == $category['id']) {
$selectedClass = 'selected';
@ -136,8 +131,8 @@
</div>
<div class="sort-container">
<button class="button" value="Sort" onClick="toggleSortOptions()" id="sort-button" title="<?= translate('sort', $i18n) ?>">
<img src="images/siteicons/sort.png" class="button-icon" />
<button class="button secondary-button" value="Sort" onClick="toggleSortOptions()" id="sort-button" title="<?= translate('sort', $i18n) ?>">
<i class="fa-solid fa-arrow-down-wide-short"></i>
</button>
<div class="sort-options" id="sort-options">
<ul>
@ -170,7 +165,7 @@
$print[$id]['currency_code'] = $currencies[$subscription['currency_id']]['code'];
$currencyId = $subscription['currency_id'];
$print[$id]['next_payment'] = date('M d, Y', strtotime($subscription['next_payment']));
$paymentIconFolder = $paymentMethodId <= 31 ? 'images/uploads/icons/' : 'images/uploads/logos/';
$paymentIconFolder = (strpos($payment_methods[$paymentMethodId]['icon'], 'images/uploads/icons/') !== false) ? "" : "images/uploads/logos/";
$print[$id]['payment_method_icon'] = $paymentIconFolder . $payment_methods[$paymentMethodId]['icon'];
$print[$id]['payment_method_name'] = $payment_methods[$paymentMethodId]['name'];
$print[$id]['payment_method_id'] = $paymentMethodId;
@ -203,7 +198,7 @@
<?= translate('no_subscriptions_yet', $i18n) ?>
</p>
<button class="button" onClick="addSubscription()">
<img class="button-icon" src="images/siteicons/plusicon.png">
<i class="fa-solid fa-circle-plus"></i>
<?= translate('add_first_subscription', $i18n) ?>
</button>
</div>
@ -260,12 +255,12 @@
<div class="inline">
<select id="frequency" name="frequency" placeholder="<?= translate('frequency', $i18n) ?>">
<?php
foreach ($frequencies as $frequency) {
?>
<option value="<?= $frequency['id'] ?>"><?= $frequency['name'] ?></option>
<?php
}
?>
for ($i = 1; $i <= 366; $i++) {
?>
<option value="<?= $i ?>"><?= $i ?></option>
<?php
}
?>
</select>
<select id="cycle" name="cycle" placeholder="Cycle">
<?php
@ -285,16 +280,25 @@
</div>
</div>
<?php
if ($notificationsEnabled) {
?>
<div class="form-group-inline">
<input type="checkbox" id="notifications" name="notifications">
<input type="checkbox" id="notifications" name="notifications" onchange="toggleNotificationDays()">
<label for="notifications"><?= translate('enable_notifications', $i18n) ?></label>
</div>
<?php
}
?>
<div class="form-group">
<label for="notify_days_before"><?= translate('notify_me', $i18n) ?></label>
<select id="notify_days_before" name="notify_days_before" disabled>
<option value="0"><?= translate('default_value_from_settings', $i18n) ?></option>
<option value="1">1 <?= translate('day_before', $i18n) ?></option>
<?php
for ($i = 2; $i <= 90; $i++) {
?>
<option value="<?= $i ?>"><?= $i ?> <?= translate('days_before', $i18n) ?></option>
<?php
}
?>
</select>
</div>
<div class="form-group">
<label for="payment_method"><?= translate('payment_method', $i18n) ?></label>

211
login.php
View File

@ -20,6 +20,54 @@ if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
exit();
}
// Check if login is disabled
$adminQuery = "SELECT login_disabled FROM admin";
$adminResult = $db->query($adminQuery);
$adminRow = $adminResult->fetchArray(SQLITE3_ASSOC);
if ($adminRow['login_disabled'] == 1) {
$query = "SELECT id, username, main_currency, language FROM user WHERE id = :id";
$stmt = $db->prepare($query);
$stmt->bindValue(':id', 1, SQLITE3_INTEGER);
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
if ($row === false) {
// Something is wrong with admin user. Reenable login
$updateQuery = "UPDATE admin SET login_disabled = 0";
$updateStmt = $db->prepare($updateQuery);
$updateStmt->execute();
$db->close();
header("Location: login.php");
} else {
$userId = $row['id'];
$main_currency = $row['main_currency'];
$username = $row['username'];
$language = $row['language'];
$_SESSION['username'] = $username;
$_SESSION['loggedin'] = true;
$_SESSION['main_currency'] = $main_currency;
$_SESSION['userId'] = $userId;
$cookieExpire = time() + (30 * 24 * 60 * 60);
setcookie('language', $language, $cookieExpire);
$query = "SELECT color_theme FROM settings";
$stmt = $db->prepare($query);
$result = $stmt->execute();
$settings = $result->fetchArray(SQLITE3_ASSOC);
setcookie('colorTheme', $settings['color_theme'], $cookieExpire);
$cookieValue = $username . "|" . "abc123ABC" . "|" . $main_currency;
setcookie('wallos_login', $cookieValue, $cookieExpire);
$db->close();
header("Location: .");
}
}
$theme = "light";
if (isset($_COOKIE['theme'])) {
$theme = $_COOKIE['theme'];
@ -31,6 +79,8 @@ if (isset($_COOKIE['colorTheme'])) {
}
$loginFailed = false;
$hasSuccessMessage = (isset($_GET['validated']) && $_GET['validated'] == "true") || (isset($_GET['registered']) && $_GET['registered'] == true) ? true : false;
$userEmailWaitingVerification = false;
if (isset($_POST['username']) && isset($_POST['password'])) {
$username = $_POST['username'];
$password = $_POST['password'];
@ -48,25 +98,47 @@ if (isset($_POST['username']) && isset($_POST['password'])) {
$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 (?, ?)";
$addLoginTokensStmt = $db->prepare($addLoginTokens);
$addLoginTokensStmt->bindValue(1, $userId, SQLITE3_INTEGER);
$addLoginTokensStmt->bindValue(2, $token, SQLITE3_TEXT);
$addLoginTokensStmt->execute();
$_SESSION['token'] = $token;
$cookieValue = $username . "|" . $token . "|" . $main_currency;
setcookie('wallos_login', $cookieValue, $cookieExpire);
// Check if the user is in the email_verification table
$query = "SELECT 1 FROM email_verification WHERE user_id = :userId";
$stmt = $db->prepare($query);
$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER);
$result = $stmt->execute();
$verificationRow = $result->fetchArray(SQLITE3_ASSOC);
if ($verificationRow) {
$userEmailWaitingVerification = true;
$loginFailed = true;
} else {
$_SESSION['username'] = $username;
$_SESSION['loggedin'] = true;
$_SESSION['main_currency'] = $main_currency;
$_SESSION['userId'] = $userId;
$cookieExpire = time() + (30 * 24 * 60 * 60);
setcookie('language', $language, $cookieExpire);
if ($rememberMe) {
$query = "SELECT color_theme FROM settings";
$stmt = $db->prepare($query);
$result = $stmt->execute();
$settings = $result->fetchArray(SQLITE3_ASSOC);
setcookie('colorTheme', $settings['color_theme'], $cookieExpire);
$token = bin2hex(random_bytes(32));
$addLoginTokens = "INSERT INTO login_tokens (user_id, token) VALUES (:userId, :token)";
$addLoginTokensStmt = $db->prepare($addLoginTokens);
$addLoginTokensStmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$addLoginTokensStmt->bindParam(':token', $token, SQLITE3_TEXT);
$addLoginTokensStmt->execute();
$_SESSION['token'] = $token;
$cookieValue = $username . "|" . $token . "|" . $main_currency;
setcookie('wallos_login', $cookieValue, $cookieExpire);
}
$db->close();
header("Location: .");
exit();
}
$db->close();
header("Location: .");
exit();
} else {
$loginFailed = true;
}
@ -74,9 +146,35 @@ if (isset($_POST['username']) && isset($_POST['password'])) {
$loginFailed = true;
}
}
//Check if registration is open
$registrations = false;
$adminQuery = "SELECT registrations_open, max_users, server_url, smtp_address FROM admin";
$adminResult = $db->query($adminQuery);
$adminRow = $adminResult->fetchArray(SQLITE3_ASSOC);
$registrationsOpen = $adminRow['registrations_open'];
$maxUsers = $adminRow['max_users'];
if ($registrationsOpen == 1 && $maxUsers == 0) {
$registrations = true;
} else if ($registrationsOpen == 1 && $maxUsers > 0) {
$userCountQuery = "SELECT COUNT(id) as userCount FROM user";
$userCountResult = $db->query($userCountQuery);
$userCountRow = $userCountResult->fetchArray(SQLITE3_ASSOC);
$userCount = $userCountRow['userCount'];
if ($userCount < $maxUsers) {
$registrations = true;
}
}
$resetPasswordEnabled = false;
if ($adminRow['smtp_address'] != "" && $adminRow['server_url'] != "") {
$resetPasswordEnabled = true;
}
?>
<!DOCTYPE html>
<html>
<html dir="<?= $languages[$lang]['dir'] ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
@ -90,10 +188,11 @@ if (isset($_POST['username']) && isset($_POST['password'])) {
<link rel="stylesheet" href="styles/themes/red.css?<?= $version ?>" id="red-theme" <?= $colorTheme != "red" ? "disabled" : "" ?>>
<link rel="stylesheet" href="styles/themes/green.css?<?= $version ?>" id="green-theme" <?= $colorTheme != "green" ? "disabled" : "" ?>>
<link rel="stylesheet" href="styles/themes/yellow.css?<?= $version ?>" id="yellow-theme" <?= $colorTheme != "yellow" ? "disabled" : "" ?>>
<link rel="stylesheet" href="styles/font-awesome.min.css">
<link rel="stylesheet" href="styles/barlow.css">
<link rel="stylesheet" href="styles/login-dark-theme.css?<?= $version ?>" id="dark-theme" <?= $theme == "light" ? "disabled" : "" ?>>
</head>
<body>
<body class="<?= $languages[$lang]['dir'] ?>">
<div class="content">
<section class="container">
<header>
@ -121,20 +220,74 @@ if (isset($_POST['username']) && isset($_POST['password'])) {
<input type="checkbox" id="remember" name="remember">
<label for="remember"><?= translate('stay_logged_in', $i18n) ?></label>
</div>
<?php
if ($loginFailed) {
?>
<sup class="error">
<?= translate('login_failed', $i18n) ?>.
</sup>
<?php
}
?>
<div class="form-group">
<input type="submit" value="<?= translate('login', $i18n) ?>">
</div>
<?php
if ($loginFailed) {
?>
<ul class="error-box">
<?php
if ($userEmailWaitingVerification) {
?>
<li><i class="fa-solid fa-triangle-exclamation"></i><?= translate('user_email_waiting_verification', $i18n) ?></li>
<?php
} else {
?>
<li><i class="fa-solid fa-triangle-exclamation"></i><?= translate('login_failed', $i18n) ?></li>
<?php
}
?>
</ul>
<?php
}
if ($hasSuccessMessage) {
?>
<ul class="success-box">
<?php
if (isset($_GET['validated']) && $_GET['validated'] == "true") {
?>
<li><i class="fa-solid fa-check"></i><?= translate('email_verified', $i18n) ?></li>
<?php
} else if (isset($_GET['registered']) && $_GET['registered']) {
?>
<li><i class="fa-solid fa-check"></i><?= translate('registration_successful', $i18n) ?></li>
<?php
if (isset($_GET['requireValidation']) && $_GET['requireValidation'] == true) {
?>
<li><?= translate('user_email_waiting_verification', $i18n) ?></li>
<?php
}
}
?>
</ul>
<?php
}
if ($resetPasswordEnabled) {
?>
<div class="login-form-link">
<a href="passwordreset.php"><?= translate('forgot_password', $i18n) ?></a>
</div>
<?php
}
?>
<?php
if ($registrations) {
?>
<div class="separator">
<input type="button" class="secondary-button" onclick="openRegitrationPage()" value="<?= translate('register', $i18n) ?>"></input>
</div>
<?php
}
?>
</form>
</section>
</div>
<script type="text/javascript">
function openRegitrationPage() {
window.location.href = "registration.php";
}
</script>
</body>
</html>

View File

@ -4,16 +4,17 @@
// get token from cookie to remove from DB
if (isset($_SESSION['token'])) {
$token = $_SESSION['token'];
$sql = "DELETE FROM login_tokens WHERE token = ?";
$sql = "DELETE FROM login_tokens WHERE token = :token AND user_id = :userId";
$stmt = $db->prepare($sql);
$stmt->bindValue(1, $token, SQLITE3_TEXT);
$stmt->bindParam(':token', $token, SQLITE3_TEXT);
$stmt->bindParam(':userId', $userId, SQLITE3_INTEGER);
$stmt->execute();
}
$_SESSION = array();
session_destroy();
$cookieExpire = time() - 3600;
setcookie('wallos_login', '', $cookieExpire);
setcookie('wallos_login', '', $cookieExpire);
$db->close();
header("Location: .");
header("Location: .");
exit();
?>

Some files were not shown because too many files have changed in this diff Show More