Compare commits

...

3 Commits

Author SHA1 Message Date
efcf06969c ver. 0.308: kolory statusow zamowien + poprawki bezpieczenstwa
- Kolorowe badge statusow na liscie zamowien (pp_shop_statuses.color)
- Walidacja hex koloru z DB (regex), sanityzacja HTML transport
- Polaczenie 2 zapytan SQL w jedno orderStatusData()
- Path-based form submit w table-list.php (admin URL routing)
- 11 nowych testow (750 total, 2114 assertions)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 20:57:56 +01:00
56c931f7da Add remote database host and update migration SQL
- Added a new remote host configuration to config.php for database connection.
- Updated migration script to ensure proper addition of 'min_order_amount' column in pp_shop_payment_methods table.
- Created .gitignore file to exclude cache directory.
- Added project configuration file for Serena with initial settings and tool configurations.
2026-02-22 18:06:15 +01:00
54edbd21f6 build: update package v0.307
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 18:04:02 +01:00
18 changed files with 416 additions and 21 deletions

View File

@@ -14,7 +14,41 @@
"Bash(powershell -Command \"& { Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::OpenRead\\(''updates/0.20/ver_0.296.zip''\\).Entries | ForEach-Object { Write-Output $_.FullName } }\")",
"Bash(powershell -Command \"Compress-Archive -Path ''*'' -DestinationPath ''../ver_0.296.zip'' -Force\")",
"Bash(powershell -Command \"Add-Type -AssemblyName System.IO.Compression.FileSystem; [IO.Compression.ZipFile]::OpenRead\\(\\(Resolve-Path ''updates/0.20/ver_0.296.zip''\\)\\).Entries.FullName\")",
"Bash(powershell -Command \"Compress-Archive -Path ''*'' -DestinationPath ''../ver_0.297.zip'' -Force\")"
"Bash(powershell -Command \"Compress-Archive -Path ''*'' -DestinationPath ''../ver_0.297.zip'' -Force\")",
"Bash(powershell -Command \"Compress-Archive -Path ''*'' -DestinationPath ''../../updates/0.20/ver_0.299.zip'' -Force\")",
"Bash(powershell -Command \"Remove-Item -Recurse -Force 'c:/visual studio code/projekty/shopPRO/temp/temp_299'\":*)",
"Bash(powershell -Command \"\\(Get-ChildItem ''c:/visual studio code/projekty/shopPRO/updates/0.20/ver_0.299.zip''\\).Length; [System.IO.Compression.ZipFile]::OpenRead\\(''c:/visual studio code/projekty/shopPRO/updates/0.20/ver_0.299.zip''\\).Entries | ForEach-Object { $_.FullName }\")",
"Bash(powershell -Command \"Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::OpenRead\\(''c:/visual studio code/projekty/shopPRO/updates/0.20/ver_0.299.zip''\\).Entries | ForEach-Object { $_.FullName }\")",
"Bash(unzip:*)",
"mcp__serena__find_symbol",
"mcp__serena__find_file",
"mcp__serena__activate_project",
"mcp__serena__check_onboarding_performed",
"Bash(tail:*)",
"WebFetch(domain:shoppro.project-dc.pl)",
"Bash(cd:*)",
"mcp__serena__get_symbols_overview",
"mcp__serena__search_for_pattern",
"mcp__serena__read_file",
"Bash(cd \"/c/visual studio code/projekty/shopPRO\" && powershell.exe -ExecutionPolicy Bypass -File \"C:/visual studio code/projekty/shopPRO/test.ps1\" 2>&1)",
"mcp__serena__replace_content",
"Bash(cd \"/c/visual studio code/projekty/shopPRO\" && npx sass admin/layout/style-scss/style.scss admin/layout/style-css/style.css --source-map 2>&1)",
"Bash(head:*)",
"Bash(cd \"/c/visual studio code/projekty/shopPRO\" && rm -rf temp/temp_304 && powershell -File \"./build-update.ps1\" -FromTag v0.303 -ToTag v0.304 -ChangelogEntry \"NEW - konfigurowalne limity kwotowe metod platnosci \\(min/max kwota zamowienia\\)\" 2>&1)",
"Bash(cd \"/c/visual studio code/projekty/shopPRO\" && rm -rf temp/temp_305 && powershell -File \"./build-update.ps1\" -FromTag v0.304 -ToTag v0.305 -ChangelogEntry \"FIX - naprawa kolejnosci atrybutow permutacji, NEW - pasek postepu darmowej dostawy w koszyku\" 2>&1)",
"Bash(xxd:*)",
"mcp__serena__list_dir",
"Bash(cd \"/c/visual studio code/projekty/shopPRO\" && powershell -ExecutionPolicy Bypass -File build-update.ps1 -FromTag v0.305 -ToTag v0.306 -ChangelogEntry \"FIX - ukrywanie form dostawy gdy nie ma dostepnych form platnosci\" 2>&1)",
"Bash(powershell:*)",
"Bash(powershell.exe:*)",
"Bash(cd \"/c/visual studio code/projekty/shopPRO\" && rm -f updates/0.30/ver_0.304.zip updates/0.30/ver_0.304_manifest.json updates/0.30/ver_0.304_sql.txt updates/0.30/ver_0.304_files.txt && powershell -ExecutionPolicy Bypass -File build-update.ps1 -FromTag v0.303 -ToTag v0.304 -ChangelogEntry \"NEW - konfigurowalne limity kwotowe metod platnosci \\(min/max kwota zamowienia\\)\" 2>&1)",
"Bash(cd \"/c/visual studio code/projekty/shopPRO\" && rm -f updates/0.30/ver_0.305.zip updates/0.30/ver_0.305_manifest.json updates/0.30/ver_0.305_sql.txt updates/0.30/ver_0.305_files.txt && powershell -ExecutionPolicy Bypass -File build-update.ps1 -FromTag v0.304 -ToTag v0.305 -ChangelogEntry \"FIX - naprawa kolejnosci atrybutow permutacji, NEW - pasek postepu darmowej dostawy w koszyku\" 2>&1)",
"Bash(cd \"/c/visual studio code/projekty/shopPRO\" && rm -rf temp/temp_305 && powershell -ExecutionPolicy Bypass -File build-update.ps1 -FromTag v0.304 -ToTag v0.305 -ChangelogEntry \"FIX - naprawa kolejnosci atrybutow permutacji, NEW - pasek postepu darmowej dostawy w koszyku\" 2>&1)",
"Bash(cd \"/c/visual studio code/projekty/shopPRO\" && rm -rf temp/temp_305 && sleep 2 && powershell -ExecutionPolicy Bypass -Command \"& { \\\\$env:DOTNET_GCServer = 1; & './build-update.ps1' -FromTag v0.304 -ToTag v0.305 -ChangelogEntry 'FIX - naprawa kolejnosci atrybutow permutacji, NEW - pasek postepu darmowej dostawy w koszyku' }\" 2>&1)",
"Bash(python3:*)",
"Bash(python:*)",
"Bash(grep:*)",
"Bash(grep ^<b>ver:*)"
]
}
}

