Мова

Зв'язатися

Плагін для WordPress · з 2019 · aka WPBE

WOLF

Табличний редактор для масового редагування, фільтрації, експорту та відкату записів, сторінок і будь-якого власного типу запису.

Перший реліз
Активні встановлення
4 000+
Рейтинг
4.7 / 5 (27)Читати відгуки
  • PHP
  • WP_Query (tax_query, meta_query)
  • фільтри posts_where
  • $wpdb + dbDelta
  • Transients
  • admin-ajax
  • jQuery
  • DataTables (серверний режим)
  • DOMDocument (експорт WXR)
  • fputcsv

Про мене

WOLF (раніше WPBE) — це версія для записів масового редактора BEAR для WooCommerce. Він показує записи будь-якого зареєстрованого типу в редагованій таблиці, дає змогу фільтрувати їх за основними полями, таксономіями та meta, а потім змінити, видалити чи експортувати весь відфільтрований набір однією операцією.

Складність у тому, що нічого не зафіксовано. Кожен тип запису приносить власні таксономії та meta-ключі, значення meta можуть бути серіалізованими масивами, списками ID зображень або прапорцями з власними значеннями, а цільовий набір може становити тисячі записів на спільному хостингу з короткими лімітами часу PHP.

Плагін відповідає на це реєстром полів, що перебудовується для кожного типу запису, фільтром, який зберігається на сервері й компілюється в один WP_Query, масовими запусками, якими браузер керує пакетами фіксованого розміру, та журналом історії, що фіксує попереднє значення кожного поля перед записом, тож масовий запуск також можна відкотити пакетами.

Преміум-збірка та безкоштовна збірка на wordpress.org мають одну кодову базу; безкоштовні обмеження вмикає один прапорець.

WOLF · масовий редактор записів

що розв'язано

ПРАЦЮЄз 2019

Один редактор для будь-якого типу запису

shipped · 2019

Що це даєСпівробітники редагують будь-який вміст в одній таблиці, що лишається швидкою на великих сайтах, з колонками, налаштованими для кожної людини та кожної мови.

Проблема
Записи, сторінки й власні типи записів мають різні таксономії, meta-ключі та застосовні основні поля. Одна таблиця має показувати будь-які з них і лишатися швидкою на сайтах із багатьма записами.
Рішення

Реєстр полів перебудовується для активного типу запису, а таблиця працює в серверному режимі DataTables поверх WP_Query, тож кожен запит читає одну сторінку записів.

Стек
  • PHP
  • WP_Query
  • DataTables
  • admin-ajax
Задачі · 5
Кожен тип запису приносить власний набір таксономійКолонки таксономій будуються з get_object_taxonomies() для активного типу запису

WPBE_SETTINGS::get_fields() зливає статичний список полів з data/fields.php з однією колонкою на кожну таксономію, зареєстровану для поточного типу запису, потім застосовує збережений користувачем порядок і заголовки колонок. Нові поля, що з'являються пізніше (нова таксономія, новий meta-ключ), дописуються, а не губляться.

Визначення полів несуть allow_post_types та prohibit_post_types. wrap_field_val() повертає комірку 'restricted', коли поле не застосовується, наприклад поля статусу й коментарів для вкладень, mime type лише для вкладень, sticky лише для записів. Вкладення запитуються з додатковим статусом 'inherit'.

Великі сайти не можуть завантажити кожен запис у браузерСерверний DataTables: кожна сторінка, сортування й пошук — це один WP_Query зі сторінками

Таблиця налаштована з serverSide: true і викликає wpbe_get_posts із параметрами пагінації DataTables. WPBE_POSTS::gets() перетворює start/length на paged/posts_per_page і переходить на впорядкування за meta_value_num, коли колонка сортування — числове чи датове meta-поле.

Рядків на сторінку обмежено 100 з коментарем у коді 'to avoid 500 error on weak servers'. Безкоштовна збірка обмежує це 10.

