---
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
---
## 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
## Project Context
@.paul/PROJECT.md
@.paul/ROADMAP.md
## Source Files
Nowy plugin — brak istniejacych plikow. Tworzymy od zera.
## 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
```
Task 1: Core plugin file + taxonomy registration
wp-content/plugins/rm2-media-folders/rm2-media-folders.php, wp-content/plugins/rm2-media-folders/includes/class-taxonomy.php
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.
- 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
AC-1 satisfied: Plugin aktywuje sie, taxonomy zarejestrowana
Task 2: AJAX handler for folder CRUD
wp-content/plugins/rm2-media-folders/includes/class-ajax-handler.php
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.
- 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
AC-2 satisfied: CRUD folderow dziala przez AJAX z walidacja
Task 3: Admin assets + folder tree UI
wp-content/plugins/rm2-media-folders/assets/css/admin.css, wp-content/plugins/rm2-media-folders/assets/js/folder-tree.js
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 -
- 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.
- 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
AC-3 satisfied: Drzewko folderow renderuje sie z CRUD operacjami
## 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)
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)
- 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