feat(media-folder-pro): add virtual folder system for WordPress media library
Custom WordPress plugin that replaces the default flat media library with a structured folder view. Features: hierarchical folders via custom taxonomy, sidebar folder tree, drag & drop, modal integration with Elementor/builders, bulk assign, upload auto-assign, toast notifications. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
258
.paul/phases/01-media-folders-plugin/01-01-PLAN.md
Normal file
258
.paul/phases/01-media-folders-plugin/01-01-PLAN.md
Normal file
@@ -0,0 +1,258 @@
|
||||
---
|
||||
phase: 01-media-folders-plugin
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- wp-content/plugins/media-folder-pro/media-folder-pro.php
|
||||
- wp-content/plugins/media-folder-pro/includes/class-taxonomy.php
|
||||
- wp-content/plugins/media-folder-pro/includes/class-ajax-handler.php
|
||||
- wp-content/plugins/media-folder-pro/assets/css/admin.css
|
||||
- wp-content/plugins/media-folder-pro/assets/js/folder-tree.js
|
||||
autonomous: true
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Stworzyc fundament pluginu WordPress "RM2 Media Folders" — rejestracja custom taxonomy `media_folder` dla attachment post type, CRUD folderow przez AJAX, oraz bazowy UI drzewka folderow w panelu admina.
|
||||
|
||||
## Purpose
|
||||
Bez tego fundamentu nie mozna budowac integracji z biblioteka mediow. Taxonomy to core mechanizm organizacji.
|
||||
|
||||
## Output
|
||||
Dzialajacy plugin z:
|
||||
- Custom taxonomy `media_folder` (hierarchiczna, niewidoczna publicznie)
|
||||
- AJAX endpoints: tworzenie, zmiana nazwy, usuwanie, przenoszenie folderow
|
||||
- Bazowe drzewko folderow renderowane w JS
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
|
||||
## Source Files
|
||||
Nowy plugin — brak istniejacych plikow. Tworzymy od zera.
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Plugin aktywuje sie bez bledow
|
||||
```gherkin
|
||||
Given WordPress 6.x z PHP 8.0+
|
||||
When uzytkownik aktywuje plugin "RM2 Media Folders"
|
||||
Then plugin aktywuje sie bez bledow i warnings
|
||||
And w bazie danych pojawia sie taxonomy "media_folder"
|
||||
```
|
||||
|
||||
## AC-2: CRUD folderow dziala przez AJAX
|
||||
```gherkin
|
||||
Given plugin jest aktywny
|
||||
When uzytkownik tworzy folder "Zdjecia produktow" przez UI
|
||||
Then folder zostaje zapisany jako term w taxonomy media_folder
|
||||
And odpowiedz AJAX zawiera id i nazwe nowego folderu
|
||||
|
||||
Given istniejacy folder "Zdjecia produktow"
|
||||
When uzytkownik zmienia nazwe na "Produkty 2026"
|
||||
Then term name i slug zostaja zaktualizowane
|
||||
|
||||
Given istniejacy pusty folder
|
||||
When uzytkownik usuwa folder
|
||||
Then term zostaje usuniety z bazy
|
||||
|
||||
Given istniejacy folder
|
||||
When uzytkownik przenosi go jako subfolder innego folderu
|
||||
Then term parent zostaje zaktualizowany
|
||||
```
|
||||
|
||||
## AC-3: Drzewko folderow renderuje sie poprawnie
|
||||
```gherkin
|
||||
Given plugin jest aktywny i istnieja foldery z podfolderami
|
||||
When uzytkownik otwiera strone Media w panelu admina
|
||||
Then widoczne jest drzewko folderow z hierarchia
|
||||
And foldery mozna rozwijac/zwijac
|
||||
And klikniecie folderu filtruje liste mediow
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Core plugin file + taxonomy registration</name>
|
||||
<files>wp-content/plugins/rm2-media-folders/rm2-media-folders.php, wp-content/plugins/rm2-media-folders/includes/class-taxonomy.php</files>
|
||||
<action>
|
||||
1. Utworzyc glowny plik pluginu rm2-media-folders.php:
|
||||
- Plugin header (Name: RM2 Media Folders, Version: 0.1.0)
|
||||
- Autoload includes/
|
||||
- Hook init: rejestracja taxonomy
|
||||
- Hook admin_enqueue_scripts: ladowanie CSS/JS na stronach media
|
||||
- Hook wp_ajax_*: rejestracja AJAX handlers
|
||||
|
||||
2. Utworzyc includes/class-taxonomy.php:
|
||||
- Klasa RM2_Media_Folders_Taxonomy
|
||||
- Metoda register(): register_taxonomy('media_folder', 'attachment', args)
|
||||
- hierarchical: true
|
||||
- public: false
|
||||
- show_ui: false (wlasny UI)
|
||||
- show_in_rest: true (dla block editora)
|
||||
- Metoda get_folder_tree(): zwraca hierarchiczne drzewo folderow
|
||||
- Uzywa get_terms() z 'hide_empty' => false
|
||||
- Buduje drzewo parent-child
|
||||
|
||||
Avoid: Nie uzywac show_ui: true — bedziemy miec wlasny interfejs.
|
||||
Avoid: Nie rejestrowac taxonomy dla innych post types niz attachment.
|
||||
</action>
|
||||
<verify>
|
||||
- Plugin pojawia sie na liscie pluginow WP
|
||||
- Po aktywacji: wp term list media_folder --format=json (WP-CLI) nie zwraca bledu
|
||||
- Taxonomy istnieje: taxonomy_exists('media_folder') === true
|
||||
</verify>
|
||||
<done>AC-1 satisfied: Plugin aktywuje sie, taxonomy zarejestrowana</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: AJAX handler for folder CRUD</name>
|
||||
<files>wp-content/plugins/rm2-media-folders/includes/class-ajax-handler.php</files>
|
||||
<action>
|
||||
Utworzyc includes/class-ajax-handler.php:
|
||||
- Klasa RM2_Media_Folders_Ajax
|
||||
|
||||
Endpointy AJAX (kazdy z nonce verification + capability check 'upload_files'):
|
||||
|
||||
1. rm2_mf_create_folder:
|
||||
- Parametry: name (string), parent_id (int, 0 = root)
|
||||
- wp_insert_term($name, 'media_folder', ['parent' => $parent_id])
|
||||
- Zwraca: {success: true, folder: {id, name, slug, parent}}
|
||||
|
||||
2. rm2_mf_rename_folder:
|
||||
- Parametry: folder_id (int), name (string)
|
||||
- wp_update_term($folder_id, 'media_folder', ['name' => $name])
|
||||
- Zwraca: {success: true, folder: {id, name, slug}}
|
||||
|
||||
3. rm2_mf_delete_folder:
|
||||
- Parametry: folder_id (int)
|
||||
- Sprawdz czy folder jest pusty (brak attachmentow i subfolderow)
|
||||
- Jesli nie pusty: zwroc blad
|
||||
- wp_delete_term($folder_id, 'media_folder')
|
||||
- Zwraca: {success: true}
|
||||
|
||||
4. rm2_mf_move_folder:
|
||||
- Parametry: folder_id (int), new_parent_id (int, 0 = root)
|
||||
- Walidacja: nie mozna przeniesc folderu do samego siebie lub swojego dziecka
|
||||
- wp_update_term($folder_id, 'media_folder', ['parent' => $new_parent_id])
|
||||
- Zwraca: {success: true, folder: {id, name, parent}}
|
||||
|
||||
5. rm2_mf_get_folders:
|
||||
- Brak parametrow
|
||||
- Zwraca pelne drzewo folderow z countami attachmentow
|
||||
|
||||
Kazdy endpoint:
|
||||
- check_ajax_referer('rm2_media_folders_nonce', 'nonce')
|
||||
- current_user_can('upload_files')
|
||||
- wp_send_json_success() / wp_send_json_error()
|
||||
|
||||
Avoid: Nie uzywac $_GET — wszystko przez $_POST.
|
||||
Avoid: Nie usuwac folderow z dziecmi — wymuszaj pusty folder.
|
||||
</action>
|
||||
<verify>
|
||||
- AJAX call do rm2_mf_create_folder zwraca 200 z JSON {success: true}
|
||||
- Utworzony term widoczny w get_terms('media_folder')
|
||||
- Proba bez nonce zwraca 403
|
||||
- Proba bez uprawnien zwraca blad
|
||||
</verify>
|
||||
<done>AC-2 satisfied: CRUD folderow dziala przez AJAX z walidacja</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Admin assets + folder tree UI</name>
|
||||
<files>wp-content/plugins/rm2-media-folders/assets/css/admin.css, wp-content/plugins/rm2-media-folders/assets/js/folder-tree.js</files>
|
||||
<action>
|
||||
1. admin.css:
|
||||
- Styl dla sidebar drzewka folderow (lewy panel, 250px szerokosci)
|
||||
- Style dla elementow drzewka: folder icon, nazwa, expand/collapse arrow
|
||||
- Styl aktywnego folderu (podswietlenie)
|
||||
- Style dla context menu (prawy klik: rename, delete, new subfolder)
|
||||
- Style dla inline edit (zmiana nazwy folderu)
|
||||
- Responsywnosc: na mniejszych ekranach sidebar chowa sie
|
||||
|
||||
2. folder-tree.js (vanilla JS, bez jQuery dependency):
|
||||
- Klasa RM2FolderTree
|
||||
- init(): fetch folderow z AJAX (rm2_mf_get_folders), renderuj drzewo
|
||||
- renderTree(folders, container): rekurencyjny render z <ul><li>
|
||||
- toggleFolder(folderId): expand/collapse children
|
||||
- selectFolder(folderId): podswietl + wyslij event 'rm2-folder-selected'
|
||||
- createFolder(parentId): prompt nazwa → AJAX create
|
||||
- renameFolder(folderId): inline edit → AJAX rename
|
||||
- deleteFolder(folderId): confirm → AJAX delete
|
||||
- moveFolder(folderId, newParentId): AJAX move (drag & drop w nastepnej fazie)
|
||||
|
||||
Toolbar nad drzewkiem:
|
||||
- Przycisk "+" (nowy folder w root)
|
||||
- Przycisk "All Media" (reset filtra)
|
||||
|
||||
Context menu (prawy klik na folder):
|
||||
- New subfolder
|
||||
- Rename
|
||||
- Delete
|
||||
|
||||
Localization: uzyj wp_localize_script z rm2MediaFolders object:
|
||||
- ajaxUrl, nonce, i18n strings
|
||||
|
||||
3. W glownym pliku pluginu dodac:
|
||||
- admin_enqueue_scripts hook ladujacy CSS i JS tylko na upload.php i media-new.php
|
||||
- wp_localize_script z nonce i ajax URL
|
||||
- Hook na admin_footer (upload.php) wstawiajacy container div dla drzewka
|
||||
|
||||
Avoid: Nie ladowac assetow na stronach innych niz media.
|
||||
Avoid: Nie uzywac jQuery — czysty vanilla JS.
|
||||
</action>
|
||||
<verify>
|
||||
- Na stronie Media > Library widoczny sidebar z drzewkiem
|
||||
- Klikniecie "+" otwiera prompt, po wpisaniu nazwy folder sie pojawia
|
||||
- Prawy klik na folder pokazuje context menu
|
||||
- Klikniecie folderu podswietla go
|
||||
- CSS nie koliduje z domyslnymi stylami WP admin
|
||||
</verify>
|
||||
<done>AC-3 satisfied: Drzewko folderow renderuje sie z CRUD operacjami</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- wp-content/plugins/elementor-pro/* (nie modyfikujemy Elementora)
|
||||
- Zadne pliki core WordPress
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Brak drag & drop mediow do folderow (Phase 2)
|
||||
- Brak integracji z media modal (Phase 3)
|
||||
- Brak bulk operations (Phase 4)
|
||||
- Brak filtrowania listy mediow po kliknieciu folderu (bedzie placeholder, pelna implementacja w Phase 2)
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] Plugin aktywuje sie bez bledow PHP
|
||||
- [ ] Taxonomy media_folder zarejestrowana i hierarchiczna
|
||||
- [ ] Wszystkie 5 AJAX endpointow odpowiada poprawnie
|
||||
- [ ] Nonce verification dziala (odrzuca requesty bez nonce)
|
||||
- [ ] Drzewko folderow wyswietla sie na stronie Media
|
||||
- [ ] Tworzenie/usuwanie/zmiana nazwy folderu dziala z UI
|
||||
- [ ] Assets ladowane tylko na stronach media (nie na innych)
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Wszystkie taski ukonczone
|
||||
- Wszystkie verification checks przeszly
|
||||
- Brak bledow PHP (error_log czysty)
|
||||
- Brak JS errors w konsoli przegladarki
|
||||
- Plugin gotowy jako baza do Phase 2
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/01-media-folders-plugin/01-01-SUMMARY.md`
|
||||
</output>
|
||||
126
.paul/phases/01-media-folders-plugin/01-01-SUMMARY.md
Normal file
126
.paul/phases/01-media-folders-plugin/01-01-SUMMARY.md
Normal file
@@ -0,0 +1,126 @@
|
||||
---
|
||||
phase: 01-media-folders-plugin
|
||||
plan: 01
|
||||
subsystem: media
|
||||
tags: [wordpress, plugin, taxonomy, ajax, vanilla-js]
|
||||
|
||||
requires: []
|
||||
provides:
|
||||
- Custom taxonomy media_folder (hierarchical)
|
||||
- AJAX CRUD endpoints for folder management
|
||||
- Folder tree sidebar UI (vanilla JS)
|
||||
affects: [02-media-library-grid, 03-media-modal]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [custom taxonomy for virtual folders, MFP_ class prefix, vanilla JS UI]
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- wp-content/plugins/media-folder-pro/media-folder-pro.php
|
||||
- wp-content/plugins/media-folder-pro/includes/class-taxonomy.php
|
||||
- wp-content/plugins/media-folder-pro/includes/class-ajax-handler.php
|
||||
- wp-content/plugins/media-folder-pro/assets/css/admin.css
|
||||
- wp-content/plugins/media-folder-pro/assets/js/folder-tree.js
|
||||
modified: []
|
||||
|
||||
key-decisions:
|
||||
- "Plugin renamed to media-folder-pro (user request)"
|
||||
- "Author set to Project Pro (https://www.project-pro.pl)"
|
||||
- "Vanilla JS instead of jQuery for tree UI"
|
||||
- "MFP_ prefix for all classes and constants"
|
||||
|
||||
patterns-established:
|
||||
- "MFP_Taxonomy centralizes all taxonomy operations"
|
||||
- "MFP_Ajax_Handler with nonce + capability check on every endpoint"
|
||||
- "Assets loaded only on upload.php and media-new.php"
|
||||
- "CustomEvent 'mfp-folder-selected' for cross-component communication"
|
||||
|
||||
duration: ~15min
|
||||
started: 2026-03-28
|
||||
completed: 2026-03-28
|
||||
---
|
||||
|
||||
# Phase 1 Plan 01: Plugin Foundation + Taxonomy Summary
|
||||
|
||||
**WordPress plugin "Media Folder Pro" with hierarchical media_folder taxonomy, 5 AJAX CRUD endpoints, and vanilla JS folder tree sidebar UI.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~15min |
|
||||
| Tasks | 3 completed |
|
||||
| Files created | 5 |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Plugin aktywuje sie bez bledow | Pass | Plugin header, taxonomy registration, autoload — all correct |
|
||||
| AC-2: CRUD folderow dziala przez AJAX | Pass | 5 endpoints with nonce verification + capability check |
|
||||
| AC-3: Drzewko folderow renderuje sie poprawnie | Pass | Sidebar with tree, context menu, inline rename, expand/collapse |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Custom taxonomy `media_folder` registered for `attachment` post type (hierarchical, hidden UI, REST enabled)
|
||||
- 5 AJAX endpoints: create, rename, delete, move, get_folders — all with security checks
|
||||
- Folder tree sidebar with context menu (right-click), inline rename, expand/collapse, active state
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `wp-content/plugins/media-folder-pro/media-folder-pro.php` | Created | Main plugin file — singleton, hooks, asset loading |
|
||||
| `wp-content/plugins/media-folder-pro/includes/class-taxonomy.php` | Created | MFP_Taxonomy — register, get_folder_tree, folder_has_children, would_create_cycle |
|
||||
| `wp-content/plugins/media-folder-pro/includes/class-ajax-handler.php` | Created | MFP_Ajax_Handler — 5 AJAX endpoints with validation |
|
||||
| `wp-content/plugins/media-folder-pro/assets/css/admin.css` | Created | Sidebar layout, tree styles, context menu, responsive |
|
||||
| `wp-content/plugins/media-folder-pro/assets/js/folder-tree.js` | Created | Vanilla JS folder tree — CRUD, context menu, inline edit |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| Plugin renamed to media-folder-pro | User request | All references use MFP_ prefix |
|
||||
| Author: Project Pro | User request | Plugin header and URI set |
|
||||
| Vanilla JS (no jQuery) | Modern, no dependency | Simpler, lighter bundle |
|
||||
| Empty-folder-only delete | Safety — prevent accidental data loss | Users must move content before deleting |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| Scope changes | 1 | Plugin name change — no functional impact |
|
||||
|
||||
**Total impact:** Naming only, no functional deviation.
|
||||
|
||||
### Details
|
||||
|
||||
1. **Plugin renamed** from `rm2-media-folders` to `media-folder-pro` per user request during APPLY. All class prefixes changed from `RM2_Media_Folders_` to `MFP_`.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Taxonomy registered and operational
|
||||
- AJAX infrastructure in place (all endpoints working)
|
||||
- Folder tree UI renders with full CRUD
|
||||
- `mfp-folder-selected` CustomEvent dispatched on folder click (Phase 2 hooks here)
|
||||
- CSS sidebar pushes main content via `margin-left`
|
||||
|
||||
**Concerns:**
|
||||
- Folder filtering not yet connected to WP media query (Phase 2 scope)
|
||||
- Drag & drop not implemented (Phase 2 scope)
|
||||
- No i18n .pot file generated yet (Phase 4)
|
||||
|
||||
**Blockers:**
|
||||
- None
|
||||
|
||||
---
|
||||
*Phase: 01-media-folders-plugin, Plan: 01*
|
||||
*Completed: 2026-03-28*
|
||||
Reference in New Issue
Block a user