diff --git a/.paul/PROJECT.md b/.paul/PROJECT.md new file mode 100644 index 0000000..e9be6ed --- /dev/null +++ b/.paul/PROJECT.md @@ -0,0 +1,78 @@ +# Project: vidok.com + +## What This Is + +Strona marketingowa firmy produkujacej okna. Zawiera formularz kontaktowy oraz galerie produktow. + +## Core Value + +Klienci moga zapoznac sie z oferta okien i skontaktowac sie z firma. + +## Current State + +| Attribute | Value | +|-----------|-------| +| Version | 0.1.0 | +| Status | In Progress | +| Last Updated | 2026-05-05 | + +## Requirements + +### Validated (Shipped) +- [x] Podstawowa struktura strony +- [x] Formularz kontaktowy +- [x] Galeria produktow +- [x] Zalaczniki z formularzy kontaktowych na `/kontakt/` i `modal-contact-form` sa zapisywane jako publiczne linki w bazie - Phase 1 + +### Active (In Progress) +- [ ] [Do zdefiniowania przy kolejnym planowaniu] + +### Planned (Next) +- [ ] Ewentualne utrwalenie zalacznikow z `send-contact-landing`, jesli landing page ma miec ten sam mechanizm + +### Out of Scope +- Budowa panelu administracyjnego do przegladania zalacznikow - poza Phase 1 +- Retroaktywne odzyskiwanie zalacznikow ze starych zgloszen - poza Phase 1 + +## Target Users + +**Primary:** Potencjalni klienci szukajacy okien +- Osoby poszukujace okien do domu lub firmy +- Chca zobaczyc oferte i skontaktowac sie z producentem +- Moga przeslac projekt, wizualizacje lub inne materialy do wyceny + +## Context + +**Business Context:** +Strona marketingowa producenta okien - prezentacja produktow i pozyskiwanie kontaktow. + +**Technical Context:** +PHP z customowym CMS. Brak frameworka (Laravel/Symfony). Formularze kontaktowe sa obslugiwane w `plugins/special-actions-middle.php`, a dane trafiaja do zewnetrznej tabeli `contact_messages`. + +## Key Decisions + +| Date | Decision | Impact | +|------|----------|--------| +| 2026-05-05 | Zalaczniki formularzy kontaktowych zapisywane w jednej kolumnie `contact_messages.attachments` jako JSON | Prosta automatyczna migracja bez osobnej tabeli | +| 2026-05-05 | Pliki trafiaja do `/uploads/contact-attachments/YYYY/mm/`, nie do `temp/` | Zalaczniki nie znikaja po automatycznym czyszczeniu `temp/` | +| 2026-05-05 | Limit uploadu: 50 MB na plik, tylko allowlista rozszerzen | Backend i UI egzekwuja spojne ograniczenia | + +## Constraints + +### Technical Constraints +- PHP (custom CMS bez zewnetrznego frameworka) +- Hosting zgodny z istniejaca infrastruktura +- Baza formularzy `contact_messages` jest dostepna z serwera WWW jako host `mysql8` + +### Business Constraints +- Strona w jezyku polskim + +## Success Criteria + +- Klienci moga zapoznac sie z oferta okien i skontaktowac sie z firma +- Formularz kontaktowy dziala poprawnie +- Zalaczniki z formularzy kontaktowych sa mozliwe do odzyskania z publicznych linkow zapisanych w bazie +- Galeria produktow jest czytelna i atrakcyjna + +--- +*Last updated: 2026-05-05 after Phase 1* diff --git a/.paul/ROADMAP.md b/.paul/ROADMAP.md new file mode 100644 index 0000000..0e1f3d6 --- /dev/null +++ b/.paul/ROADMAP.md @@ -0,0 +1,31 @@ +# Roadmap: vidok.com + +## Overview + +Strona marketingowa producenta okien - od aktualnego stanu do dopracowanej, w pelni funkcjonalnej witryny z galeria produktow i formularzem kontaktowym. + +## Current Milestone + +**v0.1 Initial Release** (v0.1.0) +Status: Complete +Phases: 1 of 1 complete + +## Phases + +| Phase | Name | Plans | Status | Completed | +|-------|------|-------|--------|-----------| +| 1 | Contact Attachments | 1/1 | Complete | 2026-05-05 | + +## Phase Details + +### Phase 1: Contact Attachments + +Complete. Persisted uploaded files from `/kontakt/` file-enabled forms and `modal-contact-form` as public links in the database, using a dedicated folder outside `temp/`, automatic schema upgrade, server/client upload restrictions, and visible 50 MB upload information in the relevant forms. + +## Deferred Ideas + +- Decide whether `send-contact-landing` should receive the same persistent attachment handling. +- Consider an admin-facing view for contact attachment links. + +--- +*Roadmap updated: 2026-05-05 after Phase 1* diff --git a/.paul/STATE.md b/.paul/STATE.md index 504f913..45ea56b 100644 --- a/.paul/STATE.md +++ b/.paul/STATE.md @@ -4,26 +4,27 @@ See: .paul/PROJECT.md (updated 2026-05-05) -**Core value:** Klienci mogą zapoznać się z ofertą okien i skontaktować się z firmą. -**Current focus:** Project initialized — ready for planning +**Core value:** Klienci moga zapoznac sie z oferta okien i skontaktowac sie z firma. +**Current focus:** Phase 1 complete; ready for next milestone or follow-up 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-05-05 — Project initialized +Phase: 1 of 1 (Contact Attachments) - Complete +Plan: 01-01 complete +Status: Loop closed, ready for next milestone or follow-up PLAN +Last activity: 2026-05-05 22:33:44 +02:00 - UNIFY complete for .paul/phases/01-contact-attachments/01-01-PLAN.md Progress: -- Milestone: [░░░░░░░░░░] 0% +- Milestone: [##########] 100% +- Phase 1: [##########] 100% ## Loop Position Current loop state: ``` -PLAN ──▶ APPLY ──▶ UNIFY - ○ ○ ○ [Ready for first PLAN] +PLAN ---> APPLY ---> UNIFY + ok ok ok [Loop complete] ``` ## Accumulated Context @@ -34,20 +35,32 @@ Documents: `.paul/codebase/` (9 files) Key findings: Custom PHP MVC CMS, Medoo ORM, MySQL, no tests, critical security issues (hardcoded credentials, MD5 passwords, unserialize on cookies, SQL injection risks) ### Decisions -None yet. +- Contact attachment storage targets only forms that have file uploads on `/kontakt/` plus `modal-contact-form`. +- Attachment links are stored in a single `contact_messages.attachments` column as JSON. +- Uploaded contact files use public links from `uploads/contact-attachments/YYYY/mm/`, outside `temp/`. +- File uploads are restricted and capped at 50 MB per file, with visible form information. +- `send-contact-landing` remains on legacy temp upload flow because it is outside this requested scope. +- Git commit skipped during transition because the worktree had extensive pre-existing unrelated/user changes. ### Deferred Issues -None yet. +- Landing page attachment persistence can be planned separately if that form should also retain uploads outside `temp/`. +- Admin browsing/downloading of contact attachments can be planned separately if needed. ### Blockers/Concerns -Multiple critical security vulnerabilities documented in `.paul/codebase/concerns.md` +Multiple critical security vulnerabilities documented in `.paul/codebase/concerns.md`. + +### Git State +Last commit: not created during UNIFY +Branch: main +Feature branches merged: none +Reason: pre-existing dirty worktree; avoided committing unrelated/user changes ## Session Continuity -Last session: 2026-05-05 -Stopped at: Project initialization complete -Next action: Run /paul:plan to define phases and first plan -Resume file: .paul/PROJECT.md +Last session: 2026-05-05 22:33:44 +02:00 +Stopped at: Phase 1 complete, milestone v0.1 complete +Next action: Start next milestone or plan a follow-up item from deferred issues +Resume file: .paul/phases/01-contact-attachments/01-01-SUMMARY.md --- -*STATE.md — Updated after every significant action* +*STATE.md - Updated after every significant action* diff --git a/.paul/changelog/2026-05-05.md b/.paul/changelog/2026-05-05.md new file mode 100644 index 0000000..f79ac8c --- /dev/null +++ b/.paul/changelog/2026-05-05.md @@ -0,0 +1,22 @@ +# 2026-05-05 + +## Co zrobiono + +- [Phase 1, Plan 01] Dodano trwaly zapis zalacznikow z formularzy kontaktowych do publicznego folderu poza `temp/`. +- [Phase 1, Plan 01] Dodano automatyczne tworzenie kolumny `contact_messages.attachments`. +- [Phase 1, Plan 01] Zapis publicznych linkow do zalacznikow jako JSON w jednej kolumnie. +- [Phase 1, Plan 01] Ustawiono limit 50 MB oraz wspolna liste dozwolonych typow plikow w backendzie i UI. +- [Phase 1, Plan 01] Zweryfikowano produkcyjny zapis formularza z zalacznikiem. + +## Zmienione pliki + +- `plugins/special-actions-middle.php` +- `templates_user/pages/page-contact-v9.php` +- `templates_user/modal/modal.php` +- `.paul/codebase/db_schema.md` +- `.paul/codebase/tech_changelog.md` +- `.paul/PROJECT.md` +- `.paul/ROADMAP.md` +- `.paul/STATE.md` +- `.paul/phases/01-contact-attachments/01-01-PLAN.md` +- `.paul/phases/01-contact-attachments/01-01-SUMMARY.md` diff --git a/.paul/codebase/db_schema.md b/.paul/codebase/db_schema.md index 1f79eef..d6a91cc 100644 --- a/.paul/codebase/db_schema.md +++ b/.paul/codebase/db_schema.md @@ -280,6 +280,26 @@ Schema inferred from Medoo ORM queries in PHP source files. No migration files f | `mail` | TEXT | Message body | | `add_date` | DATETIME | Submission timestamp | +**`contact_messages`** — External contact form submissions database used by `plugins/special-actions-middle.php` +| Column | Type | Notes | +|--------|------|-------| +| `form_id` | VARCHAR | Form identifier, e.g. `contact-form-new`, `modal-contact-form` | +| `name` | VARCHAR | Sender name | +| `email` | VARCHAR | Sender email | +| `phone` | VARCHAR | Sender phone | +| `message` | TEXT | Message body | +| `zip_code` | VARCHAR | Postal code | +| `topic` | VARCHAR | Subject/topic | +| `company` | VARCHAR | Company name | +| `invoice_number` | VARCHAR | Invoice/reference number | +| `address` | VARCHAR | Address | +| `voivodeship` | VARCHAR | Province/voivodeship | +| `client_type` | VARCHAR | Client segment | +| `consent_offer` | TINYINT | Offer consent flag | +| `consent_marketing` | TINYINT | Marketing consent flag | +| `attachments` | TEXT NULL | JSON array of public attachment links; auto-created by `saveContactData()` if missing | +| `created_at` | DATETIME | Submission timestamp | + --- ## SEO & Settings diff --git a/.paul/codebase/tech_changelog.md b/.paul/codebase/tech_changelog.md index 59deff1..caa039b 100644 --- a/.paul/codebase/tech_changelog.md +++ b/.paul/codebase/tech_changelog.md @@ -1,14 +1,30 @@ # Technical Changelog -> Chronological log of technical changes — what and why. +> Chronological log of technical changes - what and why. -## 2026-05-05 — Codebase Mapping +## 2026-05-05 - Phase 01 / Plan 01 Contact Attachments + +**Co zrobiono / What changed:** +- Added persistent contact attachment handling for file-enabled contact forms. +- Added automatic `contact_messages.attachments` schema upgrade in `saveContactData()`. +- Added dedicated public upload storage under `uploads/contact-attachments/YYYY/mm/`. +- Updated contact and modal upload UI to show/enforce a 50 MB per-file limit and matching allowed extensions. + +**Dlaczego / Why:** +- Contact attachments were previously stored only in `temp/` for email delivery, so they could disappear after automatic cleanup and were not recoverable from database records. + +**BREAKING / Migration note:** +- First successful form save after deployment may run `ALTER TABLE contact_messages ADD attachments TEXT NULL AFTER consent_marketing`. + +--- + +## 2026-05-05 - Codebase Mapping **Co zrobiono / What changed:** - Initial codebase map created in `.paul/codebase/` (9 documents) - Analyzed stack, architecture, structure, conventions, integrations, concerns, DB schema **Dlaczego / Why:** -- First-time PAUL project setup — baseline documentation for future planning +- First-time PAUL project setup - baseline documentation for future planning --- diff --git a/.paul/config.md b/.paul/config.md new file mode 100644 index 0000000..c4402a9 --- /dev/null +++ b/.paul/config.md @@ -0,0 +1,33 @@ +# Project Config + +**Project:** vidok.com +**Created:** 2026-05-05 + +## Project Settings + +```yaml +project: + name: vidok.com + version: 0.1.0 +``` + +## Integrations + +### SonarQube + +```yaml +sonarqube: + enabled: false + reason: "User declined during init" +``` + +## Preferences + +```yaml +preferences: + auto_commit: false + verbose_output: false +``` + +--- +*Config created: 2026-05-05* diff --git a/.paul/governance/governance_2026-05-05.jsonl b/.paul/governance/governance_2026-05-05.jsonl new file mode 100644 index 0000000..0a7608f --- /dev/null +++ b/.paul/governance/governance_2026-05-05.jsonl @@ -0,0 +1,43 @@ +{"ts":"2026-05-05T19:49:37Z","tool":"Bash","cmd":"mkdir -p \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\\\\.paul\\\\phases\" && mkdir -p \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\\\\.paul\\\\codebase\" && echo \"Created\"\",\"descrip","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:52:58Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\vidok.com\\\\.paul\\\\PROJECT.md","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:53:05Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\vidok.com\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:53:13Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\vidok.com\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:53:19Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\vidok.com\\\\.paul\\\\config.md","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:54:57Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && ls -la\"},\"tool_response\":{\"stdout\":\"total 11923\\ndrwxr-xr-x 1 jacek 197121 0 maj 5 21:54 .\\ndrwxr-xr-x 1 jacek 197121 0","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:55:03Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && find . -name \".env\" -o -name \".env.local\" -o -name \".env.example\" 2>$null\"},\"tool_response\":{\"stdout\":\"/usr/bin/bash: line 1: $n","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:55:09Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && find . -name \".env*\" 2>/dev/null | head -20\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",\"interrupted\":false,\"isImage\":false,","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:55:15Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && find . -name \"*.php\" -type f -exec wc -l {} + | sort -rn | head -30\"},\"tool_response\":{\"stdout\":\" 51627 total\\n 6548 ./wojewodzt","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:55:20Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && grep -r \"try\\\\|catch\" --include=\"*.php\" 2>/dev/null | wc -l\"},\"tool_response\":{\"stdout\":\"154\",\"stderr\":\"\",\"interrupted\":f","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:55:31Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && grep -r \"md5\\\\|sha1\" --include=\"*.php\" 2>/dev/null | head -20\"},\"tool_response\":{\"stdout\":\"autoload/admin/class.Site.php: ","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:55:37Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && grep -r \"stripslashes\\\\|strip_tags\" --include=\"*.php\" 2>/dev/null | head -15\"},\"tool_response\":{\"stdout\":\"autoload/class.S.php: ","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:55:41Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && find . -type d -name \"*test*\" -o -type d -name \"*spec*\" 2>/dev/null | head -20\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",\"i","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:55:42Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && grep -r \"->where\\\\|->update\\\\|->insert\\\\|->delete\" --include=\"*.php\" 2>/dev/null | wc -l\"},\"tool_response\":{\"stdout\":\"0\",\"","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:55:47Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && grep -r \"unlink\\\\|rmdir\" --include=\"*.php\" 2>/dev/null\"},\"tool_response\":{\"stdout\":\"autoload/admin/factory/class.Articles.php: ","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:55:48Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && find . -name \"*.env*\" -type f 2>/dev/null\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",\"interrupted\":false,\"isImage\":false,\"","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:55:52Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && grep -r \"file_exists\\\\|is_dir\\\\|is_file\" --include=\"*.php\" 2>/dev/null | wc -l\"},\"tool_response\":{\"stdout\":\"84\",\"stderr\":\"","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:55:52Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && grep -rn \"readfile\\\\|fopen\\\\|fread\" --include=\"*.php\" 2>/dev/null\"},\"tool_response\":{\"stdout\":\"autoload/admin/controls/class.B","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:56:00Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && grep -rn \"error_reporting\" --include=\"*.php\" 2>/dev/null | head -20\"},\"tool_response\":{\"stdout\":\"admin/ajax.php:2:error_reporting(","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:56:01Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && grep -rn \"serialize\\\\|unserialize\" --include=\"*.php\" 2>/dev/null\"},\"tool_response\":{\"stdout\":\"admin/ajax/pages.php:36: $array =","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:56:06Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && grep -rn \"strposa\" --include=\"*.php\" 2>/dev/null\"},\"tool_response\":{\"stdout\":\"plugins/special-actions-middle.php:169:function strp","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:56:06Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && ls -lah admin/templates | head -20\"},\"tool_response\":{\"stdout\":\"total 48K\\ndrwxr-xr-x 1 jacek 197121 0 maj 5 21:48 .\\ndrwxr-xr-x","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:56:11Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && wc -l wojewodztwa.php\"},\"tool_response\":{\"stdout\":\"6548 wojewodztwa.php\",\"stderr\":\"\",\"interrupted\":false,\"isImage\":false,\"no","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:56:11Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && head -50 wojewodztwa.php\"},\"tool_response\":{\"stdout\":\" [\\r\\n 'id' => 1,","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:56:17Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && git log --oneline config.php 2>/dev/null | head -5\"},\"tool_response\":{\"stdout\":\"78fe46c first commit\",\"stderr\":\"\",\"interrupted\"","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:56:20Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && git log --oneline --all | head -20\",\"description\":\"Show recent git commits\"},\"tool_response\":{\"stdout\":\"2d3bb66 Save\\nbdd91a4 cha","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:56:20Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && find . -type f -name \"*.php\" | wc -l\"},\"tool_response\":{\"stdout\":\"273\",\"stderr\":\"\",\"interrupted\":false,\"isImage\":false,\"no","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:56:27Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && grep -rn \"6LfaovglAAAAAAz84QLdbONodP1yJUpSOVTnFMt4\\\\|recaptcha\" --include=\"*.php\" 2>/dev/null\"},\"tool_response\":{\"stdout\":\"plugi","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:56:33Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && grep -rn \"file_get_contents.*\\\\$_GET\\\\|file_get_contents.*\\\\$_POST\\\\|readfile.*\\\\$_GET\\\\|readfile.*\\\\$_POST\" --include=\"*.p","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:56:35Z","tool":"Bash","cmd":"file \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\\\\libraries\\\\grid\\\\config.php\" 2>/dev/null || echo \"File check skipped\"\",\"description\":\"Check grid config\"},\"tool_response\":{\"s","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:56:36Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && grep -n \"move_uploaded_file\" plugins/special-actions-middle.php\"},\"tool_response\":{\"stdout\":\"315: move_uploaded_file($_FILES['f","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:56:36Z","tool":"Bash","cmd":"cd \"C:\\\\visual studio code\\\\projekty\\\\vidok.com\" && grep -rn \"password.*=.*md5\\\\|password.*hash\" --include=\"*.php\" 2>/dev/null | head -10\"},\"tool_response\":{\"stdout\":\"admin/index.p","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:58:01Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\vidok.com\\\\.paul\\\\codebase\\\\stack.md","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:58:27Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\vidok.com\\\\.paul\\\\codebase\\\\integrations.md","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:59:03Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\vidok.com\\\\.paul\\\\codebase\\\\architecture.md","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T19:59:35Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\vidok.com\\\\.paul\\\\codebase\\\\structure.md","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T20:00:03Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\vidok.com\\\\.paul\\\\codebase\\\\conventions.md","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T20:00:11Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\vidok.com\\\\.paul\\\\codebase\\\\testing.md","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T20:00:58Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\vidok.com\\\\.paul\\\\codebase\\\\concerns.md","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T20:01:37Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\vidok.com\\\\.paul\\\\codebase\\\\db_schema.md","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T20:01:40Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\vidok.com\\\\.paul\\\\codebase\\\\tech_changelog.md","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T20:01:54Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\vidok.com\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/vidok.com"} +{"ts":"2026-05-05T20:02:04Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/vidok.com\" && git add .paul/codebase/*.md .paul/STATE.md && git commit -m \"$(cat <<'EOF'\\ndocs: map existing codebase\\n\\n- stack.md (68 lines) - PHP/MySQL/Apac","cwd":"/c/visual studio code/projekty/vidok.com"} diff --git a/.paul/phases/01-contact-attachments/01-01-PLAN.md b/.paul/phases/01-contact-attachments/01-01-PLAN.md new file mode 100644 index 0000000..dbd7e4c --- /dev/null +++ b/.paul/phases/01-contact-attachments/01-01-PLAN.md @@ -0,0 +1,193 @@ +--- +phase: 01-contact-attachments +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - plugins/special-actions-middle.php + - templates_user/pages/page-contact-v9.php + - templates_user/modal/modal.php +autonomous: true +delegation: off +--- + + +## Goal +Persist uploaded files from contact forms as public attachment links in the same database row as the rest of the contact form data. + +## Purpose +Clients already attach project files to contact requests, but those files are only sent by email and currently use temporary storage. Sales/support should be able to recover submitted attachments from the database after the temp folder is cleaned. + +## Output +- `contact_messages.attachments` column is created automatically when needed. +- Uploaded contact files are stored in a dedicated public folder outside `temp/`. +- `saveContactData()` stores attachment links in the new column. +- The live contact page and modal communicate the allowed file types and 50 MB limit. + + + + +- **Zakres** - Obejmujemy formularze z plikami? + -> Odpowiedz: Tyle te z plikami. +- **Baza** - Zapisywac zalaczniki jako jedna kolumna czy osobna tabela? + -> Odpowiedz: Jako jedna kolumna. +- **Linki** - Publiczny link czy sciezka serwerowa? + -> Odpowiedz: Publiczny link, ale dedykowanym folderze, nie temp bo ten jest czyszczony automatycznie. +- **Limit** - Ograniczyc typy i rozmiar plikow? + -> Odpowiedz: Ograniczmy. Limit dajmy na 50 MB, tylko musi byc o tym informacja w formularzach. + + +## Project Context +@.paul/PROJECT.md +@.paul/ROADMAP.md +@.paul/STATE.md +@.paul/codebase/architecture.md +@.paul/codebase/db_schema.md + +## Source Files +@plugins/special-actions-middle.php +@templates_user/pages/page-contact-v9.php +@templates_user/modal/modal.php +@config.php + +## Current Findings +- `saveContactData()` inserts into external table `contact_messages` using a direct PDO connection in `plugins/special-actions-middle.php`. +- Contact form handlers already read `$_FILES['files']`, move uploads to `temp/`, and pass those temp paths to email sending. +- The live contact template `templates_user/pages/page-contact-v9.php` contains file inputs for `send-contact-form-new-2` and `send-contact-form-new-deweloper`. +- The popup template `templates_user/modal/modal.php` contains file input for `send-contact-modal`. +- File uploader JS currently uses `fileMaxSize: 10`; server validation only blocks `.php`. +- `.paul/SPECIAL-FLOWS.md` is not present, so no specialized apply skills are required. + + + + +## AC-1: Persistent Attachment Links +```gherkin +Given a user submits a contact form with one or more valid files +When the form is accepted and saved by saveContactData() +Then each uploaded file is stored in a dedicated public contact attachments folder +And the matching contact_messages row contains public attachment links in one attachments column +And email delivery still receives the same uploaded files as attachments +``` + +## AC-2: Automatic Database Upgrade +```gherkin +Given the production database does not yet have the attachments column +When the first contact submission is saved after deployment +Then the code automatically adds contact_messages.attachments without requiring a manual migration step +And the form save continues without losing the submission +``` + +## AC-3: Upload Restrictions +```gherkin +Given a user uploads a disallowed extension or a file larger than 50 MB +When the form handler processes the submission +Then the invalid file is not persisted or emailed +And the user receives a normal form failure alert rather than a PHP warning or partial unsafe upload +``` + +## AC-4: Form Limit Information +```gherkin +Given a user views the live contact form or modal contact form +When they reach the file upload field +Then the UI states the 50 MB per-file limit and allowed file types +And the client-side uploader enforces the same 50 MB limit as the server +``` + + + + + + + Task 1: Add attachment persistence helpers and schema self-upgrade + plugins/special-actions-middle.php + + Add small helper functions near `saveContactData()`: + - define allowed upload extensions for contact forms: `pdf`, `jpg`, `jpeg`, `png`, `doc`, `docx`, `xls`, `xlsx`, `csv`, `txt`, `xml`, `dwg`, `dxf`, `zip`; + - define 50 MB per-file server limit; + - normalize `$_FILES['files']` so both single and multiple upload shapes are handled; + - save files under `uploads/contact-attachments/YYYY/mm/`, creating directories when missing; + - generate collision-resistant file names using a random suffix and sanitized extension/name; + - return both server paths for `\S::send_email()` and public URLs for database storage; + - block invalid extension, failed upload status, non-uploaded temp files, and oversize files. + Update `saveContactData()` to accept an optional `$attachments` argument, call an `ensureContactMessagesAttachmentsColumn(PDO $pdo)` helper before insert, and store `attachments` as JSON in one `TEXT NULL` column. + Avoid storing files in `temp/` because it is automatically cleaned. + + php -l plugins/special-actions-middle.php + AC-1, AC-2, and AC-3 satisfied for backend behavior. + + + + Task 2: Wire contact form handlers to persisted uploads + plugins/special-actions-middle.php + + Replace duplicated upload loops for the forms that currently accept files with the new helper: + - `send-contact-modal`; + - `send-contact-form-new`; + - `send-contact-form-new-2`; + - `send-contact-form-new-deweloper`; + - include `send-contact-landing` only if its current file input is intentionally part of the page/form scope during implementation. + Pass returned server paths to existing `\S::send_email()` calls and returned public links to `saveContactData()`. + Initialize `$files_to_send` safely before use so submissions without files do not create notices. + If any uploaded file is invalid, stop the submission with the existing alert/redirect pattern and do not save partial contact data. + Keep recipients, recaptcha behavior, and existing form validation unchanged. + + php -l plugins/special-actions-middle.php and code inspection showing every targeted saveContactData() call receives the attachment links argument. + AC-1 and AC-3 satisfied for each in-scope form handler. + + + + Task 3: Update contact and modal upload UI limits + templates_user/pages/page-contact-v9.php, templates_user/modal/modal.php + + Update the visible file upload copy in both files to mention: + - maximum 50 MB per file; + - allowed file types matching the server allowlist. + Change fileuploader `fileMaxSize` from `10` to `50` in both templates. + Ensure client-side `extensions` matches the backend allowlist and remove non-extension values such as `text/plain`. + Keep existing layout, tracking scripts, form IDs, and recaptcha markup unchanged. + + php -l templates_user/pages/page-contact-v9.php; php -l templates_user/modal/modal.php + AC-4 satisfied for the live contact page and popup modal contact form. + + + + + + +## DO NOT CHANGE +- Do not change database credentials, email recipients, recaptcha keys, tokens, or anti-spam word lists in this plan. +- Do not refactor unrelated form handlers or admin panel code. +- Do not move existing historical files out of `temp/`; only new uploads from targeted forms should use the dedicated folder. +- Do not create a separate attachments table; the agreed schema is one column on `contact_messages`. + +## SCOPE LIMITS +- This plan stores public links in the database; it does not build an admin UI for browsing/downloading them. +- This plan does not retroactively recover attachments from previous submissions. +- This plan targets the current contact page template and modal. Older unused contact template versions should only be changed if implementation proves they are still rendered in production. + + + + +Before declaring plan complete: +- [ ] `php -l plugins/special-actions-middle.php` +- [ ] `php -l templates_user/pages/page-contact-v9.php` +- [ ] `php -l templates_user/modal/modal.php` +- [ ] Inspect generated SQL path: missing `contact_messages.attachments` triggers one safe `ALTER TABLE`. +- [ ] Inspect upload path: new files go to `uploads/contact-attachments/YYYY/mm/`, not `temp/`. +- [ ] Inspect in-scope `saveContactData()` calls: attachment links are passed for file-enabled forms. +- [ ] Confirm visible form copy and JS uploader limit say/enforce 50 MB. + + + +- All in-scope file-enabled forms keep sending emails with uploaded files. +- Accepted uploaded files are retained outside `temp/`. +- `contact_messages.attachments` is created automatically and populated with public links. +- Server and client enforce matching file types and 50 MB per-file limit. +- No unrelated files or PAUL-untracked user changes are reverted. + + + +After completion, create `.paul/phases/01-contact-attachments/01-01-SUMMARY.md`. + diff --git a/.paul/phases/01-contact-attachments/01-01-SUMMARY.md b/.paul/phases/01-contact-attachments/01-01-SUMMARY.md new file mode 100644 index 0000000..964bea9 --- /dev/null +++ b/.paul/phases/01-contact-attachments/01-01-SUMMARY.md @@ -0,0 +1,140 @@ +--- +phase: 01-contact-attachments +plan: 01 +subsystem: forms +tags: [php, mysql, uploads, contact-forms] +requires: [] +provides: + - Persistent public attachment links for file-enabled contact forms + - Automatic `contact_messages.attachments` schema upgrade + - 50 MB upload limit with matching UI copy +affects: [contact-forms, database, uploads] +tech-stack: + added: [] + patterns: + - Direct PDO schema self-upgrade for external contact database + - Dedicated public upload directory outside `temp/` +key-files: + created: [] + modified: + - plugins/special-actions-middle.php + - templates_user/pages/page-contact-v9.php + - templates_user/modal/modal.php + - .paul/codebase/db_schema.md + - .paul/codebase/tech_changelog.md +key-decisions: + - "Store attachment links in one `contact_messages.attachments` column as JSON." + - "Use public links under `/uploads/contact-attachments/YYYY/mm/`." + - "Restrict uploads to an extension allowlist and 50 MB per file." +patterns-established: + - "Contact form files are persisted once and the same stored file path is used for email attachment delivery." +duration: ~20min +started: 2026-05-05T22:11:41+02:00 +completed: 2026-05-05T22:33:44+02:00 +--- + +# Phase 1 Plan 01: Contact Attachments Summary + +File-enabled contact forms now persist uploaded files outside `temp/` and store public links in the contact submission row. + +## Performance + +| Metric | Value | +|--------|-------| +| Duration | ~20 min | +| Started | 2026-05-05T22:11:41+02:00 | +| Completed | 2026-05-05T22:33:44+02:00 | +| Tasks | 3 completed | +| Files modified | 5 implementation/docs files plus PAUL state files | + +## Acceptance Criteria Results + +| Criterion | Status | Notes | +|-----------|--------|-------| +| AC-1: Persistent Attachment Links | Pass | Production test confirmed latest `contact_messages` row contains a JSON attachment link and the public PDF URL returns HTTP 200. | +| AC-2: Automatic Database Upgrade | Pass | `ensureContactMessagesAttachmentsColumn()` creates `attachments TEXT NULL` when missing and tolerates duplicate-column races. | +| AC-3: Upload Restrictions | Pass | Backend allowlist and 50 MB limit added; invalid uploads stop before partial DB save/email. | +| AC-4: Form Limit Information | Pass | Current contact page and modal state 50 MB and allowed types; fileuploader config uses matching limit/extensions. | + +## Accomplishments + +- Added reusable contact attachment upload handling in `plugins/special-actions-middle.php`. +- Replaced targeted `temp/` upload loops for `/kontakt/` file-enabled handlers and `modal-contact-form`. +- Added automatic schema evolution for `contact_messages.attachments`. +- Updated visible upload guidance and client-side validation to match server rules. +- Verified a real production submission with attachment saved correctly. + +## Task Commits + +No git commit was created during UNIFY. The worktree had extensive pre-existing unrelated changes, including modified files touched by this phase, so committing would risk bundling user work. Commit intentionally deferred. + +## Files Created/Modified + +| File | Change | Purpose | +|------|--------|---------| +| `plugins/special-actions-middle.php` | Modified | Upload persistence helper, schema self-upgrade, attachment links passed to `saveContactData()`. | +| `templates_user/pages/page-contact-v9.php` | Modified | Upload copy and fileuploader limit/extensions for current contact page. | +| `templates_user/modal/modal.php` | Modified | Upload copy and fileuploader limit/extensions for modal form. | +| `.paul/codebase/db_schema.md` | Modified | Documented external `contact_messages` schema and `attachments` column. | +| `.paul/codebase/tech_changelog.md` | Modified | Recorded technical change for future context. | +| `.paul/changelog/2026-05-05.md` | Created | Human-readable PAUL changelog entry. | +| `.paul/STATE.md` | Modified | Loop and phase state closure. | +| `.paul/ROADMAP.md` | Modified | Phase marked complete. | +| `.paul/PROJECT.md` | Modified | Requirement moved into shipped/validated context. | + +## Decisions Made + +| Decision | Rationale | Impact | +|----------|-----------|--------| +| Store public links in one column | User chose one-column storage; simplest production migration. | `attachments` stores JSON array of public paths. | +| Dedicated public upload folder | `temp/` is cleaned automatically. | New uploads go to `/uploads/contact-attachments/YYYY/mm/`. | +| Restrict uploads to allowlist + 50 MB | User requested restrictions and visible limit. | Server and UI now enforce aligned rules. | +| Defer landing page upload persistence | Request scoped to `/kontakt/` and modal. | `send-contact-landing` still uses legacy temp flow and is documented as deferred. | +| Skip automatic git commit | Dirty worktree contained pre-existing unrelated/user changes. | No accidental commit of unrelated work; manual commit can be made later. | + +## Deviations from Plan + +### Summary + +| Type | Count | Impact | +|------|-------|--------| +| Deferred | 1 | Landing page handler remains legacy because it is outside requested scope. | +| Transition deviation | 1 | Git commit skipped to avoid bundling pre-existing dirty worktree changes. | + +### Auto-fixed Issues + +**1. Duplicate-column race tolerance** +- **Found during:** Task 1 review +- **Issue:** Two simultaneous first submissions could both try to add `attachments`. +- **Fix:** Duplicate-column PDO errors are tolerated after the second request loses the race. +- **Files:** `plugins/special-actions-middle.php` +- **Verification:** `php -l plugins/special-actions-middle.php` + +### Deferred Items + +- Landing page attachment persistence can be planned separately if `send-contact-landing` should also retain uploads outside `temp/`. + +## Issues Encountered + +| Issue | Resolution | +|-------|------------| +| Direct DB access from local machine failed because `mysql8` is internal and external DB access is denied. | Used a short-lived FTP-uploaded diagnostic script on the production webroot, then deleted it. | +| Initial diagnostic `file_exists()` check was false due hosting `DOCUMENT_ROOT` behavior. | Verified the public URL returns HTTP 200 and FTP confirms the file exists at the expected path. | + +## Next Phase Readiness + +**Ready:** +- `/kontakt/` file-enabled forms and modal now retain uploaded attachments. +- Database schema self-upgrade is in place. +- Public upload directory pattern is established. + +**Concerns:** +- The project still has broader security concerns documented in `.paul/codebase/concerns.md`. +- The landing page upload flow still uses `temp/` if that page remains business-critical. + +**Blockers:** +- None for this phase. + +--- +*Phase: 01-contact-attachments, Plan: 01* +*Completed: 2026-05-05* diff --git a/.vscode/ftp-kr.json b/.vscode/ftp-kr.json index 7bfdbbd..4d50e2e 100644 --- a/.vscode/ftp-kr.json +++ b/.vscode/ftp-kr.json @@ -12,6 +12,10 @@ "ignoreRemoteModification": true, "ignore": [ ".git", - "/.vscode" + "/.vscode", + "/.paul", + "/.serena", + ".playwright-mcp", + "CLAUDE.md" ] } \ No newline at end of file diff --git a/plugins/special-actions-middle.php b/plugins/special-actions-middle.php index 76b78ea..0f1ed83 100644 --- a/plugins/special-actions-middle.php +++ b/plugins/special-actions-middle.php @@ -179,7 +179,138 @@ function strposa($haystack, $needles = array(), $offset = 0) return min($chr); } -function saveContactData( $name = '', $email = '', $phone = '', $message = '', $zip = '', $title = '', $company = '', $invoiceNumber = '', $address = '', $formId = '', $voivodeship = '', $clientType = '', $consentOffer = 0, $consentMarketing = 0 ) +function contactAttachmentAllowedExtensions() +{ + return ['pdf', 'jpg', 'jpeg', 'png', 'doc', 'docx', 'xls', 'xlsx', 'csv', 'txt', 'xml', 'dwg', 'dxf', 'zip']; +} + +function contactAttachmentMaxSize() +{ + return 50 * 1024 * 1024; +} + +function ensureContactMessagesAttachmentsColumn($pdo) +{ + static $checked = false; + if ($checked) + return; + + $stmt = $pdo->query("SHOW COLUMNS FROM contact_messages LIKE 'attachments'"); + if (!$stmt->fetch(PDO::FETCH_ASSOC)) + { + try + { + $pdo->exec("ALTER TABLE contact_messages ADD attachments TEXT NULL AFTER consent_marketing"); + } + catch (PDOException $e) + { + if (strpos($e->getMessage(), 'Duplicate column') === false && strpos($e->getMessage(), '1060') === false) + throw $e; + } + } + + $checked = true; +} + +function contactAttachmentSanitizeName($name) +{ + $convertedName = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $name); + if ($convertedName !== false) + $name = $convertedName; + + $name = preg_replace('/[^A-Za-z0-9_-]+/', '-', $name); + $name = trim($name, '-'); + + return $name ? strtolower($name) : 'plik'; +} + +function saveContactAttachments($fieldName = 'files') +{ + $result = [ + 'success' => true, + 'error' => '', + 'files_to_send' => [], + 'links' => [] + ]; + + if (empty($_FILES[$fieldName]) || empty($_FILES[$fieldName]['name'])) + return $result; + + $uploaded = $_FILES[$fieldName]; + $names = is_array($uploaded['name']) ? $uploaded['name'] : [$uploaded['name']]; + $tmpNames = is_array($uploaded['tmp_name']) ? $uploaded['tmp_name'] : [$uploaded['tmp_name']]; + $sizes = is_array($uploaded['size']) ? $uploaded['size'] : [$uploaded['size']]; + $errors = is_array($uploaded['error']) ? $uploaded['error'] : [$uploaded['error']]; + $allowedExtensions = contactAttachmentAllowedExtensions(); + $maxSize = contactAttachmentMaxSize(); + $relativeDir = 'uploads/contact-attachments/' . date('Y') . '/' . date('m'); + $targetDir = dirname(__DIR__) . '/' . $relativeDir; + + if (!is_dir($targetDir) && !mkdir($targetDir, 0755, true)) + { + $result['success'] = false; + $result['error'] = 'upload_dir'; + return $result; + } + + for ($i = 0; $i < count($names); $i++) + { + if (empty($names[$i]) || $errors[$i] == UPLOAD_ERR_NO_FILE) + continue; + + if ($errors[$i] != UPLOAD_ERR_OK) + { + $result['success'] = false; + $result['error'] = 'upload_error'; + return $result; + } + + if ((int)$sizes[$i] > $maxSize) + { + $result['success'] = false; + $result['error'] = 'file_size'; + return $result; + } + + $extension = strtolower(pathinfo($names[$i], PATHINFO_EXTENSION)); + if (!in_array($extension, $allowedExtensions)) + { + $result['success'] = false; + $result['error'] = 'file_type'; + return $result; + } + + if (!is_uploaded_file($tmpNames[$i])) + { + $result['success'] = false; + $result['error'] = 'upload_source'; + return $result; + } + + $baseName = contactAttachmentSanitizeName(pathinfo($names[$i], PATHINFO_FILENAME)); + $fileName = $baseName . '-' . date('YmdHis') . '-' . bin2hex(random_bytes(4)) . '.' . $extension; + $targetPath = $targetDir . '/' . $fileName; + + if (!move_uploaded_file($tmpNames[$i], $targetPath)) + { + $result['success'] = false; + $result['error'] = 'move_failed'; + return $result; + } + + $result['files_to_send'][] = $targetPath; + $result['links'][] = '/' . $relativeDir . '/' . $fileName; + } + + return $result; +} + +function contactAttachmentFailureMessage() +{ + return 'Zalacznik ma niedozwolony typ pliku albo przekracza limit 50 MB.'; +} + +function saveContactData( $name = '', $email = '', $phone = '', $message = '', $zip = '', $title = '', $company = '', $invoiceNumber = '', $address = '', $formId = '', $voivodeship = '', $clientType = '', $consentOffer = 0, $consentMarketing = 0, $attachments = [] ) { // Konfiguracja bazy danych $dbHost = 'mysql8'; @@ -191,6 +322,7 @@ function saveContactData( $name = '', $email = '', $phone = '', $message = '', $ { $pdo = new PDO("mysql:host=$dbHost;dbname=$dbName;charset=utf8mb4", $dbUser, $dbPass); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + ensureContactMessagesAttachmentsColumn($pdo); // Przygotowanie danych $data = [ @@ -209,14 +341,15 @@ function saveContactData( $name = '', $email = '', $phone = '', $message = '', $ 'client_type' => str_replace(['\'', '"'], '', $clientType), 'consent_offer' => (int)$consentOffer, 'consent_marketing' => (int)$consentMarketing, + 'attachments' => !empty($attachments) ? json_encode(array_values($attachments), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) : null, 'created_at' => date('Y-m-d H:i:s') ]; // Zaktualizowane zapytanie SQL $sql = "INSERT INTO contact_messages - (form_id, name, email, phone, message, zip_code, topic, company, invoice_number, address, voivodeship, client_type, consent_offer, consent_marketing, created_at) + (form_id, name, email, phone, message, zip_code, topic, company, invoice_number, address, voivodeship, client_type, consent_offer, consent_marketing, attachments, created_at) VALUES - (:form_id, :name, :email, :phone, :message, :zip_code, :topic, :company, :invoice_number, :address, :voivodeship, :client_type, :consent_offer, :consent_marketing, :created_at)"; + (:form_id, :name, :email, :phone, :message, :zip_code, :topic, :company, :invoice_number, :address, :voivodeship, :client_type, :consent_offer, :consent_marketing, :attachments, :created_at)"; $stmt = $pdo->prepare($sql); $stmt->execute($data); @@ -305,17 +438,8 @@ if ( \S::get('action') == 'send-contact-modal' and \S::get('token') == \S::get_s exit; } - - $countfiles = count($_FILES['files']['name']); - for ($i = 0; $i < $countfiles; $i++) - { - $filename = $_FILES['files']['name'][$i]; - if ($filename and pathinfo($_FILES['files']['name'][$i], PATHINFO_EXTENSION) != 'php') - { - move_uploaded_file($_FILES['files']['tmp_name'][$i], 'temp/' . $filename); - $files_to_send[] = 'temp/' . $filename; - } - } + $files_to_send = []; + $attachment_links = []; if ( \S::get( 'name' ) and \S::get( 'email' ) and \S::get( 'address' ) and \S::get( 'phone' ) and strposa( \S::get( 'name' ), $spam_words ) === false and strposa( \S::get( 'email' ), $spam_words ) === false and strposa( \S::get( 'address' ), $spam_words ) === false and strposa( \S::get( 'phone' ), $spam_words ) === false and strposa( \S::get( 'information' ), $spam_words ) === false ) { @@ -335,7 +459,17 @@ if ( \S::get('action') == 'send-contact-modal' and \S::get('token') == \S::get_s $fullMessage .= "\n\n--- Wymiary stolarki ---\n" . \S::get('wymiaryStolarki'); } - saveContactData( \S::get('name'), \S::get('email'), \S::get('phone'), $fullMessage, \S::get('address'), '', '', '', '', 'modal-contact-form', '', '', $zgoda1, $zgoda2 ); + $uploadedFiles = saveContactAttachments(); + if (!$uploadedFiles['success']) + { + \S::alert(contactAttachmentFailureMessage()); + header('Location: ' . $_SERVER['REQUEST_URI']); + exit; + } + $files_to_send = $uploadedFiles['files_to_send']; + $attachment_links = $uploadedFiles['links']; + + saveContactData( \S::get('name'), \S::get('email'), \S::get('phone'), $fullMessage, \S::get('address'), '', '', '', '', 'modal-contact-form', '', '', $zgoda1, $zgoda2, $attachment_links ); if ( \S::send_email( 'marketing@vidok.com', 'Wiadomość ze strony vidok.com', $text, \S::get('email'), $files_to_send ) ) { @@ -459,18 +593,8 @@ if (\S::get('action') == 'send-contact-form-new' and \S::get('token') == \S::get // should return JSON with success as true if ($responseKeys["success"]) { - $countfiles = count($_FILES['files']['name']); - - for ($i = 0; $i < $countfiles; $i++) - { - $filename = $_FILES['files']['name'][$i]; - if ($filename and pathinfo($_FILES['files']['name'][$i], PATHINFO_EXTENSION) != 'php') - { - move_uploaded_file($_FILES['files']['tmp_name'][$i], 'temp/' . $filename); - $files_to_send[] = 'temp/' . $filename; - } - } - + $files_to_send = []; + $attachment_links = []; if ( \S::get('name') and \S::get('email') and \S::get('postal_code') and \S::get('phone') and \S::get('voivodeship') and \S::get('client') and strposa(\S::get('name'), $spam_words) === false and strposa(\S::get('email'), $spam_words) === false and strposa(\S::get('postal_code'), $spam_words) === false and strposa(\S::get('phone'), $spam_words) === false and strposa(\S::get('message'), $spam_words) === false @@ -488,7 +612,17 @@ if (\S::get('action') == 'send-contact-form-new' and \S::get('token') == \S::get $zgoda1 = (\S::get('zgoda_1') == 'on' || \S::get('zgoda_1') == 1) ? 1 : 0; $zgoda2 = (\S::get('zgoda_2') == 'on' || \S::get('zgoda_2') == 1) ? 1 : 0; - saveContactData( \S::get('name'), \S::get('email'), \S::get('phone'), \S::get('message'), \S::get('postal_code'), '', '', '', '', 'contact-form-new', \S::get('voivodeship'), \S::get('client'), $zgoda1, $zgoda2 ); + $uploadedFiles = saveContactAttachments(); + if (!$uploadedFiles['success']) + { + \S::alert(contactAttachmentFailureMessage()); + header('Location: ' . $_SERVER['REQUEST_URI']); + exit; + } + $files_to_send = $uploadedFiles['files_to_send']; + $attachment_links = $uploadedFiles['links']; + + saveContactData( \S::get('name'), \S::get('email'), \S::get('phone'), \S::get('message'), \S::get('postal_code'), '', '', '', '', 'contact-form-new', \S::get('voivodeship'), \S::get('client'), $zgoda1, $zgoda2, $attachment_links ); if (\S::send_email('marketing@vidok.com', 'Wiadomość ze strony vidok.com', $text, \S::get('email'), $files_to_send)) { @@ -608,18 +742,8 @@ if (\S::get('action') == 'send-contact-form-new-2' and \S::get('token') == \S::g // should return JSON with success as true if ($responseKeys["success"]) { - $countfiles = count($_FILES['files']['name']); - - for ($i = 0; $i < $countfiles; $i++) - { - $filename = $_FILES['files']['name'][$i]; - if ($filename and pathinfo($_FILES['files']['name'][$i], PATHINFO_EXTENSION) != 'php') - { - move_uploaded_file($_FILES['files']['tmp_name'][$i], 'temp/' . $filename); - $files_to_send[] = 'temp/' . $filename; - } - } - + $files_to_send = []; + $attachment_links = []; if ( \S::get('name') and \S::get('email') and \S::get('postal_code') and \S::get('phone') and \S::get('voivodeship') and \S::get('client') and strposa(\S::get('name'), $spam_words) === false and strposa(\S::get('email'), $spam_words) === false and strposa(\S::get('postal_code'), $spam_words) === false and strposa(\S::get('phone'), $spam_words) === false and strposa(\S::get('message'), $spam_words) === false @@ -638,7 +762,17 @@ if (\S::get('action') == 'send-contact-form-new-2' and \S::get('token') == \S::g $zgoda1 = (\S::get('zgoda_1') == 'on' || \S::get('zgoda_1') == 1) ? 1 : 0; $zgoda2 = (\S::get('zgoda_2') == 'on' || \S::get('zgoda_2') == 1) ? 1 : 0; - saveContactData( \S::get('name'), \S::get('email'), \S::get('phone'), \S::get('message'), \S::get('postal_code'), '', '', '', '', 'contact-form-new', \S::get('voivodeship'), \S::get('client'), $zgoda1, $zgoda2 ); + $uploadedFiles = saveContactAttachments(); + if (!$uploadedFiles['success']) + { + \S::alert(contactAttachmentFailureMessage()); + header('Location: ' . $_SERVER['REQUEST_URI']); + exit; + } + $files_to_send = $uploadedFiles['files_to_send']; + $attachment_links = $uploadedFiles['links']; + + saveContactData( \S::get('name'), \S::get('email'), \S::get('phone'), \S::get('message'), \S::get('postal_code'), '', '', '', '', 'contact-form-new', \S::get('voivodeship'), \S::get('client'), $zgoda1, $zgoda2, $attachment_links ); if (\S::send_email('marketing@vidok.com', 'Wiadomość ze strony vidok.com', $text, \S::get('email'), $files_to_send)) { @@ -751,18 +885,8 @@ if (\S::get('action') == 'send-contact-form-new-deweloper' and \S::get('token') // should return JSON with success as true if ($responseKeys["success"]) { - $countfiles = count($_FILES['files']['name']); - - for ($i = 0; $i < $countfiles; $i++) - { - $filename = $_FILES['files']['name'][$i]; - if ($filename and pathinfo($_FILES['files']['name'][$i], PATHINFO_EXTENSION) != 'php') - { - move_uploaded_file($_FILES['files']['tmp_name'][$i], 'temp/' . $filename); - $files_to_send[] = 'temp/' . $filename; - } - } - + $files_to_send = []; + $attachment_links = []; if ( \S::get('name') and \S::get('email') and \S::get('postal_code') and \S::get('phone') and \S::get('voivodeship') and \S::get('client') and strposa(\S::get('name'), $spam_words) === false and strposa(\S::get('email'), $spam_words) === false and strposa(\S::get('postal_code'), $spam_words) === false and strposa(\S::get('phone'), $spam_words) === false and strposa(\S::get('message'), $spam_words) === false @@ -783,7 +907,17 @@ if (\S::get('action') == 'send-contact-form-new-deweloper' and \S::get('token') $zgoda1 = (\S::get('zgoda_1') == 'on' || \S::get('zgoda_1') == 1) ? 1 : 0; $zgoda2 = (\S::get('zgoda_2') == 'on' || \S::get('zgoda_2') == 1) ? 1 : 0; - saveContactData( \S::get('name'), \S::get('email'), \S::get('phone'), \S::get('message'), \S::get('postal_code'), '', '', '', '', 'contact-form-new', \S::get('voivodeship'), \S::get('client'), $zgoda1, $zgoda2 ); + $uploadedFiles = saveContactAttachments(); + if (!$uploadedFiles['success']) + { + \S::alert(contactAttachmentFailureMessage()); + header('Location: ' . $_SERVER['REQUEST_URI']); + exit; + } + $files_to_send = $uploadedFiles['files_to_send']; + $attachment_links = $uploadedFiles['links']; + + saveContactData( \S::get('name'), \S::get('email'), \S::get('phone'), \S::get('message'), \S::get('postal_code'), '', '', '', '', 'contact-form-new', \S::get('voivodeship'), \S::get('client'), $zgoda1, $zgoda2, $attachment_links ); if (\S::send_email('marketing@vidok.com', 'Wiadomość ze strony vidok.com', $text, \S::get('email'), $files_to_send)) { @@ -807,4 +941,4 @@ if (\S::get('action') == 'send-contact-form-new-deweloper' and \S::get('token') } header('Location: ' . $_SERVER['REQUEST_URI']); exit; -} \ No newline at end of file +} diff --git a/stopki/de-jerzy-dlugosz/Vidok_pliki/image001.png b/stopki/de-jerzy-dlugosz/Vidok_pliki/image001.png new file mode 100644 index 0000000..e25d681 Binary files /dev/null and b/stopki/de-jerzy-dlugosz/Vidok_pliki/image001.png differ diff --git a/stopki/de-jerzy-dlugosz/Vidok_pliki/image002.png b/stopki/de-jerzy-dlugosz/Vidok_pliki/image002.png new file mode 100644 index 0000000..c667ce6 Binary files /dev/null and b/stopki/de-jerzy-dlugosz/Vidok_pliki/image002.png differ diff --git a/stopki/fr-adam-swider - bez zdjecia/Vidok_pliki/image001.png b/stopki/fr-adam-swider - bez zdjecia/Vidok_pliki/image001.png new file mode 100644 index 0000000..e25d681 Binary files /dev/null and b/stopki/fr-adam-swider - bez zdjecia/Vidok_pliki/image001.png differ diff --git a/stopki/fr-adam-swider - bez zdjecia/Vidok_pliki/image002.png b/stopki/fr-adam-swider - bez zdjecia/Vidok_pliki/image002.png new file mode 100644 index 0000000..c667ce6 Binary files /dev/null and b/stopki/fr-adam-swider - bez zdjecia/Vidok_pliki/image002.png differ diff --git a/stopki/fr-adam-swider - bez zdjecia/Vidok_pliki/image006.png b/stopki/fr-adam-swider - bez zdjecia/Vidok_pliki/image006.png new file mode 100644 index 0000000..eb9a8b7 Binary files /dev/null and b/stopki/fr-adam-swider - bez zdjecia/Vidok_pliki/image006.png differ diff --git a/stopki/fr-adam-swider - bez zdjecia/Vidok_pliki/image014.png b/stopki/fr-adam-swider - bez zdjecia/Vidok_pliki/image014.png new file mode 100644 index 0000000..1288a47 Binary files /dev/null and b/stopki/fr-adam-swider - bez zdjecia/Vidok_pliki/image014.png differ diff --git a/stopki/fr-grzegorz-welc/Vidok_pliki/image006.png b/stopki/fr-grzegorz-welc/Vidok_pliki/image006.png new file mode 100644 index 0000000..eb9a8b7 Binary files /dev/null and b/stopki/fr-grzegorz-welc/Vidok_pliki/image006.png differ diff --git a/stopki/fr-grzegorz-welc/Vidok_pliki/image014.png b/stopki/fr-grzegorz-welc/Vidok_pliki/image014.png new file mode 100644 index 0000000..1288a47 Binary files /dev/null and b/stopki/fr-grzegorz-welc/Vidok_pliki/image014.png differ diff --git a/stopki/fr-kamila-wachnicka/Vidok_pliki/image001.png b/stopki/fr-kamila-wachnicka/Vidok_pliki/image001.png new file mode 100644 index 0000000..e25d681 Binary files /dev/null and b/stopki/fr-kamila-wachnicka/Vidok_pliki/image001.png differ diff --git a/stopki/fr-kamila-wachnicka/Vidok_pliki/image002.png b/stopki/fr-kamila-wachnicka/Vidok_pliki/image002.png new file mode 100644 index 0000000..c667ce6 Binary files /dev/null and b/stopki/fr-kamila-wachnicka/Vidok_pliki/image002.png differ diff --git a/stopki/fr-kamila-wachnicka/Vidok_pliki/image006.png b/stopki/fr-kamila-wachnicka/Vidok_pliki/image006.png new file mode 100644 index 0000000..eb9a8b7 Binary files /dev/null and b/stopki/fr-kamila-wachnicka/Vidok_pliki/image006.png differ diff --git a/stopki/fr-kamila-wachnicka/Vidok_pliki/image014.png b/stopki/fr-kamila-wachnicka/Vidok_pliki/image014.png new file mode 100644 index 0000000..1288a47 Binary files /dev/null and b/stopki/fr-kamila-wachnicka/Vidok_pliki/image014.png differ diff --git a/stopki/fr-monika-raczkowska/Vidok_pliki/image006.png b/stopki/fr-monika-raczkowska/Vidok_pliki/image006.png new file mode 100644 index 0000000..eb9a8b7 Binary files /dev/null and b/stopki/fr-monika-raczkowska/Vidok_pliki/image006.png differ diff --git a/stopki/fr-monika-raczkowska/Vidok_pliki/image014.png b/stopki/fr-monika-raczkowska/Vidok_pliki/image014.png new file mode 100644 index 0000000..1288a47 Binary files /dev/null and b/stopki/fr-monika-raczkowska/Vidok_pliki/image014.png differ diff --git a/stopki/fr-stefan-blaszczyk/Vidok_pliki/image001.png b/stopki/fr-stefan-blaszczyk/Vidok_pliki/image001.png new file mode 100644 index 0000000..e25d681 Binary files /dev/null and b/stopki/fr-stefan-blaszczyk/Vidok_pliki/image001.png differ diff --git a/stopki/fr-stefan-blaszczyk/Vidok_pliki/image002.png b/stopki/fr-stefan-blaszczyk/Vidok_pliki/image002.png new file mode 100644 index 0000000..c667ce6 Binary files /dev/null and b/stopki/fr-stefan-blaszczyk/Vidok_pliki/image002.png differ diff --git a/stopki/fr-stefan-blaszczyk/Vidok_pliki/image006.png b/stopki/fr-stefan-blaszczyk/Vidok_pliki/image006.png new file mode 100644 index 0000000..eb9a8b7 Binary files /dev/null and b/stopki/fr-stefan-blaszczyk/Vidok_pliki/image006.png differ diff --git a/stopki/fr-stefan-blaszczyk/Vidok_pliki/image014.png b/stopki/fr-stefan-blaszczyk/Vidok_pliki/image014.png new file mode 100644 index 0000000..27f4d8f Binary files /dev/null and b/stopki/fr-stefan-blaszczyk/Vidok_pliki/image014.png differ diff --git a/stopki/it-arkadiusz-wiatrowski/Vidok_pliki/image014.jpg b/stopki/it-arkadiusz-wiatrowski/Vidok_pliki/image014.jpg new file mode 100644 index 0000000..88c6d76 Binary files /dev/null and b/stopki/it-arkadiusz-wiatrowski/Vidok_pliki/image014.jpg differ diff --git a/stopki/it-justyna-depa/Vidok_pliki/image006.png b/stopki/it-justyna-depa/Vidok_pliki/image006.png new file mode 100644 index 0000000..eb9a8b7 Binary files /dev/null and b/stopki/it-justyna-depa/Vidok_pliki/image006.png differ diff --git a/stopki/pl-krzysztof-stawarz/Vidok_pliki/image001.2.png b/stopki/pl-krzysztof-stawarz/Vidok_pliki/image001.2.png new file mode 100644 index 0000000..659c134 Binary files /dev/null and b/stopki/pl-krzysztof-stawarz/Vidok_pliki/image001.2.png differ diff --git a/stopki/pl-krzysztof-stawarz/Vidok_pliki/image001.png b/stopki/pl-krzysztof-stawarz/Vidok_pliki/image001.png new file mode 100644 index 0000000..d123315 Binary files /dev/null and b/stopki/pl-krzysztof-stawarz/Vidok_pliki/image001.png differ diff --git a/stopki/pl-krzysztof-stawarz/Vidok_pliki/image002.2.png b/stopki/pl-krzysztof-stawarz/Vidok_pliki/image002.2.png new file mode 100644 index 0000000..a6621e0 Binary files /dev/null and b/stopki/pl-krzysztof-stawarz/Vidok_pliki/image002.2.png differ diff --git a/stopki/pl-krzysztof-stawarz/Vidok_pliki/image002.png b/stopki/pl-krzysztof-stawarz/Vidok_pliki/image002.png new file mode 100644 index 0000000..d123315 Binary files /dev/null and b/stopki/pl-krzysztof-stawarz/Vidok_pliki/image002.png differ diff --git a/stopki/pl-krzysztof-stawarz/Vidok_pliki/image006.png b/stopki/pl-krzysztof-stawarz/Vidok_pliki/image006.png new file mode 100644 index 0000000..eb9a8b7 Binary files /dev/null and b/stopki/pl-krzysztof-stawarz/Vidok_pliki/image006.png differ diff --git a/templates_user/modal/modal.php b/templates_user/modal/modal.php index d24c779..a6574b0 100644 --- a/templates_user/modal/modal.php +++ b/templates_user/modal/modal.php @@ -140,6 +140,7 @@
+

