WOOT
WooCommerce-Plugin, das den Shop-Katalog als sortierbare, filterbare Tabellen mit „In den Warenkorb“ in jeder Zeile zeigt.
- Erstveröffentlichung
- Aktive Installationen
- 1.000+
- Bewertung
- Rezensionen lesen
Über mich
WOOT stellt einen WooCommerce-Katalog als Tabelle dar: eine Zeile pro Produkt, eine Spalte für jedes Produktfeld, Attribut, jede Taxonomie oder jeden Meta-Key und ein Warenkorb-Element in jeder Zeile. Tabellen werden im Admin-Bereich konfiguriert oder inline in einem [woot]-Shortcode beschrieben. Dieselbe Engine zeichnet auch den Warenkorb, Variationslisten, Upsells, Cross-Sells, verwandte Produkte und Einzelprodukt-Tabellen.
Schwierig ist, dass eine Spalte drei Aufgaben erfüllen muss: eine Zelle darstellen, die Serverabfrage sortieren und als Filter dienen. Produkte gibt es in mehreren Typen (einfach, variabel, gruppiert, extern, Variation), die sich in einer Zelle und im Warenkorb unterschiedlich verhalten. Alle Tabellendaten laufen über einen öffentlichen AJAX-Endpunkt, der bei großen Katalogen nutzbar bleiben muss und weder Entwürfe, nicht öffentliche Post-Typen noch Dateien auf dem Server preisgeben darf.
Die Tabellen-Engine im Browser (data-table-23.js) ist reines JavaScript mit ES6-Klassen und fetch. Sie cacht Seiten nach Request-Hash, bricht veraltete Requests ab, kann eine Tabelle in den Browser-Modus schalten und per SQL über ein JSON-Array abfragen und ändert das Layout für kleine Bildschirme. jQuery wird nur verwendet, um die eigenen Warenkorb-Events von WooCommerce auszulösen.
WOOT · Produkttabellen
was gelöst wurde
Serverseitige Tabellenabfragen
shipped · 2020Was es bringtKunden blättern, sortieren und filtern Produkttabellen nach Preis, SKU oder Lagerbestand in ihrer eigenen Sprache, und Preset-Listen wie Bestseller zeigen die genaue Anzahl an Artikeln.
- Problem
- Jeder Seitenwechsel, Sortierklick und jede Filteränderung kommt als ein AJAX-Request an. Daraus muss eine einzige WP_Query werden, die Katalogsichtbarkeit, Tabellen-Presets, Filter, die Sprache des Besuchers und Sortier-Keys berücksichtigt, die WP_Query nicht unterstützt.
- Lösung
get_table_data() liest eine Whitelist von Request-Keys, wendet Presets und Filter als Transformationen der Abfrageargumente an, patcht SQL nur dort, wo WP_Query nicht ausreicht, und liefert die Zeilen mit einer Gesamtzahl für die Paginierung zurück.
- Stack
Ein öffentlicher Endpunkt bedient jede Tabelle und jeden BesucherRequest-Keys auf eine Whitelist setzen, jeden nach Typ bereinigen, WP_Query-Argumente bauen, Zeilen und Anzahl zurückgeben
get_table_data() ist für angemeldete und anonyme Benutzer registriert. Es geht $_REQUEST per switch durch und behält nur bekannte Keys: post_type, per_page, orderby, order, current_page, table_id, wp_columns_actions, filter_data, filter_provider, fields, lang, predefinition und shortcode_args_set. Zahlen laufen durch intval, Keys durch sanitize_key, JSON-Nutzdaten durch json_decode und einen rekursiven Sanitizer.
Zwei dieser Keys sind Hook-Namen. wp_columns_actions wird an apply_filters() übergeben, um das Spaltenprofil zu laden, und filter_provider wählt den Filter woot_filter_provider_{name}, der die Filterbedingungen ergänzt. Beide werden als Text bereinigt, aber nicht gegen eine Liste registrierter Namen geprüft.
Dieselbe Funktion wird aus PHP mit einem Array für Tabellen aufgerufen, die beim Laden der Seite gerendert werden, sodass der Serverpfad und der AJAX-Pfad eine gemeinsame Implementierung nutzen. Jede Zeile entsteht durch den Aufruf der Render-Closure jeder angeforderten Spalte, wobei die Produkt-ID für Zellaktionen als pid erhalten bleibt.
Sortieren nach Preis, SKU oder Lagerbestand heißt nach postmeta zu sortierenSpalten deklarieren meta_key und orderby; die Abfrage bildet Sortier-Keys auf meta_value(_num) ab
Ein Spalteneintrag wie price trägt 'orderby' => 'meta_value_num' und 'meta_key' => '_price'. get_table_data() sucht den angeforderten Sortier-Key im Spaltenprofil und schreibt die Abfrageargumente danach um.
Das Sortier-Dropdown verwendet eigene Keys (price-desc, sku, stock-desc, sales und so weiter), die in einem woot_wp_query_args-Filter übersetzt werden. Sortierungen nach Lagerbestand und Gesamtverkäufen schließen gruppierte und externe Produkte mit einer product_type-NOT-IN-Klausel aus, da diese Typen keinen eigenen Lager- oder Verkaufswert haben. Für Dropdown-Sortierungen ruft das Plugin außerdem WC()->query->get_catalog_ordering_args() auf, das die eigenen Sortierklauseln von WooCommerce für Beliebtheit, Bewertung und Preis anhängt, sodass diese Sortierungen mit der Shopseite übereinstimmen.
WP_Query kann nicht nach post_status sortierenEinen posts_orderby-Filter nur für diese eine Abfrage anhängen und gleich nach der Abfrage wieder entfernen
Für orderby=post_status wird vor WP_Query eine Closure an posts_orderby gehängt und danach entfernt, sodass andere Abfragen im selben Request ihre eigene Sortierung behalten.
Top-N-Presets (Bestseller, Neuheiten) müssen genau N Produkte zeigenposts_per_page auf N begrenzen und found_posts auf N setzen, damit der Pager nur N Artikel zählt
Das Bestseller-Preset sortiert nach total_sales und das Neuheiten-Preset nach post_date, beide mit posts_per_page auf N. WP_Query meldet found_posts für alle passenden Produkte, daher wird found_posts nach der Abfrage auf das Preset-Limit gesetzt, und der Pager zeigt nur die obersten N.
Presets brauchen Ein- und Ausschluss über mehrere TaxonomienEinen kompakten Regelstring in eine verschachtelte tax_query parsen; die Relation für den Ausschluss umkehren
Eine Preset-Regel wird als String wie product_cat:25,26|pa_color:19|rel:AND gespeichert. get_by_taxonomy() macht daraus eine verschachtelte tax_query-Gruppe und überspringt Taxonomien, die nicht mehr existieren.
Bei der Ausschluss-Variante verwendet jede Klausel NOT IN, und die Relation wird vertauscht (aus AND wird OR und umgekehrt), was dem De Morganschen Gesetz entspricht: Produkte auszuschließen, die A und B treffen, heißt, jene zu behalten, die außerhalb von A oder außerhalb von B liegen. SKU-Presets bilden eine Variations-SKU auf die ID ihres Elternprodukts ab, weil die Tabelle Elternprodukte listet.
admin-ajax-Requests verlieren die Sprache des BesuchersDie Locale mit jedem Request senden und WPML vor Abfrage und Rendering darauf umschalten
admin-ajax.php läuft außerhalb des Sprachkontexts des Frontends. draw_table_data() schreibt get_locale() in das Tabellen-JSON, der Browser sendet sie als lang zurück, und get_table_data() reicht sie durch den Filter woot_current_lang.
Die Vokabular-Klasse hängt sich an diesen Filter und ruft $sitepress->switch_lang() auf, sodass WP_Query Produkte in der richtigen Sprache liefert. Spaltentitel und Beschriftungen übersetzt WOOT_Vocabulary::get() aus der eigenen Tabelle des Plugins, einmal pro Request geladen und nach Locale indiziert.
Spaltenmodell
shipped · 2020Was es bringtDer Betreiber wählt, welche Produktdetails als Tabellenspalten erscheinen, einschließlich der eigenen benutzerdefinierten Felder des Shops, und kann Tabellen-Setups kopieren oder verschieben.
- Problem
- Eine Spalte ist mehr als eine Zelle. Dasselbe Feld muss für jeden Produkttyp dargestellt werden, die Sortierung steuern und als Filter-Widget erscheinen, und Shop-Betreiber fügen eigene Taxonomien und Meta-Keys hinzu.
- Lösung
Jede Tabelle läuft auf einem Profil: einem Array von Feldbeschreibungen, deren Closures die Zelle darstellen, WP_Query-Argumente für das Filtern ändern und das Filter-Widget beschreiben. Taxonomien und Meta-Keys werden dem Profil zur Laufzeit hinzugefügt.
- Stack
Eine Felddefinition muss Zelle, Sortierung und Filter steuernProfil aus Beschreibungen mit Render-, Filterargument- und Filter-UI-Closures für jedes Feld
WOOT_WooCommerce::action() gibt das Profil zurück. Jeder Key ist ein Feld mit einem Titel, einer Action-Closure, die die Zelle für eine Produkt-ID darstellt, Sortierdaten (order, orderby, meta_key) und, bei filterbaren Feldern, den Closures get_filter_query_args und get_filter_draw_data. Index 0 enthält Einstellungen auf Tabellenebene: Post-Typ, erlaubte Status, Filter-Provider.
Profile werden unter einem Namen registriert und per apply_filters auf die Tabellen-Action aufgelöst. Der Warenkorb, Variationen, die Einzelproduktansicht und sogar die Admin-Bildschirme des Plugins (Tabellenliste, Meta-Keys, Vokabular) laufen mit eigenen Profilen auf derselben Engine.
Jede Produkt-Taxonomie und jedes Attribut soll eine Spalte sein könnenSpalten aus get_object_taxonomies('product') ohne interne, als Term-Bäume dargestellt
product_type, product_visibility, product_shipping_class und translation_priority von WPML werden aus der Liste entfernt. Die Zelle zeigt die eigenen Terms des Produkts über build_taxonomy_tree(), beschränkt auf die zugewiesenen Term-IDs, je nach Spaltenoption als Links oder Klartext.
Auf der Filterseite kommt eine tax_query-Klausel hinzu, deren Operator (IN, NOT IN, AND) aus den Tabelleneinstellungen stammt und gegen diese Whitelist geprüft wird.
Shop-Betreiber brauchen Spalten für beliebige Meta-KeysTypisierte Meta-Registry (Text, Zahl, Kalender) liefert für jeden Key Sortiermodus und Filter
Meta-Keys werden pro Tabelle in woot_tables_meta mit einem Typ gespeichert. assemble_meta_for_system() macht aus jedem ein Profilfeld: Text sortiert nach meta_value und filtert mit LIKE; Zahl sortiert nach meta_value_num und filtert mit einem numerischen BETWEEN über einen Bereichsregler; Kalender filtert als DATETIME oder als numerischer Unix-Timestamp, je Feld gewählt, weil Plugins wie ACF Datumswerte in unterschiedlichen Formaten speichern.
Die Felder werden über den Filter ext_{action} in das Profil eingefügt, und jeder Key erscheint auch im Sortier-Dropdown mit Einträgen für aufsteigend und absteigend.
Zeilen mal Spalten wiederholen dieselben Produkt- und OptionsabfragenStatische Caches auf Request-Ebene für Produkte, Tabellenoptionen, Spaltenzeilen und Meta-Zeilen
Jede Spalten-Closure braucht das WC_Product-Objekt, daher hält get_product() eine statische Map der wc_get_product()-Ergebnisse. Tabellenoptionen werden einmal pro Tabelle aus JSON dekodiert, Meta-Zeilen nach dem md5 des SQL-Strings gecacht, und der Filter-Builder löst das Profil einmal auf statt pro Feld.
Tabellenkonfigurationen brauchen eigenen Speicher, Klonen und ÜbertragungVier eigene Tabellen über dbDelta; Klonen kopiert Spalten und Meta; Import befüllt aus JSON neu
install.php legt woot_tables, woot_tables_columns (indiziert nach table_id und is_active, sortiert nach pos_num), woot_tables_meta und woot_vocabulary an. Die Spaltenreihenfolge per Drag-and-drop kommt als Liste von IDs an und wird als pos_num zurückgeschrieben.
clone() kopiert die Tabellenzeile, danach ihre Spalten und Meta-Keys unter der neuen Tabellen-ID. Der Export schreibt alle vier Tabellen und die Optionen als JSON; der Import leert jede Tabelle per Truncate und fügt die Zeilen ein, nach einer Prüfung von Berechtigung und Nonce.
Filtern
shipped · 2020Was es bringtKunden grenzen die Tabelle nach Bereichen, Datum, Kategorien und Text ein, und Tabellen folgen auf derselben Seite dem vorhandenen Filter-Plugin des Shops.
- Problem
- Filter kommen als flache Key/Value-Map vom Browser und müssen zu WP_Query-Argumenten werden: Meta-Bereiche, Taxonomie-Klauseln, Textsuche und Datumsbereiche. Shops, die bereits ein Filter-Plugin betreiben, erwarten, dass die Tabelle ihm folgt.
- Lösung
Ein Filter-Provider reicht jeden Key an die eigene Abfrage-Closure der Spalte weiter. Externe Filter-Plugins schließen sich als alternative Provider an, die eine Liste von Produkt-IDs zurückgeben.
- Stack
Ein Datumsbereichsfilter braucht zwei Eingaben für eine SpalteEin Kalenderfeld in key und key---to aufteilen; der Provider entfernt das Suffix und kehrt compare um
Beim Aufbau des Filterformulars wird ein Kalenderfeld unter einem ---to-Key mit den Beschriftungen Von und Bis dupliziert. Auf dem Rückweg sieht provider() einen unbekannten Key mit Endung ---to, entfernt das Suffix und ruft dieselbe Spalten-Closure mit einem Flag auf, sodass die Closure mit <= statt >= vergleicht.
Bereichsregler brauchen Grenzen, bevor ein Filter gesetzt istMIN und MAX des Meta-Keys in SQL berechnen, wenn der Admin die Grenzen nicht gesetzt hat
Hat ein Bereichsfeld kein konfiguriertes Minimum oder Maximum (-1), führt get_field_drawing_data() über $wpdb->prepare SELECT MIN oder MAX(cast(meta_value as UNSIGNED)) auf postmeta für den Meta-Key des Feldes aus. Der Funktionsname stammt aus einer internen Whitelist, nicht aus dem Request. Der Regler für die Kommentaranzahl liest seine Grenzen aus der Posts-Tabelle.
Die Textsuche muss gewählte Felder abdecken, ohne SQL-Injectionposts_where-Klausel mit esc_like und prepare; Feldliste auf drei Post-Spalten begrenzt
Die durchsuchbaren Felder stammen aus dem Shortcode, werden aber auf post_title, post_content und post_excerpt gefiltert. Jedes wird zu LOWER(field) LIKE %s, mit dem Wert durch esc_like maskiert und per prepare gebunden.
Im Browser löst eine Suche mit Enter nur aus, wenn die Eingabe ihre minlength erreicht, und ein leeres Enter oder Escape entfernt den Suchbegriff, wodurch kurze Wortfragmente den Server nicht erreichen.
Tabellen müssen auf derselben Seite dem Filter von HUSKY (WOOF) folgenDen Request von WOOF wiederholen, seine ID-Vorhersage ausführen und die IDs in post__in einspeisen
Mit filter_provider=woof erfasst der Shortcode die Request-Daten von WOOF im Tabellen-JSON. Bei jedem AJAX-Aufruf stellt der Provider daraus $_GET wieder her, fragt alle Ergebnisse ab, ruft woof_products_ids_prediction() auf und liest woof_wp_query_ids, wobei er ein Array oder einen kommagetrennten String akzeptiert und nur positive Ganzzahlen behält. Keine IDs bedeuten post__in = [0].
Der MDTF-Filterzustand muss den AJAX-Request erreichenMDTF-Zustand dekodieren, nur Abfrageargumente anfordern, eigene Flags entfernen, Produkt-IDs vorab holen
Der Parameter page_mdf wird dekodiert, und MDTF läuft mit mdf_get_query_args_only, sodass es WP_Query-Argumente statt HTML zurückgibt. Die meta-Klausel mdf_hide_post und die product_visibility-Klausel werden entfernt (WOOT fügt eigene hinzu), die Sortierung entfällt, und die Abfrage läuft mit fields=ids. Die ID-Liste reist als Filterdaten und wird zu post__in.
In den Warenkorb aus der Tabelle
shipped · 2020Was es bringtKunden kaufen jeden Produkttyp direkt aus der Tabelle, legen viele Zeilen auf einmal hinein, und der Warenkorb bleibt mit dem Rest des Shops im Gleichschritt.
- Problem
- Jeder Produkttyp braucht ein anderes Kaufelement, Mengen sind durch den Lagerbestand begrenzt, und der Tabellen-Warenkorb muss mit dem Mini-Warenkorb des Themes und der Warenkorbseite von WooCommerce synchron bleiben.
- Lösung
Der Server gibt pro Produkttyp eine kompakte Beschreibung aus, der Browser baut daraus die Elemente und sendet an den eigenen AJAX-Endpunkt von WooCommerce. Der Warenkorbinhalt wird von derselben Tabellen-Engine gezeichnet.
- Stack
Einfache, variable, gruppierte und externe Produkte brauchen unterschiedliche ElementeDer Server gibt eine typisierte data-about-Beschreibung aus; JS baut Eingabefeld, Checkbox oder Popup-Link
add_to_cart.php prüft is_purchasable() und is_in_stock() und verlangt bei verwaltetem Lagerbestand eine Menge ungleich null. Bei einfachen Produkten enthält die Beschreibung ID, Lagerbestand und SKU; der Client baut ein Zahleneingabefeld mit auf den Lagerbestand gesetztem max, bei einzeln verkauften Produkten schreibgeschützt, sowie eine Checkbox für das gesammelte Hinzufügen.
Variable und gruppierte Produkte erhalten einen Button, der eine Popup-Tabelle ihrer Kinder öffnet. Externe Produkte verlinken auf die externe URL. Unbekannte Typen laufen durch den Filter woot_woocommerce_add_to_cart, damit andere Plugins Markup liefern können.
Das Hinzufügen zum Warenkorb muss sich wie der eigene Button von WooCommerce verhaltenPOST an wc-ajax=add_to_cart, dann wc_fragment_refresh für Theme-Mini-Warenkörbe auslösen
Die Endpunkt-URL wird aus wc_add_to_cart_params übernommen, wenn WooCommerce sie ausgibt, sodass eigene AJAX-Endpunkte weiter funktionieren. Die Menge wird vor dem Senden gegen max geprüft. Nach Erfolg löst das Plugin wc_fragment_refresh aus, aktualisiert seine eigene Warenkorbinfo und leitet je nach Tabellenoption zum Warenkorb oder zur Kasse weiter.
Kunden wollen viele Zeilen mit einer Aktion hinzufügenMarkierte Zeilen und Mengen gehen in einem Request; Variationen erhalten Eltern-ID und Attribute
Eine Checkbox im Kopf der Warenkorbspalte wählt alle Zeilen aus. add_to_cart_some() durchläuft die gesendeten Artikel. Für eine Variation baut es die attribute_*-Map aus get_attributes('edit') neu auf und ruft WC()->cart->add_to_cart() mit der Eltern-ID, der Menge, der Variations-ID und dieser Map auf, also in der Form, die der WooCommerce-Warenkorb erwartet.
Variable Produkte brauchen eine Tabelle ihrer VariationenDer Filter-Provider schaltet post_type auf product_variation und begrenzt post__in auf die Kinder
WOOT_WooCommerceVariations::filter_provider() setzt post_type auf product_variation und post__in auf die Kinder des Elternprodukts, nachdem geprüft wurde, dass das Elternprodukt für den Besucher sichtbar ist. Upsell-, Cross-Sell-, gruppierte und verwandte Tabellen teilen sich die Basisklasse WOOT_WooCommerceUniversal und unterscheiden sich nur in der ID-Quelle.
In Variationszeilen werden an den Titel die Namen der Attribut-Terms angehängt, und Taxonomiespalten lesen den eigenen Attribut-Slug der Variation statt der dem Post zugewiesenen Terms.
Der Warenkorb selbst soll eine sortierbare Tabelle seinDie Closures des Warenkorbprofils nehmen Produkt-ID und Menge; die Engine zeichnet ihn im JSON-Modus
Das Profil woot_cart kombiniert die konfigurierten Produktspalten mit Menge, Zwischensumme (WC_Cart::get_product_subtotal) und Entfernen. Jede Warenkorbänderung antwortet mit einer frischen Warenkorbtabelle, Artikelanzahl und Zwischensumme. Entfernen und Aktualisieren finden die Warenkorbzeile über product_id oder variation_id.
Das Warenkorb-Popup übernimmt das Skin der Tabelle, die es geöffnet hat: Der Style-Block des Elternteils wird kopiert und seine Tabellen-ID durch die ID der Popup-Tabelle ersetzt. Eine Tabellenoption kann außerdem Produkte, die schon im Warenkorb liegen, über post__not_in ausblenden.
Tabellen-Engine im Browser
shipped · 2020Was es bringtTabellen reagieren beim Blättern und Sortieren schnell, mehrere Tabellen können sich eine Seite teilen, und breite Tabellen bleiben auf Smartphones nutzbar.
- Problem
- Blättern, Sortieren und Suchen müssen sich unmittelbar anfühlen, mehrere Tabellen mit unterschiedlichen Skins können sich eine Seite teilen, und breite Tabellen müssen auf Smartphones funktionieren.
- Lösung
data-table-23.js ist eine abhängigkeitsfreie Klasse pro Tabelle mit Request-Hash-Cache, Request-Abbruch, optionalem SQL-Modus im Browser und einem kompakten Layout-Wechsel.
- Stack
Eine bereits gesehene Seite oder Sortierung soll den Server nicht erneut belastenAntworten unter einem Hash des gesamten Request-Objekts cachen; bei Events leeren
__hash_object() serialisiert request_data zu JSON und faltet es zu einem 32-Bit-Hash, der als Cache-Key dient. Der Cache ist im JSON- und im Load-more-Modus aus, wo die Daten bereits lokal sind. Erweiterungen wie Vergleich und Favoriten lösen table23-reset-cache aus, wenn sich ihre Daten ändern.
Kleine Kataloge sollen ohne Round Trips sortieren und suchenDer Modus to_json lädt alle Zeilen einmal und sortiert, paginiert und durchsucht sie dann mit AlaSQL
Im Modus to_json liefert der Server jede Zeile (posts_per_page -1). Nach der ersten Antwort schaltet sich die Tabelle selbst in den json-Modus, und draw_data() baut SELECT ... WHERE ... ORDER BY ... LIMIT ... OFFSET und führt es mit AlaSQL über das Array aus. Eine im Cache gefundene einseitige Tabelle wird auf dieselbe Weise durchsucht und sortiert.
Eine langsame frühere Antwort darf eine neuere Seite nicht überschreibenDen vorherigen fetch mit AbortController abbrechen, bevor der nächste gesendet wird
Jede Tabelle hält ihren aktuellen AbortController. Ein neuer Seiten-, Sortier- oder Filter-Request bricht den ausstehenden ab, sodass eine langsame frühere Antwort die neuere Seite nicht mehr überschreiben kann.
Breite Tabellen müssen auf Smartphones in ein kompaktes Layout wechselnNur neu zeichnen, wenn die Breite den Breakpoint überschreitet; Resize-Handler für 999 ms gesperrt
Jede Tabelle hat ihre eigene compact_view_width. Der Resize-Listener vergleicht den neuen Zustand mit dem vorherigen und zeichnet nur bei einer Änderung neu; ein Sperr-Flag verhindert 999 ms lang ein erneutes Auslösen. Mit hide-on-mobile markierte Spalten werden beim Aufbau der Kopfzeilen übersprungen, der schwebende Header wird im Kompaktmodus abgeschaltet, und Media-Hide-Regeln pro Spalte werden als CSS für die HTML-ID der Tabelle erzeugt.
Mehrere Tabellen mit unterschiedlichen Skins auf einer SeiteSkin-CSS verwendet einen tid-Platzhalter, der beim Rendern durch die HTML-ID jeder Tabelle ersetzt wird
get_theme_css() liest das Skin zuerst aus dem Ordner woot-skins des Themes und danach aus dem Skins-Ordner des Plugins, ersetzt den tid-Platzhalter durch die HTML-ID der Tabelle und entfernt Kommentare und überflüssige Leerzeichen. Jede Regel ist auf eine Tabelle beschränkt, sodass Skins nicht zwischen Tabellen auf derselben Seite durchschlagen.
Zellen können eigene Tabellen enthaltenDie Konfiguration der verschachtelten Tabelle wird als JSON in einem versteckten div ausgeliefert und nach jedem Zeichnen instanziiert
draw_table_data() kodiert die Konfiguration mit JSON_HEX_QUOT und JSON_HEX_TAG, damit sie innerhalb von HTML stehen kann, ohne aus dem Element auszubrechen. Nachdem die Zeilen gezeichnet sind, findet do_after_draw() Tabellen in Zellen und erzeugt für jede eine WOOT_GeneratedTables-Instanz, ohne die horizontale Scrollleiste.
Sicherheit öffentlicher Endpunkte
im ProduktWas es bringtÖffentliche Tabellen zeigen nur Produkte, die sichtbar sein sollen, und Besucher können Tabellen-Requests nicht nutzen, um an versteckte Inhalte zu gelangen oder Code auszuführen.
- Problem
- Tabellendaten, Popups und Warenkorbaktionen werden anonymen Besuchern über admin-ajax ausgeliefert, und die Requests tragen Tabelleneinstellungen, Shortcode-Argumente, Skin-Namen und Hook-Namen.
- Lösung
Die Sichtbarkeit wird zur Abfragezeit und pro Post durchgesetzt, Namen, die in Pfade, CSS oder SQL gelangen, werden an einer Stelle auf sichere Zeichensätze reduziert, und jeder Admin-Schreibvorgang prüft Berechtigung und Nonce.
- Stack
Öffentliche Tabellen-Requests tragen einen vom Client gewählten Post-Typ und StatusFür Besucher den Status publish erzwingen; nicht öffentliche Typen nur für darauf aufgebaute Tabellen
Direkt bevor WP_Query läuft, wird post_status für jeden ohne read_private_posts auf publish zurückgesetzt, was auch immer Request, Shortcode oder Tabelleneinstellungen verlangt haben. Ein Status aus Shortcode-Argumenten wird nur für solche Benutzer beachtet.
Nicht einsehbare Post-Typen (Gutscheine, Bestellungen, Vorlagen) werden für Benutzer ohne edit_posts abgelehnt, es sei denn, die gespeicherte Tabelle wurde von einem Admin für diesen Post-Typ gebaut.
Popups und Shortcodes übernehmen eine Produkt-ID vom Clientwoot_can_read_post() prüft Status, Einsehbarkeit und bei Variationen das Elternprodukt
Eine Funktion entscheidet, ob ein Besucher die Daten eines Posts sehen darf: veröffentlichte Posts öffentlicher Typen sind offen, nicht öffentliche Typen brauchen Bearbeitungsrechte, andere Status brauchen read_post, und eine Variation ist nur sichtbar, wenn ihr Elternprodukt es ist. Einzelprodukt-, Variations-, Upsell- und andere Popup-Tabellen, der Galerie-Shortcode und die Erweiterungen rufen sie vor dem Rendern auf.
Skin-Namen kommen aus mehreren Quellen und landen in einem Dateipfadbasename() plus ein [A-Za-z0-9_-]-Filter an einer einzigen Engstelle; Tabellen-ID vor dem CSS gefiltert
Das Skin kann aus ?woot_skin=, Shortcodes, Erweiterungen oder Tabelleneinstellungen stammen, und alle laufen durch get_theme_css(). Dort wird der Name mit basename() und einem Zeichenfilter reduziert, der Punkte und Schrägstriche entfernt, sodass er nicht aus den Skin-Ordnern hinauszeigen kann. Die HTML-ID der Tabelle, die in das ausgegebene CSS geschrieben wird, wird auf dieselbe Weise gefiltert, damit geschweifte oder spitze Klammern nicht aus dem Style-Block ausbrechen können.
Der Popup-Endpunkt ruft Hooks und Shortcodes per Namen aufNur woot_-Hooks sind aufrufbar, call_id wird bereinigt, nur Plugin-Shortcodes werden gerendert
get_smth() ist öffentlich. Sein generischer Zweig ruft apply_filters mit einem Namen aus dem Request nur auf, wenn der Name mit woot_ beginnt, und die mitgesendete call_id wird auf Buchstaben, Ziffern, Unterstrich und Bindestrich reduziert, bevor eine nachgelagerte Abfrage sie sieht. Der Zweig shortcodes_set entfernt vor do_shortcode() jeden Shortcode, der nicht auf der Whitelist des Plugins steht. Die Zweige für Export und Import verlangen manage_woocommerce.
Admin-Schreibvorgänge und aus Spaltennamen gebautes SQLBerechtigung und Nonce bei jedem Schreibvorgang; SQL-Bezeichner auf [A-Za-z0-9_] reduziert
Die Handler für Tabellen, Spalten, Meta und Vokabular prüfen manage_woocommerce und woot_settings_nonce, bevor sie schreiben. Die Erweiterung für Anhänge ergänzt eine Anmeldeprüfung und edit_post-Prüfungen auf Objektebene.
Spaltennamen, ORDER-BY-Felder und WHERE-Keys lassen sich nicht mit prepare() binden, daher reduziert get_rows() sie auf einen sicheren Zeichensatz, begrenzt die Sortierung auf ASC oder DESC und die Verknüpfung auf AND oder OR und bindet nur Werte.
Zahlen
wordpress.org- Aktive Installationen 1.000+ Zähler von wordpress.org.
- Rezensionen 18 Durchschnitt 4.8 von 5.
- Jahre Support 6 Seit 4. September 2020, letztes Update 12. Oktober 2026.