File diff suppressed because one or more lines are too long

1
.serena/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/cache

117
.serena/project.yml Normal file
View File

@@ -0,0 +1,117 @@
# the name by which the project can be referenced within Serena
project_name: "shopPRO"
# list of languages for which language servers are started; choose from:
# al bash clojure cpp csharp
# csharp_omnisharp dart elixir elm erlang
# fortran fsharp go groovy haskell
# java julia kotlin lua markdown
# matlab nix pascal perl php
# php_phpactor powershell python python_jedi r
# rego ruby ruby_solargraph rust scala
# swift terraform toml typescript typescript_vts
# vue yaml zig
# (This list may be outdated. For the current list, see values of Language enum here:
# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py
# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.)
# Note:
# - For C, use cpp
# - For JavaScript, use typescript
# - For Free Pascal/Lazarus, use pascal
# Special requirements:
# Some languages require additional setup/installations.
# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers
# When using multiple languages, the first language server that supports a given file will be used for that file.
# The first language is the default language and the respective language server will be used as a fallback.
# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored.
languages:
- typescript
# the encoding used by text files in the project
# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings
encoding: "utf-8"
# whether to use project's .gitignore files to ignore files
ignore_all_files_in_gitignore: true
# list of additional paths to ignore in this project.
# Same syntax as gitignore, so you can use * and **.
# Note: global ignored_paths from serena_config.yml are also applied additively.
ignored_paths: []
# whether the project is in read-only mode
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
# Added on 2025-04-18
read_only: false
# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details.
# Below is the complete list of tools for convenience.
# To make sure you have the latest list of tools, and to view their descriptions,
# execute `uv run scripts/print_tool_overview.py`.
#
# * `activate_project`: Activates a project by name.
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
# * `create_text_file`: Creates/overwrites a file in the project directory.
# * `delete_lines`: Deletes a range of lines within a file.
# * `delete_memory`: Deletes a memory from Serena's project-specific memory store.
# * `execute_shell_command`: Executes a shell command.
# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced.
# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type).
# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type).
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
# * `initial_instructions`: Gets the initial instructions for the current project.
# Should only be used in settings where the system prompt cannot be set,
# e.g. in clients you have no control over, like Claude Desktop.
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
# * `insert_at_line`: Inserts content at a given line in a file.
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
# * `list_memories`: Lists memories in Serena's project-specific memory store.
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context).
# * `read_file`: Reads a file within the project directory.
# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store.
# * `remove_project`: Removes a project from the Serena configuration.
# * `replace_lines`: Replaces a range of lines within a file with new content.
# * `replace_symbol_body`: Replaces the full definition of a symbol.
# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen.
# * `search_for_pattern`: Performs a search for a pattern in the project.
# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase.
# * `switch_modes`: Activates modes by providing a list of their names
# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information.
# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task.
# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed.
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
excluded_tools: []
# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default)
included_optional_tools: []
# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools.
# This cannot be combined with non-empty excluded_tools or included_optional_tools.
fixed_tools: []
# list of mode names to that are always to be included in the set of active modes
# The full set of modes to be activated is base_modes + default_modes.
# If the setting is undefined, the base_modes from the global configuration (serena_config.yml) apply.
# Otherwise, this setting overrides the global configuration.
# Set this to [] to disable base modes for this project.
# Set this to a list of mode names to always include the respective modes for this project.
base_modes:
# list of mode names that are to be activated by default.
# The full set of modes to be activated is base_modes + default_modes.
# If the setting is undefined, the default_modes from the global configuration (serena_config.yml) apply.
# Otherwise, this overrides the setting from the global configuration (serena_config.yml).
# This setting can, in turn, be overridden by CLI parameters (--mode).
default_modes:
# initial prompt for the project. It will always be given to the LLM upon activating the project
# (contrary to the memories, which are loaded on demand).
initial_prompt: ""
# override of the corresponding setting in serena_config.yml, see the documentation there.
# If null or missing, the value from the global config is used.
symbol_info_budget:

