update
This commit is contained in:
2
.serena/.gitignore
vendored
Normal file
2
.serena/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/cache
|
||||||
|
/project.local.yml
|
||||||
44
.serena/memories/project_overview.md
Normal file
44
.serena/memories/project_overview.md
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# Project Overview: pomysloweprezenty.pl
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Polish e-commerce website (gift shop) - a custom-built online store at pomysloweprezenty.pl.
|
||||||
|
|
||||||
|
## Tech Stack
|
||||||
|
- **Language**: PHP (no framework, custom MVC-like architecture)
|
||||||
|
- **Database**: MySQL via RedBeanPHP ORM (`\R::`) and Medoo query builder
|
||||||
|
- **Email**: PHPMailer
|
||||||
|
- **Caching**: Redis (optional)
|
||||||
|
- **Frontend**: PHP templates, SCSS for admin styles, vanilla JavaScript
|
||||||
|
- **Libraries**: Included directly in `/libraries/` (no Composer)
|
||||||
|
- **Server**: Apache (.htaccess files present)
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
- **`autoload/`** - Core PHP classes (auto-loaded via custom spl_autoload_register)
|
||||||
|
- `Domain/` - Business logic repositories (Article, Product, Category, Order, Client, etc.)
|
||||||
|
- `admin/Controllers/` - Admin panel controllers
|
||||||
|
- `admin/ViewModels/` - View models for admin forms and tables
|
||||||
|
- `admin/Support/` - Form handling, table list support
|
||||||
|
- `api/` - REST API router and controllers
|
||||||
|
- `front/` - Frontend app, controllers, views, layout engine
|
||||||
|
- `Shared/` - Helpers, Cache, Email, HTML, Image, Template engine
|
||||||
|
- **`admin/`** - Admin panel (templates, JS, AJAX handlers, styles)
|
||||||
|
- **`templates/`** - Frontend templates (shop, articles, basket, client, etc.)
|
||||||
|
- **`templates_user/`** - User-customizable templates
|
||||||
|
- **`libraries/`** - Third-party libraries (medoo, RedBeanPHP, PHPMailer)
|
||||||
|
- **`cron/`** - Cron job scripts
|
||||||
|
- **`plugins/`** - Plugin system
|
||||||
|
|
||||||
|
## Entry Points
|
||||||
|
- `index.php` - Main frontend entry point
|
||||||
|
- `admin/index.php` - Admin panel
|
||||||
|
- `api.php` - API entry point
|
||||||
|
- `ajax.php` - AJAX handler (frontend)
|
||||||
|
- `admin/ajax.php` - AJAX handler (admin)
|
||||||
|
- `cron.php` - Cron jobs entry point
|
||||||
|
|
||||||
|
## Key Patterns
|
||||||
|
- Repositories pattern for data access (e.g., `ProductRepository`, `OrderRepository`)
|
||||||
|
- PHP templates with `Tpl` engine for rendering
|
||||||
|
- Admin uses form-edit and table-list component patterns
|
||||||
|
- Session-based authentication with IP validation
|
||||||
|
- Timezone: Europe/Warsaw
|
||||||
28
.serena/memories/style_and_conventions.md
Normal file
28
.serena/memories/style_and_conventions.md
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Code Style and Conventions
|
||||||
|
|
||||||
|
## PHP Style
|
||||||
|
- Spaces around brackets in array access: `$_SESSION[ 'key' ]`, `$array[ 'field' ]`
|
||||||
|
- Spaces inside parentheses: `function( $param )`, `if ( condition )`
|
||||||
|
- Opening braces on new line for functions/classes
|
||||||
|
- Opening braces on same line for control structures (mixed - some on new line)
|
||||||
|
- No type hints on most functions (legacy codebase)
|
||||||
|
- No docblocks on most methods
|
||||||
|
- camelCase for methods, PascalCase for classes
|
||||||
|
- File naming: `class.ClassName.php` for autoloaded classes, regular names for others
|
||||||
|
|
||||||
|
## Naming Conventions
|
||||||
|
- Controllers: `{Entity}Controller.php` (e.g., `ShopProductController.php`)
|
||||||
|
- Repositories: `{Entity}Repository.php` (e.g., `ProductRepository.php`)
|
||||||
|
- Views: `{Entity}.php` in `Views/` directory
|
||||||
|
- Templates: kebab-case directories (e.g., `shop-product/`, `shop-basket/`)
|
||||||
|
- Admin templates: organized by feature in `admin/templates/`
|
||||||
|
|
||||||
|
## Database
|
||||||
|
- RedBeanPHP ORM with `\R::` facade
|
||||||
|
- Medoo for complex queries via `$mdb`
|
||||||
|
- Tables use snake_case naming
|
||||||
|
|
||||||
|
## Frontend
|
||||||
|
- SCSS compiled to CSS in admin (`style.scss` -> `style.css`)
|
||||||
|
- Vanilla JavaScript (no build system, no npm/node)
|
||||||
|
- jQuery likely used in admin panel
|
||||||
26
.serena/memories/suggested_commands.md
Normal file
26
.serena/memories/suggested_commands.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Suggested Commands
|
||||||
|
|
||||||
|
## System (Windows with Git Bash)
|
||||||
|
- `git` - version control
|
||||||
|
- `ls`, `cd`, `grep`, `find` - standard Unix utils via Git Bash
|
||||||
|
|
||||||
|
## Development
|
||||||
|
- No build system or package manager (no Composer, no npm)
|
||||||
|
- PHP files are edited directly and served by Apache
|
||||||
|
- SCSS needs manual compilation for admin styles (`admin/layout/style-scss/style.scss`)
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
- No automated test framework detected
|
||||||
|
- Manual testing via browser
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
- FTP-based deployment (`.vscode/ftp-kr.sync.cache.json` in gitignore suggests VS Code FTP extension)
|
||||||
|
|
||||||
|
## Cron
|
||||||
|
- `cron.php` - main cron entry point
|
||||||
|
- `cron-turstmate.php` - Trustmate integration cron
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- No linter or formatter configured
|
||||||
|
- No CI/CD pipeline detected
|
||||||
|
- Changes are tested manually on the live/staging server
|
||||||
11
.serena/memories/task_completion_checklist.md
Normal file
11
.serena/memories/task_completion_checklist.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Task Completion Checklist
|
||||||
|
|
||||||
|
When completing a task in this project:
|
||||||
|
|
||||||
|
1. **Code style**: Follow existing spacing conventions (spaces inside brackets/parentheses)
|
||||||
|
2. **No build step**: PHP changes take effect immediately (no compilation needed, except SCSS)
|
||||||
|
3. **SCSS**: If admin styles were changed, SCSS needs to be recompiled
|
||||||
|
4. **Test manually**: No automated tests - verify changes work by describing what to test in browser
|
||||||
|
5. **Database**: If DB schema changes are needed, document them (no migration system)
|
||||||
|
6. **Git**: Commit changes with descriptive message
|
||||||
|
7. **Security**: Be careful with SQL queries, XSS, and session handling
|
||||||
135
.serena/project.yml
Normal file
135
.serena/project.yml
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
# the name by which the project can be referenced within Serena
|
||||||
|
project_name: "pomysloweprezenty.pl"
|
||||||
|
|
||||||
|
|
||||||
|
# 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"
|
||||||
|
|
||||||
|
# line ending convention to use when writing source files.
|
||||||
|
# Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default)
|
||||||
|
# This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings.
|
||||||
|
line_ending:
|
||||||
|
|
||||||
|
# The language backend to use for this project.
|
||||||
|
# If not set, the global setting from serena_config.yml is used.
|
||||||
|
# Valid values: LSP, JetBrains
|
||||||
|
# Note: the backend is fixed at startup. If a project with a different backend
|
||||||
|
# is activated post-init, an error will be returned.
|
||||||
|
language_backend:
|
||||||
|
|
||||||
|
# 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: ""
|
||||||
|
|
||||||
|
# time budget (seconds) per tool call for the retrieval of additional symbol information
|
||||||
|
# such as docstrings or parameter information.
|
||||||
|
# This overrides the corresponding setting in the global configuration; see the documentation there.
|
||||||
|
# If null or missing, use the setting from the global configuration.
|
||||||
|
symbol_info_budget:
|
||||||
|
|
||||||
|
# list of regex patterns which, when matched, mark a memory entry as read‑only.
|
||||||
|
# Extends the list from the global configuration, merging the two lists.
|
||||||
|
read_only_memory_patterns: []
|
||||||
229
admin/layout/style-css/order-details-mobile.css
Normal file
229
admin/layout/style-css/order-details-mobile.css
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
/* ==========================================================================
|
||||||
|
Order Details — Mobile Responsive
|
||||||
|
Scoped to .od-actions and .order-details
|
||||||
|
All rules inside @media (max-width: 767px) — desktop layout untouched
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
|
||||||
|
/* --- 1. Action buttons bar --- */
|
||||||
|
|
||||||
|
.od-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
overflow-x: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.od-actions .btn {
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
margin-right: 0 !important;
|
||||||
|
margin-left: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide button labels on small screens — show only icons */
|
||||||
|
.od-actions .od-actions-label {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Integrations dropdown — push to end, cancel float */
|
||||||
|
.od-actions .pull-right,
|
||||||
|
.od-actions .dropdown.pull-right {
|
||||||
|
float: none !important;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dropdown menu must not clip inside scroll container */
|
||||||
|
.od-actions .dropdown {
|
||||||
|
position: static;
|
||||||
|
}
|
||||||
|
|
||||||
|
.od-actions + .order-details .dropdown-menu,
|
||||||
|
#integrationsDropdownMenu.show {
|
||||||
|
position: fixed;
|
||||||
|
top: auto;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
border-radius: 12px 12px 0 0;
|
||||||
|
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.15);
|
||||||
|
z-index: 1060;
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#integrationsDropdownMenu.show .dropdown-item {
|
||||||
|
padding: 12px 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- 2. Full-width columns stacking --- */
|
||||||
|
|
||||||
|
.order-details .col-sm-6,
|
||||||
|
.order-details .col-lg-8,
|
||||||
|
.order-details .col-lg-4,
|
||||||
|
.order-details .col-xl-5,
|
||||||
|
.order-details .col-xl-7 {
|
||||||
|
width: 100%;
|
||||||
|
flex: 0 0 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Spacing between stacked sections */
|
||||||
|
.order-details .col-sm-6 {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* "Resend confirmation" button — allow text to wrap */
|
||||||
|
.order-details .resend_order_confirmation_email .btn {
|
||||||
|
font-size: 12px;
|
||||||
|
white-space: normal;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- 3. Status section --- */
|
||||||
|
|
||||||
|
.order-details .status_select #order-status {
|
||||||
|
max-width: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-details .buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-details .buttons .btn {
|
||||||
|
width: 100%;
|
||||||
|
white-space: normal;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-details .order-status {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-details .order-history {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- 4. Products table — compact list --- */
|
||||||
|
|
||||||
|
.order-details table thead {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-details table {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-details table tbody {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-details table tr.order-product-details {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 12px 0;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-details table tr.order-product-details:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Image cell */
|
||||||
|
.order-details table td.product-image {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
margin-right: 10px;
|
||||||
|
float: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-details table td.product-image img {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Name cell — takes remaining space next to image */
|
||||||
|
.order-details table tr.order-product-details td:nth-child(2) {
|
||||||
|
flex: 1 1 0;
|
||||||
|
min-width: 0;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-details table tr.order-product-details td:nth-child(2) a {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 13px;
|
||||||
|
display: block;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide quantity, price, discounted-price, total columns on mobile */
|
||||||
|
.order-details table tr.order-product-details td.tab-center,
|
||||||
|
.order-details table tr.order-product-details td.tab-right {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show mobile price summary line */
|
||||||
|
.od-mobile-price-line {
|
||||||
|
display: block !important;
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2a3042;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- 5. Notes --- */
|
||||||
|
|
||||||
|
.order-details #notes {
|
||||||
|
font-size: 14px;
|
||||||
|
min-height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- 6. Order summary panel --- */
|
||||||
|
|
||||||
|
.order-details .panel .panel-body {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- 7. Paid status --- */
|
||||||
|
|
||||||
|
.order-details .paid-status .panel-body a {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- 8. Message section — hide empty spacer columns --- */
|
||||||
|
|
||||||
|
.order-details .d-none.d-lg-block {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Desktop: action bar spacing (replaces mr5 class) --- */
|
||||||
|
.od-actions .btn + .btn,
|
||||||
|
.od-actions .btn + .dropdown {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile price line — hidden on desktop */
|
||||||
|
.od-mobile-price-line {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -57,6 +57,36 @@
|
|||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- Filter toggle --- */
|
||||||
|
|
||||||
|
.table-filters-wrapper {
|
||||||
|
display: none;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-filters-wrapper.open {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.js-filter-toggle-btn.active {
|
||||||
|
background-color: #e8e8e8;
|
||||||
|
border-color: #adadad;
|
||||||
|
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-filter-badge {
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 16px;
|
||||||
|
padding: 2px 5px;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
color: #fff;
|
||||||
|
background-color: #337ab7;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
/* --- Column visibility toggle --- */
|
/* --- Column visibility toggle --- */
|
||||||
|
|
||||||
.table-list-header-actions {
|
.table-list-header-actions {
|
||||||
|
|||||||
@@ -455,23 +455,14 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.site-content {
|
.site-content {
|
||||||
|
margin-left: 0;
|
||||||
&.with-menu {
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
@include respond-above(xs) {
|
|
||||||
width: calc(100% - 243px);
|
|
||||||
|
|
||||||
margin-left: 243px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include respond-below(md) {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
margin-left: 244px;
|
|
||||||
|
@include respond-above(xs) {
|
||||||
|
width: calc(100% - 243px);
|
||||||
|
margin-left: 243px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.top-user {
|
.top-user {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
@@ -1351,39 +1342,6 @@ li.sort-collapsed.sort-hover div {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="checkbox"] {
|
|
||||||
position: relative;
|
|
||||||
width: 40px;
|
|
||||||
height: 20px;
|
|
||||||
-webkit-appearance: none;
|
|
||||||
background: $cGrayLight;
|
|
||||||
outline: none;
|
|
||||||
border-radius: 10px;
|
|
||||||
box-shadow: inset 0 0 5px rgba(0, 0, 0, .2);
|
|
||||||
}
|
|
||||||
|
|
||||||
input:checked[type="checkbox"] {
|
|
||||||
background: $cMenuText;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="checkbox"]:before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
border-radius: 10px;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
background: #fff;
|
|
||||||
transform: scale(1.1);
|
|
||||||
box-shadow: 0 2px 5px rgba(0, 0, 0, .2);
|
|
||||||
transition: .5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:checked[type="checkbox"]:before {
|
|
||||||
left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#images-uploader,
|
#images-uploader,
|
||||||
#files-uploader {
|
#files-uploader {
|
||||||
clear: both;
|
clear: both;
|
||||||
@@ -1783,33 +1741,16 @@ input:checked[type="checkbox"]:before {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#table-products {
|
.product-categories {
|
||||||
.product-categories {
|
display: block;
|
||||||
display: block;
|
width: 100%;
|
||||||
width: 100%;
|
text-wrap: wrap;
|
||||||
text-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.product-name {
|
&--cats {
|
||||||
display: flex;
|
white-space: nowrap;
|
||||||
justify-content: space-between;
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
.duplicate-product {
|
max-width: 600px;
|
||||||
margin-left: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.duplicate-product {
|
|
||||||
float: right;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-success {
|
|
||||||
color: #FFF !important;
|
|
||||||
|
|
||||||
&.btn-create-product {
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2137,6 +2078,10 @@ textarea.form-control {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.order-details {
|
.order-details {
|
||||||
|
.fa-copy {
|
||||||
|
cursor: pointer !important;
|
||||||
|
}
|
||||||
|
|
||||||
.paid-status {
|
.paid-status {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,10 @@ $_SESSION['can_use_rfm'] = true;
|
|||||||
<a href="<?= htmlspecialchars($action->url) ?>" class="btn btn-dark btn-sm" id="g-edit-cancel">
|
<a href="<?= htmlspecialchars($action->url) ?>" class="btn btn-dark btn-sm" id="g-edit-cancel">
|
||||||
<i class="fa fa-reply mr5"></i>Wstecz
|
<i class="fa fa-reply mr5"></i>Wstecz
|
||||||
</a>
|
</a>
|
||||||
|
<?php elseif ($action->name === 'preview'): ?>
|
||||||
|
<a href="<?= htmlspecialchars($action->url) ?>" class="btn btn-info btn-sm" target="_blank">
|
||||||
|
<i class="fa fa-eye mr5"></i><?= htmlspecialchars($action->label) ?>
|
||||||
|
</a>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<a href="<?= htmlspecialchars($action->url) ?>" class="btn <?= htmlspecialchars($action->cssClass) ?> btn-sm">
|
<a href="<?= htmlspecialchars($action->url) ?>" class="btn <?= htmlspecialchars($action->cssClass) ?> btn-sm">
|
||||||
<?= htmlspecialchars($action->label) ?>
|
<?= htmlspecialchars($action->label) ?>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ $buildUrl = function(array $params = []) use ($list): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$qs = http_build_query($query);
|
$qs = http_build_query($query);
|
||||||
return $list->basePath . ($qs ? ('?' . $qs) : '');
|
return $list->basePath . $qs;
|
||||||
};
|
};
|
||||||
|
|
||||||
$currentSort = $list->sort['column'] ?? '';
|
$currentSort = $list->sort['column'] ?? '';
|
||||||
@@ -19,6 +19,14 @@ $totalPages = max(1, (int)($list->pagination['total_pages'] ?? 1));
|
|||||||
$total = (int)($list->pagination['total'] ?? 0);
|
$total = (int)($list->pagination['total'] ?? 0);
|
||||||
$perPage = (int)($list->pagination['per_page'] ?? 15);
|
$perPage = (int)($list->pagination['per_page'] ?? 15);
|
||||||
|
|
||||||
|
$hasActiveFilters = false;
|
||||||
|
foreach ($list->filters as $filter) {
|
||||||
|
if (isset($filter['value']) && (string)$filter['value'] !== '') {
|
||||||
|
$hasActiveFilters = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$isCompactColumn = function(array $column): bool {
|
$isCompactColumn = function(array $column): bool {
|
||||||
$key = strtolower(trim((string)($column['key'] ?? '')));
|
$key = strtolower(trim((string)($column['key'] ?? '')));
|
||||||
$label = strtolower(trim((string)($column['label'] ?? '')));
|
$label = strtolower(trim((string)($column['label'] ?? '')));
|
||||||
@@ -48,6 +56,14 @@ $isCompactColumn = function(array $column): bool {
|
|||||||
<div class="col-sm-4 text-right">
|
<div class="col-sm-4 text-right">
|
||||||
<div class="table-list-header-actions">
|
<div class="table-list-header-actions">
|
||||||
<span class="text-muted">Wyników: <?= $total; ?></span>
|
<span class="text-muted">Wyników: <?= $total; ?></span>
|
||||||
|
<?php if (!empty($list->filters)): ?>
|
||||||
|
<button type="button" class="btn btn-default btn-sm js-filter-toggle-btn<?= $hasActiveFilters ? ' active' : ''; ?>" title="Filtry">
|
||||||
|
<i class="fa fa-filter"></i>
|
||||||
|
<?php if ($hasActiveFilters): ?>
|
||||||
|
<span class="badge badge-primary table-filter-badge"><?= count(array_filter($list->filters, function($f) { return isset($f['value']) && (string)$f['value'] !== ''; })); ?></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</button>
|
||||||
|
<?php endif; ?>
|
||||||
<div class="table-col-toggle-wrapper">
|
<div class="table-col-toggle-wrapper">
|
||||||
<button type="button" class="btn btn-default btn-sm js-col-toggle-btn" title="Widoczność kolumn">
|
<button type="button" class="btn btn-default btn-sm js-col-toggle-btn" title="Widoczność kolumn">
|
||||||
<i class="fa fa-columns"></i>
|
<i class="fa fa-columns"></i>
|
||||||
@@ -75,7 +91,8 @@ $isCompactColumn = function(array $column): bool {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<form method="get" action="<?= htmlspecialchars($list->basePath, ENT_QUOTES, 'UTF-8'); ?>" class="row mb15 js-table-filters-form">
|
<div class="js-table-filters-wrapper table-filters-wrapper<?= $hasActiveFilters ? ' open' : ''; ?>">
|
||||||
|
<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 foreach ($list->filters as $filter): ?>
|
||||||
<?php
|
<?php
|
||||||
$filterKey = (string)($filter['key'] ?? '');
|
$filterKey = (string)($filter['key'] ?? '');
|
||||||
@@ -145,9 +162,10 @@ $isCompactColumn = function(array $column): bool {
|
|||||||
|
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<button type="submit" class="btn btn-primary btn-sm">Szukaj</button>
|
<button type="submit" class="btn btn-primary btn-sm">Szukaj</button>
|
||||||
<a href="<?= htmlspecialchars($list->basePath, ENT_QUOTES, 'UTF-8'); ?>" class="btn btn-default btn-sm">Wyczyść</a>
|
<a href="<?= htmlspecialchars($list->basePath, ENT_QUOTES, 'UTF-8'); ?>" class="btn btn-default btn-sm js-table-filters-clear">Wyczyść</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-hover table-striped table-bordered mbn table-list-table">
|
<table class="table table-hover table-striped table-bordered mbn table-list-table">
|
||||||
@@ -274,7 +292,7 @@ $isCompactColumn = function(array $column): bool {
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6 text-right">
|
<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 foreach ($list->query as $key => $value): ?>
|
||||||
<?php if ($key !== 'per_page' && $key !== 'page'): ?>
|
<?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'); ?>" />
|
<input type="hidden" name="<?= htmlspecialchars((string)$key, ENT_QUOTES, 'UTF-8'); ?>" value="<?= htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8'); ?>" />
|
||||||
@@ -282,7 +300,7 @@ $isCompactColumn = function(array $column): bool {
|
|||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
<input type="hidden" name="page" value="1" />
|
<input type="hidden" name="page" value="1" />
|
||||||
Wyświetlaj
|
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): ?>
|
<?php foreach ($list->perPageOptions as $opt): ?>
|
||||||
<option value="<?= (int)$opt; ?>"<?= ((int)$opt === $perPage) ? ' selected="selected"' : ''; ?>><?= (int)$opt; ?></option>
|
<option value="<?= (int)$opt; ?>"<?= ((int)$opt === $perPage) ? ' selected="selected"' : ''; ?>><?= (int)$opt; ?></option>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
@@ -294,6 +312,40 @@ $isCompactColumn = function(array $column): bool {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
// Table state persistence — redirect ASAP to saved view
|
||||||
|
(function() {
|
||||||
|
var basePath = <?= json_encode($list->basePath); ?>;
|
||||||
|
var stateKey = 'tableListQuery_' + basePath;
|
||||||
|
var clearKey = 'tableListCleared_' + basePath;
|
||||||
|
|
||||||
|
var pathname = window.location.pathname.replace(/\/+$/, '/');
|
||||||
|
var bp = basePath.replace(/\/+$/, '/');
|
||||||
|
|
||||||
|
var queryPart = '';
|
||||||
|
if (pathname.length > bp.length && pathname.indexOf(bp) === 0) {
|
||||||
|
queryPart = pathname.substring(bp.length);
|
||||||
|
}
|
||||||
|
if (!queryPart && window.location.search) {
|
||||||
|
queryPart = window.location.search.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var justCleared = sessionStorage.getItem(clearKey) === '1';
|
||||||
|
sessionStorage.removeItem(clearKey);
|
||||||
|
|
||||||
|
if (queryPart) {
|
||||||
|
localStorage.setItem(stateKey, queryPart);
|
||||||
|
} else if (!justCleared) {
|
||||||
|
var saved = localStorage.getItem(stateKey);
|
||||||
|
if (saved) {
|
||||||
|
window.location.replace(basePath + saved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
(function($) {
|
(function($) {
|
||||||
if (!$) {
|
if (!$) {
|
||||||
@@ -469,5 +521,80 @@ $isCompactColumn = function(array $column): bool {
|
|||||||
saveHiddenCols([]);
|
saveHiddenCols([]);
|
||||||
applyColumnVisibility([]);
|
applyColumnVisibility([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// --- Filter toggle ---
|
||||||
|
var filterStorageKey = 'tableListFilters_' + <?= json_encode($list->basePath); ?>;
|
||||||
|
|
||||||
|
function isFilterVisible() {
|
||||||
|
try {
|
||||||
|
return localStorage.getItem(filterStorageKey) === '1';
|
||||||
|
} catch (e) {}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveFilterState(visible) {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(filterStorageKey, visible ? '1' : '0');
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
var $filterWrapper = $('.js-table-filters-wrapper');
|
||||||
|
var $filterBtn = $('.js-filter-toggle-btn');
|
||||||
|
var hasActiveFilters = $filterWrapper.hasClass('open');
|
||||||
|
|
||||||
|
if (!hasActiveFilters && isFilterVisible()) {
|
||||||
|
$filterWrapper.addClass('open');
|
||||||
|
$filterBtn.addClass('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).off('click.filterToggle', '.js-filter-toggle-btn');
|
||||||
|
$(document).on('click.filterToggle', '.js-filter-toggle-btn', function() {
|
||||||
|
var $wrapper = $('.js-table-filters-wrapper');
|
||||||
|
var $btn = $(this);
|
||||||
|
var isOpen = $wrapper.hasClass('open');
|
||||||
|
|
||||||
|
if (isOpen) {
|
||||||
|
$wrapper.removeClass('open');
|
||||||
|
$btn.removeClass('active');
|
||||||
|
saveFilterState(false);
|
||||||
|
} else {
|
||||||
|
$wrapper.addClass('open');
|
||||||
|
$btn.addClass('active');
|
||||||
|
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');
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- Table state clear on "Wyczyść" ---
|
||||||
|
var stateStorageKey = 'tableListQuery_' + <?= json_encode($list->basePath); ?>;
|
||||||
|
var stateClearKey = 'tableListCleared_' + <?= json_encode($list->basePath); ?>;
|
||||||
|
|
||||||
|
$(document).off('click.tableClearState', '.js-table-filters-clear');
|
||||||
|
$(document).on('click.tableClearState', '.js-table-filters-clear', function() {
|
||||||
|
try {
|
||||||
|
localStorage.removeItem(stateStorageKey);
|
||||||
|
sessionStorage.setItem(stateClearKey, '1');
|
||||||
|
} catch (e) {}
|
||||||
|
});
|
||||||
})(window.jQuery);
|
})(window.jQuery);
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
19
admin/templates/integrations/logs.php
Normal file
19
admin/templates/integrations/logs.php
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?= \Shared\Tpl\Tpl::view('components/table-list', ['list' => $this->viewModel]); ?>
|
||||||
|
|
||||||
|
<div class="mt15">
|
||||||
|
<a href="/admin/integrations/logs_clear/" class="btn btn-danger btn-sm"
|
||||||
|
onclick="return confirm('Na pewno chcesz usunac wszystkie logi?');">
|
||||||
|
<i class="fa fa-trash"></i> Wyczysc wszystkie logi
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(function() {
|
||||||
|
$('body').on('click', '.log-context-btn', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var id = $(this).data('id');
|
||||||
|
$('#log-context-' + id).toggle();
|
||||||
|
$(this).text($('#log-context-' + id).is(':visible') ? 'Ukryj' : 'Pokaz');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -91,6 +91,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- API key -->
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-lg-3 control-label" for="inputDefault">API key</label>
|
||||||
|
<div class="col-lg-9">
|
||||||
|
<div class="bs-component">
|
||||||
|
<div class="input-group">
|
||||||
|
<input class="form-control" type="text" id="api_key" name="api_key" placeholder="" value="<?= $this -> settings['api_key'];?>">
|
||||||
|
<span class="input-group-addon cursor" field-id="api_key">
|
||||||
|
<i class="fa fa-save"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
|
|
||||||
@@ -123,4 +137,4 @@
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,4 +1,26 @@
|
|||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
|
.bulk-action-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
background: #fff3cd;
|
||||||
|
border: 1px solid #ffc107;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bulk-action-bar__info {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #856404;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-col-bulk-check {
|
||||||
|
width: 36px;
|
||||||
|
padding-left: 10px !important;
|
||||||
|
padding-right: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
.product-archive-thumb-wrap {
|
.product-archive-thumb-wrap {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
@@ -96,5 +118,119 @@
|
|||||||
$popup.removeClass('is-visible');
|
$popup.removeClass('is-visible');
|
||||||
$popupImage.attr('src', '');
|
$popupImage.attr('src', '');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// --- Bulk select ---
|
||||||
|
var $table = $('.table-list-table');
|
||||||
|
var $bar = $('#js-bulk-action-bar');
|
||||||
|
var $label = $bar.find('.js-bulk-count-label');
|
||||||
|
|
||||||
|
// Inject select-all checkbox into _checkbox column header
|
||||||
|
$table.find('thead th.table-col-bulk-check').html(
|
||||||
|
'<input type="checkbox" id="js-bulk-select-all" title="Zaznacz wszystkie">'
|
||||||
|
);
|
||||||
|
|
||||||
|
function updateBar() {
|
||||||
|
var count = $table.find('.js-bulk-check:checked').length;
|
||||||
|
if (count > 0) {
|
||||||
|
$label.text('Zaznaczono: ' + count);
|
||||||
|
$bar.show();
|
||||||
|
} else {
|
||||||
|
$bar.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).on('change.bulkSelect', '#js-bulk-select-all', function() {
|
||||||
|
var checked = $(this).is(':checked');
|
||||||
|
$table.find('.js-bulk-check').prop('checked', checked);
|
||||||
|
updateBar();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('change.bulkSelect', '.js-bulk-check', function() {
|
||||||
|
var total = $table.find('.js-bulk-check').length;
|
||||||
|
var checked = $table.find('.js-bulk-check:checked').length;
|
||||||
|
$('#js-bulk-select-all').prop('indeterminate', checked > 0 && checked < total);
|
||||||
|
$('#js-bulk-select-all').prop('checked', checked === total && total > 0);
|
||||||
|
updateBar();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click.bulkDelete', '.js-bulk-delete-btn', function() {
|
||||||
|
var ids = [];
|
||||||
|
$table.find('.js-bulk-check:checked').each(function() {
|
||||||
|
ids.push($(this).val());
|
||||||
|
});
|
||||||
|
if (ids.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var confirmMsg = 'UWAGA! Operacja nieodwracalna!\n\n'
|
||||||
|
+ 'Wybrane produkty (' + ids.length + ' szt.) zostaną trwale usunięte razem ze wszystkimi zdjęciami i załącznikami z serwera.\n\n'
|
||||||
|
+ 'Czy na pewno chcesz usunąć zaznaczone produkty?';
|
||||||
|
|
||||||
|
var doDelete = function() {
|
||||||
|
var $btn = $('.js-bulk-delete-btn');
|
||||||
|
$btn.prop('disabled', true).text('Usuwanie…');
|
||||||
|
|
||||||
|
var formData = [];
|
||||||
|
for (var i = 0; i < ids.length; i++) {
|
||||||
|
formData.push('ids%5B%5D=' + encodeURIComponent(ids[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/admin/product_archive/bulk_delete_permanent/',
|
||||||
|
type: 'POST',
|
||||||
|
data: formData.join('&'),
|
||||||
|
contentType: 'application/x-www-form-urlencoded',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(resp) {
|
||||||
|
if (resp && resp.deleted > 0) {
|
||||||
|
window.location.reload();
|
||||||
|
} else {
|
||||||
|
alert('Nie udało się usunąć produktów. Spróbuj ponownie.');
|
||||||
|
$btn.prop('disabled', false).html('<i class="fa fa-trash-o"></i> Usuń zaznaczone trwale');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
alert('Błąd podczas usuwania produktów. Spróbuj ponownie.');
|
||||||
|
$btn.prop('disabled', false).html('<i class="fa fa-trash-o"></i> Usuń zaznaczone trwale');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof $.confirm === 'function') {
|
||||||
|
$.confirm({
|
||||||
|
title: 'Potwierdzenie',
|
||||||
|
content: confirmMsg,
|
||||||
|
type: 'red',
|
||||||
|
boxWidth: '560px',
|
||||||
|
useBootstrap: false,
|
||||||
|
animation: 'scale',
|
||||||
|
closeAnimation: 'scale',
|
||||||
|
backgroundDismissAnimation: 'shake',
|
||||||
|
container: 'body',
|
||||||
|
theme: 'modern',
|
||||||
|
columnClass: '',
|
||||||
|
typeAnimated: true,
|
||||||
|
lazyOpen: false,
|
||||||
|
draggable: false,
|
||||||
|
closeIcon: true,
|
||||||
|
containerFluid: true,
|
||||||
|
escapeKey: true,
|
||||||
|
backgroundDismiss: true,
|
||||||
|
buttons: {
|
||||||
|
cancel: {
|
||||||
|
text: 'Anuluj',
|
||||||
|
btnClass: 'btn-default'
|
||||||
|
},
|
||||||
|
confirm: {
|
||||||
|
text: 'Tak, usuń trwale',
|
||||||
|
btnClass: 'btn-danger',
|
||||||
|
action: doDelete
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (window.confirm(confirmMsg)) {
|
||||||
|
doDelete();
|
||||||
|
}
|
||||||
|
});
|
||||||
})(window.jQuery);
|
})(window.jQuery);
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
<div id="js-bulk-action-bar" class="bulk-action-bar" style="display:none;">
|
||||||
|
<span class="bulk-action-bar__info js-bulk-count-label">Zaznaczono: 0</span>
|
||||||
|
<button type="button" class="btn btn-danger btn-sm js-bulk-delete-btn">
|
||||||
|
<i class="fa fa-trash-o"></i> Usuń zaznaczone trwale
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<?= \Shared\Tpl\Tpl::view('components/table-list', ['list' => $this->viewModel]); ?>
|
<?= \Shared\Tpl\Tpl::view('components/table-list', ['list' => $this->viewModel]); ?>
|
||||||
|
|
||||||
<?php if (!empty($this->viewModel->customScriptView)): ?>
|
<?php if (!empty($this->viewModel->customScriptView)): ?>
|
||||||
|
|||||||
@@ -1,3 +1,30 @@
|
|||||||
|
<style type="text/css">
|
||||||
|
.attr-copy-btn {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 1px 5px;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 1.5;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid #d0d0d0;
|
||||||
|
border-radius: 3px;
|
||||||
|
color: #999;
|
||||||
|
cursor: pointer;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-left: 4px;
|
||||||
|
transition: background .12s, color .12s, border-color .12s;
|
||||||
|
}
|
||||||
|
.attr-copy-btn:hover {
|
||||||
|
background: #f4f4f4;
|
||||||
|
border-color: #aaa;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
.attr-copy-btn--copied {
|
||||||
|
background: #d4edda !important;
|
||||||
|
border-color: #28a745 !important;
|
||||||
|
color: #28a745 !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
(function() {
|
(function() {
|
||||||
var orderId = <?= (int)($this->order_id ?? 0);?>;
|
var orderId = <?= (int)($this->order_id ?? 0);?>;
|
||||||
@@ -378,6 +405,68 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
function fallbackCopy(text) {
|
||||||
|
var $tmp = $('<textarea>').css({position: 'fixed', top: 0, left: 0, opacity: 0}).val(text);
|
||||||
|
$('body').append($tmp);
|
||||||
|
$tmp[0].select();
|
||||||
|
try { document.execCommand('copy'); } catch (e) {}
|
||||||
|
$tmp.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.atributes').each(function() {
|
||||||
|
var $div = $(this);
|
||||||
|
var html = $.trim($div.html());
|
||||||
|
if (!html) { return; }
|
||||||
|
|
||||||
|
var parts = html.split(/<br\s*\/?>/i);
|
||||||
|
var newParts = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < parts.length; i++) {
|
||||||
|
var part = $.trim(parts[i]);
|
||||||
|
if (!part) { continue; }
|
||||||
|
|
||||||
|
var match = part.match(/^(<b>[^<]*<\/b>\s*:\s*)(.+)$/);
|
||||||
|
if (match) {
|
||||||
|
var labelHtml = match[1];
|
||||||
|
var value = $.trim(match[2]);
|
||||||
|
var escapedValue = $('<div>').text(value).html();
|
||||||
|
part = labelHtml + escapedValue
|
||||||
|
+ ' <button type="button" class="js-attr-copy-btn attr-copy-btn" data-value="'
|
||||||
|
+ escapedValue + '" title="Kopiuj: ' + escapedValue + '">'
|
||||||
|
+ '<i class="fa fa-copy"></i></button>';
|
||||||
|
}
|
||||||
|
newParts.push(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
$div.html(newParts.join('<br>'));
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '.js-attr-copy-btn', function() {
|
||||||
|
var $btn = $(this);
|
||||||
|
var value = String($btn.data('value'));
|
||||||
|
|
||||||
|
function showCopied() {
|
||||||
|
$btn.addClass('attr-copy-btn--copied');
|
||||||
|
$btn.find('i').removeClass('fa-copy').addClass('fa-check');
|
||||||
|
setTimeout(function() {
|
||||||
|
$btn.removeClass('attr-copy-btn--copied');
|
||||||
|
$btn.find('i').removeClass('fa-check').addClass('fa-copy');
|
||||||
|
}, 1500);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||||
|
navigator.clipboard.writeText(value).then(showCopied, function() {
|
||||||
|
fallbackCopy(value);
|
||||||
|
showCopied();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
fallbackCopy(value);
|
||||||
|
showCopied();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
$('body').on('click', '.btn-toggle-trustmate', function(e) {
|
$('body').on('click', '.btn-toggle-trustmate', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
|||||||
@@ -3,24 +3,25 @@ $orderId = (int)($this -> order['id'] ?? 0);
|
|||||||
?>
|
?>
|
||||||
|
|
||||||
<div class="site-title">Szczegóły zamówienia: <?= htmlspecialchars((string)($this -> order['number'] ?? ''), ENT_QUOTES, 'UTF-8');?></div>
|
<div class="site-title">Szczegóły zamówienia: <?= htmlspecialchars((string)($this -> order['number'] ?? ''), ENT_QUOTES, 'UTF-8');?></div>
|
||||||
|
<script>document.title = 'Zamówienie <?= htmlspecialchars((string)($this -> order['number'] ?? ''), ENT_QUOTES, 'UTF-8');?> - shopPro';</script>
|
||||||
|
|
||||||
<div class="mb15">
|
<div class="od-actions mb15">
|
||||||
<a href="/admin/shop_order/list/" class="btn btn-dark btn-sm mr5">
|
<a href="/admin/shop_order/list/" class="btn btn-dark btn-sm">
|
||||||
<i class="fa fa-reply"></i> Wstecz
|
<i class="fa fa-reply"></i> <span class="od-actions-label">Wstecz</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="/admin/shop_order/order_edit/order_id=<?= $orderId;?>" class="btn btn-danger btn-sm mr5">
|
<a href="/admin/shop_order/order_edit/order_id=<?= $orderId;?>" class="btn btn-danger btn-sm">
|
||||||
<i class="fa fa-pencil"></i> Edytuj zamówienie
|
<i class="fa fa-pencil"></i> <span class="od-actions-label">Edytuj</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<? if ( $this -> prev_order_id ):?>
|
<? if ( $this -> prev_order_id ):?>
|
||||||
<a href="/admin/shop_order/order_details/order_id=<?= (int)$this -> prev_order_id;?>" class="btn btn-success btn-sm mr5">
|
<a href="/admin/shop_order/order_details/order_id=<?= (int)$this -> prev_order_id;?>" class="btn btn-success btn-sm">
|
||||||
<i class="fa fa-arrow-left"></i> Poprzednie zamówienie
|
<i class="fa fa-arrow-left"></i> <span class="od-actions-label">Poprzednie</span>
|
||||||
</a>
|
</a>
|
||||||
<? endif;?>
|
<? endif;?>
|
||||||
|
|
||||||
<? if ( $this -> next_order_id ):?>
|
<? if ( $this -> next_order_id ):?>
|
||||||
<a href="/admin/shop_order/order_details/order_id=<?= (int)$this -> next_order_id;?>" class="btn btn-success btn-sm mr5">
|
<a href="/admin/shop_order/order_details/order_id=<?= (int)$this -> next_order_id;?>" class="btn btn-success btn-sm">
|
||||||
<i class="fa fa-arrow-right"></i> Następne zamówienie
|
<i class="fa fa-arrow-right"></i> <span class="od-actions-label">Następne</span>
|
||||||
</a>
|
</a>
|
||||||
<? endif;?>
|
<? endif;?>
|
||||||
|
|
||||||
@@ -89,6 +90,19 @@ $orderId = (int)($this -> order['id'] ?? 0);
|
|||||||
<div>
|
<div>
|
||||||
<b><?= $this -> order[ 'payment_method' ];?> </b>
|
<b><?= $this -> order[ 'payment_method' ];?> </b>
|
||||||
</div>
|
</div>
|
||||||
|
<? if ( !empty($this -> order['apilo_order_id']) ):?>
|
||||||
|
<br/>
|
||||||
|
<div>
|
||||||
|
<i class="fa fa-cloud"></i> Apilo: <b style="color: #27ae60;">tak</b>
|
||||||
|
— ID: <b id="order-apilo-id"><?= htmlspecialchars((string)$this -> order['apilo_order_id'], ENT_QUOTES, 'UTF-8');?></b>
|
||||||
|
<i class="fa fa-copy" onclick="copyToClipboard( 'order-apilo-id' ); return false;"></i>
|
||||||
|
</div>
|
||||||
|
<? else:?>
|
||||||
|
<br/>
|
||||||
|
<div>
|
||||||
|
<i class="fa fa-cloud"></i> Apilo: <b style="color: #c0392b;">nie</b>
|
||||||
|
</div>
|
||||||
|
<? endif;?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="paid-status panel">
|
<div class="paid-status panel">
|
||||||
@@ -183,11 +197,15 @@ $orderId = (int)($this -> order['id'] ?? 0);
|
|||||||
<div class="product-message">
|
<div class="product-message">
|
||||||
<?= $product[ 'message' ] != '' ? '<strong>Wiadomość:</strong> ' . $product['message'] : '';?>
|
<?= $product[ 'message' ] != '' ? '<strong>Wiadomość:</strong> ' . $product['message'] : '';?>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="od-mobile-price-line">
|
||||||
|
<? $effective = ((float)$product['price_brutto_promo'] > 0 && (float)$product['price_brutto_promo'] < (float)$product['price_brutto']) ? (float)$product['price_brutto_promo'] : (float)$product['price_brutto'];?>
|
||||||
|
<?= (int)$product['quantity'];?> × <?= \Shared\Helpers\Helpers::decimal( $effective );?> = <?= \Shared\Helpers\Helpers::decimal( $effective * $product['quantity'] );?> zł
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="tab-center"><?= $product[ 'quantity' ];?></td>
|
<td class="tab-center"><?= $product[ 'quantity' ];?></td>
|
||||||
<td class="tab-right"><?= \Shared\Helpers\Helpers::decimal( $product[ 'price_brutto' ] );?> zł</td>
|
<td class="tab-right"><?= \Shared\Helpers\Helpers::decimal( $product[ 'price_brutto' ] );?> zł</td>
|
||||||
<td class="tab-right"><?= \Shared\Helpers\Helpers::decimal( $product[ 'price_brutto_promo' ] );?> zł</td>
|
<td class="tab-right"><?= \Shared\Helpers\Helpers::decimal( $effective );?> zł</td>
|
||||||
<td class="tab-right"><?= \Shared\Helpers\Helpers::decimal( $product[ 'price_brutto_promo' ] * $product[ 'quantity' ] );?> zł</td>
|
<td class="tab-right"><?= \Shared\Helpers\Helpers::decimal( $effective * $product[ 'quantity' ] );?> zł</td>
|
||||||
</tr>
|
</tr>
|
||||||
<? endforeach; endif;?>
|
<? endforeach; endif;?>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -81,9 +81,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<link rel="stylesheet" type="text/css" href="/libraries/grid/plugins/icheck/skins/minimal/minimal.css">
|
|
||||||
<link rel="stylesheet" type="text/css" href="/libraries/grid/plugins/icheck/skins/minimal/blue.css">
|
|
||||||
<script type="text/javascript" src="/libraries/grid/plugins/icheck/icheck.min.js"></script>
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$( function()
|
$( function()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,44 +8,52 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="author" content="www.project-pro.pl - internetowe rozwiązania dla biznesu">
|
<meta name="author" content="www.project-pro.pl - internetowe rozwiązania dla biznesu">
|
||||||
<link rel='stylesheet' type="text/css" href='https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700'>
|
<link rel='stylesheet' type="text/css" href='https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700'>
|
||||||
<link rel="stylesheet" type="text/css" href="/libraries/framework/skin/default_skin/css/theme.css">
|
<link rel="stylesheet" type="text/css" href="/libraries/framework/skin/default_skin/css/theme.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/skin/default_skin/css/theme.css'); ?>">
|
||||||
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/plugins/magnific/magnific-popup.css">
|
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/plugins/magnific/magnific-popup.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/plugins/magnific/magnific-popup.css'); ?>">
|
||||||
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/plugins/datepicker/css/bootstrap-datetimepicker.css">
|
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/plugins/datepicker/css/bootstrap-datetimepicker.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/plugins/datepicker/css/bootstrap-datetimepicker.css'); ?>">
|
||||||
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/jquery/jquery_ui/jquery-ui.structure.min.css">
|
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/jquery/jquery_ui/jquery-ui.structure.min.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/jquery/jquery_ui/jquery-ui.structure.min.css'); ?>">
|
||||||
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/jquery/jquery_ui/jquery-ui.theme.min.css">
|
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/jquery/jquery_ui/jquery-ui.theme.min.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/jquery/jquery_ui/jquery-ui.theme.min.css'); ?>">
|
||||||
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/plugins/daterange/daterangepicker.css">
|
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/plugins/daterange/daterangepicker.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/plugins/daterange/daterangepicker.css'); ?>">
|
||||||
<link rel="stylesheet" type="text/css" href="/libraries/jquery-confirm/jquery-confirm.min.css">
|
<link rel="stylesheet" type="text/css" href="/libraries/jquery-confirm/jquery-confirm.min.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/jquery-confirm/jquery-confirm.min.css'); ?>">
|
||||||
<link rel="stylesheet" type="text/css" href="/libraries/easy-tabs/css/easy-responsive-tabs.css">
|
<link rel="stylesheet" type="text/css" href="/libraries/easy-tabs/css/easy-responsive-tabs.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/easy-tabs/css/easy-responsive-tabs.css'); ?>">
|
||||||
<link rel="stylesheet" type="text/css" href="/libraries/bootstrap-4.5.2-dist/css/bootstrap.css">
|
<link rel="stylesheet" type="text/css" href="/libraries/bootstrap-4.5.2-dist/css/bootstrap.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/bootstrap-4.5.2-dist/css/bootstrap.css'); ?>">
|
||||||
<link rel="stylesheet" type="text/css" href="/libraries/font-awesome-4.7.0/css/font-awesome.css">
|
<link rel="stylesheet" type="text/css" href="/libraries/font-awesome-4.7.0/css/font-awesome.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/font-awesome-4.7.0/css/font-awesome.css'); ?>">
|
||||||
<link rel="stylesheet" type="text/css" href="/libraries/grid/plugins/icheck/skins/square/blue.css">
|
<link rel="stylesheet" type="text/css" href="/libraries/grid/plugins/icheck/skins/minimal/minimal.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/grid/plugins/icheck/skins/minimal/minimal.css'); ?>">
|
||||||
<script type="text/javascript" src="/libraries/framework/vendor/jquery/jquery-1.11.1.min.js"></script>
|
<link rel="stylesheet" type="text/css" href="/libraries/grid/plugins/icheck/skins/minimal/blue.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/grid/plugins/icheck/skins/minimal/blue.css'); ?>">
|
||||||
<script type="text/javascript" src="/libraries/framework/vendor/jquery/jquery_ui/jquery-ui.min.js"></script>
|
<script type="text/javascript" src="/libraries/framework/vendor/jquery/jquery-1.11.1.min.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/jquery/jquery-1.11.1.min.js'); ?>"></script>
|
||||||
<script type="text/javascript" src="/libraries/framework/js/utility/utility.js"></script>
|
<script type="text/javascript" src="/libraries/framework/vendor/jquery/jquery_ui/jquery-ui.min.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/jquery/jquery_ui/jquery-ui.min.js'); ?>"></script>
|
||||||
<script type="text/javascript" src="/libraries/framework/vendor/plugins/magnific/jquery.magnific-popup.js"></script>
|
<script type="text/javascript" src="/libraries/framework/js/utility/utility.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/js/utility/utility.js'); ?>"></script>
|
||||||
<script type="text/javascript" src="/libraries/easy-tabs/js/easyResponsiveTabs.js"></script>
|
<script type="text/javascript" src="/libraries/framework/vendor/plugins/magnific/jquery.magnific-popup.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/plugins/magnific/jquery.magnific-popup.js'); ?>"></script>
|
||||||
<script type="text/javascript" src="/libraries/framework/vendor/plugins/moment/moment.js"></script>
|
<script type="text/javascript" src="/libraries/easy-tabs/js/easyResponsiveTabs.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/easy-tabs/js/easyResponsiveTabs.js'); ?>"></script>
|
||||||
<script type="text/javascript" src="/libraries/framework/vendor/plugins/moment/pl.js"></script>
|
<script type="text/javascript" src="/libraries/framework/vendor/plugins/moment/moment.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/plugins/moment/moment.js'); ?>"></script>
|
||||||
<script type="text/javascript" src="/libraries/framework/vendor/plugins/datepicker/js/bootstrap-datetimepicker.js"></script>
|
<script type="text/javascript" src="/libraries/framework/vendor/plugins/moment/pl.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/plugins/moment/pl.js'); ?>"></script>
|
||||||
<script type="text/javascript" src="/libraries/framework/vendor/plugins/daterange/daterangepicker.js"></script>
|
<script type="text/javascript" src="/libraries/framework/vendor/plugins/datepicker/js/bootstrap-datetimepicker.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/plugins/datepicker/js/bootstrap-datetimepicker.js'); ?>"></script>
|
||||||
<script type="text/javascript" src="/libraries/jquery-confirm/jquery-confirm.min.js"></script>
|
<script type="text/javascript" src="/libraries/framework/vendor/plugins/daterange/daterangepicker.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/framework/vendor/plugins/daterange/daterangepicker.js'); ?>"></script>
|
||||||
<script type="text/javascript" src="/libraries/bootstrap-4.5.2-dist/js/bootstrap.min.js"></script>
|
<script type="text/javascript" src="/libraries/jquery-confirm/jquery-confirm.min.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/jquery-confirm/jquery-confirm.min.js'); ?>"></script>
|
||||||
<script type="text/javascript" src="/libraries/bootstrap-4.5.2-dist/js/bootstrap.bundle.min.js"></script>
|
<script type="text/javascript" src="/libraries/bootstrap-4.5.2-dist/js/bootstrap.min.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/bootstrap-4.5.2-dist/js/bootstrap.min.js'); ?>"></script>
|
||||||
<script type="text/javascript" src="/libraries/grid/plugins/icheck/icheck.js"></script>
|
<script type="text/javascript" src="/libraries/bootstrap-4.5.2-dist/js/bootstrap.bundle.min.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/bootstrap-4.5.2-dist/js/bootstrap.bundle.min.js'); ?>"></script>
|
||||||
<script type="text/javascript" src="/libraries/functions.js"></script>
|
<script type="text/javascript" src="/libraries/grid/plugins/icheck/icheck.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/grid/plugins/icheck/icheck.js'); ?>"></script>
|
||||||
<script type="text/javascript" src="/admin/js/functions.js"></script>
|
<script type="text/javascript" src="/libraries/functions.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/libraries/functions.js'); ?>"></script>
|
||||||
<link rel="stylesheet" href="/admin/layout/style-css/style.css" />
|
<script type="text/javascript" src="/admin/js/functions.js?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/admin/js/functions.js'); ?>"></script>
|
||||||
<link rel="stylesheet" href="/admin/layout/style-css/table-list.css" />
|
<link rel="stylesheet" href="/admin/layout/style-css/style.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/admin/layout/style-css/style.css'); ?>" />
|
||||||
|
<link rel="stylesheet" href="/admin/layout/style-css/table-list.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/admin/layout/style-css/table-list.css'); ?>" />
|
||||||
|
<link rel="stylesheet" href="/admin/layout/style-css/order-details-mobile.css?ver=<?= filemtime($_SERVER['DOCUMENT_ROOT'] . '/admin/layout/style-css/order-details-mobile.css'); ?>" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="admin-page">
|
<div class="admin-page">
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
<div class="logo sticky-top">
|
<div class="logo sticky-top">
|
||||||
shop<b>Pro</b>
|
shop<b>Pro</b>
|
||||||
<span>ver. <?= \Shared\Helpers\Helpers::get_version();?></span><br>
|
<span>ver. <?= \Shared\Helpers\Helpers::get_version();?>
|
||||||
<? if ( $settings[ 'update' ] and \Shared\Helpers\Helpers::get_new_version() > \Shared\Helpers\Helpers::get_version() ):?>
|
<? if ( $settings[ 'update' ] ):?>
|
||||||
<a href="/admin/update/main_view/" class="label label-danger">aktualizacja</a>
|
<i class="fa fa-refresh check-update-btn" id="check-update-btn" title="Sprawdź aktualizacje" style="cursor:pointer;margin-left:4px;font-size:11px;opacity:0.7;"></i>
|
||||||
<? endif;?>
|
<? endif;?>
|
||||||
|
</span><br>
|
||||||
|
<span id="update-badge-wrap">
|
||||||
|
<? if ( $settings[ 'update' ] and \Shared\Helpers\Helpers::get_new_version() > \Shared\Helpers\Helpers::get_version() ):?>
|
||||||
|
<a href="/admin/update/main_view/" class="label label-danger">aktualizacja</a>
|
||||||
|
<? endif;?>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="menu-content">
|
<div class="menu-content">
|
||||||
<ul>
|
<ul>
|
||||||
@@ -145,6 +153,11 @@
|
|||||||
<i class="fa fa-cogs" aria-hidden="true"></i>shopPRO
|
<i class="fa fa-cogs" aria-hidden="true"></i>shopPRO
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/admin/integrations/logs/">
|
||||||
|
<i class="fa fa-list-alt" aria-hidden="true"></i>Logi
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="preview">
|
<div class="preview">
|
||||||
@@ -309,7 +322,7 @@
|
|||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/admin/settings/globalSearchAjax/',
|
url: '/admin/settings/globalSearchAjax/',
|
||||||
type: 'GET',
|
type: 'POST',
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
data: { q: phrase },
|
data: { q: phrase },
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
@@ -320,8 +333,12 @@
|
|||||||
|
|
||||||
renderResults(response.items || []);
|
renderResults(response.items || []);
|
||||||
},
|
},
|
||||||
error: function() {
|
error: function(xhr) {
|
||||||
$results.html('<div class="admin-global-search-empty">Błąd połączenia</div>').addClass('open');
|
var msg = 'Błąd połączenia';
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
msg = 'Błąd parsowania odpowiedzi';
|
||||||
|
}
|
||||||
|
$results.html('<div class="admin-global-search-empty">' + msg + '</div>').addClass('open');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -354,6 +371,32 @@
|
|||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
$(document).off('click.checkUpdate', '#check-update-btn').on('click.checkUpdate', '#check-update-btn', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var $btn = $(this);
|
||||||
|
if ($btn.hasClass('fa-spin')) return;
|
||||||
|
$btn.addClass('fa-spin').css('opacity', 1);
|
||||||
|
$.ajax({
|
||||||
|
url: '/admin/update/checkUpdate/',
|
||||||
|
type: 'GET',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(data) {
|
||||||
|
$btn.removeClass('fa-spin').css('opacity', 0.7);
|
||||||
|
var $wrap = $('#update-badge-wrap');
|
||||||
|
if (data.has_update) {
|
||||||
|
$wrap.html('<a href="/admin/update/main_view/" class="label label-danger">aktualizacja</a>');
|
||||||
|
} else {
|
||||||
|
$wrap.html('');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
$btn.removeClass('fa-spin').css('opacity', 0.7);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
var user_agent = navigator.userAgent.toLowerCase();
|
var user_agent = navigator.userAgent.toLowerCase();
|
||||||
var click_event = user_agent.match(/(iphone|ipod|ipad)/) ? "touchend" : "click";
|
var click_event = user_agent.match(/(iphone|ipod|ipad)/) ? "touchend" : "click";
|
||||||
|
|||||||
@@ -41,12 +41,23 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<? if ( !empty( $this->log ) ): ?>
|
||||||
|
<div class="panel">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<span class="panel-title">Log ostatniej aktualizacji</span>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<pre style="max-height: 400px; overflow-y: auto; font-size: 12px;"><?= htmlspecialchars( $this->log ); ?></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<? endif; ?>
|
||||||
|
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<span class="panel-title">Changelog</span>
|
<span class="panel-title">Changelog</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<?= @file_get_contents( 'https://shoppro.project-dc.pl/updates/changelog.php' ); ?>
|
<?= @file_get_contents( 'https://shoppro.project-dc.pl/updates/changelog.php?ver=' . $this->ver ); ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -360,6 +360,9 @@ class ArticleRepository
|
|||||||
public function archive(int $articleId): bool
|
public function archive(int $articleId): bool
|
||||||
{
|
{
|
||||||
$result = $this->db->update('pp_articles', ['status' => -1], ['id' => $articleId]);
|
$result = $this->db->update('pp_articles', ['status' => -1], ['id' => $articleId]);
|
||||||
|
if ($result) {
|
||||||
|
$this->db->delete('pp_routes', ['article_id' => $articleId]);
|
||||||
|
}
|
||||||
return (bool)$result;
|
return (bool)$result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,6 +384,7 @@ class ArticleRepository
|
|||||||
$this->db->delete('pp_articles_langs', ['article_id' => $articleId]);
|
$this->db->delete('pp_articles_langs', ['article_id' => $articleId]);
|
||||||
$this->db->delete('pp_articles_images', ['article_id' => $articleId]);
|
$this->db->delete('pp_articles_images', ['article_id' => $articleId]);
|
||||||
$this->db->delete('pp_articles_files', ['article_id' => $articleId]);
|
$this->db->delete('pp_articles_files', ['article_id' => $articleId]);
|
||||||
|
$this->db->delete('pp_routes', ['article_id' => $articleId]);
|
||||||
$this->db->delete('pp_articles', ['id' => $articleId]);
|
$this->db->delete('pp_articles', ['id' => $articleId]);
|
||||||
|
|
||||||
\Shared\Helpers\Helpers::delete_dir('../upload/article_images/article_' . $articleId . '/');
|
\Shared\Helpers\Helpers::delete_dir('../upload/article_images/article_' . $articleId . '/');
|
||||||
|
|||||||
@@ -174,6 +174,7 @@ class CategoryRepository
|
|||||||
|
|
||||||
$deleted = (bool)$this->db->delete('pp_shop_categories', ['id' => $id]);
|
$deleted = (bool)$this->db->delete('pp_shop_categories', ['id' => $id]);
|
||||||
if ($deleted) {
|
if ($deleted) {
|
||||||
|
$this->db->delete('pp_routes', ['category_id' => $id]);
|
||||||
$this->refreshCategoryArtifacts();
|
$this->refreshCategoryArtifacts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -296,7 +296,7 @@ class LayoutsRepository
|
|||||||
if (is_array($layoutRows) && isset($layoutRows[0])) {
|
if (is_array($layoutRows) && isset($layoutRows[0])) {
|
||||||
$layout = $layoutRows[0];
|
$layout = $layoutRows[0];
|
||||||
} else {
|
} else {
|
||||||
$layout = $this->db->get('pp_layouts', '*', ['categories_default' => 1]);
|
$layout = $this->db->get('pp_layouts', '*', ['status' => 1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -134,7 +134,11 @@ class PagesRepository
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (bool)$this->db->delete('pp_pages', ['id' => $pageId]);
|
$deleted = (bool)$this->db->delete('pp_pages', ['id' => $pageId]);
|
||||||
|
if ($deleted) {
|
||||||
|
$this->db->delete('pp_routes', ['page_id' => $pageId]);
|
||||||
|
}
|
||||||
|
return $deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -654,6 +654,10 @@ class ProductRepository
|
|||||||
'custom_label_2' => $product['custom_label_2'],
|
'custom_label_2' => $product['custom_label_2'],
|
||||||
'custom_label_3' => $product['custom_label_3'],
|
'custom_label_3' => $product['custom_label_3'],
|
||||||
'custom_label_4' => $product['custom_label_4'],
|
'custom_label_4' => $product['custom_label_4'],
|
||||||
|
'new_to_date' => $product['new_to_date'],
|
||||||
|
'additional_message' => (int)($product['additional_message'] ?? 0),
|
||||||
|
'additional_message_required' => (int)($product['additional_message_required'] ?? 0),
|
||||||
|
'additional_message_text' => $product['additional_message_text'],
|
||||||
'set_id' => $product['set_id'] !== null ? (int)$product['set_id'] : null,
|
'set_id' => $product['set_id'] !== null ? (int)$product['set_id'] : null,
|
||||||
'product_unit_id' => $product['product_unit_id'] !== null ? (int)$product['product_unit_id'] : null,
|
'product_unit_id' => $product['product_unit_id'] !== null ? (int)$product['product_unit_id'] : null,
|
||||||
'producer_id' => $product['producer_id'] !== null ? (int)$product['producer_id'] : null,
|
'producer_id' => $product['producer_id'] !== null ? (int)$product['producer_id'] : null,
|
||||||
|
|||||||
@@ -172,13 +172,19 @@ class UpdateRepository
|
|||||||
|
|
||||||
foreach ( $manifest['sql'] as $query ) {
|
foreach ( $manifest['sql'] as $query ) {
|
||||||
$query = trim( $query );
|
$query = trim( $query );
|
||||||
if ( $query !== '' ) {
|
if ( $query === '' || strpos( $query, '--' ) === 0 ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
if ( $this->db->query( $query ) ) {
|
if ( $this->db->query( $query ) ) {
|
||||||
$success++;
|
$success++;
|
||||||
} else {
|
} else {
|
||||||
$errors++;
|
$errors++;
|
||||||
$log[] = '[WARNING] Błąd SQL: ' . $query;
|
$log[] = '[WARNING] Błąd SQL: ' . $query;
|
||||||
}
|
}
|
||||||
|
} catch ( \Exception $e ) {
|
||||||
|
$errors++;
|
||||||
|
$log[] = '[WARNING] Wyjątek SQL: ' . $e->getMessage() . ' | Query: ' . substr( $query, 0, 200 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -425,42 +425,206 @@ class Helpers
|
|||||||
$site_map .= '<priority>1</priority>' . PHP_EOL;
|
$site_map .= '<priority>1</priority>' . PHP_EOL;
|
||||||
$site_map .= '</url>' . PHP_EOL;
|
$site_map .= '</url>' . PHP_EOL;
|
||||||
|
|
||||||
$htaccess_data = file_get_contents( $dir . 'libraries/htaccess.conf' );
|
//
|
||||||
$htaccess_data = str_replace( '{PAGE}', $url, $htaccess_data );
|
// SYSTEM ROUTES — delete all and reinsert
|
||||||
|
//
|
||||||
|
$mdb->delete( 'pp_routes', [ 'type' => 'system' ] );
|
||||||
|
|
||||||
$results = $mdb -> select( 'pp_langs', [ 'id' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
// Static system routes (hardcoded, never change)
|
||||||
|
$systemRoutes = [
|
||||||
|
// Wyszukiwarka
|
||||||
|
[ 'pattern' => '^wyszukiwarka/([^/]+)/([0-9]+)$', 'destination' => 'index.php?module=search&action=search_results&query=$1&bs=$2' ],
|
||||||
|
[ 'pattern' => '^wyszukiwarka/([^/]+)$', 'destination' => 'index.php?module=search&action=search_results&query=$1&bs=1' ],
|
||||||
|
// Zamowienia
|
||||||
|
[ 'pattern' => '^zamowienie/([a-zA-Z0-9-]+)$', 'destination' => 'index.php?module=shop_order&action=order_details&order_hash=$1' ],
|
||||||
|
[ 'pattern' => '^potwierdzenie-platnosci/([a-zA-Z0-9-]+)$', 'destination' => 'index.php?module=shop_order&action=payment_confirmation&order_hash=$1' ],
|
||||||
|
// Platnosci
|
||||||
|
[ 'pattern' => '^tpay-status$', 'destination' => 'index.php?module=shop_order&action=payment_status_tpay' ],
|
||||||
|
[ 'pattern' => '^platnosc-status$', 'destination' => 'index.php?module=shop_order&action=payment_status_hotpay' ],
|
||||||
|
[ 'pattern' => '^przelewy24-status$', 'destination' => 'index.php?module=shop_order&action=payment_status_przelewy24pl' ],
|
||||||
|
// Koszyk
|
||||||
|
[ 'pattern' => '^koszyk$', 'destination' => 'index.php?module=shop_basket&action=main_view' ],
|
||||||
|
[ 'pattern' => '^koszyk-podsumowanie$', 'destination' => 'index.php?module=shop_basket&action=summary_view' ],
|
||||||
|
[ 'pattern' => '^zloz-zamowienie$', 'destination' => 'index.php?module=shop_basket&action=basket_save' ],
|
||||||
|
// Klient
|
||||||
|
[ 'pattern' => '^rejestracja$', 'destination' => 'index.php?module=shop_client&action=register_form' ],
|
||||||
|
[ 'pattern' => '^logowanie$', 'destination' => 'index.php?module=shop_client&action=login_form' ],
|
||||||
|
[ 'pattern' => '^wylogowanie$', 'destination' => 'index.php?module=shop_client&action=logout' ],
|
||||||
|
[ 'pattern' => '^odzyskiwanie-hasla$', 'destination' => 'index.php?module=shop_client&action=recover_password' ],
|
||||||
|
[ 'pattern' => '^panel-klienta/zamowienia$', 'destination' => 'index.php?module=shop_client&action=client_orders' ],
|
||||||
|
[ 'pattern' => '^panel-klienta/adresy$', 'destination' => 'index.php?module=shop_client&action=client_addresses' ],
|
||||||
|
[ 'pattern' => '^panel-klienta/nowy-adres$', 'destination' => 'index.php?module=shop_client&action=address_edit' ],
|
||||||
|
[ 'pattern' => '^panel-klienta/edytuj-adres/([0-9]+)$', 'destination' => 'index.php?module=shop_client&action=address_edit&id=$1' ],
|
||||||
|
[ 'pattern' => '^panel-klienta/usun-adres/([0-9]+)$', 'destination' => 'index.php?module=shop_client&action=address_delete&id=$1' ],
|
||||||
|
// Newsletter
|
||||||
|
[ 'pattern' => '^newsletter/signin$', 'destination' => 'index.php?module=newsletter&action=signin' ],
|
||||||
|
[ 'pattern' => '^newsletter/confirm/hash=(.+)$', 'destination' => 'index.php?module=newsletter&action=confirm&hash=$1' ],
|
||||||
|
[ 'pattern' => '^newsletter/unsubscribe/hash=(.+)$', 'destination' => 'index.php?module=newsletter&action=unsubscribe&hash=$1' ],
|
||||||
|
// Moduły AJAX (shopBasket, shopClient, shopProduct, shopCoupon, search)
|
||||||
|
[ 'pattern' => '^shopBasket/([^/]+)/(.+)$', 'destination' => 'index.php?module=shopBasket&action=$1&$2' ],
|
||||||
|
[ 'pattern' => '^shopBasket/([^/]+)$', 'destination' => 'index.php?module=shopBasket&action=$1' ],
|
||||||
|
[ 'pattern' => '^shopClient/([^/]+)/(.+)$', 'destination' => 'index.php?module=shopClient&action=$1&$2' ],
|
||||||
|
[ 'pattern' => '^shopClient/([^/]+)$', 'destination' => 'index.php?module=shopClient&action=$1' ],
|
||||||
|
[ 'pattern' => '^shopProduct/([^/]+)/(.+)$', 'destination' => 'index.php?module=shopProduct&action=$1&$2' ],
|
||||||
|
[ 'pattern' => '^shopProduct/([^/]+)$', 'destination' => 'index.php?module=shopProduct&action=$1' ],
|
||||||
|
[ 'pattern' => '^shopCoupon/([^/]+)/(.+)$', 'destination' => 'index.php?module=shopCoupon&action=$1&$2' ],
|
||||||
|
[ 'pattern' => '^shopCoupon/([^/]+)$', 'destination' => 'index.php?module=shopCoupon&action=$1' ],
|
||||||
|
[ 'pattern' => '^search/([^/]+)/(.+)$', 'destination' => 'index.php?module=search&action=$1&$2' ],
|
||||||
|
[ 'pattern' => '^search/([^/]+)$', 'destination' => 'index.php?module=search&action=$1' ],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ( $systemRoutes as $route )
|
||||||
|
{
|
||||||
|
$mdb->insert( 'pp_routes', [
|
||||||
|
'type' => 'system',
|
||||||
|
'lang_id' => 0,
|
||||||
|
'pattern' => $route['pattern'],
|
||||||
|
'destination' => $route['destination'],
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dynamic system routes — languages
|
||||||
|
$results = $mdb->select( 'pp_langs', [ 'id' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||||
{
|
{
|
||||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $row['id'] . '/$ index.php?a=change_language&id=' . $row['id'] . ' [L]';
|
$mdb->insert( 'pp_routes', [
|
||||||
|
'type' => 'system',
|
||||||
|
'lang_id' => 0,
|
||||||
|
'pattern' => '^' . $row['id'] . '$',
|
||||||
|
'destination' => 'index.php?a=change_language&id=' . $row['id'],
|
||||||
|
] );
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
// Dynamic system routes — producenci
|
||||||
// INNE
|
|
||||||
//
|
|
||||||
$htaccess_data .= PHP_EOL;
|
|
||||||
$htaccess_data .= 'RewriteRule ^newsletter/signin/$ index.php?module=newsletter&action=signin [L]' . PHP_EOL;
|
|
||||||
$htaccess_data .= 'RewriteRule ^newsletter/confirm/hash=(.*)$ index.php?module=newsletter&action=confirm&hash=$1 [L]' . PHP_EOL;
|
|
||||||
$htaccess_data .= 'RewriteRule ^newsletter/unsubscribe/hash=(.*)$ index.php?module=newsletter&action=unsubscribe&hash=$1 [L]' . PHP_EOL;
|
|
||||||
|
|
||||||
//
|
|
||||||
// PRODUCENCI
|
|
||||||
//
|
|
||||||
$categoryDefaultLayoutId = ( new \Domain\Layouts\LayoutsRepository( $mdb ) )->categoryDefaultLayoutId();
|
$categoryDefaultLayoutId = ( new \Domain\Layouts\LayoutsRepository( $mdb ) )->categoryDefaultLayoutId();
|
||||||
$htaccess_data .= 'RewriteRule ^producenci$ index.php?module=shop_producer&action=list&layout_id=' . $categoryDefaultLayoutId . '&%{QUERY_STRING} [L]' . PHP_EOL;
|
|
||||||
|
|
||||||
$rows = $mdb -> select( 'pp_shop_producer', '*', [ 'status' => 1 ] );
|
$mdb->insert( 'pp_routes', [
|
||||||
|
'type' => 'system',
|
||||||
|
'lang_id' => 0,
|
||||||
|
'pattern' => '^producenci$',
|
||||||
|
'destination' => 'index.php?module=shop_producer&action=list&layout_id=' . $categoryDefaultLayoutId,
|
||||||
|
] );
|
||||||
|
|
||||||
|
$rows = $mdb->select( 'pp_shop_producer', '*', [ 'status' => 1 ] );
|
||||||
if ( self::is_array_fix( $rows ) ) foreach ( $rows as $row )
|
if ( self::is_array_fix( $rows ) ) foreach ( $rows as $row )
|
||||||
{
|
{
|
||||||
$htaccess_data .= 'RewriteRule ^producent/' . self::seo( $row['name'] ) . '$ index.php?module=shop_producer&action=products&producer_id=' . $row['id'] . '&layout_id=' . $categoryDefaultLayoutId . '&%{QUERY_STRING} [L]' . PHP_EOL;
|
$mdb->insert( 'pp_routes', [
|
||||||
$htaccess_data .= 'RewriteRule ^producent/' . self::seo( $row['name'] ) . '/([0-9]+)$ index.php?module=shop_producer&action=products&producer_id=' . $row['id'] . '&layout_id=' . $categoryDefaultLayoutId . '&bs=$1&%{QUERY_STRING} [L]' . PHP_EOL;
|
'type' => 'system',
|
||||||
|
'lang_id' => 0,
|
||||||
|
'pattern' => '^producent/' . self::seo( $row['name'] ) . '$',
|
||||||
|
'destination' => 'index.php?module=shop_producer&action=products&producer_id=' . $row['id'] . '&layout_id=' . $categoryDefaultLayoutId,
|
||||||
|
] );
|
||||||
|
$mdb->insert( 'pp_routes', [
|
||||||
|
'type' => 'system',
|
||||||
|
'lang_id' => 0,
|
||||||
|
'pattern' => '^producent/' . self::seo( $row['name'] ) . '/([0-9]+)$',
|
||||||
|
'destination' => 'index.php?module=shop_producer&action=products&producer_id=' . $row['id'] . '&layout_id=' . $categoryDefaultLayoutId . '&bs=$1',
|
||||||
|
] );
|
||||||
}
|
}
|
||||||
|
|
||||||
$results = $mdb -> select( 'pp_langs', [ 'id', 'start' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
//
|
||||||
|
// HTACCESS — generuj z PHP (bez szablonu htaccess.conf)
|
||||||
|
//
|
||||||
|
$htaccess_data = 'RewriteEngine On' . PHP_EOL;
|
||||||
|
$htaccess_data .= 'RewriteBase /' . PHP_EOL;
|
||||||
|
$htaccess_data .= 'Options +FollowSymlinks' . PHP_EOL;
|
||||||
|
$htaccess_data .= 'Options -Indexes' . PHP_EOL;
|
||||||
|
$htaccess_data .= PHP_EOL;
|
||||||
|
$htaccess_data .= '# Przekierowanie z www na bez www i z http na https w jednym kroku' . PHP_EOL;
|
||||||
|
$htaccess_data .= 'RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]' . PHP_EOL;
|
||||||
|
$htaccess_data .= 'RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]' . PHP_EOL;
|
||||||
|
$htaccess_data .= PHP_EOL;
|
||||||
|
$htaccess_data .= '# Przekierowanie z http na https, jesli nie zawiera www' . PHP_EOL;
|
||||||
|
$htaccess_data .= 'RewriteCond %{HTTPS} off' . PHP_EOL;
|
||||||
|
$htaccess_data .= 'RewriteCond %{REQUEST_URI} !^/(tpay-status|platnosc-status|przelewy24-status)$ [NC]' . PHP_EOL;
|
||||||
|
$htaccess_data .= 'RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]' . PHP_EOL;
|
||||||
|
$htaccess_data .= PHP_EOL;
|
||||||
|
$htaccess_data .= '# Usuwanie koncowego slasha dla niekatalogów' . PHP_EOL;
|
||||||
|
$htaccess_data .= 'RewriteCond %{REQUEST_FILENAME} !-d' . PHP_EOL;
|
||||||
|
$htaccess_data .= 'RewriteCond %{REQUEST_URI} !^/admin/.*$ [NC]' . PHP_EOL;
|
||||||
|
$htaccess_data .= 'RewriteCond %{REQUEST_URI} (.+)/$' . PHP_EOL;
|
||||||
|
$htaccess_data .= 'RewriteRule ^ %1 [R=301,L]' . PHP_EOL;
|
||||||
|
$htaccess_data .= PHP_EOL;
|
||||||
|
$htaccess_data .= 'RewriteCond %{REQUEST_URI} !^(.*)/libraries/(.*) [NC]' . PHP_EOL;
|
||||||
|
$htaccess_data .= 'RewriteCond %{REQUEST_URI} !^(.*)/layout/(.*) [NC]' . PHP_EOL;
|
||||||
|
$htaccess_data .= 'RewriteRule ^admin/([^/]*)/([^/]*)/(.*)$ admin/index.php?module=$1&action=$2&$3 [L]' . PHP_EOL;
|
||||||
|
$htaccess_data .= PHP_EOL;
|
||||||
|
$htaccess_data .= 'RewriteRule ^admin/$ admin/index.php [L]' . PHP_EOL;
|
||||||
|
$htaccess_data .= PHP_EOL;
|
||||||
|
$htaccess_data .= 'RewriteRule ^thumb/([0-9]*)/([0-9]*)/(.*)$ /libraries/thumb.php?img=$3&w=$1&h=$2 [L]' . PHP_EOL;
|
||||||
|
$htaccess_data .= PHP_EOL;
|
||||||
|
$htaccess_data .= 'RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /index.php' . PHP_EOL;
|
||||||
|
$htaccess_data .= 'RewriteRule ^ /%1 [R=301,L]' . PHP_EOL;
|
||||||
|
|
||||||
|
/* cache block */
|
||||||
|
if ( $settings['htaccess_cache'] )
|
||||||
|
{
|
||||||
|
$htaccess_data .= '<IfModule mod_deflate.c>' . PHP_EOL
|
||||||
|
. 'AddOutputFilterByType DEFLATE text/html text/plain text/xml application/xml application/xhtml+xml text/css text/javascript application/javascript application/x-javascript' . PHP_EOL
|
||||||
|
. '</IfModule>' . PHP_EOL
|
||||||
|
. '<IfModule mod_headers.c>' . PHP_EOL
|
||||||
|
. 'Header set Access-Control-Allow-Origin "*"' . PHP_EOL
|
||||||
|
. '</IfModule>' . PHP_EOL
|
||||||
|
. '<IfModule mod_expires.c>' . PHP_EOL
|
||||||
|
. 'ExpiresActive on' . PHP_EOL
|
||||||
|
. 'ExpiresDefault "access plus 1 month"' . PHP_EOL
|
||||||
|
. 'ExpiresByType text/css "access plus 1 year"' . PHP_EOL
|
||||||
|
. 'ExpiresByType application/json "access plus 0 seconds"' . PHP_EOL
|
||||||
|
. 'ExpiresByType application/xml "access plus 0 seconds"' . PHP_EOL
|
||||||
|
. 'ExpiresByType text/xml "access plus 0 seconds"' . PHP_EOL
|
||||||
|
. 'ExpiresByType image/x-icon "access plus 1 week"' . PHP_EOL
|
||||||
|
. 'ExpiresByType text/x-component "access plus 1 month"' . PHP_EOL
|
||||||
|
. 'ExpiresByType text/html "access plus 0 seconds"' . PHP_EOL
|
||||||
|
. 'ExpiresByType application/javascript "access plus 1 year"' . PHP_EOL
|
||||||
|
. 'ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds"' . PHP_EOL
|
||||||
|
. 'ExpiresByType text/cache-manifest "access plus 0 seconds"' . PHP_EOL
|
||||||
|
. 'ExpiresByType audio/ogg "access plus 1 month"' . PHP_EOL
|
||||||
|
. 'ExpiresByType image/gif "access plus 1 month"' . PHP_EOL
|
||||||
|
. 'ExpiresByType image/jpeg "access plus 1 month"' . PHP_EOL
|
||||||
|
. 'ExpiresByType image/png "access plus 1 month"' . PHP_EOL
|
||||||
|
. 'ExpiresByType video/mp4 "access plus 1 month"' . PHP_EOL
|
||||||
|
. 'ExpiresByType video/ogg "access plus 1 month"' . PHP_EOL
|
||||||
|
. 'ExpiresByType video/webm "access plus 1 month"' . PHP_EOL
|
||||||
|
. 'ExpiresByType application/atom+xml "access plus 1 hour"' . PHP_EOL
|
||||||
|
. 'ExpiresByType application/rss+xml "access plus 1 hour"' . PHP_EOL
|
||||||
|
. 'ExpiresByType application/font-woff "access plus 1 month"' . PHP_EOL
|
||||||
|
. 'ExpiresByType application/vnd.ms-fontobject "access plus 1 month"' . PHP_EOL
|
||||||
|
. 'ExpiresByType application/x-font-ttf "access plus 1 month"' . PHP_EOL
|
||||||
|
. 'ExpiresByType font/opentype "access plus 1 month"' . PHP_EOL
|
||||||
|
. 'ExpiresByType image/svg+xml "access plus 1 month"' . PHP_EOL
|
||||||
|
. '</IfModule>' . PHP_EOL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$htaccess_data .= '<IfModule mod_headers.c>' . PHP_EOL
|
||||||
|
. 'Header set Cache-Control "no-cache, no-store, must-revalidate"' . PHP_EOL
|
||||||
|
. 'Header set Pragma "no-cache"' . PHP_EOL
|
||||||
|
. 'Header set Expires 0' . PHP_EOL
|
||||||
|
. '</IfModule>' . PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
$htaccess_data .= '<Files *.conf>' . PHP_EOL;
|
||||||
|
$htaccess_data .= ' Order Deny,Allow' . PHP_EOL;
|
||||||
|
$htaccess_data .= ' Deny from all' . PHP_EOL;
|
||||||
|
$htaccess_data .= '</Files>' . PHP_EOL;
|
||||||
|
$htaccess_data .= '<Files *.log>' . PHP_EOL;
|
||||||
|
$htaccess_data .= ' Order Deny,Allow' . PHP_EOL;
|
||||||
|
$htaccess_data .= ' Deny from all' . PHP_EOL;
|
||||||
|
$htaccess_data .= '</Files>' . PHP_EOL;
|
||||||
|
$htaccess_data .= '<Files *.ini>' . PHP_EOL;
|
||||||
|
$htaccess_data .= ' Order Deny,Allow' . PHP_EOL;
|
||||||
|
$htaccess_data .= ' Deny from all' . PHP_EOL;
|
||||||
|
$htaccess_data .= '</Files>' . PHP_EOL;
|
||||||
|
|
||||||
|
//
|
||||||
|
// KATEGORIE — sitemap + pp_routes (bez zmian)
|
||||||
|
//
|
||||||
|
$results = $mdb->select( 'pp_langs', [ 'id', 'start' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||||
{
|
{
|
||||||
!$row['start'] ? $language_link = $row['id'] . '/' : $language_link = '';
|
!$row['start'] ? $language_link = $row['id'] . '/' : $language_link = '';
|
||||||
|
|
||||||
$results2 = $mdb -> select( 'pp_shop_categories_langs', [ '[><]pp_shop_categories' => [ 'category_id' => 'id' ] ], [ 'seo_link', 'title', 'category_id' ], [ 'lang_id' => $row['id'], 'ORDER' => [ 'o' => 'ASC' ] ] );
|
$results2 = $mdb->select( 'pp_shop_categories_langs', [ '[><]pp_shop_categories' => [ 'category_id' => 'id' ] ], [ 'seo_link', 'title', 'category_id' ], [ 'lang_id' => $row['id'], 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||||
if ( is_array( $results2 ) ) foreach ( $results2 as $row2 )
|
if ( is_array( $results2 ) ) foreach ( $results2 as $row2 )
|
||||||
{
|
{
|
||||||
if ( $row2['title'] )
|
if ( $row2['title'] )
|
||||||
@@ -475,35 +639,42 @@ class Helpers
|
|||||||
$site_map .= '<priority>1</priority>' . PHP_EOL;
|
$site_map .= '<priority>1</priority>' . PHP_EOL;
|
||||||
$site_map .= '</url>' . PHP_EOL;
|
$site_map .= '</url>' . PHP_EOL;
|
||||||
|
|
||||||
if ( $row2['seo_link'] )
|
$seoSlug = $row2['seo_link'] ? self::seo( $row2['seo_link'] ) : 'k-' . $row2['category_id'] . '-' . self::seo( $row2['title'] );
|
||||||
{
|
|
||||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . self::seo( $row2['seo_link'] ) . '$ index.php?category=' . $row2['category_id'] . '&lang=' . $row['id'] . '&%{QUERY_STRING} [L]';
|
$mdb->delete( 'pp_routes', [ 'AND' => [ 'category_id' => $row2['category_id'], 'lang_id' => $row['id'] ] ] );
|
||||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . self::seo( $row2['seo_link'] ) . '/([0-9]+)$ index.php?category=' . $row2['category_id'] . '&lang=' . $row['id'] . '&bs=$1&%{QUERY_STRING} [L]';
|
|
||||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . self::seo( $row2['seo_link'] ) . '/1$ ' . $language_link . self::seo( $row2['seo_link'] ) . ' [R=301,L]';
|
$mdb->insert( 'pp_routes', [
|
||||||
}
|
'category_id' => $row2['category_id'],
|
||||||
else
|
'lang_id' => $row['id'],
|
||||||
{
|
'pattern' => '^' . $language_link . $seoSlug . '$',
|
||||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . 'k-' . $row2['category_id'] . '-' . self::seo( $row2['title'] ) . '$ index.php?category=' . $row2['category_id'] . '&lang=' . $row['id'] . '&%{QUERY_STRING} [L]';
|
'destination' => 'index.php?category=' . $row2['category_id'] . '&lang=' . $row['id'],
|
||||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . 'k-' . $row2['category_id'] . '-' . self::seo( $row2['title'] ) . '/([0-9]+)$ index.php?category=' . $row2['category_id'] . '&lang=' . $row['id'] . '&bs=$1&%{QUERY_STRING} [L]';
|
] );
|
||||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . 'k-' . $row2['category_id'] . '-' . self::seo( $row2['title'] ) . '/1$ ' . $language_link . 'k-' . $row2['category_id'] . '-' . self::seo( $row2['title'] ) . ' [R=301,L]';
|
$mdb->insert( 'pp_routes', [
|
||||||
}
|
'category_id' => $row2['category_id'],
|
||||||
|
'lang_id' => $row['id'],
|
||||||
|
'pattern' => '^' . $language_link . $seoSlug . '/([0-9]+)$',
|
||||||
|
'destination' => 'index.php?category=' . $row2['category_id'] . '&lang=' . $row['id'] . '&bs=$1',
|
||||||
|
] );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$results = $mdb -> select( 'pp_langs', [ 'id', 'start' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
//
|
||||||
|
// PRODUKTY — sitemap + pp_routes (bez zmian)
|
||||||
|
//
|
||||||
|
$results = $mdb->select( 'pp_langs', [ 'id', 'start' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||||
if ( is_array( $results ) )
|
if ( is_array( $results ) )
|
||||||
{
|
{
|
||||||
foreach ( $results as $row )
|
foreach ( $results as $row )
|
||||||
{
|
{
|
||||||
!$row['start'] ? $language_link = $row['id'] . '/' : $language_link = '';
|
!$row['start'] ? $language_link = $row['id'] . '/' : $language_link = '';
|
||||||
|
|
||||||
$results2 = $mdb -> select( 'pp_shop_products_langs', [ '[><]pp_shop_products' => [ 'product_id' => 'id' ] ], [ 'seo_link', 'name', 'product_id' ], [ 'lang_id' => $row['id'], 'ORDER' => [ 'name' => 'ASC' ] ] );
|
$results2 = $mdb->select( 'pp_shop_products_langs', [ '[><]pp_shop_products' => [ 'product_id' => 'id' ] ], [ 'seo_link', 'name', 'product_id' ], [ 'lang_id' => $row['id'], 'ORDER' => [ 'name' => 'ASC' ] ] );
|
||||||
if ( is_array( $results2 ) )
|
if ( is_array( $results2 ) )
|
||||||
{
|
{
|
||||||
foreach ( $results2 as $row2 )
|
foreach ( $results2 as $row2 )
|
||||||
{
|
{
|
||||||
$mdb -> delete( 'pp_routes', [ 'AND' => [ 'product_id' => $row2['product_id'], 'lang_id' => $row['id'] ] ] );
|
$mdb->delete( 'pp_routes', [ 'AND' => [ 'product_id' => $row2['product_id'], 'lang_id' => $row['id'] ] ] );
|
||||||
|
|
||||||
if ( $row2['name'] )
|
if ( $row2['name'] )
|
||||||
{
|
{
|
||||||
@@ -519,27 +690,13 @@ class Helpers
|
|||||||
|
|
||||||
if ( $row2['seo_link'] )
|
if ( $row2['seo_link'] )
|
||||||
{
|
{
|
||||||
$pattern = '^' . $language_link . self::seo( $row2['seo_link'] ) . '$';
|
$mdb->insert( 'pp_routes', [ 'product_id' => $row2['product_id'], 'lang_id' => $row['id'], 'pattern' => '^' . $language_link . self::seo( $row2['seo_link'] ) . '$', 'destination' => 'index.php?product=' . $row2['product_id'] ] );
|
||||||
$destination = 'index.php?product=' . $row2['product_id'];
|
$mdb->insert( 'pp_routes', [ 'product_id' => $row2['product_id'], 'lang_id' => $row['id'], 'pattern' => '^' . $language_link . self::seo( $row2['seo_link'] ) . '/([0-9-]+)$', 'destination' => 'index.php?product=' . $row2['product_id'] . '&permutation_hash=$1' ] );
|
||||||
|
|
||||||
$mdb -> insert( 'pp_routes', [ 'product_id' => $row2['product_id'], 'lang_id' => $row['id'], 'pattern' => $pattern, 'destination' => $destination ] );
|
|
||||||
|
|
||||||
$pattern = '^' . $language_link . self::seo( $row2['seo_link'] ) . '/([0-9-]+)$';
|
|
||||||
$destination = 'index.php?product=' . $row2['product_id'] . '&permutation_hash=$1';
|
|
||||||
|
|
||||||
$mdb -> insert( 'pp_routes', [ 'product_id' => $row2['product_id'], 'lang_id' => $row['id'], 'pattern' => $pattern, 'destination' => $destination ] );
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$pattern = '^' . $language_link . 'p-' . $row2['product_id'] . '-' . self::seo( $row2['name'] ) . '$';
|
$mdb->insert( 'pp_routes', [ 'product_id' => $row2['product_id'], 'lang_id' => $row['id'], 'pattern' => '^' . $language_link . 'p-' . $row2['product_id'] . '-' . self::seo( $row2['name'] ) . '$', 'destination' => 'index.php?product=' . $row2['product_id'] ] );
|
||||||
$destination = 'index.php?product=' . $row2['product_id'];
|
$mdb->insert( 'pp_routes', [ 'product_id' => $row2['product_id'], 'lang_id' => $row['id'], 'pattern' => '^' . $language_link . 'p-' . $row2['product_id'] . '-' . self::seo( $row2['name'] ) . '/([0-9-]+)$', 'destination' => 'index.php?product=' . $row2['product_id'] . '&permutation_hash=$1' ] );
|
||||||
|
|
||||||
$mdb -> insert( 'pp_routes', [ 'product_id' => $row2['product_id'], 'lang_id' => $row['id'], 'pattern' => $pattern, 'destination' => $destination ] );
|
|
||||||
|
|
||||||
$pattern = '^' . $language_link . 'p-' . $row2['product_id'] . '-' . self::seo( $row2['name'] ) . '/([0-9-]+)$';
|
|
||||||
$destination = 'index.php?product=' . $row2['product_id'] . '&permutation_hash=$1';
|
|
||||||
|
|
||||||
$mdb -> insert( 'pp_routes', [ 'product_id' => $row2['product_id'], 'lang_id' => $row['id'], 'pattern' => $pattern, 'destination' => $destination ] );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -547,13 +704,16 @@ class Helpers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$results = $mdb -> select( 'pp_langs', [ 'id', 'start' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
//
|
||||||
|
// STRONY + ARTYKULY — sitemap + pp_routes (bez zmian)
|
||||||
|
//
|
||||||
|
$results = $mdb->select( 'pp_langs', [ 'id', 'start' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||||
if ( is_array( $results ) )
|
if ( is_array( $results ) )
|
||||||
foreach ( $results as $row )
|
foreach ( $results as $row )
|
||||||
{
|
{
|
||||||
( !$row['start'] and count( $results ) > 1 ) ? $language_link = $row['id'] . '/' : $language_link = '';
|
( !$row['start'] and count( $results ) > 1 ) ? $language_link = $row['id'] . '/' : $language_link = '';
|
||||||
|
|
||||||
$results2 = $mdb -> select( 'pp_pages_langs', [ '[><]pp_pages' => [ 'page_id' => 'id' ] ], [ 'seo_link', 'title', 'page_id', 'noindex', 'start', 'link', 'page_type' ], [ 'lang_id' => $row['id'], 'ORDER' => [ 'start' => 'DESC', 'o' => 'ASC' ] ] );
|
$results2 = $mdb->select( 'pp_pages_langs', [ '[><]pp_pages' => [ 'page_id' => 'id' ] ], [ 'seo_link', 'title', 'page_id', 'noindex', 'start', 'link', 'page_type' ], [ 'lang_id' => $row['id'], 'ORDER' => [ 'start' => 'DESC', 'o' => 'ASC' ] ] );
|
||||||
if ( is_array( $results2 ) )
|
if ( is_array( $results2 ) )
|
||||||
foreach ( $results2 as $row2 )
|
foreach ( $results2 as $row2 )
|
||||||
{
|
{
|
||||||
@@ -590,44 +750,39 @@ class Helpers
|
|||||||
{
|
{
|
||||||
$htaccess_data .= PHP_EOL . 'RewriteCond %{REQUEST_URI} ^/s-' . $row2['page_id'] . '-' . self::seo( $row2['title'] ) . '$';
|
$htaccess_data .= PHP_EOL . 'RewriteCond %{REQUEST_URI} ^/s-' . $row2['page_id'] . '-' . self::seo( $row2['title'] ) . '$';
|
||||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^(.*)$ http://www.' . $url . '/' . $language_link . ' [R=permanent,L]';
|
$htaccess_data .= PHP_EOL . 'RewriteRule ^(.*)$ http://www.' . $url . '/' . $language_link . ' [R=permanent,L]';
|
||||||
|
|
||||||
$htaccess_data .= PHP_EOL . 'RewriteCond %{REQUEST_URI} ^/s-' . $row2['page_id'] . '-' . self::seo( $row2['title'] ) . '-1$';
|
$htaccess_data .= PHP_EOL . 'RewriteCond %{REQUEST_URI} ^/s-' . $row2['page_id'] . '-' . self::seo( $row2['title'] ) . '-1$';
|
||||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^(.*)$ http://www.' . $url . '/' . $language_link . ' [R=permanent,L]';
|
$htaccess_data .= PHP_EOL . 'RewriteRule ^(.*)$ http://www.' . $url . '/' . $language_link . ' [R=permanent,L]';
|
||||||
}
|
}
|
||||||
|
|
||||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^$ index.php?a=page&id=' . $row2['page_id'] . '&lang=' . $row['id'] . ' [L]';
|
$htaccess_data .= PHP_EOL . 'RewriteRule ^$ index.php?a=page&id=' . $row2['page_id'] . '&lang=' . $row['id'] . ' [L]';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $row2['seo_link'] )
|
$seoSlug = $row2['seo_link'] ? self::seo( $row2['seo_link'] ) : 's-' . $row2['page_id'] . '-' . self::seo( $row2['title'] );
|
||||||
{
|
$langPrefix = $row2['start'] ? '' : $language_link;
|
||||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . self::seo( $row2['seo_link'] ) . '$ index.php?a=page&id=' . $row2['page_id'] . '&lang=' . $row['id'] . '&%{QUERY_STRING} [L]';
|
|
||||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . self::seo( $row2['seo_link'] ) . '/([0-9]+)$ index.php?a=page&id=' . $row2['page_id'] . '&lang=' . $row['id'] . '&bs=$1&%{QUERY_STRING} [L]';
|
$mdb->delete( 'pp_routes', [ 'AND' => [ 'page_id' => $row2['page_id'], 'lang_id' => $row['id'] ] ] );
|
||||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . self::seo( $row2['seo_link'] ) . '/1$ ' . $language_link . self::seo( $row2['seo_link'] ) . ' [R=301,L]';
|
|
||||||
}
|
$mdb->insert( 'pp_routes', [
|
||||||
else
|
'page_id' => $row2['page_id'],
|
||||||
{
|
'lang_id' => $row['id'],
|
||||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . 's-' . $row2['page_id'] . '-' . self::seo( $row2['title'] ) . '$ index.php?a=page&id=' . $row2['page_id'] . '&lang=' . $row['id'] . '&%{QUERY_STRING} [L]';
|
'pattern' => '^' . $langPrefix . $seoSlug . '$',
|
||||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . 's-' . $row2['page_id'] . '-' . self::seo( $row2['title'] ) . '/([0-9]+)$ index.php?a=page&id=' . $row2['page_id'] . '&lang=' . $row['id'] . '&bs=$1&%{QUERY_STRING} [L]';
|
'destination' => 'index.php?a=page&id=' . $row2['page_id'] . '&lang=' . $row['id'],
|
||||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . 's-' . $row2['page_id'] . '-' . self::seo( $row2['title'] ) . '/1$ ' . $language_link . 's-' . $row2['page_id'] . '-' . self::seo( $row2['title'] ) . ' [R=301,L]';
|
] );
|
||||||
}
|
$mdb->insert( 'pp_routes', [
|
||||||
|
'page_id' => $row2['page_id'],
|
||||||
|
'lang_id' => $row['id'],
|
||||||
|
'pattern' => '^' . $langPrefix . $seoSlug . '/([0-9]+)$',
|
||||||
|
'destination' => 'index.php?a=page&id=' . $row2['page_id'] . '&lang=' . $row['id'] . '&bs=$1',
|
||||||
|
] );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$results2 = $mdb -> select( 'pp_articles_langs', [ '[><]pp_articles' => [ 'article_id' => 'id' ] ], [ 'seo_link', 'title', 'article_id', 'noindex', 'copy_from' ], [ 'AND' => [ 'status' => 1, 'lang_id' => $row['id'], 'block_direct_access' => 0 ] ] );
|
$results2 = $mdb->select( 'pp_articles_langs', [ '[><]pp_articles' => [ 'article_id' => 'id' ] ], [ 'seo_link', 'title', 'article_id', 'noindex', 'copy_from' ], [ 'AND' => [ 'status' => 1, 'lang_id' => $row['id'], 'block_direct_access' => 0 ] ] );
|
||||||
if ( is_array( $results2 ) )
|
if ( is_array( $results2 ) )
|
||||||
foreach ( $results2 as $row2 )
|
foreach ( $results2 as $row2 )
|
||||||
{
|
{
|
||||||
if ( $row2['copy_from'] != null )
|
if ( $row2['copy_from'] != null )
|
||||||
{
|
{
|
||||||
$results_tmp = $mdb -> get( 'pp_articles_langs', [
|
$results_tmp = $mdb->get( 'pp_articles_langs', [ 'seo_link', 'title' ], [ 'AND' => [ 'article_id' => $row2['article_id'], 'lang_id' => $row2['copy_from'] ] ] );
|
||||||
'seo_link',
|
|
||||||
'title'
|
|
||||||
], [
|
|
||||||
'AND' => [
|
|
||||||
'article_id' => $row2['article_id'],
|
|
||||||
'lang_id' => $row2['copy_from']
|
|
||||||
]
|
|
||||||
] );
|
|
||||||
$row2['seo_link'] = $results_tmp['seo_link'];
|
$row2['seo_link'] = $results_tmp['seo_link'];
|
||||||
$row2['title'] = $results_tmp['title'];
|
$row2['title'] = $results_tmp['title'];
|
||||||
}
|
}
|
||||||
@@ -650,81 +805,52 @@ class Helpers
|
|||||||
$robots .= 'Disallow: /' . $row2['seo_link'] . PHP_EOL;
|
$robots .= 'Disallow: /' . $row2['seo_link'] . PHP_EOL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$mdb->delete( 'pp_routes', [ 'AND' => [ 'article_id' => $row2['article_id'], 'lang_id' => $row['id'] ] ] );
|
||||||
|
|
||||||
if ( $row2['seo_link'] )
|
if ( $row2['seo_link'] )
|
||||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . self::seo( $row2['seo_link'] ) . '$ index.php?article=' . $row2['article_id'] . '&lang=' . $row['id'] . '&%{QUERY_STRING} [L]';
|
{
|
||||||
|
$mdb->insert( 'pp_routes', [
|
||||||
|
'article_id' => $row2['article_id'],
|
||||||
|
'lang_id' => $row['id'],
|
||||||
|
'pattern' => '^' . $language_link . self::seo( $row2['seo_link'] ) . '$',
|
||||||
|
'destination' => 'index.php?article=' . $row2['article_id'] . '&lang=' . $row['id'],
|
||||||
|
] );
|
||||||
|
}
|
||||||
else if ( $row2['title'] != null )
|
else if ( $row2['title'] != null )
|
||||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . 'a-' . $row2['article_id'] . '-' . self::seo( $row2['title'] ) . '$ index.php?article=' . $row2['article_id'] . '&lang=' . $row['id'] . '&%{QUERY_STRING} [L]';
|
{
|
||||||
|
$mdb->insert( 'pp_routes', [
|
||||||
|
'article_id' => $row2['article_id'],
|
||||||
|
'lang_id' => $row['id'],
|
||||||
|
'pattern' => '^' . $language_link . 'a-' . $row2['article_id'] . '-' . self::seo( $row2['title'] ) . '$',
|
||||||
|
'destination' => 'index.php?article=' . $row2['article_id'] . '&lang=' . $row['id'],
|
||||||
|
] );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$results = $mdb -> get( 'pp_settings', 'value', [ 'param' => 'htaccess' ] );
|
// Invalidacja cache tras
|
||||||
|
try {
|
||||||
|
( new \Shared\Cache\CacheHandler() )->delete( 'pp_routes:all' );
|
||||||
|
} catch ( \Exception $e ) {
|
||||||
|
// Redis niedostepny — ignorujemy
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = $mdb->get( 'pp_settings', 'value', [ 'param' => 'htaccess' ] );
|
||||||
if ( $results )
|
if ( $results )
|
||||||
$htaccess_data .= PHP_EOL . $results;
|
$htaccess_data .= PHP_EOL . $results;
|
||||||
|
|
||||||
$results = $mdb -> get( 'pp_settings', 'value', [ 'param' => 'robots' ] );
|
$results = $mdb->get( 'pp_settings', 'value', [ 'param' => 'robots' ] );
|
||||||
if ( $results )
|
if ( $results )
|
||||||
$robots .= PHP_EOL . $results;
|
$robots .= PHP_EOL . $results;
|
||||||
|
|
||||||
$site_map .= '</urlset>';
|
$site_map .= '</urlset>';
|
||||||
|
|
||||||
/* cache */
|
|
||||||
if ( $settings['htaccess_cache'] )
|
|
||||||
{
|
|
||||||
$htaccess_data = str_replace( '{HTACCESS_CACHE}',
|
|
||||||
'<IfModule mod_deflate.c>' . PHP_EOL
|
|
||||||
. 'AddOutputFilterByType DEFLATE text/html text/plain text/xml application/xml application/xhtml+xml text/css text/javascript application/javascript application/x-javascript' . PHP_EOL
|
|
||||||
. '</IfModule>' . PHP_EOL
|
|
||||||
. '<IfModule mod_headers.c>' . PHP_EOL
|
|
||||||
. 'Header set Access-Control-Allow-Origin "*"' . PHP_EOL
|
|
||||||
. '</IfModule>' . PHP_EOL
|
|
||||||
. '<IfModule mod_expires.c>' . PHP_EOL
|
|
||||||
. 'ExpiresActive on' . PHP_EOL
|
|
||||||
. 'ExpiresDefault "access plus 1 month"' . PHP_EOL
|
|
||||||
. 'ExpiresByType text/css "access plus 1 year"' . PHP_EOL
|
|
||||||
. 'ExpiresByType application/json "access plus 0 seconds"' . PHP_EOL
|
|
||||||
. 'ExpiresByType application/xml "access plus 0 seconds"' . PHP_EOL
|
|
||||||
. 'ExpiresByType text/xml "access plus 0 seconds"' . PHP_EOL
|
|
||||||
. 'ExpiresByType image/x-icon "access plus 1 week"' . PHP_EOL
|
|
||||||
. 'ExpiresByType text/x-component "access plus 1 month"' . PHP_EOL
|
|
||||||
. 'ExpiresByType text/html "access plus 0 seconds"' . PHP_EOL
|
|
||||||
. 'ExpiresByType application/javascript "access plus 1 year"' . PHP_EOL
|
|
||||||
. 'ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds"' . PHP_EOL
|
|
||||||
. 'ExpiresByType text/cache-manifest "access plus 0 seconds"' . PHP_EOL
|
|
||||||
. 'ExpiresByType audio/ogg "access plus 1 month"' . PHP_EOL
|
|
||||||
. 'ExpiresByType image/gif "access plus 1 month"' . PHP_EOL
|
|
||||||
. 'ExpiresByType image/jpeg "access plus 1 month"' . PHP_EOL
|
|
||||||
. 'ExpiresByType image/png "access plus 1 month"' . PHP_EOL
|
|
||||||
. 'ExpiresByType video/mp4 "access plus 1 month"' . PHP_EOL
|
|
||||||
. 'ExpiresByType video/ogg "access plus 1 month"' . PHP_EOL
|
|
||||||
. 'ExpiresByType video/webm "access plus 1 month"' . PHP_EOL
|
|
||||||
. 'ExpiresByType application/atom+xml "access plus 1 hour"' . PHP_EOL
|
|
||||||
. 'ExpiresByType application/rss+xml "access plus 1 hour"' . PHP_EOL
|
|
||||||
. 'ExpiresByType application/font-woff "access plus 1 month"' . PHP_EOL
|
|
||||||
. 'ExpiresByType application/vnd.ms-fontobject "access plus 1 month"' . PHP_EOL
|
|
||||||
. 'ExpiresByType application/x-font-ttf "access plus 1 month"' . PHP_EOL
|
|
||||||
. 'ExpiresByType font/opentype "access plus 1 month"' . PHP_EOL
|
|
||||||
. 'ExpiresByType image/svg+xml "access plus 1 month"' . PHP_EOL
|
|
||||||
. '</IfModule>'
|
|
||||||
, $htaccess_data );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$htaccess_data = str_replace( '{HTACCESS_CACHE}',
|
|
||||||
'<IfModule mod_headers.c>' . PHP_EOL
|
|
||||||
. 'Header set Cache-Control "no-cache, no-store, must-revalidate"' . PHP_EOL
|
|
||||||
. 'Header set Pragma "no-cache"' . PHP_EOL
|
|
||||||
. 'Header set Expires 0' . PHP_EOL
|
|
||||||
. '</IfModule>',
|
|
||||||
$htaccess_data );
|
|
||||||
}
|
|
||||||
|
|
||||||
$htaccess_data .= PHP_EOL;
|
$htaccess_data .= PHP_EOL;
|
||||||
$htaccess_data .= 'RewriteCond %{REQUEST_FILENAME} !-f' . PHP_EOL;
|
$htaccess_data .= 'RewriteCond %{REQUEST_FILENAME} !-f' . PHP_EOL;
|
||||||
$htaccess_data .= 'RewriteCond %{REQUEST_FILENAME} !-d' . PHP_EOL;
|
$htaccess_data .= 'RewriteCond %{REQUEST_FILENAME} !-d' . PHP_EOL;
|
||||||
$htaccess_data .= 'RewriteRule ^ index.php [L]';
|
$htaccess_data .= 'RewriteRule ^ index.php [L]';
|
||||||
|
|
||||||
// Niektore hostingi blokuja zmiane wersji PHP przez .htaccess.
|
// Niektore hostingi blokuja zmiane wersji PHP przez .htaccess.
|
||||||
// Automatycznie komentujemy niedozwolone dyrektywy, aby generowany plik byl kompatybilny.
|
|
||||||
$htaccess_data = preg_replace( '/^(\\s*)(AddHandler|SetHandler|ForceType)\\b/im', '$1# $2', $htaccess_data );
|
$htaccess_data = preg_replace( '/^(\\s*)(AddHandler|SetHandler|ForceType)\\b/im', '$1# $2', $htaccess_data );
|
||||||
|
|
||||||
$fp = fopen( $dir . '.htaccess', 'w' );
|
$fp = fopen( $dir . '.htaccess', 'w' );
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ class ProductArchiveController
|
|||||||
. $skuEanHtml;
|
. $skuEanHtml;
|
||||||
|
|
||||||
$rows[] = [
|
$rows[] = [
|
||||||
|
'_checkbox' => '<input type="checkbox" class="js-bulk-check" value="' . $id . '" aria-label="Zaznacz produkt">',
|
||||||
'lp' => $lp++ . '.',
|
'lp' => $lp++ . '.',
|
||||||
'product' => $productCell,
|
'product' => $productCell,
|
||||||
'price_brutto' => $priceBrutto !== '' ? $priceBrutto : '-',
|
'price_brutto' => $priceBrutto !== '' ? $priceBrutto : '-',
|
||||||
@@ -123,6 +124,7 @@ class ProductArchiveController
|
|||||||
|
|
||||||
$viewModel = new \admin\ViewModels\Common\PaginatedTableViewModel(
|
$viewModel = new \admin\ViewModels\Common\PaginatedTableViewModel(
|
||||||
[
|
[
|
||||||
|
['key' => '_checkbox', 'label' => '', 'class' => 'text-center table-col-bulk-check', 'sortable' => false, 'raw' => true],
|
||||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||||
['key' => 'product', 'sort_key' => 'name', 'label' => 'Nazwa', 'sortable' => true, 'raw' => true],
|
['key' => 'product', 'sort_key' => 'name', 'label' => 'Nazwa', 'sortable' => true, 'raw' => true],
|
||||||
['key' => 'price_brutto', 'sort_key' => 'price_brutto', 'label' => 'Cena', 'class' => 'text-center', 'sortable' => true],
|
['key' => 'price_brutto', 'sort_key' => 'price_brutto', 'label' => 'Cena', 'class' => 'text-center', 'sortable' => true],
|
||||||
@@ -190,4 +192,40 @@ class ProductArchiveController
|
|||||||
header( 'Location: /admin/product_archive/list/' );
|
header( 'Location: /admin/product_archive/list/' );
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function bulk_delete_permanent(): void
|
||||||
|
{
|
||||||
|
header( 'Content-Type: application/json; charset=utf-8' );
|
||||||
|
|
||||||
|
$rawIds = isset( $_POST['ids'] ) && is_array( $_POST['ids'] ) ? $_POST['ids'] : [];
|
||||||
|
$ids = [];
|
||||||
|
foreach ( $rawIds as $raw ) {
|
||||||
|
$id = (int) $raw;
|
||||||
|
if ( $id > 0 ) {
|
||||||
|
$ids[] = $id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( empty( $ids ) ) {
|
||||||
|
echo json_encode( ['success' => false, 'message' => 'Nie wybrano żadnych produktów.'] );
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$deleted = 0;
|
||||||
|
$errors = [];
|
||||||
|
foreach ( $ids as $id ) {
|
||||||
|
if ( $this->productRepository->delete( $id ) ) {
|
||||||
|
$deleted++;
|
||||||
|
} else {
|
||||||
|
$errors[] = $id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode( [
|
||||||
|
'success' => empty( $errors ),
|
||||||
|
'deleted' => $deleted,
|
||||||
|
'errors' => $errors,
|
||||||
|
] );
|
||||||
|
exit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,6 +104,9 @@ class ApiRouter
|
|||||||
$producerRepo = new \Domain\Producer\ProducerRepository($db);
|
$producerRepo = new \Domain\Producer\ProducerRepository($db);
|
||||||
return new Controllers\DictionariesApiController($statusRepo, $transportRepo, $paymentRepo, $attrRepo, $producerRepo);
|
return new Controllers\DictionariesApiController($statusRepo, $transportRepo, $paymentRepo, $attrRepo, $producerRepo);
|
||||||
},
|
},
|
||||||
|
'categories' => function () use ($db) {
|
||||||
|
return new Controllers\CategoriesApiController();
|
||||||
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
104
autoload/api/Controllers/CategoriesApiController.php
Normal file
104
autoload/api/Controllers/CategoriesApiController.php
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
<?php
|
||||||
|
namespace api\Controllers;
|
||||||
|
|
||||||
|
use api\ApiRouter;
|
||||||
|
|
||||||
|
class CategoriesApiController
|
||||||
|
{
|
||||||
|
public function list(): void
|
||||||
|
{
|
||||||
|
if (!ApiRouter::requireMethod('GET')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = $GLOBALS['mdb'] ?? null;
|
||||||
|
if (!$db) {
|
||||||
|
ApiRouter::sendError('INTERNAL_ERROR', 'Database not available', 500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default shop language
|
||||||
|
$defaultLang = $db->get('pp_langs', 'id', ['start' => 1]);
|
||||||
|
if (!$defaultLang) {
|
||||||
|
$defaultLang = 'pl';
|
||||||
|
}
|
||||||
|
$defaultLang = (string)$defaultLang;
|
||||||
|
|
||||||
|
// All active categories, ordered by display order
|
||||||
|
$rows = $db->select(
|
||||||
|
'pp_shop_categories',
|
||||||
|
['id', 'parent_id'],
|
||||||
|
[
|
||||||
|
'status' => 1,
|
||||||
|
'ORDER' => ['o' => 'ASC'],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!is_array($rows) || empty($rows)) {
|
||||||
|
ApiRouter::sendSuccess(['categories' => []]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$categoryIds = array_values(array_filter(
|
||||||
|
array_map(fn($row) => (int)($row['id'] ?? 0), $rows),
|
||||||
|
fn($id) => $id > 0
|
||||||
|
));
|
||||||
|
|
||||||
|
// Bulk fetch titles for default language
|
||||||
|
$titlesByCategory = [];
|
||||||
|
$titleRows = $db->select('pp_shop_categories_langs', ['category_id', 'title'], [
|
||||||
|
'AND' => [
|
||||||
|
'category_id' => $categoryIds,
|
||||||
|
'lang_id' => $defaultLang,
|
||||||
|
'title[!]' => '',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
if (is_array($titleRows)) {
|
||||||
|
foreach ($titleRows as $tr) {
|
||||||
|
$tid = (int)($tr['category_id'] ?? 0);
|
||||||
|
if ($tid > 0 && !isset($titlesByCategory[$tid])) {
|
||||||
|
$titlesByCategory[$tid] = (string)($tr['title'] ?? '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bulk fetch fallback titles for categories without a title in default language
|
||||||
|
$missingIds = array_values(array_filter($categoryIds, fn($id) => !isset($titlesByCategory[$id])));
|
||||||
|
if (!empty($missingIds)) {
|
||||||
|
$fallbackRows = $db->select('pp_shop_categories_langs', ['category_id', 'title'], [
|
||||||
|
'AND' => [
|
||||||
|
'category_id' => $missingIds,
|
||||||
|
'title[!]' => '',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
if (is_array($fallbackRows)) {
|
||||||
|
foreach ($fallbackRows as $fr) {
|
||||||
|
$fid = (int)($fr['category_id'] ?? 0);
|
||||||
|
if ($fid > 0 && !isset($titlesByCategory[$fid])) {
|
||||||
|
$titlesByCategory[$fid] = (string)($fr['title'] ?? '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build flat category list
|
||||||
|
$categories = [];
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$categoryId = (int)($row['id'] ?? 0);
|
||||||
|
if ($categoryId <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parentId = $row['parent_id'] !== null ? (int)$row['parent_id'] : null;
|
||||||
|
$title = $titlesByCategory[$categoryId] ?? ('Kategoria #' . $categoryId);
|
||||||
|
|
||||||
|
$categories[] = [
|
||||||
|
'id' => $categoryId,
|
||||||
|
'parent_id' => $parentId,
|
||||||
|
'title' => $title,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiRouter::sendSuccess(['categories' => $categories]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -437,7 +437,7 @@ class ProductsApiController
|
|||||||
// String fields — direct mapping
|
// String fields — direct mapping
|
||||||
$stringFields = [
|
$stringFields = [
|
||||||
'sku', 'ean', 'custom_label_0', 'custom_label_1', 'custom_label_2',
|
'sku', 'ean', 'custom_label_0', 'custom_label_1', 'custom_label_2',
|
||||||
'custom_label_3', 'custom_label_4', 'wp',
|
'custom_label_3', 'custom_label_4', 'wp', 'new_to_date', 'additional_message_text',
|
||||||
];
|
];
|
||||||
foreach ($stringFields as $field) {
|
foreach ($stringFields as $field) {
|
||||||
if (isset($body[$field])) {
|
if (isset($body[$field])) {
|
||||||
@@ -447,6 +447,18 @@ class ProductsApiController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($body['additional_message'])) {
|
||||||
|
$d['additional_message'] = !empty($body['additional_message']) ? 'on' : '';
|
||||||
|
} elseif ($existing !== null) {
|
||||||
|
$d['additional_message'] = !empty($existing['additional_message']) ? 'on' : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($body['additional_message_required'])) {
|
||||||
|
$d['additional_message_required'] = !empty($body['additional_message_required']) ? 'on' : '';
|
||||||
|
} elseif ($existing !== null) {
|
||||||
|
$d['additional_message_required'] = !empty($existing['additional_message_required']) ? 'on' : '';
|
||||||
|
}
|
||||||
|
|
||||||
// Foreign keys
|
// Foreign keys
|
||||||
if (isset($body['set_id'])) {
|
if (isset($body['set_id'])) {
|
||||||
$d['set'] = $body['set_id'];
|
$d['set'] = $body['set_id'];
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ if ( is_array( $this -> pages ) ) {
|
|||||||
|
|
||||||
foreach ( $this -> pages as $page ) {
|
foreach ( $this -> pages as $page ) {
|
||||||
$url = "";
|
$url = "";
|
||||||
|
$hasChildren = is_array( $page['pages'] ) && !empty( $page['pages'] );
|
||||||
|
|
||||||
if ( $page['page_type'] == 3 ) {
|
if ( $page['page_type'] == 3 ) {
|
||||||
$page['language']['link'] ? $url = $page['language']['link'] : $url = '#';
|
$page['language']['link'] ? $url = $page['language']['link'] : $url = '#';
|
||||||
@@ -26,10 +27,10 @@ if ( is_array( $this -> pages ) ) {
|
|||||||
if ( $page['id'] == $this -> current_page )
|
if ( $page['id'] == $this -> current_page )
|
||||||
echo ' active';
|
echo ' active';
|
||||||
|
|
||||||
if ( is_array( $page['pages'] ) and in_array( $this -> current_page, $children ) )
|
if ( $hasChildren and is_array( $children ) and in_array( $this -> current_page, $children ) )
|
||||||
echo ' open';
|
echo ' open';
|
||||||
|
|
||||||
if ( is_array( $page['pages'] ) )
|
if ( $hasChildren )
|
||||||
echo ' parent';
|
echo ' parent';
|
||||||
|
|
||||||
echo '">';
|
echo '">';
|
||||||
@@ -40,16 +41,17 @@ if ( is_array( $this -> pages ) ) {
|
|||||||
if ( $page['language']['noindex'] )
|
if ( $page['language']['noindex'] )
|
||||||
echo 'rel="nofollow"';
|
echo 'rel="nofollow"';
|
||||||
echo ' title="' . $page['language']['title'] . '"';
|
echo ' title="' . $page['language']['title'] . '"';
|
||||||
if ( is_array( $page['pages'] ) )
|
if ( $hasChildren )
|
||||||
echo "class='menu-trigger'";
|
echo "class='menu-trigger'";
|
||||||
echo '>';
|
echo '>';
|
||||||
echo $page['language']['title'];
|
echo $page['language']['title'];
|
||||||
echo '</a>';
|
echo '</a>';
|
||||||
if ( is_array( $page['pages'] ) )
|
if ( $hasChildren )
|
||||||
echo '<i class="fa fa-chevron-down menu-toggle" menu-id="link-' . $page['id'] . '"></i>';
|
echo '<i class="fa fa-chevron-down menu-toggle" menu-id="link-' . $page['id'] . '"></i>';
|
||||||
echo \front\Views\Menu::pages( $page['pages'], $this -> level + 1, $this -> current_page );
|
if ( $hasChildren )
|
||||||
|
echo \front\Views\Menu::pages( $page['pages'], $this -> level + 1, $this -> current_page );
|
||||||
echo '</li>';
|
echo '</li>';
|
||||||
}
|
}
|
||||||
echo '</ul>';
|
echo '</ul>';
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -13,7 +13,14 @@
|
|||||||
$basket_summary = \Domain\Basket\BasketCalculator::summaryPrice( $basket, $coupon ) + $transport_cost;
|
$basket_summary = \Domain\Basket\BasketCalculator::summaryPrice( $basket, $coupon ) + $transport_cost;
|
||||||
?>
|
?>
|
||||||
<? if ( is_array( $this -> payment_methods ) ): foreach ( $this -> payment_methods as $payment_method ):?>
|
<? if ( is_array( $this -> payment_methods ) ): foreach ( $this -> payment_methods as $payment_method ):?>
|
||||||
<? if ( $payment_method['id'] != 6 or $payment_method['id'] == 6 and $basket_summary >= 40 and $basket_summary <= 1000 ):?>
|
<?
|
||||||
|
$min = isset($payment_method['min_order_amount']) ? (float)$payment_method['min_order_amount'] : null;
|
||||||
|
$max = isset($payment_method['max_order_amount']) ? (float)$payment_method['max_order_amount'] : null;
|
||||||
|
$show = true;
|
||||||
|
if ($min !== null && $min > 0 && $basket_summary < $min) $show = false;
|
||||||
|
if ($max !== null && $max > 0 && $basket_summary > $max) $show = false;
|
||||||
|
?>
|
||||||
|
<? if ( $show ):?>
|
||||||
<div class="options">
|
<div class="options">
|
||||||
<div class="check">
|
<div class="check">
|
||||||
<input type="radio" class="icheck" name="payment_method" value="<?= $payment_method['id'];?>"
|
<input type="radio" class="icheck" name="payment_method" value="<?= $payment_method['id'];?>"
|
||||||
|
|||||||
@@ -52,6 +52,32 @@ if ( is_array( $this -> transports_methods ) )
|
|||||||
</div>
|
</div>
|
||||||
<? endif;?>
|
<? endif;?>
|
||||||
<? endforeach; endif;?>
|
<? endforeach; endif;?>
|
||||||
|
<?php if ( $this->free_delivery > 0 ): ?>
|
||||||
|
<?php
|
||||||
|
$percentage = min(100, ($this->basket_summary / $this->free_delivery) * 100);
|
||||||
|
$remaining = $this->free_delivery - $this->basket_summary;
|
||||||
|
?>
|
||||||
|
<div class="free-delivery-bar <?= $percentage >= 100 ? 'success' : '' ?>">
|
||||||
|
<div class="free-delivery-bar__icon">🚚</div>
|
||||||
|
<div class="free-delivery-bar__content">
|
||||||
|
<?php if ($percentage >= 100): ?>
|
||||||
|
<div class="free-delivery-bar__text">Gratulacje! Masz darmową dostawę!</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="free-delivery-bar__text">
|
||||||
|
Zrób zakupy za <?= \Shared\Helpers\Helpers::decimal( $this->free_delivery, 2 );?> zł i otrzymaj darmową dostawę
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<div class="free-delivery-bar__progress">
|
||||||
|
<div class="free-delivery-bar__progress-fill" style="width: <?= $percentage ?>%"></div>
|
||||||
|
</div>
|
||||||
|
<?php if ($percentage < 100): ?>
|
||||||
|
<div class="free-delivery-bar__remaining">
|
||||||
|
Brakuje <strong><?= \Shared\Helpers\Helpers::decimal( $remaining, 2 );?> zł</strong> do darmowej dostawy.
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
<script class="footer" type="text/javascript">
|
<script class="footer" type="text/javascript">
|
||||||
$( function() {
|
$( function() {
|
||||||
|
|
||||||
|
|||||||
@@ -179,7 +179,7 @@
|
|||||||
'id': <?= (int)$product['product_id'];?>,
|
'id': <?= (int)$product['product_id'];?>,
|
||||||
'name': '<?= $product['name'];?>',
|
'name': '<?= $product['name'];?>',
|
||||||
'quantity': <?= $product['quantity'];?>,
|
'quantity': <?= $product['quantity'];?>,
|
||||||
'price': <?= $product['price_brutto_promo'];?>
|
'price': <?= ((float)$product['price_brutto_promo'] > 0 && (float)$product['price_brutto_promo'] < (float)$product['price_brutto']) ? (float)$product['price_brutto_promo'] : (float)$product['price_brutto'];?>
|
||||||
}<? if ( $product != end( $this -> order['products'] ) ) echo ',';?>
|
}<? if ( $product != end( $this -> order['products'] ) ) echo ',';?>
|
||||||
<? endforeach;?>
|
<? endforeach;?>
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user