This commit is contained in:
2026-04-30 21:31:32 +02:00
parent e22bbde336
commit e6a09f6c95
16 changed files with 1929 additions and 1 deletions

69
.paul/PROJECT.md Normal file
View File

@@ -0,0 +1,69 @@
# centrumcopy.com.pl
## What This Is
Strona internetowa dla małej firmy, prezentująca ofertę produktów i dane kontaktowe. Serwis informacyjny dla firmy sprzedającej drukarki, zbudowany w oparciu o framework Kohana (PHP).
## Core Value
Klienci mogą poznać ofertę drukarek firmy i skontaktować się z nią.
## Current State
| Attribute | Value |
|-----------|-------|
| Version | 0.1.0 |
| Status | Prototype |
| Last Updated | 2026-04-30 |
## Requirements
### Validated (Shipped)
- (none yet)
### Active (In Progress)
- [ ] [To be defined during planning]
### Planned (Next)
- [ ] [To be defined during planning]
### Out of Scope
- [To be identified during planning]
## Target Users
**Primary:** Potencjalni klienci szukający drukarek
- Osoby i firmy poszukujące drukarek i materiałów eksploatacyjnych
- Potrzebują szybkiego dostępu do oferty i danych kontaktowych
## Context
**Business Context:**
Mała firma sprzedająca drukarki, potrzebuje profesjonalnej strony internetowej do prezentacji oferty.
**Technical Context:**
PHP z frameworkiem Kohana, struktura MVC, brak systemu zarządzania pakietami (composer).
## Constraints
### Technical Constraints
- PHP / Kohana framework
- Hosting z obsługą PHP
- Brak composer.json — ręczne zarządzanie zależnościami
### Business Constraints
- Strona informacyjna (nie e-commerce)
## Success Criteria
- Klienci mogą poznać pełną ofertę drukarek firmy
- Klienci mogą skontaktować się z firmą przez stronę
- Strona działa poprawnie i jest responsywna
---
*PROJECT.md — Updated when requirements or context change*
*Last updated: 2026-04-30*

25
.paul/ROADMAP.md Normal file
View File

@@ -0,0 +1,25 @@
# Roadmap: centrumcopy.com.pl
## Overview
Budowa i ulepszanie strony informacyjnej firmy sprzedającej drukarki — od audytu istniejącego kodu, przez ulepszenia funkcjonalne, po wdrożenie produkcyjne.
## Current Milestone
**v0.1 Initial Release** (v0.1.0)
Status: Not started
Phases: 0 of TBD complete
## Phases
| Phase | Name | Plans | Status | Completed |
|-------|------|-------|--------|-----------|
| 1 | TBD | TBD | Not started | - |
## Phase Details
Phases will be defined during `/paul:plan`.
---
*Roadmap created: 2026-04-30*
*Last updated: 2026-04-30*

48
.paul/STATE.md Normal file
View File

@@ -0,0 +1,48 @@
# Project State
## Project Reference
See: .paul/PROJECT.md (updated 2026-04-30)
**Core value:** Klienci mogą poznać ofertę drukarek firmy i skontaktować się z nią.
**Current focus:** Project initialized — ready for planning
## Current Position
Milestone: v0.1 Initial Release
Phase: Not yet defined
Plan: None yet
Status: Ready to create roadmap and first PLAN
Last activity: 2026-04-30 — Project initialized
Progress:
- Milestone: [░░░░░░░░░░] 0%
## Loop Position
Current loop state:
```
PLAN ──▶ APPLY ──▶ UNIFY
○ ○ ○ [Ready for first PLAN]
```
## Accumulated Context
### Decisions
None yet.
### Deferred Issues
None yet.
### Blockers/Concerns
None yet.
## Session Continuity
Last session: 2026-04-30
Stopped at: Project initialization complete
Next action: Run /paul:plan to define phases and first plan
Resume file: .paul/PROJECT.md
---
*STATE.md — Updated after every significant action*

View File

