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>
259 lines
9.3 KiB
Markdown
259 lines
9.3 KiB
Markdown
---
|
|
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>
|