Код редактора не повинен гальмувати решту wp-adminРозширення та моделі завантажуються лише на сторінці редактора й на AJAX-діях wpbe_*

init() спершу виконує перевірку ролі, а потім is_should_init(), який повертає true лише для page=wpbe або AJAX-дії, чиє ім'я містить 'wpbe_'. На інших екранах адмінки реєструються лише пункт меню, кнопка в панелі адміністратора та сповіщення.

Розширення дотримуються угоди про іменування (ext/<slug>/<slug>.php, клас WPBE_<SLUG>) і отримують сховище, налаштування та модель записів через init_vars(). Той самий завантажувач підхоплює й сторонні розширення з wp-content/wpbe_ext.

Кожному користувачеві потрібні власні колонки, за потреби на тип записуОпції зберігаються на ID користувача, за бажанням розділені на тип запису (преміум-налаштування)

Налаштування живуть в опції wpbe_options_<user_id>. Коли перемикач 'separate settings' увімкнено, тип запису дописується до ключа, а список зареєстрованих meta-полів так само розділяється на тип запису. Активний тип запису запам'ятовується для кожного користувача в сховищі плагіна.

У безкоштовній збірці перемикач вимкнено.

На сайтах із WPML редактор має працювати поточною мовою адмінкиПоточна мова WPML передається в кожен запит; список sticky ведеться на мову

Коли SitePress присутній, фільтр wpbe_current_language повертає ICL_LANGUAGE_CODE, gets() викликає $sitepress->switch_lang() перед побудовою WP_Query, а опція sticky-записів читається й пишеться під sticky_posts_<lang>.

Пакетне масове редагування, видалення та експорт

shipped · 2019

Що це даєЗміни тисяч записів надійно завершуються на звичайному хостингу, з видимим прогресом і можливістю зупинити посередині.

Проблема
Редагування тисяч записів в одному запиті PHP упирається в ліміти часу й пам'яті, а користувачеві потрібен прогрес і спосіб зупинити запуск.
Рішення

Браузер веде запуск пакетами ID записів фіксованого розміру; сама операція зберігається на сервері один раз під випадковим bulk-ключем.

Стек
  • PHP
  • jQuery
  • Transients
  • API термінів WordPress
  • fputcsv
  • DOMDocument
Задачі · 5
Один запит PHP не може відредагувати тисячі записівID визначаються один раз, потім надсилаються пакетами по 50 на безстанову точку-виконавця

wpbe_bulk_posts_count зберігає форму масового редагування під 'wpbe_bulk_<key>' (16 випадкових символів, згенерованих у браузері) і повертає ID, що відповідають поточному фільтру, з fields=ids та no_found_rows. Браузер нарізає цей список на пакети по 50 і по черзі надсилає їх на wpbe_bulk_posts, оновлюючи індикатор прогресу та індикатор у панелі адміністратора. Скасування перериває очікуваний XHR; wpbe_bulk_finish повертає підраховану загальну кількість.

Масове видалення використовує той самий шаблон з пакетами по 10 і wp_trash_post(), тож видалені записи можна відновити з кошика.

Після запуску браузер перемальовує лише зачеплені рядки, коли змінено чотири записи чи менше, а в іншому разі перемальовує поточну сторінку.

Текстові поля потребують правок відносно поточного значенняAppend, prepend, replace (з урахуванням регістру чи без), set new; макроси на запис

__process_text_data() читає поточне значення кожного запису й застосовує вибрану поведінку. Значення можуть містити {TITLE}, {ID}, {MENU_ORDER} та {meta:key}, що розв'язуються для кожного запису, і макроси регістру, як-от {DO_STRING_UP} або {DO_STRING_TITLE}, які використовують функції mb_*, тож нелатинські заголовки обробляються коректно.

Числовим meta потрібні відносні зміни та округлення, як у цінЗа значенням чи відсотком, формула з іншим meta-ключем, випадковий діапазон, округлення до .05, .10 або .x9