@@ -0,0 +1,351 @@
# Architecture & Structure
**Analysis Date:** 2026-04-30
---
## Directory Layout
```
centrumcopy.com.pl/ # Docroot / front controller
├── index.php # Front controller (entry point)
├── robots.txt
├── application/ # Application code (APPPATH)
│ ├── cache/ # Kohana internal cache (file-based)
│ ├── config/ # All configuration files
│ ├── controllers/ # MVC Controllers
│ │ ├── base_admin.php # Abstract base for admin controllers
│ │ ├── base_front.php # Abstract base for front controllers
│ │ ├── install.php # Installation controller
│ │ ├── admin/ # Admin panel controllers
│ │ └── front/ # Public-facing controllers
│ ├── helpers/ # (empty — helpers live in libraries/drivers)
│ ├── i18n/pl_PL/ # Polish locale strings
│ ├── libraries/ # Extended/custom libraries
│ │ └── drivers/ # Driver extensions
│ ├── logs/ # Error/application logs
│ ├── models/ # ORM models
│ └── views/ # PHP view templates
│ ├── admin/ # Admin panel partial views
│ ├── front/ # Public partial views
│ ├── gmaps/ # Google Maps JS view
│ ├── pagination/ # Pagination style templates
│ ├── admin_layout.php # Admin full-page layout
│ ├── admin_login.php # Admin login page
│ └── default_layout.php # Public full-page layout
├── modules/ # Kohana modules (MODPATH)
│ ├── gmaps/ # Google Maps integration module
│ └── debug_toolbar/ # Debug toolbar module
├── system/ # Kohana framework core (SYSPATH)
│ └── core/ # Bootstrap, Event, Kohana, Benchmark
├── css/ # Public stylesheets
├── js/ # Public JavaScript (jQuery, TinyMCE, etc.)
├── images/ # Public images
├── flash/ # Flash banner assets
├── uploads/ # User-uploaded files
└── stara/ # Legacy static HTML site (archived)
```
---
## MVC Structure
### Controllers
**Location:** `application/controllers/`
Controllers use Kohana 2.x naming: `{Name}_Controller` extends a base class. File name is lowercase, class name is `PascalCase_Controller`.
**Base classes (abstract):**
| File | Class | Extends | Role |
|------|-------|---------|------|
| `application/controllers/base_front.php` | `Base_Front_Controller` | `Controller` | Sets up front layout (`default_layout`), session, menu, SEO metadata |
| `application/controllers/base_admin.php` | `Base_Admin_Controller` | `Controller` | Sets up admin layout (`admin_layout`), enforces auth guard, manages flash messages |
**Front (public) controllers**`application/controllers/front/`:
| File | Class | Handles |
|------|-------|---------|
| `front/page.php` | `Page_Controller` | Static CMS pages, homepage redirect, contact page with Google Maps |
| `front/welcome.php` | `Welcome_Controller` | (unused/fallback) |
**Admin controllers**`application/controllers/admin/`:
| File | Class | Handles |
|------|-------|---------|
| `admin/page.php` | `Page_Controller` | Edit CMS page content via TinyMCE |
| `admin/user.php` | `User_Controller` | Login, logout, password change |
| `admin/welcome.php` | `Welcome_Controller` | Admin dashboard index |
Note: Both `front/page.php` and `admin/page.php` define `Page_Controller` — they are isolated by directory scoping during routing.
**Auth guard pattern in `Base_Admin_Controller`:**
```php
if (!$this->session->get('admin') && Router::$method != 'login' && Router::$method != 'logout') {
url::redirect('admin/login');
}
```
**`__call` magic method** is used in `Page_Controller` (front) to route any unknown URL segment to `show($name)`, enabling slug-based page lookup.
### Models
**Location:** `application/models/`
All models extend `ORM` (Kohana's ActiveRecord-style ORM). Model file name is lowercase; class name is `{Name}_Model`.
| File | Class | Table | Notes |
|------|-------|-------|-------|
| `models/page.php` | `Page_Model` | `page` | Singular table name; supports lookup by `name` slug |
| `models/user.php` | `User_Model` | `user` | Primary val = `username`; SHA1+salt password hashing |
| `models/news.php` | `News_Model` | `news` | Singular; sorted by `created_at DESC` |
| `models/gallery.php` | `Gallery_Model` | `gallery` | Has many `gallery_images`; sorted `created_at ASC` |
| `models/gallery_image.php` | `Gallery_Image_Model` | `gallery_image` | Belongs to `gallery`; sorted by `id ASC` |
**ORM lookup pattern:**
```php
ORM::factory('page')->where('name', $slug)->find();
if (!$page->loaded) { return $this->error404(); }
```
### Views
**Location:** `application/views/`
Views are plain PHP templates. Layout views are full HTML documents; partial views are HTML fragments assigned to `$content`.
**Layout views (full pages):**
| File | Used by |
|------|---------|
| `views/default_layout.php` | All front controllers via `Base_Front_Controller` |
| `views/admin_layout.php` | All admin controllers via `Base_Admin_Controller` |
| `views/admin_login.php` | `User_Controller::login()` |
**Partial views — front:** `views/front/`
| File | Rendered by |
|------|-------------|
| `front/page_show.php` | `Page_Controller::show()` |
| `front/page_contact.php` | `Page_Controller::contact()` |
| `front/error404.php` | `Base_Front_Controller::error404()` |
**Partial views — admin:** `views/admin/`
| File | Rendered by |
|------|-------------|
| `admin/page_edit.php` | `admin/Page_Controller::edit()` |
| `admin/password.php` | `User_Controller::password()` |
| `admin/welcome.php` | `admin/Welcome_Controller::index()` |
| `admin/error404.php` | `Base_Admin_Controller::error404()` |
**View rendering pattern:**
```php
$page_view = new View('front/page_show');
$page_view->page = $page; // assign data to partial
$this->view->content = $page_view; // embed partial in layout
$this->view->render(true); // output full layout
```
---
## Routing
**Config file:** `application/config/routes.php`
Kohana 2.x routing maps URL patterns (regex) to `directory/controller/method` targets. Routes are evaluated top-to-bottom; first match wins.
```php
$config['_default'] = 'front/page/homepage'; // / → Front\Page::homepage()
// Admin routes
$config['admin'] = 'admin/welcome/'; // /admin
$config['admin/login'] = 'admin/user/login'; // /admin/login
$config['admin/logout'] = 'admin/user/logout'; // /admin/logout
$config['admin/password'] = 'admin/user/password'; // /admin/password
$config['admin/page/(.*)'] = 'admin/page/edit/$1'; // /admin/page/{slug}
$config['admin/(.*)'] = 'admin/$1'; // /admin/...
// Front routes
$config['kontakt'] = 'front/page/contact'; // /kontakt
$config['galeria/?(.*)'] = 'front/gallery/$1'; // /galeria/...
$config['aktualnosci/?(.*)'] = 'front/news/$1'; // /aktualnosci/...
$config['(.+)'] = 'front/page/$1'; // /{any-slug} → Page::show()
```
**Slug routing:** The catch-all `(.+)` route passes the URL slug to `front/page/`. `Page_Controller::show($name)` then queries the database for a page with `name = $slug`. This means all CMS pages live in a flat namespace matched against the `page.name` column.
**URL rewriting:** `index_page` is commented out in `application/config/config.php`, meaning clean URLs (no `index.php` in path) require Apache `mod_rewrite` or equivalent.
---
## Module System
**Module path:** `modules/` (MODPATH)
Modules are configured in `application/config/config.php` under `$config['modules']`. Kohana merges module paths into the file search cascade (controllers, views, libraries, etc. from a module are auto-discoverable).
**Active modules:**
| Module | Path | Purpose |
|--------|------|---------|
| `gmaps` | `modules/gmaps/` | Google Maps v2 API wrapper — `Gmap` library, marker helpers, JS view |
**Available but disabled modules** (commented out in config):
- `auth`, `forge`, `kodoc`, `media`, `archive`, `payment`, `unit_test`, `object_db`
**`debug_toolbar` module** exists at `modules/debug_toolbar/` but is **not loaded** in config.
**Module internal structure** (gmaps example):
```
modules/gmaps/
├── config/ # Module config
├── controllers/ # Module controllers (if any)
├── i18n/
├── libraries/ # Gmap, Gmap_Marker libraries
├── models/
└── views/ # gmaps/javascript.php
```
**Custom application-level extensions** (not modules — override framework classes):
| File | Purpose |
|------|---------|
| `application/libraries/MY_Database.php` | Extends `Database_Core` — fixes `COUNT()` column escaping with table prefix |
| `application/libraries/MY_Gmap_Marker.php` | Extends Gmap marker from the gmaps module |
| `application/libraries/drivers/Database.php` | Custom database driver wrapper |
| `application/libraries/drivers/MY_form.php` | Extended form helper |
| `application/libraries/drivers/MY_html.php` | Extended html helper (adds `span_class()`, etc.) |
| `application/libraries/drivers/MY_valid.php` | Extended validation rules |
| `application/libraries/drivers/categories.php` | Category tree helper |
| `application/libraries/drivers/javascript.php` | JavaScript generation helper |
---
## Business Domains
This is a **B2B product catalogue + CMS** for a photocopier/office equipment distributor (Centrum Copy Rzeszów).
### CMS / Static Pages
- **Primary domain.** All content pages are rows in the `page` table, looked up by a URL `name` slug.
- Pages have: `title`, `header`, `content` (HTML via TinyMCE), `meta_description`, `meta_keywords`.
- Admin edits pages via WYSIWYG at `/admin/page/{slug}`.
- Menu structure is **hardcoded** in `application/config/application.php` under `$config['menu_nav']` — changing the menu requires a code deploy.
- Content domains visible in admin menu: O firmie, Powielacze cyfrowe RISO, Pełnokolorowe urządzenia Inkjet RISO ComColor, Urządzenia biurowe (kolorowe/monochromatyczne A4/A3), Plotery i skanery, Finansowanie, Serwis, Usługi, Kontakt, Szybki kontakt.
### Gallery
- Model: `Gallery_Model` (has_many `Gallery_Image_Model`)
- Route: `/galeria/...``front/gallery` controller
- Note: `application/controllers/front/gallery.php` does not exist as a file — gallery controller is **missing or not committed**.
### News / Aktualności
- Model: `News_Model`
- Route: `/aktualnosci/...``front/news` controller
- Note: `application/controllers/front/news.php` does not exist — news controller is **missing or not committed**.
### Contact Page
- Route `/kontakt``Page_Controller::contact()`
- Renders Google Maps v2 marker at configured lat/lon (`application/config/application.php``$config['gmaps']`).
- Google Maps centre: `50.0491231, 21.9869502` (Rzeszów, ul. Okulickiego 9).
### Admin Panel
- Session-based authentication. Credentials: SHA1(salt + password) stored in `user` table.
- Single-user admin — no roles enforced beyond session presence.
- Admin navigates to hardcoded page slugs from the sidebar in `admin_layout.php`.
- Features: edit page content, change admin password, logout.
---
## Request Lifecycle
```
Browser Request
|
v
index.php (front controller)
- defines DOCROOT, APPPATH, MODPATH, SYSPATH
- sets IN_PRODUCTION = false
- requires system/core/Bootstrap.php
|
v
system/core/Bootstrap.php
- loads Benchmark, utf8, Event, Kohana
- Kohana::setup() (loads config, modules, sets error handlers)
|
v
Event::run('system.ready') (hooks, if enabled — currently disabled)
|
v
Event::run('system.routing')
- Router matches URI against application/config/routes.php
- Determines: directory, controller, method, arguments
|
v
Event::run('system.execute')
- Instantiates controller: new {Name}_Controller()
- __construct() runs:
- parent::__construct() (Kohana base Controller)
- Base_Front/Admin_Controller::__construct()
- Creates View object for layout
- Starts Session
- Loads config (title, meta, menu_nav, etc.)
- [Admin only] Auth guard → redirect to login if not authed
- Calls controller method (e.g., show(), edit(), login())
- Method queries ORM models
- Creates partial View, assigns data
- Assigns partial to $this->view->content
- $this->view->render(true) → outputs full HTML
|
v
Event::run('system.shutdown') (cleanup)
|
v
Response sent to browser
```
**Session:** Native PHP sessions, 30-minute expiration, validated by `user_agent`. Session name: `Frisson_session`.
**Database:** MySQLi driver, single `default` connection, no table prefix, UTF-8 charset, query benchmarking enabled.
**Output compression:** gzip enabled (`$config['output_compression'] = TRUE`).
**Error handling:** Errors logged to `application/logs/` at threshold 1 (errors + exceptions). `display_errors` is off in production.
---
## Configuration Files
| File | Purpose |
|------|---------|
| `application/config/application.php` | Site title, email, Google Maps coords, navigation menu, Google Analytics |
| `application/config/config.php` | Kohana core settings: domain, modules list, cache, compression, hooks |
| `application/config/database.php` | MySQLi connection credentials |
| `application/config/routes.php` | URL routing rules |
| `application/config/session.php` | Session driver, name, expiration |
| `application/config/pagination.php` | Pagination defaults (5 items/page, segment 3) |
| `application/config/email.php` | Email settings |
| `application/config/cookie.php` | Cookie settings |
| `application/config/tiny_mce.php` | TinyMCE upload paths for admin editor |
| `application/config/upload.php` | File upload settings |
| `application/config/locale.php` | Locale/timezone |
| `application/config/gmaps.php` | Google Maps module config |
| `application/config/debug_toolbar.php` | Debug toolbar settings |
---
## Key Observations & Gaps
1. **Missing controllers:** `application/controllers/front/gallery.php` and `application/controllers/front/news.php` are referenced in routes but absent. Routes for `/galeria/` and `/aktualnosci/` will 404.
2. **Hardcoded navigation:** `$config['menu_nav']` in `application/config/application.php` defines the entire site menu statically. Adding/removing menu items requires editing this config file and redeploying.
3. **Admin menu also hardcoded:** `application/views/admin_layout.php` contains inline HTML listing every editable page slug. Must be manually updated to match `page` table contents.
4. **IN_PRODUCTION = false:** Set in `index.php`. Must be changed to `true` before deploying to live server to enable Google Analytics and suppress demo controllers.
5. **Kohana version:** 2.3.4 (codename "buteo regalis") — very old, unmaintained framework. No Composer, no autoloading beyond Kohana's own file cascade.
6. **Auth security:** `User_Controller::login()` contains `print_r($_POST)` debug statement (line 29). This leaks credentials in the HTTP response. Remove before any production use.
7. **No test runner configured.** No test files detected.

274
.paul/codebase/concerns.md Normal file
View File

@@ -0,0 +1,274 @@
# Concerns & Technical Debt
**Analysis Date:** 2026-04-30
---
## Security Issues
### [HIGH] Database credentials committed to version control
- **File:** `application/config/database.php`
- **Detail:** Production MySQL username (`host420804_db`) and plaintext password (`VanMzwjUn85ySRyR`) are hardcoded and committed to git.
- **Impact:** Anyone with access to the repository has full database access. This is especially dangerous given the repo is on a shared hosting environment.
- **Fix:** Move credentials to a `.env` file or server-level environment variables. Add `application/config/database.php` to `.gitignore` and use a `database.php.example` as template.
### [HIGH] FTP credentials committed to version control
- **File:** `.vscode/ftp-kr.json`
- **Detail:** Production FTP credentials are committed — host `host420804.hostido.net.pl`, username `www@centrumcopy.com.pl`, and plaintext password (`JHycfrHnyEAYsJHtR26C`). Protocol is plain FTP (not SFTP).
- **Impact:** Full read/write access to the production server's `public_html` for anyone with repo access.
- **Fix:** Add `.vscode/ftp-kr.json` to `.gitignore`. Switch from FTP to SFTP. Store credentials outside the repository.
### [HIGH] Unprotected install controller accessible in production
- **File:** `application/controllers/install.php`
- **Detail:** The `install` route is publicly routable (`$config['install/*(.*)'] = 'install/$1'` in `application/config/routes.php`). The controller can create users and insert database records. No authentication check. Visiting `/install/init` in a browser would recreate seed data, including writing admin users.
- **Impact:** Any visitor can trigger `/install/user/admin` which resets admin credentials to `admin/admin`.
- **Fix:** Delete or disable the install controller in production. At minimum wrap every method with an IP whitelist or a production check using `IN_PRODUCTION`.
### [HIGH] Force-login backdoor controller
- **File:** `application/controllers/admin/force.php`
- **Detail:** `Force_Controller::login()` logs in as user ID 1 with no password check whatsoever, then sets the admin session. It is reachable at `/admin/force/login` via the catch-all route `$config['admin/(.*)'] = 'admin/$1'`.
- **Impact:** Any visitor can visit `/admin/force/login` and immediately gain full admin access without credentials.
- **Fix:** Delete this file immediately. It appears to be a developer debug shortcut left in the codebase.
### [HIGH] Debug output (`print_r($_POST)`) in the login controller
- **File:** `application/controllers/admin/user.php`, line 29
- **Detail:** `print_r($_POST)` is called on every POST to the login form. This outputs all submitted POST data (including username and password) directly to the browser response.
- **Impact:** Credentials are exposed in the HTTP response body. May also interfere with redirects.
- **Fix:** Remove this line immediately.
### [HIGH] Unescaped HTML output of page content in front-end views
- **Files:** `application/views/front/page_show.php` (line 5), `application/views/front/page_contact.php` (line 5), `application/views/default_layout.php` (lines 73, 75)
- **Detail:** `$page->content`, `$page->header`, `$szybki_kontakt->content`, and `$szybki_kontakt->header` are echoed raw with no escaping. Admin-saved HTML content is intentional (TinyMCE WYSIWYG), but if the admin account is compromised, an attacker could inject arbitrary JavaScript.
- **Impact:** Stored XSS is possible through the admin panel if admin credentials are stolen.
- **Fix:** Accept risk intentionally for trusted admin HTML content, but enforce strong auth (see CSRF and password hashing concerns). Document this explicitly.
### [HIGH] No CSRF protection on any form
- **Files:** All forms — `admin_login.php`, `admin/page_edit.php`, `admin/password.php`
- **Detail:** Zero CSRF tokens are generated or validated on any POST form. Kohana 2.x includes no built-in CSRF protection. The forms rely solely on JavaScript-side validation, which is easily bypassed.
- **Impact:** Any site can submit a form on behalf of a logged-in admin, changing page content or admin credentials.
- **Fix:** Generate a per-session CSRF token in the base controllers and validate it on every POST.
### [HIGH] Weak password hashing (SHA-1 with weak salt)
- **Files:** `application/controllers/admin/user.php` (lines 33, 93, 97), `application/controllers/install.php` (lines 29, 36, 87, 93)
- **Detail:** Passwords are hashed using `sha1($salt . $password)`. The salt is generated as `md5(rand(100000,999999) . $username . $email)` — a low-entropy 6-digit random number seeded with predictable data. SHA-1 is cryptographically broken for password storage.
- **Impact:** If the database is breached, passwords are easily crackable via rainbow tables or brute force.
- **Fix:** Migrate to `password_hash()` / `password_verify()` with `PASSWORD_BCRYPT` or `PASSWORD_ARGON2ID`.
### [MEDIUM] No rate limiting or lockout on the login endpoint
- **File:** `application/controllers/admin/user.php`
- **Detail:** The login form has no brute-force protection. There is no account lockout, CAPTCHA, or IP-based rate limiting after repeated failures. The `last_failed` timestamp is recorded but never acted upon.
- **Impact:** Automated brute-force attacks against the admin login are unimpeded.
- **Fix:** Add lockout logic after N failures within a time window, or integrate a CAPTCHA (Kohana has a Captcha library available).
### [MEDIUM] Session ID never regenerated after login
- **File:** `application/config/session.php` (line 43: `'regenerate' => 0`), `application/controllers/admin/user.php`
- **Detail:** `session.regenerate` is set to `0` (disabled). No manual session ID regeneration occurs on successful login. This allows session fixation attacks.
- **Fix:** Call `session_regenerate_id(true)` immediately after successful login, or set `'regenerate' => 1`.
### [MEDIUM] Cookies are not HttpOnly
- **File:** `application/config/cookie.php` (line 28: `'httponly' => FALSE`)
- **Detail:** Session cookies are accessible via JavaScript. Combined with any XSS vector, this enables session hijacking.
- **Fix:** Set `'httponly' => TRUE`.
### [MEDIUM] Cookies are not Secure (no HTTPS enforcement)
- **File:** `application/config/cookie.php` (line 23: `'secure' => FALSE`), `.htaccess`
- **Detail:** No HTTPS redirect is configured in `.htaccess`. Cookies are sent over plain HTTP. Google Maps API is loaded over HTTP (`http://maps.google.com/...`).
- **Fix:** Add HTTPS redirect to `.htaccess`. Set cookie `'secure' => TRUE`. Use `https://` for external resources.
### [MEDIUM] Google Maps v2 API key committed to repository
- **File:** `application/config/gmaps.php`
- **Detail:** A Google Maps API v2 key is hardcoded and committed. Google Maps v2 has been deprecated and shut down for years.
- **Fix:** Remove the key. Migrate to Google Maps v3 (which uses domain restriction, not a key in the same way), or use an alternative mapping service.
### [LOW] `IN_PRODUCTION` flag is `false` in the committed index.php
- **File:** `index.php` (line 16: `define('IN_PRODUCTION', false)`)
- **Detail:** The production flag is hardcoded as `false`. This means Google Analytics is never included on the live site (guarded by `IN_PRODUCTION`). It also suggests dev and production use the same code file with no environment separation.
- **Fix:** Use an environment variable or server-detected value to set this flag per environment.
---
## Technical Debt
### [HIGH] `filter_var()` emulation with security bypass
- **File:** `index.php` (lines 6880)
- **Detail:** If the PHP `filter` extension is not installed, `filter_var()` is replaced with a stub that always returns `true`, meaning all IP/URL validation silently passes without actually validating anything.
- **Impact:** If running on a server without the filter extension, all input validation using `filter_var` is bypassed.
- **Fix:** Require the `filter` extension explicitly in server requirements. Remove the stub.
### [HIGH] Deprecated `mysql_*` functions used in system database driver
- **File:** `system/libraries/drivers/Database/Mysql.php`
- **Detail:** The `Mysql` driver uses `mysql_connect()`, `mysql_query()`, `mysql_close()`, etc. These functions were removed in PHP 7.0. The application is configured to use the `mysqli` driver (`application/config/database.php`), but the old `Mysql.php` driver still exists and would fail completely if the config were changed.
- **Impact:** The old driver is dead code but signals the PHP version constraint this codebase was designed for.
- **Fix:** Confirm `mysqli` driver is always used. Delete `system/libraries/drivers/Database/Mysql.php` or mark it clearly as unsupported.
### [HIGH] Commented-out dead code throughout controllers
- **Files:** `application/controllers/admin/user.php` (lines 810, 2930, 46, 63), `application/controllers/base_admin.php` (lines 13, 31, 4041), `application/controllers/base_front.php` (lines 1217, 4043)
- **Detail:** Large blocks of commented-out functionality exist: disabled profiler, disabled cookie-based redirect, disabled homepage layout, disabled `__destruct` rendering. These represent abandoned design decisions.
- **Fix:** Remove commented-out code. Use git history if rollback is ever needed.
### [MEDIUM] `stara/` directory — entire old website committed to repository
- **File:** `stara/` directory
- **Detail:** An old version of the website (`index.htm`, Flash files, old HTM pages, images) is committed to the repository root and likely deployed to production alongside the current application.
- **Impact:** Old pages may be publicly accessible. Increases repository size. Potential security exposure if old files have vulnerabilities.
- **Fix:** Remove `stara/` from the repository. If archival is needed, move it to a separate repository or offline storage.
### [MEDIUM] Google Maps v2 integration (defunct API)
- **Files:** `application/config/gmaps.php`, `application/controllers/front/page.php`, `modules/gmaps/`
- **Detail:** The entire `gmaps` module integrates Google Maps JavaScript API v2, which was shut down in 2013. The map on the contact page will not render.
- **Fix:** Rewrite the contact page map using Google Maps v3 or an alternative (e.g., Leaflet with OpenStreetMap). Remove the `gmaps` module.
### [MEDIUM] Adobe Flash integration
- **Files:** `flash/centrumcopy.swf`, `flash/expressInstall.swf`, `application/views/default_layout.php` (lines 4446, 94102)
- **Detail:** Adobe Flash Player support was dropped by all browsers at end of 2020. The Flash banner on the homepage cannot be displayed.
- **Fix:** Remove the Flash files and `swfobject` integration from the layout. Replace with a static image or CSS/HTML5 animation.
### [MEDIUM] Menu and navigation structure hardcoded in two separate places
- **Files:** `application/config/application.php` (`$config['menu_nav']`), `application/views/admin_layout.php` (admin sidebar links)
- **Detail:** The navigation menu is defined once in config (rendered for front-end) and a parallel, nearly duplicate list exists hardcoded in the admin layout view. Adding a new page requires editing both files.
- **Fix:** Drive the admin sidebar from the same data source as the front-end menu config.
### [LOW] TinyMCE 3.x (ancient WYSIWYG editor)
- **File:** `js/tiny_mce/`
- **Detail:** TinyMCE 3.x is bundled. TinyMCE is now at version 7. Version 3 is no longer maintained and may have known XSS vulnerabilities in the editor itself.
- **Fix:** Upgrade to TinyMCE 6/7 or replace with a modern editor (e.g., Quill, ProseMirror).
### [LOW] jQuery and jQuery UI versions are unversioned/unknown
- **Files:** `js/jquery.min.js`, `js/jquery-ui.min.js`
- **Detail:** No version number is visible in the filenames. Given the use of IE7 polyfills and Flash, these are likely very old versions.
- **Fix:** Replace with a pinned, modern version (jQuery 3.x). Consider migrating away from jQuery UI entirely.
### [LOW] IE7 polyfill scripts loaded from external CDN via HTTP
- **Files:** `application/views/default_layout.php` (line 15), `application/views/admin_layout.php` (line 10), `application/views/admin_login.php` (line 10)
- **Detail:** `ie7-js.googlecode.com` is loaded as an external HTTP script. Google Code shut down in 2016; this URL returns a 404 or is hijacked. IE7 support is irrelevant in 2024+.
- **Fix:** Remove all IE7 conditional comments and scripts.
---
## Performance Concerns
### [MEDIUM] N+1 query risk on every page load — `szybki-kontakt` page
- **File:** `application/controllers/base_front.php` (line 31)
- **Detail:** Every page load on the front-end executes `ORM::factory('page')->where('name', 'szybki-kontakt')->find()` in the base controller constructor. This is a database query run on every single front-end request, even pages that don't display the sidebar contact snippet.
- **Fix:** Cache this result in a static variable or application-level cache. Consider storing it in config if the content rarely changes.
### [MEDIUM] Database query benchmarking enabled in production config
- **File:** `application/config/database.php` (line 28: `'benchmark' => TRUE`)
- **Detail:** Query benchmarking adds overhead to every database call.
- **Fix:** Set `'benchmark' => FALSE` in production, or use the `IN_PRODUCTION` flag to switch configs.
### [LOW] No HTTP caching headers configured
- **Files:** `.htaccess`, `application/config/`
- **Detail:** No `Cache-Control`, `Expires`, or ETags are configured for static assets (CSS, JS, images). The `.htaccess` has no asset caching rules.
- **Fix:** Add browser caching rules in `.htaccess` for static file extensions.
### [LOW] All front-end JavaScript loaded synchronously in `<head>`
- **File:** `application/views/default_layout.php` (lines 1830)
- **Detail:** 7+ JS files are loaded with blocking `<script>` tags in the document `<head>`, including the defunct Flash embed script.
- **Fix:** Move scripts to bottom of `<body>` or use `defer`/`async` attributes.
---
## Dependency Risks
### [HIGH] Kohana 2.3.x — end-of-life framework
- **Files:** `system/` directory
- **Detail:** The application uses Kohana 2.3 (evidenced by the `$Id: index.php 4134 2009-03-28...` comment and Kohana framework version markers). Kohana has been end-of-life since approximately 2016. No security patches, no community support.
- **Impact:** Any framework-level vulnerability will never be patched. The framework depends on deprecated PHP 5.2+ APIs.
- **Fix:** Plan a migration to a maintained framework (Laravel, Symfony, or even Kohana 3.x). This is a major long-term effort.
### [HIGH] PHP 5.2 minimum — framework requirement
- **File:** `index.php` (line 45: `version_compare(PHP_VERSION, '5.2', '<')`)
- **Detail:** The codebase targets PHP 5.2. PHP 5.x has been end-of-life since December 2018. Modern PHP (8.x) has breaking changes for deprecated functions used in this codebase (especially `mysql_*` functions removed in PHP 7).
- **Impact:** If the server is upgraded to PHP 8.x, the application will break. If the server remains on PHP 5.x, it runs on an unsupported, vulnerability-riddled runtime.
- **Fix:** Verify the current PHP version in production. Audit for PHP 7/8 incompatibilities and fix them before any server upgrade.
### [HIGH] SwiftMailer v3 (abandoned)
- **File:** `system/vendor/swift/`
- **Detail:** SwiftMailer v3 is bundled in the framework vendor directory. SwiftMailer was abandoned and superseded by Symfony Mailer. Version 3 is extremely old.
- **Fix:** If email sending is ever needed, replace with PHPMailer or Symfony Mailer.
### [MEDIUM] Google Maps API v2 (shut down 2013)
- See Technical Debt section above. The map simply does not work.
### [MEDIUM] Adobe Flash (deprecated end of 2020)
- See Technical Debt section above.
---
## Maintainability
### [HIGH] No database schema documentation
- **Files:** `.paul/codebase/` (referenced in `CLAUDE.md` but `db_schema.md` does not exist), no migration files anywhere
- **Detail:** There is no schema file, no migration system, and no documentation of the database structure. The only way to know the schema is to connect to the production database.
- **Fix:** Reverse-engineer the current schema and create `.paul/codebase/db_schema.md`. Consider introducing a migration tool (e.g., Phinx) for future schema changes.
### [HIGH] No `.gitignore` for sensitive or generated files
- **Files:** Project root
- **Detail:** No `.gitignore` exists (or it is not present in the working tree). As a result, `application/config/database.php` (with production credentials), `.vscode/ftp-kr.json` (with FTP credentials), `application/cache/` (generated cache files), and `application/logs/` (log files) are all committed.
- **Fix:** Create a `.gitignore` immediately. At minimum exclude: `application/config/database.php`, `.vscode/ftp-kr.json`, `application/cache/*`, `application/logs/*`, `uploads/`.
### [MEDIUM] Error from log reveals a known PHP compatibility bug
- **File:** `application/logs/2024-05-18.log.php`
- **Detail:** Log entry: `Creating default object from empty value in application/controllers/admin/user.php at line 10`. This is caused by the commented-out `$this->message->password_success = ...` code — `$this->message` is never initialized as an object, so assigning to it on a modern PHP version throws an error. The code in `User_Controller::password()` calls `$this->message->password_success` (line 108) which would also fail.
- **Fix:** Initialize `$this->message` as `new stdClass()` in the `Base_Admin_Controller` constructor, or replace with an array.
### [MEDIUM] Duplicated `forward()` method in both base controllers
- **Files:** `application/controllers/base_admin.php` (lines 6886), `application/controllers/base_front.php` (lines 6785)
- **Detail:** The `forward()` method is identical in both base controllers. Any change must be made in two places.
- **Fix:** Extract to a shared parent controller or a trait (PHP 5.4+).
### [MEDIUM] Duplicated `error404()` method in both base controllers
- **Files:** `application/controllers/base_admin.php` (lines 5161), `application/controllers/base_front.php` (lines 5160)
- **Detail:** Same issue as `forward()` — identical implementation in two classes.
- **Fix:** Same resolution — extract to a shared parent.
### [LOW] Copyright date is frozen at 2010
- **Files:** `application/views/default_layout.php` (line 84), `application/views/admin_layout.php` (line 104), `application/views/admin_login.php` (line 51)
- **Detail:** All footer copyright notices show `© 2010`. The site appears abandoned from a maintenance perspective.
- **Fix:** Use `date('Y')` dynamically or update to current year range.
### [LOW] Inactive/unused models committed
- **Files:** `application/models/gallery.php`, `application/models/gallery_image.php`, `application/models/news.php`
- **Detail:** Models for `gallery`, `gallery_image`, and `news` exist but no corresponding controllers, routes, or views for these features are present (the gallery and news routes exist in `routes.php` but route to non-existent controllers `front/gallery` and `front/news`).
- **Impact:** Visiting `/galeria` or `/aktualnosci` will produce a 404 or Kohana error.
- **Fix:** Either implement these features or remove the dead models and routes.
---
## Missing Practices
### [HIGH] No test suite whatsoever
- **Detail:** There are no test files anywhere. Kohana's `unit_test` module is commented out in `application/config/config.php`. There is no PHPUnit setup, no test directory, no test runner.
- **Impact:** Any change to the codebase carries unknown risk. Regressions cannot be detected automatically.
- **Fix:** At minimum, add PHPUnit and write smoke tests for the login flow and page rendering. The Kohana unit_test module can serve as a starting point.
### [HIGH] No error tracking / monitoring
- **Detail:** The only error tracking is file-based logging to `application/logs/`. There is no integration with Sentry, Bugsnag, Rollbar, or equivalent. Log files are PHP files (which is a Kohana convention to prevent direct web access, but is not a proper log management solution).
- **Fix:** Integrate Sentry (free tier) for real-time error tracking.
### [HIGH] No deployment procedure documented
- **File:** `CLAUDE.md`
- **Detail:** The `CLAUDE.md` explicitly notes `(Uzupełnij procedurę deploy)`. The only deployment mechanism is an FTP auto-upload via VS Code extension. There is no deployment script, no CI/CD pipeline, no staging environment concept.
- **Fix:** Document the deploy procedure. Consider a simple deployment script. Move away from FTP (use SFTP or SSH-based deployment).
### [MEDIUM] No environment separation (dev vs production)
- **Detail:** There is a single set of config files. `IN_PRODUCTION = false` is hardcoded. Database credentials are for production. Debug toolbar (`application/config/debug_toolbar.php`, `auto_render => TRUE`) is enabled and would render if the debug module were active.
- **Fix:** Implement environment detection (e.g., via `$_SERVER['HTTP_HOST']` or a server env var) and load environment-specific config overrides.
### [MEDIUM] No HTTPS enforcement
- **Files:** `.htaccess`, `application/config/cookie.php`
- **Detail:** No redirect from HTTP to HTTPS exists in `.htaccess`. The site is likely accessible over plain HTTP, exposing session cookies and login credentials over the wire.
- **Fix:** Add HTTPS redirect to `.htaccess`. Enable `'secure' => TRUE` in cookie config after HTTPS is enforced.
### [LOW] No Content Security Policy (CSP) headers
- **Detail:** No CSP headers are set anywhere. Given the TinyMCE editor and Google Maps integration, a CSP would need to be permissive, but even a basic policy would reduce XSS risk.
- **Fix:** Add `Content-Security-Policy` header in `.htaccess` or in the base controller output.
### [LOW] Google Analytics configuration is empty
- **File:** `application/config/application.php` (line 11: `$config['google_analytics'] = ''`)
- **Detail:** The Google Analytics tracking code is empty. The legacy GA v1 code in `default_layout.php` (lines 109118) uses the deprecated `ga.js` library, not `gtag.js`. Even if a tracking ID were provided, it would use an obsolete tracking method.
- **Fix:** Either remove Google Analytics entirely or migrate to GA4 with `gtag.js`.
---
*Concerns audit: 2026-04-30*

View File

@@ -0,0 +1,391 @@
# Conventions & Quality
**Analysis Date:** 2026-04-30
**Framework:** Kohana (legacy PHP MVC, version ~2.x)
---
## Naming Conventions
### Classes
- **PascalCase** with framework-mandated `_Controller`, `_Model` suffix:
- Controllers: `Page_Controller`, `User_Controller`, `Base_Admin_Controller`
- Models: `Page_Model`, `User_Model`, `Gallery_Model`, `News_Model`
- Abstract base controllers: `Base_Admin_Controller`, `Base_Front_Controller`
- Helper extensions: `MY_` prefix on filename and class — e.g. `MY_form.php``class form extends form_Core`
### Methods
- **camelCase** throughout: `uniqueKey()`, `uniqueKeyExists()`, `nameNotExists()`, `emailNotExists()`
- Exception: Kohana routing maps URL segments to method names, so public controller action methods use lowercase: `index()`, `login()`, `logout()`, `edit()`, `show()`, `contact()`, `password()`
- Fluent/utility methods follow framework conventions: `set_flash()`, `get_once()`
### Variables & Properties
- **snake_case** for local variables and view variables:
- `$page_view`, `$password_view`, `$path_arrow`, `$admin_title`
- **camelCase** for object properties that mirror DB columns (Kohana ORM maps directly to column names):
- `$user->sha1_password`, `$user->last_success`, `$user->is_active`
- Session data keys: lowercase with underscores — `'admin'`, `'admin_redirect'`, `'message'`, `'tiny_mce_public_html_dir'`
### Files
- Controllers: `lowercase.php` matching class name without `_Controller` suffix
- `application/controllers/admin/page.php``Page_Controller`
- `application/controllers/front/page.php``Page_Controller` (different namespace via directory)
- Models: `lowercase.php` matching class name without `_Model` suffix
- `application/models/page.php``Page_Model`
- Views: `lowercase_with_underscores.php`
- `application/views/admin/page_edit.php`
- `application/views/front/page_show.php`
- Helper/library extensions: `MY_` prefix
- `application/helpers/MY_form.php`, `application/helpers/MY_html.php`, `application/helpers/MY_valid.php`
- `application/libraries/MY_Database.php`
- Layout views: `*_layout.php``default_layout.php`, `admin_layout.php`
### Database Columns (ORM)
- **snake_case**: `sha1_password`, `last_success`, `last_failed`, `password_date`, `is_active`, `parent_id`, `created_at`
---
## Code Style
### Indentation
- **2-space indentation** throughout — consistent across all application files
- Example from `application/controllers/admin/page.php`:
```php
if($this->input->post())
{
$page->title = $this->input->post('page_title');
```
### Brace Style
- **Allman style** (opening brace on new line) for class and method bodies
- Kohana-era convention — does NOT follow PSR-2/PSR-12 which requires same-line braces
### PSR Compliance
- **Not PSR-12 compliant.** The codebase predates PSR and uses Kohana framework's own style:
- Allman braces (PSR-12 requires same-line)
- 2-space indent (PSR-12 requires 4-space)
- No strict types declaration
- CLAUDE.md states PSR-12 as a goal — this is aspirational, not current practice
### Comments
- `#` style used for inline disabled code and short notes (Kohana convention):
```php
#$this->profiler = new Profiler();
# TODO ? zastosowac parametr GET...
```
- `//` style for explanatory comments:
```php
// Load the class extension
```
- `/* */` for blocks of temporarily disabled code
- PHPDoc (`/** */`) only in framework library files, not in application code
- Comments explain "why" rarely — most comments are disabled code blocks
### Direct Access Guard
Every PHP file starts with:
```php
<?php defined('SYSPATH') OR die('No direct access allowed.');
```
---
## Common Patterns
### Controller Structure
Controllers always extend a base class (never `Controller` directly for app controllers):
```php
class Page_Controller extends Base_Admin_Controller
{
public function __construct()
{
parent::__construct();
$this->view->path = 'Strony'; // breadcrumb label
}
public function edit($name = null)
{
// 1. Build view object
$page_view = new View('admin/page_edit');
// 2. Load model via ORM
$page = ORM::factory('page')->where('name', $name)->find();
// 3. Guard - 404 if not found
if (!$page->loaded) { return $this->error404(); }
// 4. Handle POST
if($this->input->post()) { ... $page->save(); url::redirect(...); }
// 5. Pass data to view, render
$page_view->page = $page;
$this->view->content = $page_view;
$this->view->render(true);
}
}
```
### ORM / Database Queries
The app uses Kohana ORM (Active Record pattern). All queries go through `ORM::factory()`:
```php
// Find by primary key (integer) or unique key (string, via unique_key() override)
ORM::factory('page')->where('name', $name)->find();
ORM::factory('user')->find($this->input->post('username'));
ORM::factory('user', 1); // find by PK integer
// Save after setting properties
$page->title = $this->input->post('page_title');
$page->save();
if ($page->saved) { ... } // check success
```
Raw DB builder (used in `install.php` and models):
```php
$this->db->set(array('col' => $val))->insert('table');
$this->db->where('id', 1)->update('user')->count();
$this->db->where($this->unique_key($value), $value)->count_records($this->table_name);
```
Models define uniqueness helpers to support string-based lookup:
- `unique_key($id)` — returns column name for string vs. integer lookup
- `unique_key_exists($value)` — boolean uniqueness check
- `name_not_exists()`, `username_not_exists()`, `email_not_exists()` — used before save
### View Rendering Pattern
Two-layer view system: content view embedded in layout view.
```php
// In controller:
$page_view = new View('admin/page_edit'); // inner/content view
$page_view->page = $page; // pass data to inner view
$this->view->content = $page_view; // embed in layout
$this->view->render(true); // render layout with output buffering
```
In layout view (`admin_layout.php`, `default_layout.php`):
```php
<?php echo $content ?> // renders the inner view
```
### Form Handling Pattern
Forms use Kohana's `form::` helper class for HTML generation:
```php
echo form::open(null, array('id' => 'edit_form'), array('id' => $page->id));
echo form::label('page_title', 'Tytuł strony: ');
echo form::input(array('name'=>'page_title', 'size'=>75, 'maxlength' => 95), $page->title);
echo form::textarea(array('name' => 'page_content', 'class' => 'mceEditor'), $page->content);
echo form::submit('save','Zapisz');
echo form::close();
```
POST handling in controller (pattern: check → validate → save → flash message → redirect):
```php
if($this->input->post())
{
$page->title = $this->input->post('page_title');
$page->save();
if ($page->saved) {
$this->session->set_flash('message','Strona została zapisana.');
}
url::redirect(url::current()); // PRG pattern
}
```
### Flash Messages
Flash messages are set in the controller and displayed in the layout:
```php
// Set (controller):
$this->session->set_flash('message', 'Strona została zapisana.');
// Read (base controller constructor):
$this->view->message = $this->session->get('message');
// Display (layout view):
<?php if ($message): ?><div class="message"><?php echo $message ?></div><?php endif; ?>
```
### Validation Pattern
For complex forms, Kohana `Validation` object is used:
```php
$post = new Validation($this->input->post());
$post->pre_filter('trim')
->add_rules('username', 'required', 'length[3,20]', 'chars[a-zA-Z0-9_.]')
->add_rules('email', 'required', 'length[5,50]', 'valid::email')
->add_rules('password', 'required', 'length[3,40]')
->add_rules('password2', 'matches[password]');
if($post->validate()) { ... }
```
For simple forms (page editing), no server-side validation — JavaScript regex validation only.
### URL/Routing
Routes defined in `application/config/routes.php` as regex → controller/method:
```php
$config['admin/page/(.*)'] = 'admin/page/edit/$1';
$config['(.+)'] = 'front/page/$1'; // catch-all for front pages
```
Redirects: `url::redirect('admin/login')` — always relative path strings.
---
## Error Handling
### Strategy
- **No try/catch in application code.** Error handling is framework-delegated.
- Kohana catches exceptions via its own error handler and logs them.
- 404 is handled explicitly via controller method:
```php
public function error404()
{
header('HTTP/1.1 404 File Not Found');
$error_view = new View('admin/error404');
$error_view->page_name = Router::$current_uri . Router::$url_suffix;
$this->view->content = $error_view;
$this->view->render(true);
}
// Magic method catches all undefined actions:
public function __call($method, $arguments)
{
return $this->error404();
}
```
### Logging
- Kohana framework logging used in system/library code (`Kohana::log('debug', '...')`)
- Application log threshold set to `1` (errors and exceptions only) in `application/config/config.php`
- Log files written to `application/logs/`
- No application-level `Kohana::log()` calls in custom code
### Model Not Found
Pattern: check `$model->loaded` (boolean) after `->find()`:
```php
$page = ORM::factory('page')->where('name', $name)->find();
if (!$page->loaded) {
return $this->error404();
}
```
---
## Authentication & Sessions
### Auth Mechanism
Custom session-based authentication. No auth module used (Kohana auth module is commented out in config).
**Login flow** (`application/controllers/admin/user.php`):
1. Find user by username via ORM
2. Check `$user->is_active` flag
3. Compare `sha1($user->salt . $plaintext_password)` against stored `$user->sha1_password`
4. On success: store user data array in session under key `'admin'`
5. Redirect to saved `admin_redirect` URL
**Password storage:** SHA1 with random MD5 salt — weak by modern standards (SHA1 is not suitable for passwords; bcrypt/argon2 should be used).
**Auth check** (in `Base_Admin_Controller::__construct()`):
```php
if(!$this->session->get('admin') && Router::$method != 'login' && Router::$method != 'logout')
{
url::redirect('admin/login');
}
```
**Admin session payload:**
```php
$admin = array(
'id' => $user->id,
'role' => $user->role,
'username' => $user->username,
'email' => $user->email,
'last_success' => $user->last_success,
'last_failed' => $user->last_failed,
);
$this->session->set('admin', $admin);
```
### Session Configuration (`application/config/session.php`)
- Driver: `native` (PHP native sessions)
- Session name: `Frisson_session`
- Expiration: 1800 seconds (30 minutes)
- Validation: `user_agent` (ties session to browser UA)
- Encryption: disabled
- Session regeneration: disabled (`'regenerate' => 0`) — CSRF risk
### Redirect After Login
```php
// Save intended URL before redirect to login:
$this->session->set('admin_redirect', url::current());
// After login, consume and redirect:
$redirect = $this->session->get_once('admin_redirect', 'admin');
url::redirect($redirect);
```
### Security Gap: Force Login Controller
`application/controllers/admin/force.php` contains a `Force_Controller::login()` method that logs in user ID 1 **without any password check**. This is a development backdoor left in production code.
---
## Testing
**No test framework is configured or used.**
- Kohana's `unit_test` module exists in `modules/` but is commented out in `application/config/config.php`:
```php
// MODPATH.'unit_test', // Unit testing
```
- No test files found (no `*.test.php`, `*_test.php`, `*Test.php`, `*spec*` files)
- No PHPUnit, Pest, or any other testing tool detected
- No test directory (`tests/`, `spec/`, etc.) exists
**Current state:** Zero automated test coverage.
---
## Security Practices
### Input Handling
- POST data accessed via `$this->input->post('field')` — Kohana's Input class applies global XSS filtering when `global_xss_filtering = true` (set in `application/config/config.php`):
```php
$config['global_xss_filtering'] = true;
```
- This provides baseline XSS protection on GET/POST/SERVER data
### Output Escaping
- Layout files escape page metadata with `html::specialchars()`:
```php
echo html::specialchars($title);
echo html::specialchars($meta_description);
```
- Content output in views is **unescaped** — `echo $page->content` — intentional for HTML content managed via TinyMCE editor, but trusts admin input fully
- Front-end user-controlled output: none currently (public site is read-only display)
### SQL Injection Prevention
- ORM query builder with `'escape' => TRUE` in database config handles escaping automatically
- Raw `$this->db->query()` calls exist only in `install.php` with hardcoded strings
- No prepared statements with `bindParam()` in app code — reliance on ORM escaping
### CSRF
- **No CSRF protection.** No CSRF tokens on any form. Kohana's form helpers do not add tokens automatically.
- Admin panel forms are vulnerable to CSRF attacks
### Cookie Security (`application/config/cookie.php`)
- `'secure' => FALSE` — cookies sent over HTTP as well as HTTPS
- `'httponly' => FALSE` — cookies accessible to JavaScript (XSS risk)
- Both should be `TRUE` in production
### Session Security
- Session ID regeneration disabled (`'regenerate' => 0`) — increases session fixation risk
- No HTTPS enforcement detected in application code
### Database Credentials
- Plaintext credentials stored in `application/config/database.php` — committed to git (critical issue)
### Significant Security Issues (summary)
| Issue | Location |
|-------|----------|
| Development backdoor login (no password) | `application/controllers/admin/force.php` |
| SHA1 password hashing (not bcrypt/argon2) | `application/controllers/admin/user.php` |
| No CSRF tokens on admin forms | All admin views |
| `print_r($_POST)` left in production code | `application/controllers/admin/user.php:29` |
| DB credentials in version control | `application/config/database.php` |
| HttpOnly and Secure cookie flags disabled | `application/config/cookie.php` |
| Session ID never regenerated | `application/config/session.php` |

216
.paul/codebase/db_schema.md Normal file
View File

@@ -0,0 +1,216 @@
# Database Schema
**Last updated:** 2026-04-30
**Source:** Inferred from ORM models, controllers, install script, and raw query analysis.
---
## Connection
| Setting | Value |
|----------------|------------------------|
| Driver | `mysqli` |
| Host | `localhost` |
| Database | `host420804_db` |
| User | `host420804_db` |
| Character set | `utf8` |
| Table prefix | _(none)_ |
| Persistent | `FALSE` |
| Config file | `application/config/database.php` |
---
## Tables
### `user`
**Purpose:** Admin user accounts. Used for CMS login and authentication. Only admin-role users access the back-end panel.
**Model:** `application/models/user.php``User_Model extends ORM`
- `$table_name = 'user'`
- `$primary_val = 'username'` (allows lookup by username string in addition to numeric `id`)
**Columns:**
| Column | Type | Nullable | Notes |
|-----------------|----------------|----------|-------------------------------------------------------------------|
| `id` | INT (PK, AI) | NO | Primary key, auto-increment (Kohana ORM default) |
| `role` | VARCHAR | NO | Role name, e.g. `'sysadmin'`, `'admin'`. Stored as string. |
| `username` | VARCHAR(20) | NO | Unique login name. Validated: 320 chars, `[a-zA-Z0-9_.]` |
| `email` | VARCHAR(50) | NO | Unique email address. Validated: 550 chars. |
| `salt` | VARCHAR | NO | MD5 random salt used when hashing the password. |
| `sha1_password` | VARCHAR(40) | NO | `SHA1(salt . plaintext_password)` |
| `password_date` | DATETIME | YES | Timestamp of last password change. Format: `Y-m-d H:i:s` |
| `is_active` | TINYINT / BOOL | NO | `1` = active account, `0` = disabled. Checked on login. |
| `last_success` | DATETIME | YES | Timestamp of last successful login. Updated on each login. |
| `last_failed` | DATETIME | YES | Timestamp of last failed login attempt. Updated on bad password. |
**Relationships:** None declared in ORM. Standalone table.
**Key usages:**
- `application/controllers/admin/user.php` — login, logout, password change
- `application/controllers/install.php` — seed rows for `sysadmin` and `admin`
- `application/controllers/admin/force.php` — forced login (reads `username`, `last_success`, `last_failed`)
---
### `page`
**Purpose:** CMS content pages served on the public front-end. Each row is a named page (e.g. `o-firmie`, `kontakt`, `serwis`). Content is edited via the admin panel with a TinyMCE rich-text editor.
**Model:** `application/models/page.php``Page_Model extends ORM`
- `$table_names_plural = FALSE` → table name is `page` (not `pages`)
- Lookup by string `name` supported via `unique_key()`.
**Columns:**
| Column | Type | Nullable | Notes |
|-------------------|---------------|----------|-----------------------------------------------------------------------|
| `id` | INT (PK, AI) | NO | Primary key, auto-increment. |
| `name` | VARCHAR | NO | URL slug / unique identifier. e.g. `'o-firmie'`, `'kontakt'`. Unique.|
| `title` | VARCHAR(95) | YES | HTML `<title>` tag value and displayed page title. |
| `header` | VARCHAR(95) | YES | Heading shown inside the page `<h1>`. May differ from `title`. |
| `content` | MEDIUMTEXT | YES | Main HTML body. Edited via TinyMCE. Can be empty. |
| `meta_description`| TEXT | YES | `<meta name="description">` content. |
| `meta_keywords` | TEXT | YES | `<meta name="keywords">` content. |
| `parent_id` | INT (FK) | YES | Self-referential parent page ID. `NULL` = top-level page. |
**Relationships:**
- Self-referential: `parent_id` references `page.id` (used by `categories` helper for hierarchical menu rendering).
- No ORM `$has_many`/`$belongs_to` declared — relationships are resolved manually in the `categories` helper (`application/helpers/categories.php`).
**Seed pages (from install script):**
| `name` | `title` |
|-----------------------------------------------|---------------------------------------------------|
| `o-firmie` | O firmie |
| `urzadzenia-biurowe-monochromatyczne` | Urządzenia biurowe monochromatyczne |
| `urzadzenia-biurowe-kolorowe` | Urządzenia biurowe kolorowe |
| `urzadzenia-uslugowe-monochromatyczne` | Urządzenia usługowe monochromatyczne |
| `urzadzenia-uslugowe-kolorowe` | Urządzenia usługowe kolorowe |
| `urzadzenia-produkcyjne-monochromatyczne` | Urządzenia produkcyjne monochromatyczne |
| `urzadzenia-produkcyjne-kolorowe` | Urządzenia produkcyjne kolorowe |
| `drukarki-monochromatyczne` | Drukarki monochromatyczne |
| `drukarki-kolorowe` | Drukarki kolorowe |
| `powielacze-cyfrowe-riso-dlaczego-riso` | Powielacze cyfrowe RISO - dlaczego RISO? |
| `powielacze-cyfrowe-riso-urzadzenia` | Powielacze cyfrowe RISO - urządzenia |
| `plotery` | Plotery |
| `finansowanie` | Finansowanie |
| `serwis` | Serwis |
| `uslugi` | Usługi |
| `kontakt` | Kontakt |
| `szybki-kontakt` | Szybki kontakt |
**Key usages:**
- `application/controllers/admin/page.php` — edit page content (title, header, content, meta_*)
- `application/controllers/front/page.php` — public display; `show($name)` and `contact()` methods
- `application/helpers/categories.php` — renders hierarchical navigation menus using `id`, `name`, `title`, `parent_id`
- `application/views/front/page_show.php` — renders `header` and `content`
- `application/views/front/page_contact.php` — renders contact page `header` and `content`
---
### `gallery`
**Purpose:** Photo gallery albums. Each gallery has a name/slug and a collection of images. Sorted by creation date ascending.
**Model:** `application/models/gallery.php``Gallery_Model extends ORM`
- `$table_names_plural = FALSE` → table name is `gallery`
- `$has_many = array('gallery_images')` → links to `gallery_image` table via `gallery_id` FK
- `$sorting = array('created_at' => 'ASC')` → default sort order
**Columns:**
| Column | Type | Nullable | Notes |
|--------------|---------------|----------|----------------------------------------------------------------|
| `id` | INT (PK, AI) | NO | Primary key, auto-increment. |
| `name` | VARCHAR | NO | URL slug / unique identifier. Used for lookup. |
| `title` | VARCHAR | YES | Human-readable gallery name displayed in UI. |
| `created_at` | DATETIME | YES | Creation timestamp. Used for ASC sort order. |
> **Note:** Additional columns (e.g. `description`, `cover_image`) may exist but are not referenced in source code. The column set above is confirmed by code evidence.
**Relationships:**
- `has_many``gallery_image` via `gallery_image.gallery_id`
**Key usages:**
- `application/models/gallery.php` — ORM definition
- No admin controller found in the current codebase snapshot — gallery management may not yet be implemented or may exist outside the explored scope.
---
### `gallery_image`
**Purpose:** Individual images belonging to a gallery album. Sorted by `id` ascending (insertion order).
**Model:** `application/models/gallery_image.php``Gallery_Image_Model extends ORM`
- `$table_names_plural = FALSE` → table name is `gallery_image`
- `$belongs_to = array('gallery')` → foreign key `gallery_id` references `gallery.id`
- `$sorting = array('id' => 'ASC')`
**Columns:**
| Column | Type | Nullable | Notes |
|--------------|---------------|----------|--------------------------------------------------------------------|
| `id` | INT (PK, AI) | NO | Primary key, auto-increment. |
| `gallery_id` | INT (FK) | NO | Foreign key → `gallery.id`. Populated by Kohana ORM convention. |
| `filename` | VARCHAR | YES | Stored filename of the uploaded image (inferred from upload config).|
| `title` | VARCHAR | YES | Optional image caption / alt text. |
> **Note:** Column names for `gallery_image` (beyond `id` and `gallery_id`) are not directly referenced in any controller or view in the explored source. `filename` and `title` are inferred from typical Kohana gallery patterns and the upload config presence. Verify against live database.
**Relationships:**
- `belongs_to``gallery` via `gallery_id``gallery.id`
---
### `news`
**Purpose:** News/blog articles. Sorted by creation date descending (newest first). No admin controller was found in the explored source — may be partially implemented or planned.
**Model:** `application/models/news.php``News_Model extends ORM`
- `$table_names_plural = FALSE` → table name is `news`
- `$sorting = array('created_at' => 'DESC')`
- Unique key lookup by `name` slug supported.
**Columns:**
| Column | Type | Nullable | Notes |
|-------------------|---------------|----------|---------------------------------------------------------------|
| `id` | INT (PK, AI) | NO | Primary key, auto-increment. |
| `name` | VARCHAR | NO | URL slug / unique identifier. Unique constraint expected. |
| `title` | VARCHAR | YES | Article headline. |
| `content` | MEDIUMTEXT | YES | Article body HTML. |
| `created_at` | DATETIME | YES | Publication/creation timestamp. Used for DESC sort. |
> **Note:** Only `name` and `created_at` are confirmed by model code. `title` and `content` are inferred from structural similarity to the `page` table and common news patterns. Additional columns may exist. Verify against live database.
**Relationships:** None declared in ORM.
---
## ORM Conventions (Kohana 2.x)
These conventions apply to all tables above and govern how Kohana resolves columns automatically:
| Convention | Detail |
|-----------------------------|------------------------------------------------------------------------|
| Primary key | `id` (INT, auto-increment) — default for all models |
| Timestamps | `created_at` and `updated_at` — auto-populated when present |
| Foreign keys | `{related_model}_id` — e.g. `gallery_id` in `gallery_image` |
| `has_many` target table | Uses the child model's `$table_name` and looks up `{parent}_id` FK |
| `$table_names_plural = FALSE`| Overrides default pluralisation — table name matches model name exactly|
---
## Confidence Levels
| Table | Confidence | Basis |
|-----------------|------------|----------------------------------------------------------|
| `user` | HIGH | All columns directly referenced in controllers/install |
| `page` | HIGH | All columns directly referenced in controllers/views |
| `gallery` | MEDIUM | `id`, `name`, `created_at` confirmed; other cols inferred|
| `gallery_image` | LOW-MEDIUM | Only `id` and `gallery_id` confirmed; rest inferred |
| `news` | LOW-MEDIUM | Only `id`, `name`, `created_at` confirmed; rest inferred |
> Columns marked as inferred should be verified against the live `host420804_db` MySQL database or a mysqldump before any schema migrations are written.

View File

@@ -0,0 +1,68 @@
# External Integrations
## Status Summary
All external integrations in this codebase are either defunct or non-functional. There are no active third-party API connections.
## Google Maps API v2
| Item | Detail |
|------|--------|
| Status | **BROKEN** — API v2 shut down in 2010 |
| Location | `modules/gmaps/`, `application/config/gmaps.php` |
| Purpose | Interactive map on contact page |
| Fix | Replace with Google Maps Embed API (no API key needed for basic embed) |
## Google Analytics
| Item | Detail |
|------|--------|
| Status | **INACTIVE** — key is empty string, `IN_PRODUCTION = false` suppresses all tracking |
| Location | `application/config/google_analytics.php` |
| Purpose | Site traffic analytics |
| Fix | Set real GA4 measurement ID, set `IN_PRODUCTION = true` for production |
## Adobe Flash Banner
| Item | Detail |
|------|--------|
| Status | **BROKEN** — Flash EOL December 2020 |
| Location | `flash/centrumcopy.swf` |
| Purpose | Animated hero banner |
| Fix | `.webp` fallback already in use — remove Flash references entirely |
## IE7.js (Google Code CDN)
| Item | Detail |
|------|--------|
| Status | **BROKEN** — Google Code CDN shut down 2016 |
| Location | Both layout files (`application/views/front/layout.php`, `application/views/admin/layout.php`) |
| Purpose | IE7 compatibility shim |
| Fix | Remove entirely — IE7 has <0.01% market share |
## SwiftMailer v3 (Bundled)
| Item | Detail |
|------|--------|
| Status | **FUNCTIONAL** but deprecated |
| Location | `application/libraries/swift/` |
| Purpose | Sending contact form emails |
| Fix | Migrate to PHPMailer or Symfony Mailer when upgrading PHP |
## Email (Contact Form)
| Item | Detail |
|------|--------|
| Status | **Likely functional** — using PHP `mail()` or SwiftMailer via local MTA |
| Location | `application/controllers/front/contact.php` |
| Purpose | Contact form submissions |
| Notes | No SMTP credentials in config — relies on host's local mail server |
## FTP Deployment
| Item | Detail |
|------|--------|
| Status | **Active** |
| Location | `.vscode/ftp-kr.json` |
| Tool | VS Code `ftp-kr` extension |
| Warning | FTP password stored in plaintext in git repository |

View File

@@ -0,0 +1,65 @@
# Codebase Overview — centrumcopy.com.pl
## Project Summary
B2B e-commerce/catalogue website for a photocopier and office equipment distributor. The site is a legacy PHP application built on Kohana 2.3.4 (2009-era, EOL framework) hosted on shared hosting (Hostido) and deployed via FTP.
**Live domain:** centrumcopy.com.pl
**Deployment:** FTP upload via VS Code `ftp-kr` extension
## Quick Stats
| Item | Value |
|------|-------|
| Framework | Kohana 2.3.4 (EOL ~2016) |
| Language | PHP (5.2+ minimum, likely running 7.x on host) |
| Database | MySQL — 5 tables |
| Frontend | Vanilla HTML/CSS/JS, no build tools |
| Test coverage | 0% |
| External APIs | None functional (all legacy/defunct) |
## Key Features
- **CMS pages** — slug-based content via `page` table, hierarchical navigation (`parent_id`)
- **Product catalogue** — static pages managed via admin CMS
- **Admin panel** — CRUD for pages, users, gallery, news (some incomplete)
- **Gallery** — image gallery module (admin side unimplemented)
- **News** — news module (controller missing, planned only)
- **Contact** — form with Google Maps (v2, non-functional)
## Critical Issues (act immediately)
1. **Backdoor**`admin/force/login` grants full admin access with no password (`application/controllers/admin/force.php`)
2. **Credentials in git** — DB password in `application/config/database.php`, FTP password in `.vscode/ftp-kr.json`
3. **Debug data leak**`print_r($_POST)` in login controller exposes credentials in HTTP response
4. **Install controller**`application/controllers/install.php` is publicly accessible and can reset users
## Architecture in One Paragraph
`index.php` bootstraps Kohana's event system. URLs route to controllers under `application/controllers/front/` (public) or `application/controllers/admin/` (protected). Controllers extend `Base_Front_Controller` or `Base_Admin_Controller` which set up layout/auth. Models are thin Kohana ORM wrappers. Views use a layout+partial pattern: controller assigns an inner view to `$this->view->content`, then calls `$this->view->render(true)`.
## Document Index
| Document | Contents |
|----------|----------|
| [stack.md](stack.md) | Languages, framework, libraries, external services, infrastructure |
| [architecture.md](architecture.md) | Directory structure, MVC layout, routing, business domains |
| [conventions.md](conventions.md) | Naming, code style, patterns, auth, security practices |
| [concerns.md](concerns.md) | Security issues, technical debt, risks — with severity ratings |
| [db_schema.md](db_schema.md) | Table structures, columns, relationships |
## What's Working
- Public CMS pages render correctly
- Admin login and page management
- Contact form (email via SwiftMailer)
- Static product catalogue pages
## What's Broken / Missing
- Google Maps contact page (API v2 shutdown 2010)
- Flash banner (Flash EOL)
- Gallery admin (no controller)
- News section (no controller)
- Google Analytics (key empty + `IN_PRODUCTION=false`)
- IE7.js CDN (Google Code shutdown 2016)

211
.paul/codebase/stack.md Normal file
View File

@@ -0,0 +1,211 @@
# Stack & Integrations
**Analysis Date:** 2026-04-30
---
## Languages
**PHP**
- Minimum required: PHP 5.2 (enforced in `index.php` line 45: `version_compare(PHP_VERSION, '5.2', '<')`)
- Actual server target: unknown — hosting is `host420804.hostido.net.pl`
- Uses `mysqli` extension for database connectivity (not PDO)
- No `composer.json` — all dependencies are managed manually (vendored into `system/` and `js/`)
**JavaScript**
- Vanilla JS (no build pipeline, no npm)
- jQuery (bundled, minified) — version approximate pre-1.5 based on API patterns (`.browser`, no `.on()`)
- All JS files are static, pre-built assets committed directly to repo
**HTML / CSS**
- XHTML 1.0 Transitional doctype on frontend (`default_layout.php`)
- HTML 4.01 doctype on admin layout (`admin_layout.php`)
- Plain CSS — no preprocessor (no SASS/LESS/PostCSS)
- No responsive/mobile CSS detected
---
## Frameworks & Libraries
### Backend
**Kohana 2.3.4** (legacy PHP MVC framework)
- Version confirmed in `system/core/Bootstrap.php`: `define('KOHANA_VERSION', '2.3.4')`
- EOL framework — last released ~2009
- Directory layout:
- `system/` — Kohana core (controllers, libraries, helpers, ORM, router)
- `application/` — application code (controllers, models, views, config)
- `modules/` — optional Kohana modules
- Entry point: `index.php``system/core/Bootstrap.php`
- Routing: `application/config/routes.php`
**Kohana ORM** (bundled in `system/libraries/ORM.php`)
- Active Record pattern
- Models extend `ORM` class: `class Page_Model extends ORM`
- Files: `system/libraries/ORM.php`, `system/libraries/ORM_Iterator.php`, `system/libraries/ORM_Tree.php`, `system/libraries/ORM_Versioned.php`
- Usage example: `ORM::factory('page')->where('name', $name)->find()`
**SwiftMailer v3** (bundled in `system/vendor/swift/`)
- Used via Kohana's email helper (`system/helpers/email.php`)
- Configured in `application/config/email.php`
- Current driver: `native` (PHP `mail()` function)
- SMTP and sendmail drivers available but not configured
### Active Kohana Modules
- **gmaps** (`modules/gmaps/`) — Google Maps v2 API integration for contact page
- **debug_toolbar** (`modules/debug_toolbar/`) — dev toolbar, configured in `application/config/debug_toolbar.php` (NOT listed in active modules in `config.php` — disabled)
Disabled (commented out in `application/config/config.php`):
- `auth`, `forge`, `kodoc`, `media`, `archive`, `payment`, `unit_test`, `object_db`
### Frontend JS Libraries (all vendored in `js/`)
| File | Library | Notes |
|------|---------|-------|
| `js/jquery.min.js` | jQuery | ~1.31.4 era (minified, no version comment) |
| `js/jquery-ui.min.js` | jQuery UI | Bundled with themes in `js/jquery-ui/themes/` |
| `js/jquery.fancybox.pack.js` | FancyBox | Lightbox/modal plugin |
| `js/jquery.jcarousellite.min.js` | jCarouselLite | Carousel plugin |
| `js/jquery.mousewheel.min.js` | jQuery Mousewheel | Scroll support |
| `js/jquery.easing.pack.js` | jQuery Easing | Animation easing |
| `js/jquery.lightbox.min.js` | jQuery Lightbox | Alternative lightbox (unused, commented out in layout) |
| `js/jquery.bgiframe.min.js` | bgiframe | IE6 z-index fix |
| `js/jquery.pngfix.min.js` | PNG Fix | IE6 PNG transparency (unused, commented out) |
| `js/swfobject.min.js` | SWFObject 2.1 | Flash embed for `flash/centrumcopy.swf` |
| `js/tiny_mce/tiny_mce.js` | TinyMCE 3.2.7 | WYSIWYG editor in admin panel |
| `js/swampy_browser/` | SwampyBrowser 1.1 | File/image manager integrated with TinyMCE |
---
## Database
**Engine:** MySQL (via `mysqli` PHP extension)
**Connection config:** `application/config/database.php`
- Host: `localhost`
- Database: `host420804_db`
- User: `host420804_db`
- Password: stored in config file (plaintext — see CONCERNS)
- Character set: `utf8`
- Table prefix: none
- Persistent connections: disabled
- Query caching: disabled
- Benchmarking: enabled
**ORM / Query builder:** Kohana's built-in ORM (`system/libraries/ORM.php`) and query builder (`system/libraries/Database.php`). No Doctrine, no Eloquent.
**Application models** (`application/models/`):
| Model file | ORM class | Table |
|-----------|-----------|-------|
| `application/models/page.php` | `Page_Model extends ORM` | `page` |
| `application/models/user.php` | (user auth model) | `user` |
| `application/models/gallery.php` | Gallery_Model | `gallery` |
| `application/models/gallery_image.php` | Gallery_Image_Model | `gallery_image` |
| `application/models/news.php` | News_Model | `news` |
**Schema documentation:** Not present. See `.paul/codebase/db_schema.md` when created.
**Custom DB extension:** `application/libraries/MY_Database.php` — overrides `select()` to fix COUNT() handling with table prefix.
---
## External Integrations
### Google Maps API v2
- Module: `modules/gmaps/`
- Config: `application/config/gmaps.php`
- API key: hardcoded in `application/config/gmaps.php` (old v2 browser key — likely invalid/deprecated)
- Domain: `google.pl`
- Usage: contact page map centered at `50.0491231, 21.9869502` (Rzeszów, ul. Okulickiego 9)
- **Note:** Google Maps JavaScript API v2 was shut down in 2010. This integration is non-functional.
- Implementation: `application/controllers/front/page.php``contact()` method
### Google Analytics (Legacy)
- Config key: `application/config/application.php``$config['google_analytics'] = ''` (empty — disabled)
- Code exists in `application/views/default_layout.php` (classic `ga.js` snippet, not gtag.js)
- Only fires when `IN_PRODUCTION === true` AND `$google_analytics` is non-empty
- Currently: `IN_PRODUCTION` is `false` in `index.php` → Analytics never fires
### Email (SwiftMailer / native PHP mail)
- Library: SwiftMailer v3 bundled in `system/vendor/swift/`
- Config: `application/config/email.php` — driver set to `native` (PHP `mail()`)
- Application email address: `application/config/application.php``$config['email'] = ''` (empty — not configured)
- No transactional email service (no SendGrid, Mailgun, etc.)
### IE7.js (Google Code CDN)
- Loaded from external CDN: `http://ie7-js.googlecode.com/svn/version/2.1(beta4)/IE7.js`
- Conditional comment — only loads on IE < 7
- Google Code was shut down in 2016 → this resource is unavailable
- Referenced in: `application/views/default_layout.php` and `application/views/admin_layout.php`
### Flash (SWF)
- Banner: `flash/centrumcopy.swf` (embedded via SWFObject 2.1)
- Fallback: `images/banner.webp` (shown if Flash unavailable)
- Flash is EOL — browsers no longer support it. The `<img>` fallback is what users see.
---
## Infrastructure
### Web Server
- **Apache** with `mod_rewrite` (inferred from `.htaccess`)
- `.htaccess` location: project root
- Key rules:
- Blocks direct access to `application/`, `modules/`, `system/` directories (403)
- 301 redirects for two old URL patterns (service/product pages renamed)
- All non-file/non-directory requests rewritten to `index.php?kohana_uri=$1`
### Hosting
- Provider: **Hostido** (`host420804.hostido.net.pl`)
- Remote path: `/public_html`
- Protocol: FTP (port 21)
- Credentials: `application/config/database.php` (DB), `.vscode/ftp-kr.json` and `.vscode/sftp.json` (FTP)
### Deployment
- **Manual FTP upload** via VS Code extensions:
- `ftp-kr` extension config: `.vscode/ftp-kr.json``autoUpload: true` on file save
- `sftp` extension config: `.vscode/sftp.json``uploadOnSave: false`
- No CI/CD pipeline
- No staging environment detected
- Ignored on upload: `.git`, `/.vscode`, `/.paul`, `/.serena`, `CLAUDE.md`
### Logs
- Application logs: `application/logs/` (PHP files, e.g. `2024-05-18.log.php`)
- Server access logs: `logs/access.log` (rotated with `.xz` compression)
- Server error logs: `logs/error.log`
- Log threshold: 1 (errors and exceptions only) — set in `application/config/config.php`
### Caching
- **Internal file cache:** enabled, 30 seconds — stores file paths and config (`application/config/config.php``internal_cache = 30`)
- Cache files: `application/cache/kohana_configuration`, `kohana_find_file_paths`, `kohana_language`
- No Memcache, Redis, or opcode caching configured
### Sessions
- Driver: native PHP sessions
- Name: `Frisson_session`
- Expiry: 1800 seconds (30 minutes)
- Config: `application/config/session.php`
### Output Compression
- gzip output compression: enabled (`application/config/config.php``output_compression = TRUE`)
- Applied at PHP level by Kohana
---
## Build Tools
**None.** There is no build pipeline.
- No `composer.json` / Composer
- No `package.json` / npm / yarn
- No Webpack, Vite, Gulp, Grunt
- No CSS preprocessor (no SASS/LESS)
- No JS minification step — minified files are committed as-is
- No asset versioning/cache-busting
All JS and CSS files are raw static assets served directly from `js/` and `css/` directories. Updates require manually replacing files and deploying via FTP.
---
*Stack analysis: 2026-04-30*

33
.paul/config.md Normal file
View File

@@ -0,0 +1,33 @@
# Project Config
**Project:** centrumcopy.com.pl
**Created:** 2026-04-30
## Project Settings
```yaml
project:
name: centrumcopy.com.pl
version: 0.0.0
```
## Integrations
### SonarQube
```yaml
sonarqube:
enabled: false
reason: "User declined during init"
```
## Preferences
```yaml
preferences:
auto_commit: false
verbose_output: false
```
---
*Config created: 2026-04-30*

View File

@@ -0,0 +1,24 @@
{"ts":"2026-04-30T12:40:23Z","tool":"Bash","cmd":"mkdir -p .paul/phases .paul/codebase && echo \"Created\"\",\"description\":\"Create .paul directory structure\"},\"tool_response\":{\"stdout\":\"Created\",\"stderr\":\"\",\"interrupted\":false,\"isIma","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}
{"ts":"2026-04-30T12:41:07Z","tool":"Bash","cmd":"basename \"$(pwd)\"\",\"description\":\"Get current directory name\"},\"tool_response\":{\"stdout\":\"centrumcopy.com.pl\",\"stderr\":\"\",\"interrupted\":false,\"isImage\":false,\"noOutputExpected\":","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}
{"ts":"2026-04-30T12:41:44Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\centrumcopy.com.pl\\\\.paul\\\\PROJECT.md","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}
{"ts":"2026-04-30T12:41:50Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\centrumcopy.com.pl\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}
{"ts":"2026-04-30T12:41:57Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\centrumcopy.com.pl\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}
{"ts":"2026-04-30T12:42:02Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\centrumcopy.com.pl\\\\.paul\\\\config.md","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}
{"ts":"2026-04-30T12:42:50Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\centrumcopy.com.pl\\\\CLAUDE.md","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}
{"ts":"2026-04-30T12:46:44Z","tool":"Edit","file":"C:\\\\Users\\\\jacek\\\\.claude\\\\settings.json","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}
{"ts":"2026-04-30T12:50:25Z","tool":"Edit","file":"C:\\\\Users\\\\jacek\\\\.claude\\\\statusline-command.sh","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}
{"ts":"2026-04-30T12:50:29Z","tool":"Edit","file":"C:\\\\Users\\\\jacek\\\\.claude\\\\settings.json","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}
{"ts":"2026-04-30T12:51:57Z","tool":"Edit","file":"C:\\\\Users\\\\jacek\\\\.claude\\\\statusline-command.sh","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}
{"ts":"2026-04-30T12:52:03Z","tool":"Edit","file":"C:\\\\Users\\\\jacek\\\\.claude\\\\statusline-command.sh","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}
{"ts":"2026-04-30T12:52:59Z","tool":"Edit","file":"C:\\\\Users\\\\jacek\\\\.claude\\\\statusline-command.sh","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}
{"ts":"2026-04-30T12:53:03Z","tool":"Edit","file":"C:\\\\Users\\\\jacek\\\\.claude\\\\statusline-command.sh","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}
{"ts":"2026-04-30T12:55:41Z","tool":"Bash","cmd":"sleep 15 && cat /c/Users/jacek/AppData/Local/Temp/claude/C--visual-studio-code-projekty-centrumcopy-com-pl/b20e6009-edaa-493e-875e-eb799445b078/tasks/bug0ywgg2.output 2>/dev/null\",\"timeout\":30000,\","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}
{"ts":"2026-04-30T12:55:56Z","tool":"Bash","cmd":"sleep 10 && cat /c/Users/jacek/AppData/Local/Temp/claude/C--visual-studio-code-projekty-centrumcopy-com-pl/b20e6009-edaa-493e-875e-eb799445b078/tasks/bug0ywgg2.output 2>/dev/null\",\"timeout\":20000,\","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}
{"ts":"2026-04-30T13:00:29Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\centrumcopy.com.pl\\\\.paul\\\\codebase\\\\architecture.md","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}
{"ts":"2026-04-30T13:00:37Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\centrumcopy.com.pl\\\\.paul\\\\codebase\\\\stack.md","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}
{"ts":"2026-04-30T13:00:56Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\centrumcopy.com.pl\\\\.paul\\\\codebase\\\\conventions.md","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}
{"ts":"2026-04-30T13:01:45Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\centrumcopy.com.pl\\\\.paul\\\\codebase\\\\concerns.md","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}
{"ts":"2026-04-30T13:02:58Z","tool":"Bash","cmd":"mkdir -p \"/c/visual studio code/projekty/centrumcopy.com.pl/.paul/codebase\"\",\"description\":\"Ensure .paul/codebase directory exists\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",\"interru","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}
{"ts":"2026-04-30T13:03:49Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\centrumcopy.com.pl\\\\.paul\\\\codebase\\\\db_schema.md","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}
{"ts":"2026-04-30T13:04:18Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\centrumcopy.com.pl\\\\.paul\\\\codebase\\\\overview.md","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}
{"ts":"2026-04-30T13:04:31Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\centrumcopy.com.pl\\\\.paul\\\\codebase\\\\integrations.md","cwd":"/c/visual studio code/projekty/centrumcopy.com.pl"}

2
.serena/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/cache
/project.local.yml

119
.serena/project.yml Normal file
View File

@@ -0,0 +1,119 @@
# the name by which the project can be referenced within Serena
project_name: "centrumcopy.com.pl"
# list of languages for which language servers are started; choose from:
# al ansible bash clojure cpp
# cpp_ccls crystal csharp csharp_omnisharp dart
# elixir elm erlang fortran fsharp
# go groovy haskell haxe hlsl
# java json julia kotlin lean4
# lua luau markdown matlab msl
# nix ocaml pascal perl php
# php_phpactor powershell python python_jedi python_ty
# r rego ruby ruby_solargraph rust
# scala solidity swift systemverilog 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:
- php
# 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
# advanced configuration option allowing to configure language server-specific options.
# Maps the language key to the options.
# Have a look at the docstring of the constructors of the LS implementations within solidlsp (e.g., for C# or PHP) to see which options are available.
# No documentation on options means no options are available.
ls_specific_settings: {}
# 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.
# This extends the existing exclusions (e.g. from the global configuration)
# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html
excluded_tools: []
# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default).
# This extends the existing inclusions (e.g. from the global configuration).
# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html
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.
# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html
fixed_tools: []
# list of mode names that are to be activated by default, overriding the setting in the global configuration.
# The full set of modes to be activated is base_modes (from global config) + default_modes + added_modes.
# If the setting is undefined/empty, the default_modes from the global configuration (serena_config.yml) apply.
# Otherwise, this overrides the setting from the global configuration (serena_config.yml).
# Therefore, you can set this to [] if you do not want the default modes defined in the global config to apply
# for this project.
# This setting can, in turn, be overridden by CLI parameters (--mode).
# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes
default_modes:
# list of mode names to be activated additionally for this project, e.g. ["query-projects"]
# The full set of modes to be activated is base_modes (from global config) + default_modes + added_modes.
# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes
added_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 readonly.
# Extends the list from the global configuration, merging the two lists.
read_only_memory_patterns: []
# list of regex patterns for memories to completely ignore.
# Matching memories will not appear in list_memories or activate_project output
# and cannot be accessed via read_memory or write_memory.
# To access ignored memory files, use the read_file tool on the raw file path.
# Extends the list from the global configuration, merging the two lists.
# Example: ["_archive/.*", "_episodes/.*"]
ignored_memory_patterns: []

6
.vscode/ftp-kr.json vendored
View File

@@ -12,6 +12,10 @@
"ignoreRemoteModification": true,
"ignore": [
".git",
"/.vscode"
"/.vscode",
"/.paul",
"/.serena",
"CLAUDE.md",
"/.claude"
]
}

28
CLAUDE.md Normal file
View File

@@ -0,0 +1,28 @@
# Projektowe zasady dla centrumcopy.com.pl
## Stack
- **Język:** PHP
- **Framework:** Kohana (legacy)
- **Frontend:** HTML/CSS/JS (vanilla)
- **Baza danych:** MySQL
## Zasady kodu
- Stosuj PSR-12 dla formatowania kodu PHP
- Nazewnictwo: PascalCase dla klas, camelCase dla metod, snake_case dla zmiennych DB
- Unikaj zagnieżdżeń > 3 poziomy — wydzielaj do metod
- Komentarze tylko gdy wyjaśniają "dlaczego", nie "co"
## Baza danych
- Schemat dokumentowany w `.paul/codebase/db_schema.md`
- Każda zmiana schematu wymaga migracji
- Nie modyfikuj istniejących migracji — twórz nowe
## Testy
- (brak wykrytego test runnera — uzupełnij)
## Dokumentacja
- Dokumentacja techniczna w `.paul/codebase/`
- Przy każdej zmianie aktualizuj odpowiednie pliki
## Wdrażanie
- (Uzupełnij procedurę deploy)