View File

@@ -36,7 +36,7 @@ composer test
PHPUnit 9.6 via `phpunit.phar`. Bootstrap: `tests/bootstrap.php`. Config: `phpunit.xml`.
Current suite: **739 tests, 2089 assertions**.
Current suite: **750 tests, 2114 assertions**.
### Creating Updates
See `docs/UPDATE_INSTRUCTIONS.md` for the full procedure. Updates are ZIP packages in `updates/0.XX/`. Never include `*.md` files, `updates/changelog.php`, or root `.htaccess` in update ZIPs.

View File

@@ -9,7 +9,7 @@ $buildUrl = function(array $params = []) use ($list): string {
}
}
$qs = http_build_query($query);
return $list->basePath . ($qs ? ('?' . $qs) : '');
return $list->basePath . $qs;
};
$currentSort = $list->sort['column'] ?? '';
@@ -92,7 +92,7 @@ $isCompactColumn = function(array $column): bool {
<div class="panel-body">
<div class="js-table-filters-wrapper table-filters-wrapper<?= $hasActiveFilters ? ' open' : ''; ?>">
<form method="get" action="<?= htmlspecialchars($list->basePath, ENT_QUOTES, 'UTF-8'); ?>" class="row mb15 js-table-filters-form">
<form method="get" action="<?= htmlspecialchars($list->basePath, ENT_QUOTES, 'UTF-8'); ?>" data-path-submit="<?= htmlspecialchars($list->basePath, ENT_QUOTES, 'UTF-8'); ?>" class="row mb15 js-table-filters-form">
<?php foreach ($list->filters as $filter): ?>
<?php
$filterKey = (string)($filter['key'] ?? '');
@@ -292,7 +292,7 @@ $isCompactColumn = function(array $column): bool {
</ul>
</div>
<div class="col-sm-6 text-right">
<form method="get" action="<?= htmlspecialchars($list->basePath, ENT_QUOTES, 'UTF-8'); ?>" class="form-inline table-list-per-page-form">
<form method="get" action="<?= htmlspecialchars($list->basePath, ENT_QUOTES, 'UTF-8'); ?>" data-path-submit="<?= htmlspecialchars($list->basePath, ENT_QUOTES, 'UTF-8'); ?>" class="form-inline table-list-per-page-form">
<?php foreach ($list->query as $key => $value): ?>
<?php if ($key !== 'per_page' && $key !== 'page'): ?>
<input type="hidden" name="<?= htmlspecialchars((string)$key, ENT_QUOTES, 'UTF-8'); ?>" value="<?= htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8'); ?>" />
@@ -300,7 +300,7 @@ $isCompactColumn = function(array $column): bool {
<?php endforeach; ?>
<input type="hidden" name="page" value="1" />
Wyświetlaj
<select name="per_page" class="form-control input-sm" onchange="this.form.submit()">
<select name="per_page" class="form-control input-sm js-per-page-select">
<?php foreach ($list->perPageOptions as $opt): ?>
<option value="<?= (int)$opt; ?>"<?= ((int)$opt === $perPage) ? ' selected="selected"' : ''; ?>><?= (int)$opt; ?></option>
<?php endforeach; ?>
@@ -529,5 +529,26 @@ $isCompactColumn = function(array $column): bool {
saveFilterState(true);
}
});
// --- Path-based form submission (admin URL routing) ---
$(document).off('submit.tablePathSubmit', 'form[data-path-submit]');
$(document).on('submit.tablePathSubmit', 'form[data-path-submit]', function(e) {
e.preventDefault();
var basePath = $(this).attr('data-path-submit');
var data = $(this).serializeArray();
var parts = [];
for (var i = 0; i < data.length; i++) {
if (String(data[i].value) !== '') {
parts.push(encodeURIComponent(data[i].name) + '=' + encodeURIComponent(data[i].value));
}
}
window.location.href = basePath + (parts.length ? parts.join('&') : '');
});
// Per-page select auto-submit
$(document).off('change.tablePerPage', '.js-per-page-select');
$(document).on('change.tablePerPage', '.js-per-page-select', function() {
$(this).closest('form').trigger('submit');
});
})(window.jQuery);
</script>