__process_number_data() підтримує збільшення/зменшення на значення чи відсоток, а потім за бажанням поєднує результат з іншим meta-полем того ж запису (+, -, *, /, з заміною ділення на нуль на 1) і з випадковим числом із діапазону, масштабованим десятковим коефіцієнтом.

Округлення дробової частини до кроків .05 чи .10 або до закінчення на кшталт .99 відбувається в update_page_field() у налаштованій кількості десяткових знаків; копійки перетворюються через цілочисельний крок, бо модуль від дробових чисел дає хибні результати.

Ієрархічним і плоским таксономіям потрібне різне введення термінівID термінів для ієрархічних таксономій, slug термінів по одному для плоских

WordPress очікує ID для ієрархічних таксономій та назви чи slug для таксономій типу міток. Виконавець масових змін перевіряє is_taxonomy_hierarchical(); для плоских таксономій перетворює кожен ID на slug, а для 'replace' пише перший термін з append=false, а решту з append=true. 'Remove' використовує wp_remove_object_terms().

Хук історії викликається вручну перед змінами термінів, бо цей шлях оминає update_page_field().

Експорти великих вибірок упираються в ті самі лімітиCSV дописується пакетами по 10 через fputcsv; XML пишеться у форматі WordPress WXR

Крок підрахунку записує рядок заголовка; кожен пакет дописує рядки через fputcsv() у файл, відкритий у режимі a+. У коді зазначено, що в WP_Filesystem немає дописування по рядках, тому тут використовуються необроблені файлові функції.

Експорт XML будує документ RSS з просторами імен WXR 1.2 (wp:, content:, excerpt:), терміни як <category domain nicename> і meta як wp:postmeta з CDATA, тож файл відповідає формату експортера WordPress. Імена файлів несуть дату й хвилину експорту, а файли старші за два дні видаляються перевіркою, обмеженою transient.

Фільтрація за полями, термінами та meta

shipped · 2019

Що це даєСпівробітники вибирають саме ті записи, які хочуть, за полями, категоріями, власними значеннями чи вставленими списками ID, а редагування й експорти застосовуються до тієї самої вибірки.

Проблема
Масове редагування й експорт застосовуються до всього, що відповідає фільтру, тож фільтр має перетворити багато умов полів на один коректний WP_Query і лишатися ідентичним між окремими AJAX-запитами та вкладками браузера.
Рішення

Стан фільтра зберігається на сервері під випадковим ключем на вкладку й компілюється в частини tax_query, meta_query та posts_where в одному хуку, який використовує кожен запит.

Стек
  • WP_Query
  • tax_query
  • meta_query
  • posts_where
  • MySQL REGEXP
Задачі · 6
Запити таблиці, масового редагування та експорту мають бачити один і той самий фільтрФільтр зберігається на сервері під 16-символьним ключем, згенерованим для кожної вкладки браузера

Браузер генерує wpbe_filter_current_key, коли фільтр застосовано, і надсилає його з кожним запитом; коментар у джерелі каже, що це дозволяє масове редагування за фільтром у різних вкладках. wpbe_apply_query_filter_data() читає збережений фільтр і застосовується всередині WPBE_POSTS::gets(), тож список ID для масового редагування будується тим самим кодом, що й видима таблиця.

Збіг з усіма термінами одночасно потребує однієї частини tax_query на кожен термінAND розбивається на одну частину tax_query на термін; додано EXISTS і NOT EXISTS

Для оператора AND кожен вибраний термін стає власною частиною, з'єднаною зовнішнім relation AND — це обхідний шлях, згаданий у коментарі коду. Інші оператори (IN, NOT IN) передають список термінів як є. include_children керується фільтром замість типового значення WordPress.

Числовим діапазонам meta потрібне приведення до DECIMAL з явною точністюBETWEEN з приведенням до DECIMAL(30,20); односторонні й рівні межі оброблено

