WOLF
Un éditeur de type tableur pour modifier en masse, filtrer, exporter et annuler les modifications des articles, des pages et de tout type de contenu personnalisé.
- Première version
- Installations actives
- 4 000+
- Note
- Lire les avis
À propos
WOLF (anciennement WPBE) est le pendant pour les articles de l'éditeur en masse WooCommerce BEAR. Il affiche les articles de n'importe quel type de contenu enregistré dans un tableau modifiable, permet de les filtrer par champs natifs, taxonomies et méta, puis de modifier, supprimer ou exporter tout l'ensemble filtré en une seule opération.
La difficulté est que rien n'est fixe. Chaque type de contenu apporte ses propres taxonomies et clés méta, les valeurs méta peuvent être des tableaux sérialisés, des listes d'ID d'images ou des indicateurs à valeurs personnalisées, et l'ensemble visé peut compter des milliers d'articles sur un hébergement mutualisé aux limites de temps PHP courtes.
Le plugin y répond par un registre de champs reconstruit pour chaque type de contenu, un filtre stocké sur le serveur et compilé en une seule WP_Query, des exécutions en masse pilotées par le navigateur par lots de taille fixe, et un journal d'historique qui enregistre la valeur précédente de chaque champ avant son écriture, de sorte qu'une exécution en masse peut aussi être annulée par lots.
La version premium et la version gratuite de wordpress.org partagent une même base de code ; un seul indicateur active les limites de la version gratuite.
WOLF · éditeur d'articles en masse
ce qui a été résolu
Un seul éditeur pour tout type de contenu
shipped · 2019Ce que cela apporteLe personnel modifie tout type de contenu dans un seul tableau qui reste rapide sur les grands sites, avec des colonnes configurées par personne et par langue.
- Problème
- Les articles, les pages et les types de contenu personnalisés ont des taxonomies, des clés méta et des champs natifs applicables différents. Un seul tableau doit pouvoir afficher n'importe lequel d'entre eux et rester rapide sur les sites comptant beaucoup d'enregistrements.
- Solution
Un registre de champs est reconstruit pour le type de contenu actif et le tableau fonctionne en mode serveur de DataTables au-dessus de WP_Query, de sorte que chaque requête lit une page d'articles.
- Stack
Chaque type de contenu apporte son propre ensemble de taxonomiesLes colonnes de taxonomie sont construites à partir de get_object_taxonomies() pour le type de contenu actif
WPBE_SETTINGS::get_fields() fusionne la liste statique de champs de data/fields.php avec une colonne par taxonomie enregistrée pour le type de contenu courant, puis applique l'ordre des colonnes et les titres enregistrés par l'utilisateur. Les nouveaux champs qui apparaissent plus tard (une nouvelle taxonomie, une nouvelle clé méta) sont ajoutés à la suite au lieu d'être perdus.
Les définitions de champs portent allow_post_types et prohibit_post_types. wrap_field_val() renvoie une cellule « restricted » quand un champ ne s'applique pas, par exemple les champs de statut et de commentaires pour les pièces jointes, le type MIME uniquement pour les pièces jointes, sticky uniquement pour les articles. Les pièces jointes sont interrogées avec le statut supplémentaire 'inherit'.
Les grands sites ne peuvent pas charger tous les articles dans le navigateurDataTables côté serveur : chaque page, tri et recherche est une WP_Query paginée
Le tableau est configuré avec serverSide: true et appelle wpbe_get_posts avec les paramètres de pagination de DataTables. WPBE_POSTS::gets() convertit start/length en paged/posts_per_page et passe au tri meta_value_num quand la colonne triée est un champ méta numérique ou de date.
Le nombre de lignes par page est plafonné à 100, avec le commentaire de code « to avoid 500 error on weak servers ». La version gratuite le plafonne à 10.
Le code de l'éditeur ne doit pas ralentir le reste de wp-adminLes extensions et les modèles ne se chargent que sur la page de l'éditeur et sur les actions AJAX wpbe_*
init() exécute d'abord un contrôle de rôle puis is_should_init(), qui ne renvoie true que pour page=wpbe ou une action AJAX dont le nom contient 'wpbe_'. Sur les autres écrans d'administration, seuls l'entrée de menu, le bouton de la barre d'administration et les notices sont enregistrés.
Les extensions suivent une convention de nommage (ext/<slug>/<slug>.php, classe WPBE_<SLUG>) et reçoivent le stockage, les réglages et le modèle des articles par init_vars(). Le même chargeur prend aussi en compte les extensions tierces placées dans wp-content/wpbe_ext.
Chaque utilisateur a besoin de ses propres colonnes, par type de contenu si nécessaireOptions stockées par ID d'utilisateur, éventuellement scindées par type de contenu (réglage premium)
Les réglages vivent dans l'option wpbe_options_<user_id>. Quand l'interrupteur « réglages séparés » est activé, le type de contenu est ajouté à la clé, et la liste des champs méta enregistrés est scindée de la même façon par type de contenu. Le type de contenu actif est mémorisé par utilisateur dans le stockage du plugin.
L'interrupteur est désactivé dans la version gratuite.
Sur les sites WPML, l'éditeur doit fonctionner dans la langue d'administration couranteLangue WPML courante transmise à chaque requête ; liste des articles épinglés tenue par langue
Quand SitePress est présent, le filtre wpbe_current_language renvoie ICL_LANGUAGE_CODE, gets() appelle $sitepress->switch_lang() avant de construire WP_Query, et l'option des articles épinglés est lue et écrite sous sticky_posts_<lang>.
Modification, suppression et export en masse par lots
shipped · 2019Ce que cela apporteLes modifications portant sur des milliers d'articles se terminent de façon fiable sur un hébergement ordinaire, avec une progression visible et la possibilité d'arrêter en cours de route.
- Problème
- Modifier des milliers d'articles dans une seule requête PHP se heurte aux limites de temps et de mémoire, et l'utilisateur a besoin d'une progression et d'un moyen d'arrêter l'exécution.
- Solution
Le navigateur pilote l'exécution par lots de taille fixe d'ID d'articles ; l'opération elle-même est stockée une fois sur le serveur sous une clé de masse aléatoire.
- Stack
Une requête PHP ne peut pas modifier des milliers d'articlesID résolus une fois, puis envoyés par lots de 50 à un point d'accès de travail sans état
wpbe_bulk_posts_count stocke le formulaire de modification en masse sous 'wpbe_bulk_<key>' (16 caractères aléatoires générés dans le navigateur) et renvoie les ID correspondant au filtre courant, avec fields=ids et no_found_rows. Le navigateur découpe cette liste en lots de 50 et les envoie l'un après l'autre à wpbe_bulk_posts, en mettant à jour une barre de progression et un indicateur dans la barre d'administration. Annuler interrompt le XHR en attente ; wpbe_bulk_finish renvoie le total compté.
La suppression en masse suit le même schéma avec des lots de 10 et wp_trash_post(), de sorte que les articles supprimés restent récupérables depuis la corbeille.
Après une exécution, le navigateur ne redessine que les lignes touchées quand quatre articles ou moins ont été modifiés, et redessine la page courante dans le cas contraire.
Les champs de texte demandent des modifications relatives à la valeur actuelleAjouter à la fin, ajouter au début, remplacer (avec ou sans casse), définir une nouvelle valeur ; macros par article
__process_text_data() lit la valeur actuelle de chaque article et applique le comportement choisi. Les valeurs peuvent contenir {TITLE}, {ID}, {MENU_ORDER} et {meta:key}, résolues par article, ainsi que des macros de casse telles que {DO_STRING_UP} ou {DO_STRING_TITLE} qui utilisent les fonctions mb_*, de sorte que les titres non latins sont pris en charge.
Les méta numériques demandent des modifications relatives et un arrondi de type prixPar valeur ou pourcentage, formule avec une autre clé méta, plage aléatoire, arrondi à .05, .10 ou .x9
__process_number_data() gère l'augmentation et la diminution par valeur ou pourcentage, puis combine éventuellement le résultat avec un autre champ méta du même article (+, -, *, /, la division par zéro étant remplacée par 1) et avec un nombre aléatoire tiré d'une plage et mis à l'échelle par un facteur décimal.
L'arrondi de la fraction par pas de .05 ou .10, ou vers une terminaison comme .99, se fait dans update_page_field() avec le nombre de décimales configuré ; les centimes sont convertis par un pas entier, car le modulo sur des flottants donne des résultats faux.
Les taxonomies hiérarchiques et plates demandent une saisie de termes différenteID de termes pour les taxonomies hiérarchiques, slugs de termes un par un pour les plates
WordPress attend des ID pour les taxonomies hiérarchiques et des noms ou slugs pour celles de type étiquette. Le travailleur de modification en masse vérifie is_taxonomy_hierarchical() ; pour les taxonomies plates, il convertit chaque ID en slug, et pour « replace » il écrit le premier terme avec append=false et les suivants avec append=true. « Remove » utilise wp_remove_object_terms().
Le hook d'historique est déclenché à la main avant les changements de termes, car ce chemin contourne update_page_field().
Les exports de grandes sélections se heurtent aux mêmes limitesCSV complété par lots de 10 via fputcsv ; XML écrit au format WXR de WordPress
L'étape de comptage écrit la ligne d'en-tête ; chaque lot ajoute des lignes avec fputcsv() sur un fichier ouvert en mode a+. Le code note que WP_Filesystem n'a pas d'ajout ligne par ligne, ce qui explique l'usage des fonctions de fichier brutes ici.
L'export XML construit un document RSS avec les espaces de noms WXR 1.2 (wp:, content:, excerpt:), les termes sous forme <category domain nicename> et les méta sous forme wp:postmeta avec CDATA, de sorte que le fichier suit le format de l'exportateur de WordPress. Les noms de fichiers portent la date et la minute de l'export, et les fichiers de plus de deux jours sont supprimés par une vérification limitée par un transient.
Filtrage par champs, termes et méta
shipped · 2019Ce que cela apporteLe personnel sélectionne exactement les articles voulus par champs, catégories, valeurs personnalisées ou listes d'ID collées, et les modifications et exports s'appliquent à cette même sélection.
- Problème
- La modification en masse et l'export s'appliquent à tout ce que le filtre sélectionne ; le filtre doit donc transformer de nombreuses conditions de champs en une seule WP_Query correcte et rester identique d'une requête AJAX et d'un onglet de navigateur à l'autre.
- Solution
L'état du filtre est enregistré sur le serveur sous une clé aléatoire par onglet et compilé en clauses tax_query, meta_query et posts_where dans un seul hook utilisé par chaque requête.
- Stack
Les requêtes du tableau, de la modification en masse et de l'export doivent voir le même filtreFiltre enregistré côté serveur sous une clé de 16 caractères générée par onglet du navigateur
Le navigateur génère wpbe_filter_current_key quand un filtre est appliqué et l'envoie avec chaque requête ; le commentaire du code source indique que cela permet la modification en masse par filtre dans différents onglets. wpbe_apply_query_filter_data() lit le filtre enregistré et est appliqué dans WPBE_POSTS::gets(), de sorte que la liste d'ID de la modification en masse est construite par le même code que le tableau visible.
Faire correspondre tous les termes d'un ensemble demande une clause tax_query par termeAND scindé en une clause tax_query par terme ; EXISTS et NOT EXISTS ajoutés
Pour l'opérateur AND, chaque terme sélectionné devient sa propre clause reliée par la relation AND externe, ce qui est le contournement mentionné dans le commentaire du code. Les autres opérateurs (IN, NOT IN) transmettent la liste de termes telle quelle. include_children est contrôlé par un filtre au lieu de la valeur par défaut de WordPress.
Les plages de méta numériques demandent un cast DECIMAL avec une échelle expliciteBETWEEN avec cast DECIMAL(30,20) ; bornes unilatérales et égales gérées
Le commentaire du code donne le cas : avec DECIMAL sans précision, une recherche de 100 à 150 renvoyait aussi 99.xx. Le filtre utilise DECIMAL(30,20), passe à >= quand seul 'from' est défini et à = quand les deux bornes sont égales. Les méta de date passent d'abord par normalize_calendar_date().
Une valeur méta vide peut être '' ou une ligne absente« Empty » se compile en value = '' OR NOT EXISTS ; « not empty » en value != ''
Les articles créés avant l'introduction d'une clé méta n'ont aucune ligne, de sorte qu'un simple test d'égalité les manque. Le comportement « empty » émet un meta_query imbriqué avec la relation OR.
La recherche de texte a besoin de OR, AND et NOT sans SQL brutPetite syntaxe (^ pour OR, ~ pour AND, ! pour NOT) compilée en REGEXP préparé
posts_txt_where() passe la saisie en minuscules, la découpe sur ^ et construit des conditions LOWER(column) REGEXP / NOT REGEXP avec les modes begin, end, exact et not. Chaque valeur est liée par $wpdb->prepare() et la colonne doit appartenir à une liste d'autorisation de cinq colonnes de wp_posts.
Les utilisateurs collent des listes et des plages d'IDUne saisie comme '12,40-55' est développée dans post__in
Les éléments séparés par des virgules sont pris comme des ID ; un élément avec un tiret est développé en la plage complète, quel que soit l'ordre. Les plages de dates et de nombres sur les colonnes de wp_posts (menu_order, post_parent, dates de l'article) passent par un filtre posts_where distinct avec une liste d'autorisation de colonnes.
Historique et annulation
shipped · 2019Ce que cela apporteUne mauvaise modification en masse peut être défaite en une seule étape au lieu de corriger des milliers d'articles à la main.
- Problème
- Une mauvaise modification en masse sur des milliers d'articles est difficile à défaire : les révisions de WordPress ne couvrent ni les méta, ni les termes, ni la plupart des champs natifs, et l'annulation elle-même peut être aussi volumineuse que la modification.
- Solution
Chaque écriture de champ est précédée d'une ligne de journal contenant la valeur précédente ; les lignes d'une même exécution en masse partagent sa clé et sont rétablies par lots.
- Stack
Capturer l'ancienne valeur de n'importe quel type de champ avant qu'elle changeUn hook avant chaque écriture stocke la valeur précédente dans wp_wpbe_history
write() est attachée à wpbe_before_update_page_field, que update_page_field() déclenche avant chaque écriture ; le chemin de taxonomies en masse, qui écrit directement les termes, le déclenche à la main. Les valeurs de taxonomies sont stockées comme liste JSON d'ID de termes, les méta de type tableau en JSON (avec JSON_HEX_QUOT et JSON_HEX_TAG pour l'éditeur de méta sérialisées), les scalaires tels quels. Chaque ligne porte user_id et post_type, ainsi que la clé de masse quand elle fait partie d'une exécution.
Des milliers de modifications doivent former une seule opération annulableTable d'en-tête wpbe_history_bulk par exécution : clés, nombre d'articles et état
start_bulk() insère la ligne d'en-tête avec la liste des champs modifiés en JSON ; chaque lot ajoute sa taille par UPDATE posts_count = posts_count + %d ; finish_bulk() marque l'exécution comme completed. Une exécution annulée par l'utilisateur garde l'état par défaut 'terminated', de sorte que la liste l'affiche comme partielle. Les deux tables sont créées avec dbDelta et le jeu de caractères du site, indexées sur bulk_key et user_id, et supprimées à la désinstallation.
L'annulation d'une grosse exécution se heurte aux mêmes limites de tempsAnnulation par portions de 10 lignes par requête ; ligne d'en-tête retirée à la fin
Le navigateur demande le nombre de lignes de la clé de masse, puis appelle wpbe_history_revert_bulk_portion à répétition avec limit 10 et une barre de progression. Chaque ligne rétablie est supprimée, de sorte que la portion suivante reprend celles qui restent ; la ligne d'en-tête est supprimée quand le compteur atteint le total.
Une annulation ne doit pas se journaliser elle-même comme une nouvelle modificationremove_all_actions() sur le hook de journalisation avant d'écrire les anciennes valeurs
revert() détache l'écrivain d'historique, décode la valeur stockée selon le type de champ (listes d'ID de termes, tableaux méta JSON) et la réécrit via le même update_page_field() que celui des modifications, de sorte que save_post et edit_post se déclenchent pour une annulation comme pour une modification.
Les modifications isolées et les exécutions en masse doivent apparaître dans une même chronologieLignes isolées et en-têtes de masse fusionnés et triés par date, par utilisateur et par type de contenu
get_history() lit les lignes isolées et les en-têtes de masse de l'utilisateur courant et du type de contenu courant, les fusionne et les trie par date, les plus récents d'abord, de sorte qu'une seule liste montre les deux sortes de modifications.
La version gratuite affiche les deux dernières entrées et élague les lignes isolées et les en-têtes de masse plus anciens à l'ouverture de la liste.
Champs méta et contenu structuré
shipped · 2019Ce que cela apporteLes données personnalisées telles que listes, indicateurs et galeries d'images peuvent être modifiées sans risque de les casser, même quand le personnel ne sait pas comment elles sont stockées.
- Problème
- Les types de contenu personnalisés gardent l'essentiel de leurs données dans postmeta, souvent sous forme de tableaux sérialisés, de listes d'ID d'images ou d'indicateurs à valeurs propres au site, qu'une simple cellule de texte corromprait.
- Solution
Les clés méta sont enregistrées avec un type et une vue d'édition, et chaque vue a son propre lecteur, écrivain, filtre et gestionnaire de modification en masse.
- Stack
Les administrateurs ne savent pas quelles clés méta un type de contenu utiliseClés lues sur un article échantillon ; les clés enregistrées deviennent colonnes, filtres, champs de modification en masse
wpbe_meta_get_keys renvoie les clés méta d'un ID d'article saisi par l'administrateur. Une clé enregistrée est injectée par les hooks wpbe_extend_fields, wpbe_filter_* et wpbe_bulk_*, de sorte qu'elle apparaît en même temps dans le tableau, le panneau de filtres et le panneau de modification en masse. Les clés sont débarrassées de leurs espaces mais pas passées en minuscules, car des clés comme _woocs_sale_price_USD sont sensibles à la casse.
La version gratuite n'utilise que les deux premiers champs méta enregistrés.
Les tableaux sérialisés doivent être modifiés sans casser leur structureLe constructeur en fenêtre envoie des tableaux clé/valeur ; le serveur reconstruit les tableaux imbriqués
La fenêtre envoie keys[]/values[] pour les tableaux plats et imbriqués et keys2[][]/values2[][] pour les listes d'objets. __process_jsoned_meta_data() reconstruit le tableau PHP, décode les valeurs qui sont du JSON valide et se rabat sur la valeur brute pour les chaînes simples. update_post_meta() le stocke ensuite sérialisé, comme WordPress l'attend. Le même chemin est utilisé par le travailleur de modification en masse.
Les indicateurs sont stockés comme 'yes'/'no' ou d'autres paires propres au siteDes règles de correspondance 'key:on^off' traduisent entre l'interrupteur 1/0 et les valeurs stockées
over_switcher_swicher_to_val() et over_switcher_val_to_swicher() lisent une chaîne de réglage telle que 'dist:yes,stest:true' et convertissent dans les deux sens, de sorte que l'interface à interrupteur fonctionne avec les valeurs écrites par d'autres plugins.
Galeries d'images conservées en méta sous forme de listes d'ID de pièces jointesType de méta galerie enregistré comme liste d'ID de pièces jointes séparés par des virgules
La fenêtre de galerie envoie un formulaire encodé en URL ; le gestionnaire détecte l'encodage, l'analyse avec parse_str(), le nettoie et stocke les ID sous forme de chaîne séparée par des virgules. Le travailleur de modification en masse accepte le même format.
Le contenu de l'éditeur de blocs ne peut pas être modifié dans une simple zone de texteArticle ouvert dans l'éditeur de blocs à l'intérieur d'une iframe ; contenu lu via wp.data
Option « Use gutenberg ». Pour les types de contenu qui utilisent l'éditeur de blocs, la fenêtre charge post.php avec wpbe_popup_editor=1 ; wp_editor_compatibility() masque les contrôles de publication et de barre latérale dans ce cadre et expose une fonction qui renvoie wp.data.select('core/editor').getEditedPostContent(). La page parente la lit et l'enregistre par la mise à jour de champ normale.
Contrôle d'accès et durcissement des requêtes
shipped · 2023Ce que cela apporteSeuls les rôles de confiance peuvent utiliser l'éditeur, les auteurs ne travaillent que sur leurs propres articles, et le personnel ne voit que les champs qu'il est autorisé à modifier.
- Problème
- Un outil capable de réécrire en masse n'importe quel champ de n'importe quel article est une cible de grande valeur : chaque point d'accès AJAX doit vérifier l'appelant, et les entrées de filtre aboutissent près du SQL.
- Solution
Une seule barrière de rôle avant l'enregistrement de tout point d'accès, des nonces sur les actions de champ, de modification en masse, d'outils et de réglages, des droits au niveau des champs pour les rôles non administrateurs, et du SQL préparé avec des listes d'autorisation de colonnes.
- Stack
Seuls les rôles de confiance peuvent atteindre un point d'accès de l'éditeurLe contrôle de rôle dans init() s'exécute avant l'ajout de tout gestionnaire wp_ajax_wpbe_*
can_manage_data() autorise les administrateurs, les éditeurs et les rôles ajoutés par le filtre wpbe_author_area_roles. S'il échoue, init() retourne avant le chargement des extensions, de sorte que les actions AJAX n'existent pas pour cet utilisateur. Les mises à jour de champs, la modification et la suppression en masse, le panneau d'outils (créer, dupliquer, supprimer, termes), les réglages méta, le formulaire d'options, la liste de l'historique et les annulations isolées vérifient aussi un nonce.
Les éditeurs ne devraient pas voir ni modifier tous les champs et types de contenuL'administrateur règle la visibilité des champs et les types de contenu autorisés pour les rôles non administrateurs (premium)
Pour les rôles non administrateurs, get_fields() retire les colonnes masquées, qui ne sont donc proposées ni dans le tableau ni dans le panneau de modification en masse ; update_page_field() vérifie is_current_user_can_edit_field() et renvoie 'forbidden' pour un champ masqué ; filter_post_types() restreint le sélecteur de type de contenu, et le constructeur des réglages déplace l'utilisateur vers un type de contenu autorisé. Dans la version gratuite, les cases de visibilité sont rendues désactivées.
Les auteurs ne devraient travailler que sur leurs propres articlesLes rôles de l'espace auteur reçoivent author=self dans les requêtes de l'éditeur et ne peuvent pas modifier post_author
Pour les rôles ajoutés par le filtre wpbe_author_area_roles, WPBE_AUTHOR_AREA ajoute 'author' => ID de l'utilisateur courant via le filtre de requête utilisé par le tableau, la liste d'ID de la modification en masse et l'export, de sorte que ces listes ne montrent que les propres articles de l'utilisateur. Son callback wpbe_user_can_edit bloque post_author pour ces rôles.
Les entrées de filtre et les données d'article copiées aboutissent dans le SQLNoms de colonnes issus de listes d'autorisation, chaque valeur liée par $wpdb->prepare()
Les filtres de texte et de plage n'acceptent que des colonnes de wp_posts listées et lient les valeurs via prepare(). Les requêtes d'historique lient l'utilisateur, le type de contenu et la clé de masse, et construisent les listes NOT IN à partir de paramètres réservés générés par array_fill().
La duplication copie les termes par slug et tous les postmeta sauf _wp_old_slug dans une seule instruction INSERT ... SELECT %d, %s, %s UNION ALL, chaque ligne étant construite avec prepare(), puis met la copie en brouillon.
Les champs de contenu doivent garder les contenus intégrés mais supprimer les scriptswp_kses avec la liste d'autorisation des articles plus une liste d'attributs iframe étroite
wpbe_update_page_field() nettoie les tableaux de façon récursive avec wp_kses et la liste d'autorisation des articles. Pour le contenu, l'extrait et les champs de texte enrichi, il ajoute iframe avec un ensemble fixe d'attributs afin que les vidéos intégrées survivent. Un filtre peut basculer vers sanitize_text_field() pour les sites plus stricts.
Chiffres
wordpress.org- Installations actives 4 000+ compteur de wordpress.org.
- Avis 27 Moyenne de 4.7 sur 5.
- Années de maintenance 7 Depuis 3 septembre 2019, dernière mise à jour 12 octobre 2026.