View File

@@ -30,6 +30,14 @@ class OrderAdminService
return $this->orders->orderStatuses();
}
/**
* @return array{names: array<int, string>, colors: array<int, string>}
*/
public function statusData(): array
{
return $this->orders->orderStatusData();
}
/**
* @return array{items: array<int, array<string, mixed>>, total: int}
*/

View File

@@ -245,25 +245,43 @@ class OrderRepository
public function orderStatuses(): array
{
$rows = $this->db->select('pp_shop_statuses', ['id', 'status'], [
$data = $this->orderStatusData();
return $data['names'];
}
/**
* Zwraca nazwy i kolory statusów w jednym zapytaniu.
*
* @return array{names: array<int, string>, colors: array<int, string>}
*/
public function orderStatusData(): array
{
$rows = $this->db->select('pp_shop_statuses', ['id', 'status', 'color'], [
'ORDER' => ['o' => 'ASC'],
]);
$names = [];
$colors = [];
if (!is_array($rows)) {
return [];
return ['names' => $names, 'colors' => $colors];
}
$result = [];
foreach ($rows as $row) {
$id = (int)($row['id'] ?? 0);
if ($id < 0) {
continue;
}
$result[$id] = (string)($row['status'] ?? '');
$names[$id] = (string)($row['status'] ?? '');
$color = trim((string)($row['color'] ?? ''));
if ($color !== '' && preg_match('/^#[0-9a-fA-F]{3,6}$/', $color)) {
$colors[$id] = $color;
}
}
return $result;
return ['names' => $names, 'colors' => $colors];
}
public function nextOrderId(int $orderId): ?int

View File

@@ -69,7 +69,9 @@ class ShopOrderController
$listRequest['perPage']
);
$statusesMap = $this->service->statuses();
$statusData = $this->service->statusData();
$statusesMap = $statusData['names'];
$statusColorsMap = $statusData['colors'];
$rows = [];
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
@@ -77,7 +79,15 @@ class ShopOrderController
$orderId = (int)($item['id'] ?? 0);
$orderNumber = (string)($item['number'] ?? '');
$statusId = (int)($item['status'] ?? 0);
$statusLabel = (string)($statusesMap[$statusId] ?? ('Status #' . $statusId));
$statusLabel = htmlspecialchars((string)($statusesMap[$statusId] ?? ('Status #' . $statusId)), ENT_QUOTES, 'UTF-8');
$statusColor = isset($statusColorsMap[$statusId]) ? $statusColorsMap[$statusId] : '';
if ($statusColor !== '') {
$textColor = $this->contrastTextColor($statusColor);
$statusHtml = '<span class="label" style="background-color:' . htmlspecialchars($statusColor, ENT_QUOTES, 'UTF-8') . ';color:' . $textColor . '">' . $statusLabel . '</span>';
} else {
$statusHtml = $statusLabel;
}
$rows[] = [
'lp' => $lp++ . '.',
@@ -86,13 +96,13 @@ class ShopOrderController
'paid' => ((int)($item['paid'] ?? 0) === 1)
? '<i class="fa fa-check text-success"></i>'
: '<i class="fa fa-times text-dark"></i>',
'status' => htmlspecialchars($statusLabel, ENT_QUOTES, 'UTF-8'),
'status' => $statusHtml,
'summary' => number_format((float)($item['summary'] ?? 0), 2, '.', ' ') . ' zł',
'client' => htmlspecialchars((string)($item['client'] ?? ''), ENT_QUOTES, 'UTF-8') . ' | zamówienia: <strong>' . (int)($item['total_orders'] ?? 0) . '</strong>',
'address' => (string)($item['address'] ?? ''),
'order_email' => (string)($item['order_email'] ?? ''),
'client_phone' => (string)($item['client_phone'] ?? ''),
'transport' => (string)($item['transport'] ?? ''),
'transport' => $this->sanitizeInlineHtml((string)($item['transport'] ?? '')),
'payment_method' => (string)($item['payment_method'] ?? ''),
'_actions' => [
[
@@ -127,7 +137,7 @@ class ShopOrderController
['key' => 'address', 'label' => 'Adres', 'sortable' => false],
['key' => 'order_email', 'sort_key' => 'order_email', 'label' => 'Email', 'sortable' => true],
['key' => 'client_phone', 'sort_key' => 'client_phone', 'label' => 'Telefon', 'sortable' => true],
['key' => 'transport', 'sort_key' => 'transport', 'label' => 'Dostawa', 'sortable' => true],
['key' => 'transport', 'sort_key' => 'transport', 'label' => 'Dostawa', 'sortable' => true, 'raw' => true],
['key' => 'payment_method', 'sort_key' => 'payment_method', 'label' => 'Płatność', 'sortable' => true],
],
$rows,
@@ -361,4 +371,26 @@ class ShopOrderController
return date('Y-m-d H:i', $ts);
}
}
private function contrastTextColor(string $hex): string
{
$hex = ltrim($hex, '#');
if (strlen($hex) === 3) {
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
}
if (strlen($hex) !== 6) {
return '#fff';
}
$r = hexdec(substr($hex, 0, 2));
$g = hexdec(substr($hex, 2, 2));
$b = hexdec(substr($hex, 4, 2));
$luminance = (0.299 * $r + 0.587 * $g + 0.114 * $b) / 255;
return $luminance > 0.5 ? '#000' : '#fff';
}
private function sanitizeInlineHtml(string $html): string
{
$html = strip_tags($html, '<b><strong><i><em>');
return preg_replace('/<(b|strong|i|em)\s[^>]*>/i', '<$1>', $html);
}
}

