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>
|
||||
Reference in New Issue
Block a user