Skip to content

Commit 7dc7ac1

Browse files
pteroca-comksroga
andauthored
v0.5.9 (#75)
* Missing translations * Web Wizard Configurator improvements * Configurator improvements * Translations * Finish configuration process improvements * Version update * Missing translations --------- Co-authored-by: Konrad Sroga <[email protected]>
1 parent f6675f7 commit 7dc7ac1

25 files changed

+375
-57
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@
22

33
---
44

5+
## [0.5.9] - 2025-10-05
6+
7+
### Added
8+
- Added missing translations to improve localization coverage.
9+
10+
### Changed
11+
- Improved project configurator and prepared groundwork for the upcoming all-in-one installation support.
12+
13+
### Fixed
14+
- Fixed pipeline responsible for detecting missing translations.
15+
16+
---
17+
518
## [0.5.8] - 2025-09-24
619

720
### Added

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "pteroca/panel",
33
"description": "PteroCA.com is a free, open-source client area and management panel designed specifically for Pterodactyl server users and hosting providers. The platform simplifies and automates server management with a user-friendly interface and robust billing features.",
4-
"version": "0.5.8",
4+
"version": "0.5.9",
55
"type": "project",
66
"license": "MIT",
77
"minimum-stability": "stable",

src/Core/Command/ShowMissingTranslationsCommand.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,17 @@ protected function execute(InputInterface $input, OutputInterface $output): int
5656

5757
if (empty($missingKeys)) {
5858
$io->success('No missing translations found.');
59+
return Command::SUCCESS;
5960
} else {
60-
$io->warning(sprintf('Found %d missing translations in "%s":', count($missingKeys), $compareFile));
61+
$io->error(sprintf('Found %d missing translations in "%s":', count($missingKeys), $compareFile));
6162

6263
$missingKeys = array_filter($missingKeys, function($key) {
6364
return !empty(trim($key));
6465
});
6566

6667
$io->writeln(implode(', ', $missingKeys));
68+
return Command::FAILURE;
6769
}
68-
69-
return Command::SUCCESS;
7070
}
7171

7272
private function findMissingKeys(array $mainData, array $compareData, string $prefix = ''): array

src/Core/Resources/config/services.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
parameters:
2-
version: '0.5.8'
2+
version: '0.5.9'
33
categories_base_path: '/uploads/categories'
44
categories_directory: 'public/uploads/categories'
55
products_base_path: '/uploads/products'

src/Core/Resources/templates/configurator/configurator.html.twig