View File

@@ -1,5 +1,6 @@
<?php
$database['host'] = 'localhost';
$database['remote_host'] = 'host700513.hostido.net.pl';
$database['user'] = 'host117523_shoppro';
$database['password'] = 'mhA9WCEXEnRfTtbN33hL';
$database['name'] = 'host117523_shoppro';

View File

@@ -4,6 +4,17 @@ Logi zmian z migracji na Domain-Driven Architecture. Najnowsze na gorze.
---
## ver. 0.308 (2026-02-22) - Kolory statusow zamowien + poprawki bezpieczenstwa
- **NEW**: Kolorowe badge statusow na liscie zamowien w admin panelu — kolory pobierane z `pp_shop_statuses.color`, kontrast tekstu obliczany automatycznie
- **FIX**: Walidacja formatu hex koloru z bazy (`/^#[0-9a-fA-F]{3,6}$/`) — odrzucanie nieprawidlowych wartosci
- **FIX**: Sanityzacja HTML w kolumnie "Dostawa" — `strip_tags()` + regex usuwajacy atrybuty z dozwolonych tagow (zapobieganie XSS via `onclick` itp.)
- **OPTYMALIZACJA**: Polaczenie dwoch zapytan SQL do `pp_shop_statuses` (nazwy + kolory) w jedno `orderStatusData()`
- **ZMIANA**: Path-based form submit w `table-list.php` — formularze filtrow i per-page uzywaja JS interceptora z `data-path-submit` zamiast natywnego GET, kompatybilne z admin URL routing
- **NEW**: 11 nowych testow jednostkowych (750 total, 2114 assertions)
---
## ver. 0.307 (2026-02-22) - Przycisk sprawdzania aktualizacji + auto-changelog
- **NEW**: Przycisk "Sprawdz aktualizacje" w panelu admina — ikona odswiezenia obok numeru wersji, klik odpytuje serwer AJAX-em i pokazuje/ukrywa badge "aktualizacja" bez przeladowania strony