Коментар у коді описує випадок: з DECIMAL без точності пошук від 100 до 150 повертав також 99.xx. Фільтр використовує DECIMAL(30,20), переходить на >=, коли задано лише 'from', і на =, коли обидві межі рівні. Дата в meta спершу проходить через normalize_calendar_date().

Порожнє значення meta може бути '' або відсутнім рядком'Empty' компілюється у value = '' OR NOT EXISTS; 'not empty' — у value != ''

Записи, створені до появи meta-ключа, взагалі не мають рядка, тож однієї перевірки на рівність недостатньо. Поведінка 'empty' видає вкладений meta_query з relation OR.

Текстовий пошук потребує OR, AND і NOT без сирого SQLМалий синтаксис (^ для OR, ~ для AND, ! для NOT), що компілюється в підготовлений REGEXP

posts_txt_where() переводить введене в нижній регістр, розбиває за ^ і будує умови LOWER(column) REGEXP / NOT REGEXP з режимами begin, end, exact і not. Кожне значення прив'язується через $wpdb->prepare(), а колонка має входити в білий список із п'яти колонок wp_posts.

Користувачі вставляють списки та діапазони IDВведення на кшталт '12,40-55' розгортається в post__in

Елементи через кому сприймаються як ID; елемент із дефісом розгортається в повний діапазон незалежно від порядку. Діапазони дат і чисел за колонками wp_posts (menu_order, post_parent, дати запису) проходять через окремий фільтр posts_where із білим списком колонок.

Історія та відкат

shipped · 2019

Що це даєПомилкове масове редагування можна скасувати одним кроком, а не виправляти тисячі записів вручну.

Проблема
Помилкове масове редагування тисяч записів важко скасувати: ревізії WordPress не охоплюють meta, терміни чи більшість основних полів, а сам відкат може бути таким самим великим, як редагування.
Рішення

Перед кожним записом поля робиться рядок журналу з попереднім значенням; рядки одного масового запуску ділять його ключ і відкочуються пакетами.

Стек
  • $wpdb
  • dbDelta
  • MySQL
  • jQuery
Задачі · 5
Зафіксувати старе значення поля будь-якого типу, перш ніж воно змінитьсяХук перед кожним записом зберігає попереднє значення в wp_wpbe_history

write() прикріплений до wpbe_before_update_page_field, який update_page_field() викликає перед кожним записом; шлях масового редагування таксономій, що пише терміни напряму, викликає його вручну. Значення таксономій зберігаються як JSON-список ID термінів, масиви meta — як JSON (з JSON_HEX_QUOT і JSON_HEX_TAG для редактора серіалізованого meta), скаляри — як є. Кожен рядок має user_id і post_type, а також bulk-ключ, коли він є частиною запуску.

Тисячі змін мають утворювати одну операцію, яку можна скасуватиТаблиця заголовків wpbe_history_bulk на запуск: ключі, кількість записів і стан

start_bulk() вставляє рядок заголовка зі списком відредагованих полів як JSON; кожен пакет додає свій розмір через UPDATE posts_count = posts_count + %d; finish_bulk() позначає запуск завершеним. Запуск, скасований користувачем, зберігає стан за замовчуванням 'terminated', тож список показує його як частковий. Обидві таблиці створюються через dbDelta з кодуванням сайту, індексуються за bulk_key і user_id та видаляються при видаленні плагіна.

Відкат великого запуску впирається в ті самі ліміти часуВідкат порціями по 10 рядків на запит; рядок заголовка видаляється наприкінці

Браузер запитує кількість рядків для bulk-ключа, потім багаторазово викликає wpbe_history_revert_bulk_portion з limit 10 та індикатором прогресу. Кожен відкочений рядок видаляється, тож наступна порція підхоплює решту; рядок заголовка видаляється, коли лічильник досягає загальної кількості.

Відкат не повинен логувати себе як нову змінуremove_all_actions() на хуку логування перед записом старих значень