Lines changed: 146 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@
7878
{% block body_javascript %}
7979
<script>
8080
document.addEventListener('DOMContentLoaded', function() {
81-
let currentStep = 0
81+
let currentStep = 0,
82+
useExistingPterodactylSettings = false
8283
8384
const stepCount = 5,
8485
forms = document.querySelectorAll('form[data-step]'),
@@ -105,7 +106,7 @@
105106
.then(response => {
106107
if (response.ok) {
107108
if (currentStep === stepCount) {
108-
return sendConfiguration()
109+
return sendConfiguration(submitButton)
109110
}
110111
111112
response.json().then(response => {
@@ -144,6 +145,23 @@
144145
})
145146
})
146147
148+
document.addEventListener('click', function(e) {
149+
const overrideButton = e.target.closest('.btn-override-settings')
150+
if (overrideButton) {
151+
const step = overrideButton.getAttribute('data-step')
152+
showPterodactylForm(parseInt(step))
153+
return
154+
}
155+
156+
const nextConfiguredButton = e.target.closest('.btn-next-configured')
157+
if (nextConfiguredButton) {
158+
e.preventDefault()
159+
const form = document.querySelector('form[data-step="2"]')
160+
form.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }))
161+
return
162+
}
163+
})
164+
147165
skipButtons.forEach(skipButton => {
148166
skipButton.addEventListener('click', function() {
149167
setCurrentStep(currentStep + 1)
@@ -161,27 +179,124 @@
161179
document.querySelector('div[data-step="' + step + '"]')?.classList.remove('d-none')
162180
currentStep = step
163181
document.getElementById('current-step').innerText = currentStep
182+
183+
if (step === 2) {
184+
checkIfPterodactylConfigured()
185+
}
186+
}
187+
188+
function checkIfPterodactylConfigured() {
189+
const validateEndpointUrl = '{{ path('first_configuration_validate_step') }}'
190+
const formData = new FormData()
191+
formData.append('step', 2)
192+
formData.append('checkExistingSettings', 'true')
193+
formData.append('language', languageSelect.value)
194+
195+
fetch(validateEndpointUrl, {
196+
method: 'POST',
197+
body: formData
198+
}).then(response => {
199+
if (response.ok) {
200+
showPterodactylAlreadyConfigured()
201+
} else {
202+
showPterodactylForm(2)
203+
}
204+
}).catch(() => {
205+
showPterodactylForm(2)
206+
})
207+
}
208+
209+
function showPterodactylAlreadyConfigured() {
210+
const alreadyConfiguredDiv = document.querySelector('.pterodactyl-already-configured[data-step="2"]')
211+
const formFields = document.querySelectorAll('.pterodactyl-form-fields')
212+
const overrideButton = document.querySelector('.btn-override-settings[data-step="2"]')
213+
214+
if (alreadyConfiguredDiv) {
215+
alreadyConfiguredDiv.classList.remove('d-none')
216+
}
217+
formFields.forEach(field => {
218+
field.classList.add('d-none')
219+
})
220+
if (overrideButton) {
221+
overrideButton.classList.remove('d-none')
222+
}
223+
useExistingPterodactylSettings = true
224+
}
225+
226+
227+
function showPterodactylForm(step) {
228+
const alreadyConfiguredDiv = document.querySelector('.pterodactyl-already-configured[data-step="' + step + '"]')
229+
const formFields = document.querySelectorAll('.pterodactyl-form-fields')
230+
const overrideButton = document.querySelector('.btn-override-settings[data-step="' + step + '"]')
231+
const textError = document.querySelector('.text-error[data-step="' + step + '"]')
232+
233+
if (alreadyConfiguredDiv) {
234+
alreadyConfiguredDiv.classList.add('d-none')
235+
}
236+
formFields.forEach(field => {
237+
field.classList.remove('d-none')
238+
})
239+
if (overrideButton) {
240+
overrideButton.classList.add('d-none')
241+
}
242+
if (textError) {
243+
textError.classList.add('d-none')
244+
}
245+
enableNextButton(step)
246+
useExistingPterodactylSettings = false
247+
}
248+
249+
function enableNextButton(step) {
250+
const form = document.querySelector('form[data-step="' + step + '"]')
251+
if (form) {
252+
const submitButton = form.querySelector('button[type="submit"]')
253+
if (submitButton) {
254+
submitButton.disabled = false
255+
}
256+
}
257+
}
258+
259+
function disableNextButton(step) {
260+
const form = document.querySelector('form[data-step="' + step + '"]')
261+
if (form) {
262+
const submitButton = form.querySelector('button[type="submit"]')
263+
if (submitButton) {
264+
submitButton.disabled = true
265+
}
266+
}
164267
}
165268
166269
function validateStep(step) {
167270
const validateEndpointUrl = '{{ path('first_configuration_validate_step') }}',
168271
form = document.querySelector('form[data-step="' + step + '"]'),
169272
formData = new FormData(form)
170273
171-
// For step 5 (user validation), we also need Pterodactyl API data from step 2
172-
if (step === 5) {
173-
const pterodactylForm = document.querySelector('form[data-step="2"]')
174-
if (pterodactylForm) {
175-
const pterodactylUrlInput = pterodactylForm.querySelector('input[name="pterodactyl_panel_url"]')
176-
const pterodactylApiKeyInput = pterodactylForm.querySelector('input[name="pterodactyl_panel_api_key"]')
177-
178-
if (pterodactylUrlInput && pterodactylUrlInput.value) {
179-
formData.append('pterodactyl_panel_url', pterodactylUrlInput.value)
274+
switch (step) {
275+
case 2:
276+
if (useExistingPterodactylSettings) {
277+
formData.delete('pterodactyl_panel_url')
278+
formData.delete('pterodactyl_panel_api_key')
279+
formData.append('checkExistingSettings', 'true')
180280
}
181-
if (pterodactylApiKeyInput && pterodactylApiKeyInput.value) {
182-
formData.append('pterodactyl_panel_api_key', pterodactylApiKeyInput.value)
281+
break
282+
case 5:
283+
if (useExistingPterodactylSettings) {
284+
formData.append('useExistingPterodactylSettings', 'true')
285+
} else {
286+
const pterodactylForm = document.querySelector('form[data-step="2"]')
287+
if (pterodactylForm) {
288+
const pterodactylUrlInput = pterodactylForm.querySelector('input[name="pterodactyl_panel_url"]')
289+
const pterodactylApiKeyInput = pterodactylForm.querySelector('input[name="pterodactyl_panel_api_key"]')
290+
291+
if (pterodactylUrlInput && pterodactylUrlInput.value) {
292+
formData.append('pterodactyl_panel_url', pterodactylUrlInput.value)
293+
}
294+
if (pterodactylApiKeyInput && pterodactylApiKeyInput.value) {
295+
formData.append('pterodactyl_panel_api_key', pterodactylApiKeyInput.value)
296+
}
297+
}
183298
}
184-
}
299+
break
185300
}
186301
187302
formData.append('step', step)
@@ -193,7 +308,7 @@
193308
})
194309
}
195310
196-
function sendConfiguration() {
311+
function sendConfiguration(submitButton) {
197312
const sendConfigurationEndpointUrl = '{{ path('first_configuration_finish') }}',
198313
successUrl = '{{ path('app_login') }}',
199314
allForms = document.querySelectorAll('form'),
@@ -207,13 +322,29 @@
207322
})
208323
})
209324
325+
if (useExistingPterodactylSettings) {
326+
formData.append('useExistingPterodactylSettings', 'true')
327+
formData.delete('pterodactyl_panel_url')
328+
formData.delete('pterodactyl_panel_api_key')
329+
}
330+
210331
fetch(sendConfigurationEndpointUrl, {
211332
method: 'POST',
212333
body: formData
213334
}).then(response => {
214335
if (response.ok) {
215336
window.location.href = successUrl
337+
} else {
338+
submitButton.disabled = false
339+
return response.json().then(data => {
340+
handleStepError(5, data.message || '{{ 'pteroca.first_configuration.messages.validation_error'|trans }}')
341+
}).catch(() => {
342+
handleStepError(5, '{{ 'pteroca.first_configuration.messages.validation_error'|trans }}')
343+
})
216344
}
345+
}).catch(() => {
346+
handleStepError(5, '{{ 'pteroca.first_configuration.messages.validation_error'|trans }}')
347+
submitButton.disabled = false
217348
})
218349
}
219350

src/Core/Resources/templates/configurator/steps/pterodactyl_settings.html.twig

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,28 @@
1111
</div>
1212
</div>
1313

14+
<div class="pterodactyl-already-configured d-none" data-step="2">
15+
<div class="alert alert-success text-center mb-4" role="alert">
16+
<i class="fas fa-check-circle me-2"></i>
17+
{{ 'pteroca.first_configuration.messages.pterodactyl_already_configured'|trans }}
18+
</div>
19+
</div>
20+
1421
<form data-step="2">
15-
<div class="form-group">
22+
<div class="form-group pterodactyl-form-fields">
1623
<label class="form-control-label required" for="pterodactyl_panel_url">{{ 'pteroca.first_configuration.pterodactyl_panel_url'|trans }}</label>
1724
<div class="form-widget">
18-
<input type="url" id="pterodactyl_panel_url" name="pterodactyl_panel_url" class="form-control" value="" required autofocus autocomplete="pterodactyl_panel_url">
25+
<input type="url" id="pterodactyl_panel_url" name="pterodactyl_panel_url" class="form-control" value="" autofocus autocomplete="pterodactyl_panel_url">
1926
<div class="form-text">
2027
{{ 'pteroca.first_configuration.pterodactyl_panel_url_help'|trans }}
2128
</div>
2229
</div>
2330
</div>
2431

25-
<div class="form-group">
32+
<div class="form-group pterodactyl-form-fields">
2633
<label class="form-control-label required" for="pterodactyl_panel_api_key">{{ 'pteroca.first_configuration.pterodactyl_panel_api_key'|trans }}</label>
2734
<div class="form-widget">
28-
<input type="password" id="pterodactyl_panel_api_key" name="pterodactyl_panel_api_key" class="form-control" value="" required autofocus autocomplete="pterodactyl_panel_api_key">
35+
<input type="password" id="pterodactyl_panel_api_key" name="pterodactyl_panel_api_key" class="form-control" value="" autofocus autocomplete="pterodactyl_panel_api_key">
2936
<div class="form-text">
3037
{{ 'pteroca.first_configuration.pterodactyl_panel_api_key_help'|trans }}
3138
</div>
@@ -41,6 +48,9 @@
4148

4249
<div class="form-group">
4350
<button type="button" class="btn btn-lg btn-block configuration-previous-step float-start me-2">{{ 'pteroca.first_configuration.back'|trans }}</button>
51+
<button type="button" class="btn btn-secondary btn-override-settings float-start me-2 d-none" data-step="2">
52+
{{ 'pteroca.first_configuration.messages.override_settings'|trans }}
53+
</button>
4454
<button type="submit" class="btn btn-primary btn-lg btn-block float-end me-2">{{ 'pteroca.first_configuration.next'|trans }}</button>
4555
</div>
4656
</form>

src/Core/Resources/translations/messages.cn.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,9 @@ pteroca:
817817
pterodactyl_sso_secret: 'Pterodactyl SSO 登录密钥'
818818
show_pterodactyl_logs_in_server_activity: '在服务器活动中显示 Pterodactyl 日志'
819819
current_theme: '当前面板的主题'
820+
renewal_notification_enabled: '启用续订通知'
821+
renewal_notification_min_period_hours: '通知最小频率(小时)'
822+
renewal_notification_on_demand_min_hours: '发送通知前按需最小小时数'
820823
template:
821824
name: '模板名称'
822825
description: '模板描述'
@@ -1170,6 +1173,10 @@ pteroca:
11701173
user_validation_success: '用户验证成功。'
11711174
user_checking_account_creation: '正在检查是否可以创建账户...'
11721175
validation_error: '验证步骤时发生错误。请重试。'
1176+
pterodactyl_not_configured: 'Pterodactyl 尚未配置'
1177+
pterodactyl_already_configured: '检测到现有的 Pterodactyl 配置。您可以跳过此步骤或覆盖现有设置。'
1178+
override_settings: '覆盖设置'
1179+
user_already_exists_in_local_database: '具有此电子邮件地址的用户已在本地数据库中存在。'
11731180

11741181
error:
11751182
404:

src/Core/Resources/translations/messages.de.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pteroca:
2020
user_blocked: 'Ihr Konto wurde gesperrt.'
2121
dont_have_account_yet: 'Noch kein Konto? Jetzt registrieren!'
2222
subtitle: 'Melden Sie sich in Ihrem Konto an'
23+
invalid_credentials: 'Ungültige E-Mail oder Passwort.'
2324

2425
register:
2526
title: 'Registrieren'
@@ -112,6 +113,7 @@ pteroca:
112113
amount_hint: 'Geben Sie den Betrag ein, den Sie aufladen möchten.'
113114
view_transaction_history: 'Transaktionsverlauf anzeigen'
114115
view_all_payments_description: 'Zeigen Sie alle Zahlungen an, die auf Ihrem Konto getätigt wurden.'
116+
amount_must_be_positive: 'Der Betrag muss positiv sein.'
115117

116118
product:
117119
information: 'Informationen'
@@ -814,6 +816,9 @@ pteroca:
814816
pterodactyl_sso_enabled: 'Aktiviere Single-Sign-On-Anmeldung mit dem Pterodactyl-Panel (SSO-Schlüssel muss festgelegt sein)'
815817
pterodactyl_sso_secret: 'Pterodactyl SSO-Anmeldeschlüssel'
816818
show_pterodactyl_logs_in_server_activity: 'Zeige Pterodactyl-Logs in der Serveraktivität an'
819+
renewal_notification_enabled: 'Erneuerungsbenachrichtigungen aktivieren'
820+
renewal_notification_min_period_hours: 'Minimale Benachrichtigungshäufigkeit (in Stunden)'
821+
renewal_notification_on_demand_min_hours: 'Mindeststunden vor dem Senden der Benachrichtigung auf Anfrage'
817822
current_theme: 'Aktuelles Thema des Panels'
818823
template:
819824
name: 'Vorlagenname'
@@ -859,6 +864,8 @@ pteroca:
859864
restore: 'Wiederherstellen'
860865
pterodactyl_user_not_found: 'Benutzer nicht in Pterodactyl gefunden, aber erfolgreich aus PteroCA gelöscht.'
861866
cannot_delete_user_with_active_servers: 'Benutzer mit {{ count }} aktiven Server(n) kann nicht gelöscht werden. Bitte löschen oder suspendieren Sie die Server zuerst.'
867+
account_restored: 'Das Benutzerkonto wurde aus einem zuvor gelöschten Konto wiederhergestellt.'
868+
created_successfully: 'Der Benutzer wurde erfolgreich erstellt.'
862869
payment:
863870
session_id: 'Sitzungs-ID'
864871
status: 'Status'
@@ -1166,6 +1173,10 @@ pteroca:
11661173
user_validation_success: 'Benutzervalidierung erfolgreich.'
11671174
user_checking_account_creation: 'Überprüfung, ob das Konto erstellt werden kann...'
11681175
validation_error: 'Ein Fehler ist bei der Validierung des Schritts aufgetreten. Bitte versuchen Sie es erneut.'
1176+
pterodactyl_not_configured: 'Pterodactyl ist noch nicht konfiguriert'
1177+
pterodactyl_already_configured: 'Vorhandene Pterodactyl-Konfiguration erkannt. Sie können diesen Schritt überspringen oder die vorhandenen Einstellungen überschreiben.'
1178+
override_settings: 'Einstellungen überschreiben'
1179+
user_already_exists_in_local_database: 'Ein Benutzer mit dieser E-Mail-Adresse existiert bereits in der lokalen Datenbank.'
11691180

11701181
error:
11711182
404:

0 commit comments

Comments
 (0)