View File

@@ -23,7 +23,7 @@ composer test # standard
## Aktualny stan
```text
OK (739 tests, 2089 assertions)
OK (750 tests, 2114 assertions)
```
Zweryfikowano: 2026-02-22 (ver. 0.304)

View File

@@ -1,2 +1,2 @@
ALTER TABLE pp_shop_payment_methods ADD COLUMN min_order_amount DECIMAL(10,2) DEFAULT NULL;
ALTER TABLE pp_shop_payment_methods ADD COLUMN min_order_amount DECIMAL(10,2) DEFAULT NULL;
ALTER TABLE pp_shop_payment_methods ADD COLUMN max_order_amount DECIMAL(10,2) DEFAULT NULL;

View File

@@ -29,6 +29,66 @@ class OrderRepositoryTest extends TestCase
$this->assertSame('W realizacji', $statuses[4]);
}
public function testOrderStatusDataReturnsBothNamesAndColors(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->method('select')
->willReturnCallback(function ($table, $columns, $where) {
if ($table === 'pp_shop_statuses') {
return [
['id' => 0, 'status' => 'Nowe', 'color' => '#ff0000'],
['id' => 4, 'status' => 'W realizacji', 'color' => '#00ff00'],
['id' => 5, 'status' => 'Wysłane', 'color' => ''],
];
}
return [];
});
$repository = new OrderRepository($mockDb);
$data = $repository->orderStatusData();
$this->assertArrayHasKey('names', $data);
$this->assertArrayHasKey('colors', $data);
$this->assertSame('Nowe', $data['names'][0]);
$this->assertSame('W realizacji', $data['names'][4]);
$this->assertSame('Wysłane', $data['names'][5]);
$this->assertSame('#ff0000', $data['colors'][0]);
$this->assertSame('#00ff00', $data['colors'][4]);
$this->assertArrayNotHasKey(5, $data['colors']);
}
public function testOrderStatusDataFiltersInvalidHexColors(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->method('select')
->willReturn([
['id' => 1, 'status' => 'OK', 'color' => '#abc'],
['id' => 2, 'status' => 'Bad', 'color' => 'red'],
['id' => 3, 'status' => 'XSS', 'color' => '#000" onclick="alert(1)'],
['id' => 4, 'status' => 'Valid', 'color' => '#AABBCC'],
]);
$repository = new OrderRepository($mockDb);
$data = $repository->orderStatusData();
$this->assertSame('#abc', $data['colors'][1]);
$this->assertArrayNotHasKey(2, $data['colors']);
$this->assertArrayNotHasKey(3, $data['colors']);
$this->assertSame('#AABBCC', $data['colors'][4]);
}
public function testOrderStatusDataReturnsEmptyOnDbFailure(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->method('select')->willReturn(false);
$repository = new OrderRepository($mockDb);
$data = $repository->orderStatusData();
$this->assertSame([], $data['names']);
$this->assertSame([], $data['colors']);
}
public function testNextAndPrevOrderIdReturnNullForInvalidInput(): void
{
$mockDb = $this->createMock(\medoo::class);

View File

@@ -85,4 +85,72 @@ class ShopOrderControllerTest extends TestCase
$this->assertEquals('Domain\\Product\\ProductRepository', $params[1]->getType()->getName());
$this->assertTrue($params[1]->isOptional());
}
// --- contrastTextColor tests (via reflection) ---
public function testContrastTextColorReturnsBlackForLightColor(): void
{
$result = $this->invokePrivate('contrastTextColor', ['#ffffff']);
$this->assertSame('#000', $result);
}
public function testContrastTextColorReturnsWhiteForDarkColor(): void
{
$result = $this->invokePrivate('contrastTextColor', ['#000000']);
$this->assertSame('#fff', $result);
}
public function testContrastTextColorHandlesShortHex(): void
{
$result = $this->invokePrivate('contrastTextColor', ['#fff']);
$this->assertSame('#000', $result);
$result = $this->invokePrivate('contrastTextColor', ['#000']);
$this->assertSame('#fff', $result);
}
public function testContrastTextColorDefaultsToWhiteForInvalidHex(): void
{
$result = $this->invokePrivate('contrastTextColor', ['invalid']);
$this->assertSame('#fff', $result);
$result = $this->invokePrivate('contrastTextColor', ['#zz']);
$this->assertSame('#fff', $result);
}
// --- sanitizeInlineHtml tests (via reflection) ---
public function testSanitizeInlineHtmlStripsDisallowedTags(): void
{
$result = $this->invokePrivate('sanitizeInlineHtml', ['<b>Bold</b> <script>alert(1)</script> <em>Italic</em>']);
$this->assertSame('<b>Bold</b> alert(1) <em>Italic</em>', $result);
}
public function testSanitizeInlineHtmlStripsAttributesFromAllowedTags(): void
{
$result = $this->invokePrivate('sanitizeInlineHtml', ['<b onclick="alert(1)">Bold</b>']);
$this->assertSame('<b>Bold</b>', $result);
$result = $this->invokePrivate('sanitizeInlineHtml', ['<strong style="color:red" class="x">text</strong>']);
$this->assertSame('<strong>text</strong>', $result);
}
public function testSanitizeInlineHtmlPreservesCleanTags(): void
{
$result = $this->invokePrivate('sanitizeInlineHtml', ['<b>Bold</b> <i>Italic</i> <strong>Strong</strong> <em>Em</em>']);
$this->assertSame('<b>Bold</b> <i>Italic</i> <strong>Strong</strong> <em>Em</em>', $result);
}
public function testSanitizeInlineHtmlHandlesPlainText(): void
{
$result = $this->invokePrivate('sanitizeInlineHtml', ['Kurier DPD']);
$this->assertSame('Kurier DPD', $result);
}
private function invokePrivate(string $method, array $args)
{
$reflection = new \ReflectionMethod($this->controller, $method);
$reflection->setAccessible(true);
return $reflection->invokeArgs($this->controller, $args);
}
}

BIN
updates/0.30/ver_0.307.zip Normal file

Binary file not shown.

View File

@@ -0,0 +1,24 @@
{
"changelog": "NEW - przycisk Sprawdz aktualizacje w panelu admina, NEW - auto-generowany changelog z manifestow",
"version": "0.307",
"files": {
"added": [
],
"deleted": [
],
"modified": [
"admin/templates/site/main-layout.php",
"autoload/admin/Controllers/UpdateController.php"
]
},
"checksum_zip": "sha256:175c64537f0238a03903758c2d8ab154277b3889d35551ede17d158e3f62de7d",
"sql": [
],
"date": "2026-02-22",
"directories_deleted": [
]
}

View File

@@ -1,5 +1,5 @@
<?
$current_ver = 307;
$current_ver = 308;
for ($i = 1; $i <= $current_ver; $i++)
{