revert() від'єднує записувач історії, декодує збережене значення за типом поля (списки ID термінів, JSON-масиви meta) і записує його назад через той самий update_page_field(), що й для редагування, тож save_post та edit_post спрацьовують при відкаті так само, як при редагуванні.

Одиночні редагування та масові запуски мають показуватися в одній стрічціОдиночні рядки й масові заголовки зливаються та сортуються за часом, на користувача і тип запису

get_history() читає одиночні рядки та масові заголовки поточного користувача і типу запису, зливає їх і сортує за часом, найновіші першими, тож один список показує обидва види змін.

Безкоштовна збірка показує два останні записи й видаляє старіші одиночні рядки та масові заголовки, коли список відкривається.

Meta-поля та структурований вміст

shipped · 2019

Що це даєВласні дані, як-от списки, прапорці та галереї зображень, можна безпечно редагувати, не ламаючи їх, навіть коли співробітники не знають, як вони зберігаються.

Проблема
Власні типи записів зберігають більшу частину даних у postmeta, часто як серіалізовані масиви, списки ID зображень або прапорці зі значеннями, специфічними для сайту, які звичайна текстова комірка пошкодила б.
Рішення

Meta-ключі реєструються з типом і виглядом редагування, і кожен вигляд має власні читач, записувач, фільтр та обробник масового редагування.

Стек
  • PHP
  • API postmeta
  • jQuery
  • Сховище даних блокового редактора
Задачі · 5
Адміністратори не знають, які meta-ключі використовує тип записуКлючі зчитуються зі зразкового запису; зареєстровані ключі стають колонками, фільтрами, полями масового редагування

wpbe_meta_get_keys повертає meta-ключі запису з ID, який ввів адміністратор. Зареєстрований ключ впроваджується через хуки wpbe_extend_fields, wpbe_filter_* і wpbe_bulk_*, тож з'являється одразу в таблиці, панелі фільтрів і панелі масового редагування. Ключі обрізаються по пробілах, але не приводяться до нижнього регістру, бо ключі на кшталт _woocs_sale_price_USD чутливі до регістру.

Безкоштовна збірка використовує лише перші два зареєстровані meta-поля.

Серіалізовані масиви треба редагувати, не ламаючи структуруКонструктор у спливному вікні надсилає масиви ключ/значення; сервер перебудовує вкладені масиви

Спливне вікно надсилає keys[]/values[] для пласких і вкладених масивів та keys2[][]/values2[][] для списків об'єктів. __process_jsoned_meta_data() перебудовує масив PHP, декодує значення, що є коректним JSON, і для звичайних рядків повертається до сирого значення. Потім update_post_meta() зберігає його серіалізованим, як очікує WordPress. Той самий шлях використовує виконавець масових змін.

Прапорці зберігаються як 'yes'/'no' або інші пари, специфічні для сайтуПравила зіставлення 'key:on^off' перетворюють між перемикачем 1/0 та збереженими значеннями

over_switcher_swicher_to_val() та over_switcher_val_to_swicher() читають рядок налаштувань на кшталт 'dist:yes,stest:true' і перетворюють в обидва боки, тож інтерфейс перемикача працює зі значеннями, записаними іншими плагінами.

Галереї зображень зберігаються в meta як списки ID вкладеньMeta-тип gallery зберігається як список ID вкладень через кому

Спливне вікно галереї надсилає URL-кодовану форму; обробник визначає кодування, розбирає її через parse_str(), санітизує і зберігає ID як рядок через кому. Виконавець масових змін приймає той самий формат.

Вміст блокового редактора не можна редагувати в звичайному textareaЗапис відкривається в блоковому редакторі всередині iframe; вміст читається через wp.data

Опція 'Use gutenberg'. Для типів записів, що використовують блоковий редактор, спливне вікно завантажує post.php з wpbe_popup_editor=1; wp_editor_compatibility() приховує в цьому фреймі елементи публікації та бічної панелі й надає функцію, що повертає wp.data.select('core/editor').getEditedPostContent(). Батьківська сторінка читає його й зберігає через звичайне оновлення поля.