Maksymalny rozmiar: 50 MB na plik. Dozwolone typy: PDF, JPG, PNG, DOC, DOCX, XLS, XLSX, CSV, TXT, XML, DWG, DXF, ZIP.

@@ -262,8 +263,8 @@ \ No newline at end of file + diff --git a/templates_user/pages/page-contact-v9.php b/templates_user/pages/page-contact-v9.php index 67c805f..b9093cb 100644 --- a/templates_user/pages/page-contact-v9.php +++ b/templates_user/pages/page-contact-v9.php @@ -255,7 +255,7 @@
-

Chcesz przesłać nam wizualizacje, projekt lub inne materiały? Skorzystaj z pola poniżej i dodaj plik do wiadomości.

+

Chcesz przesłać nam wizualizacje, projekt lub inne materiały? Skorzystaj z pola poniżej i dodaj plik do wiadomości. Maksymalny rozmiar: 50 MB na plik. Dozwolone typy: PDF, JPG, PNG, DOC, DOCX, XLS, XLSX, CSV, TXT, XML, DWG, DXF, ZIP.

@@ -316,7 +316,7 @@
-

Chcesz przesłać nam wizualizacje, projekt lub inne materiały? Skorzystaj z pola poniżej i dodaj plik do wiadomości.

+

Chcesz przesłać nam wizualizacje, projekt lub inne materiały? Skorzystaj z pola poniżej i dodaj plik do wiadomości. Maksymalny rozmiar: 50 MB na plik. Dozwolone typy: PDF, JPG, PNG, DOC, DOCX, XLS, XLSX, CSV, TXT, XML, DWG, DXF, ZIP.

@@ -666,8 +666,8 @@ * File uploader */ $('#contact-form-new input.files').fileuploader({ - fileMaxSize: 10, - extensions: ['jpg', 'jpeg', 'png', 'pdf', 'text/plain', 'csv', 'xml', 'xls', 'xlsx', 'doc', 'dot', 'docx'], + fileMaxSize: 50, + extensions: ['jpg', 'jpeg', 'png', 'pdf', 'csv', 'txt', 'xml', 'xls', 'xlsx', 'doc', 'docx', 'dwg', 'dxf', 'zip'], addMore: true, captions: { button: function(e) { @@ -898,4 +898,4 @@ } }) }); - \ No newline at end of file +