Контроль доступу та захист запитів

shipped · 2023

Що це даєРедактором можуть користуватися лише довірені ролі, автори працюють лише зі своїми записами, а співробітники бачать лише ті поля, які їм дозволено змінювати.

Проблема
Інструмент, що може переписати будь-яке поле будь-якого запису масово, — цінна ціль: кожна AJAX-точка має перевіряти викликача, а введення фільтра потрапляє близько до SQL.
Рішення

Одна перевірка ролі до реєстрації будь-якої точки, nonces на діях полів, масових змін, інструментів і налаштувань, права на рівні полів для ролей, що не є адміністраторами, та підготовлений SQL із білими списками колонок.

Стек
  • Ролі WordPress
  • Nonces
  • $wpdb->prepare
  • wp_kses
Задачі · 5
Лише довірені ролі можуть дістатися до будь-якої точки редактораПеревірка ролі в init() виконується до додавання будь-якого обробника wp_ajax_wpbe_*

can_manage_data() дозволяє адміністраторам, редакторам і ролям, доданим через фільтр wpbe_author_area_roles. Якщо вона не проходить, init() повертається до завантаження розширень, тож AJAX-дій для цього користувача не існує. Оновлення полів, масове редагування й масове видалення, панель інструментів (створення, дублювання, видалення, терміни), налаштування meta, форма опцій, список історії та одиничні відкати також перевіряють nonce.

Редактори не повинні бачити чи змінювати кожне поле й тип записуАдміністратор задає видимість полів і дозволені типи записів для ролей, що не є адміністраторами (преміум)

Для ролей, що не є адміністраторами, get_fields() відкидає приховані колонки, тож вони не пропонуються ні в таблиці, ні в панелі масового редагування; update_page_field() перевіряє is_current_user_can_edit_field() і повертає 'forbidden' для прихованого поля; filter_post_types() звужує вибір типу запису, а конструктор налаштувань переносить користувача на дозволений тип запису. У безкоштовній збірці прапорці видимості відображаються вимкненими.

Автори мають працювати лише зі своїми записамиРолі зони автора отримують author=self у запитах редактора і не можуть змінювати post_author

Для ролей, доданих через фільтр wpbe_author_area_roles, WPBE_AUTHOR_AREA додає 'author' => ID поточного користувача через фільтр запиту, який використовують таблиця, список ID для масового редагування та експорт, тож ці списки показують лише власні записи користувача. Його callback wpbe_user_can_edit блокує post_author для цих ролей.

Введення фільтра та скопійовані дані запису потрапляють у SQLІмена колонок із білих списків, кожне значення прив'язується через $wpdb->prepare()

Текстові фільтри та фільтри діапазонів приймають лише перелічені колонки wp_posts і прив'язують значення через prepare(). Запити історії прив'язують користувача, тип запису та bulk-ключ і будують списки NOT IN із плейсхолдерів array_fill().

Дублювання копіює терміни за slug і всі postmeta, крім _wp_old_slug, одним оператором INSERT ... SELECT %d, %s, %s UNION ALL, кожен рядок будується через prepare(), потім переводить копію в статус чернетки.

Поля вмісту мають зберігати вбудовування, але відкидати скриптиwp_kses з білим списком запису плюс вузький список атрибутів iframe

wpbe_update_page_field() рекурсивно санітизує масиви через wp_kses та білий список запису. Для вмісту, уривка й полів із розширеним текстом він додає iframe з фіксованим набором атрибутів, щоб вбудовування відео вціліли. Фільтр може перемкнути на sanitize_text_field() для суворіших сайтів.

Цифри

wordpress.org
  • Активні встановлення 4 000+ лічильник wordpress.org.
  • Відгуки 27 Середня оцінка: 4.7 з 5.
  • Років підтримки 7 З 3 вересня 2019 р., останнє оновлення 12 жовтня 2026 р..