Changelog iga-core
Note on terms: "mệnh" is the Vietnamese five-element (feng shui) type of a person. The five elements are Kim (Metal), Mộc (Wood), Thủy (Water), Hỏa (Fire) and Thổ (Earth).
0.5.33 - 2026-10-17
/chon-menh/: when every stone type has been chosen, the page opens showing all 62 types (the "Tất cả" (All) filter) so staff can review and edit them; a type not yet chosen still shows only that type.
0.5.32 - 2026-10-16
data/menh.json+data/stones.json: Quartz (type unknown), green quartz and rutilated quartz (color unknown) are assigned all 5 elements as chosen by the advisors (when the color is unknown, all are selected).
0.5.31 - 2026-10-16
/chon-menh/: by default shows only the stone types not yet chosen (the "Chưa chọn" (Not chosen yet) filter button comes first); as soon as a type is chosen it hides itself; when none are left it reports that everything has been chosen.
0.5.30 - 2026-10-16
data/menh.json+data/stones.json: applied the advisors' choices (a screenshot of the /chon-menh/ page at 23:12) for 23 stone types that differ from the color table: Topaz (Metal, Water); golden rutilated quartz, yellow quartz (Metal, Water, Earth); tiger-eye quartz, storm stone (Wood, Water); dragon-eye stone, fluorite (Wood, Fire, Earth); labradorite (Wood, Fire); pyrite (Metal, Water); all 5 elements: opal, zircon, beryl, ametrine, tri-color rutilated quartz, five-color quartz, Super 23, rainbow quartz, Super Seven, chalcedony, water-algae ice jade, amazonite, mica, Mix.
0.5.29 - 2026-10-16
/chon-menh/: choosing an element saves immediately (the note saves itself after 1.2 seconds), with no Xác nhận (Confirm) step that is easy to forget; a missing name is reported clearly and saved as soon as the name is entered; choices that could not be saved are kept in the browser (localStorage) and sent automatically when the page is reopened.
0.5.28 - 2026-10-16
data/menh.json+data/stones.json: "ưu linh" quartz (phantom quartz) and Super Seven quartz are assigned all 5 elements (no element to avoid, per the shop).
0.5.27 - 2026-10-16
data/menh.json+data/stones.json: ruby, sapphire, diamond, jadeite, amber, tourmaline and "ưu linh" quartz are assigned all 5 elements (fieldmenh+moi_menh: true) per the shop: gemstones have no element to avoid. This override wins even over the color in the product name (for example a yellow sapphire).
0.5.26 - 2026-10-16
- New internal page
/chon-menh/(classTools\Menh_Vote,assets/js/chon-menh.js,assets/css/chon-menh.css): advisors choose the suitable elements for each stone type indata/menh.json, with a note box and a field for the chooser's name. Saved throughGET/POST /wp-json/iga/v1/chon-menh, one optioniga_chon_menh__<slug>per stone type (autoload no, keeping 10 old versions). Only enabled whenWP_ENVIRONMENT_TYPEis staging orIGA_CHON_MENH= true; the page is noindex and not cached.
0.5.25 - 2026-10-16
Data\Menh::for_product: applies the stone type'smenhoverride when assigning to each product (before, onlystone_menhread this field, so 0.5.24 changed no product); the override wins even over the color written in the product name.
0.5.24 - 2026-10-16
data/menh.json: a stone type can override its element with themenhfield (Data\Menh::stone_menh); jadeite is assigned Wood only (Fire removed) per the advisors' opinion.
0.5.23 - 2026-10-16
- New command
wp iga menh(classCLI\Menh_Command, pure logic inData\Menh, table indata/menh.json): reassigns the "Mệnh hợp" (Suitable element) attribute (pa_menh) by the stone type's DOMINANT COLOR. A stone of element E suits element E and the element that E generates (red ruby: Fire, Earth; golden rutilated quartz: Metal, Earth; blue/black sapphire: Wood, Water). A multi-color stone type reads the color written right after the stone name in the product name; if there is none, it takes the element stated explicitly in the shop's description; if there is none either, it stays empty (no guessing).apply --dry-run [--report=<file>]gives statistics and lists products whose shop description states a different element from the new table;apply --backup-dir=<dir>backs uprows.jsonl+rows.sha256(read back to verify), then writesterm_relationshipsin one transaction, recounts the term counts, reindexes the filter for exactly the changed products, changes the filter cache version and reads back to compare;rollback --dir=<dir> [--force];tableprints the table. Running it again changes nothing more.
data/stones.json: themenhcolumn of the 62 stone types changed to followmenh.json(32 types changed;menh_sourceis now onlyruleornone; multi-color types are left empty instead of listing all 5 elements), with a matching test (tests/phpunit/Data/MenhTest.php).Schema\Product_Schema: removedhasMerchantReturnPolicy(a 3-day return MerchantReturnPolicy) andshippingDetails(an OfferShippingDetails with free shipping at 0 VND) from every Offer of Product/ProductGroup. The shop does not commit to returns, and production only gives free shipping when the customer pays by bank transfer in advance, so the schema must not make promises on the shop's behalf. An Offer with no price is still dropped as before.
0.5.22 - 2026-10-16
Content\Desc_Fix(the lead paragraph ofwp iga desc-fix) dropped two self-inferences: (1) "natural" is only written when the product NAME or a SEPARATE line of the short description says it is natural (the shop's general commitment line, which starts with a tick mark or contains "cam kết" (commitment) and appears in most short descriptions, no longer counts; new functionexcerpt_product_text()); (2) origin is only written as "originates from X" exactly as in the data (preferring the most specific entry such as "Phan Thiết (Bình Thuận)"), no longer "mined at", because production only has the label "Xuất xứ" (Origin). Tests updated + addedtest_sapo_natural_only_from_product_own_text.CLI\Lp5_Hero(the overview page /bo-suu-tap/): the intro sentences of the 3 groups take the production wording verbatim or just list the model names ("Tantric Buddhism proposes 8 Bản Tôn deities who govern the 12 zodiac animals"; removed "the most worshipped and worn" and "bringing luck, wealth and peace" because production does not have them).
0.5.21 - 2026-10-16
wp iga desc-fixv2 (a better-structured description, run on the ORIGINAL content): the LEAD paragraph is built from the product's real data (the name without a bold "#code", product type, motif, stone type, origin, stone group + an easy-to-understand introductory sentence taken from the short description); a NEW H2 names the old opening paragraphs ("Meaning of {motif}", "Meaning of the Buddha of your birth sign ...", "Meaning of ... stone", "About {name}"); the part A heading (motif) becomes an H3 under the opening H2, the heading that opens part B (stone type) is an H2 and the later sections are H3/H4; with no part B the old heading is an H3 (an H2 is kept when there are 3 or more major topics of the same level). A description that already opens with a lead paragraph is skipped (idempotent).- Option
--original-from=<backup folder>(only together with--dry-run): preview on the original content taken from therows.jsonlof an earlier run without having to roll back the DB. Content\Headings::retag()/step()were split out to assign heading levels by their own rule; the testtests/phpunit/Content/DescFixTest.phpwas rewritten for v2.
0.5.20 - 2026-10-16
- New command
wp iga desc-fix(classCLI\Desc_Fix_Command, pure logic inContent\Desc_Fix): normalizes product descriptions (post_content) directly in the DB.- Removes a title line at the start that repeats the product name (any plain-text h1; h2/h3 only when they repeat the name; section headings such as "Ý nghĩa ..." (Meaning of ...) or "... là gì?" (What is ...?) are kept).
- Builds the lead paragraph: if the next block is a
<blockquote>it is converted into<p>elements (the first p getsclass="sapo"); if it is a<p>/bare text of 60 characters or more it getsclass="sapo"; otherwise it takes the first introductory line of the short description (not a "Label: value" line, not a commitment line with a tick mark, 40 characters or more) as<p class="sapo">. - Headings: h1 becomes h2, the highest level is h2, with no skipped levels (an opening h3 becomes h2, an h4 after an h2 becomes h3).
- New command
wp iga headings-fix [--type=page,post](classCLI\Headings_Fix_Command, pure logic inContent\Headings): the same heading rules for published pages and posts. For pages built with blocks: changeslevelin thewp:headingcomment and the HTML tag together, checked withparse_blocksbefore/after (same block count, level matches the tag). Skips WooCommerce pages; an empty heading or one that holds only an image is not changed (only noted). - Both commands share
CLI\Content_Fix_Base:--dry-run(statistics + before/after samples), a backup ofrows.jsonloutside the webroot (read-back check + hash) before writing, writing with$wpdb->updatein one transaction and then reading back to compare hashes,clean_post_cachefor each ID,--rollback=<folder>(hash check per row), and running it again changes nothing more. Every row goes through a before/after text check; a row that fails is left untouched. - Tests:
tests/phpunit/Content/HeadingsTest.php,DescFixTest.php.
0.5.19 - 2026-10-15 (batch G1)
- New command
wp iga image-swap(classCLI\Image_Swap_Command, pure logic inMedia\Image_Swap): replaces images taken from the internet with AI images in thepost_contentandpost_excerptof posts/products.import --dir=<folder>: imports new images into the Library withwp media import(title, Vietnamese alt text, caption "Ảnh AI tạo (G1)" (AI-generated image (G1))), and sets the meta_iga_g1_job; running it again does not import duplicates.swap [--dry-run|--apply --backup-dir=<outside the webroot>]: replaces the original-size URL and every-WxHsize (src, srcset, percent-encoded form), thewp-image-<id>class and the"id":<id>of thewp:imageblock. Banners useproductsin the manifest to change only in the right product group. Backs uprows.jsonl(read-back check + hash), writes in one transaction, and reads back to compare hashes before the COMMIT; it does not delete the old images and does not changepost_modified.rollback --dir=<backup folder> [--force]: restores and checks the hash of each row.- A job with
supersedes(a redo) also replaces the AI image of the earlier version; a job withfeatured_postsalso changes the featured image (_thumbnail_id) of those posts (backs upthumbs.json, and the rollback checks the values). - The job list:
data/image-swap/g1.json(cumulative across rounds). Tests:tests/phpunit/Media/ImageSwapTest.php.
0.5.18 - 2026-10-15
- Fixed the "Xem thêm" (See more) button not being clickable in a group that has a search box (the button took the "Không có lựa chọn phù hợp" (No matching option) line as the list).
0.5.17 - 2026-10-15
- Price ranges: the label "nghìn" (thousand) was changed to "k" (
data/price-buckets.json); the "Giá liên hệ" (Contact for price) option is not shown (but is still kept when an old URL has it selected, and is still counted in the price coverage rate).
0.5.16 - 2026-10-15
- A filter group with more than 10 options has a search box (typing without diacritics still matches, and while typing the entries after "Xem thêm" (See more) are shown too; the typed text is kept when the filter group is redrawn).
sort: "alpha"(Vietnamese A-Z throughCollatorvi_VN) for Stone type and Motif.- Removed the Origin (Xuất xứ) filter group from every
type_facetsset (the definition is kept so old URLs withxuat-xustill filter).
0.5.15 - 2026-10-15
- The Stone type, Craft (Chế tác) and Motif (Hình tượng) filters show a flat list (
uilist) instead of a group tree;Query_Engine::term_options()drops the pure group termsg-*from the flat list (unless one is selected through the URL). Origin stays a tree.
0.5.14 - 2026-10-14 (batch LP5)
wp iga page-fixesstep 9 (Lp5_Hero::enable_load_more): turns on loadMore for theiga/product-gridblocks, keeping columns and rows; running it again changes nothing.
0.5.13 - 2026-10-14 (batch LH1)
wp iga page-fixesstep 10 (Lh1_Map): on thelien-he(contact) page, replaces the HTML block containing a Google Maps iframe (plus the 15px spacer block right after it) with the theme'siga/store-mapblock (coordinates, name, address and place_id of the "Infinity Gems An Thịnh" Google profile). Only writes when there is exactly one Google Maps frame and no map block yet; running it again changes nothing. Backs up outside the webroot, checks hashes, and rolls back with--rollback.
0.5.12 - 2026-10-14 (batch F2)
- Removed the
ton-kho(Availability) filter group:data/facets.json,Query_Engine(kindstock),Index_Builder(no longer writes aton-khorow).Archive::redirect_legacy_params()(template_redirect) 301-redirects a URL containington-khoto the URL without that parameter;Requestignores the parameter. Index_Builder::PRIORITY_AVAILABLE/PRIORITY_SOLD:stock_priorityis now only 1 (on sale, including onbackorder) and 3 (outofstock);Indexer::reconcile()fixes mismatches under the new rule.Schema::DB_VERSION2: the upgrade step deletes theton-khoindex rows, changesstock_priority2 and 4 to 1, and raises the filter cache version.- New class
Filter\Stock_Order: the query variableiga_sold_last;posts_clausesadds a LEFT JOIN on theiga_product_sorttable (primary key product_id; if there is no index yet it uses the meta_stock_status) and inserts the sold-out expression at the start of ORDER BY (so pagination is correct). Measured: a JOIN on postmeta is 10-40 times slower (postmeta has no index on post_id + meta_key), while a JOIN on the sort table is not; it turns on automatically for the main product queries (search, tag, attribute, category, shop);posts_pre_queryreorders the search results of the tandoanh-code engine. - The old product grid shortcode (
Compat\FlatsomeShortcodes::sc_products) turns oniga_sold_last.
0.5.11 - 2026-10-14 (batch LP5)
wp iga page-fixesstep 8 (Lp5_Hero::strip_dup_heading): removes the first heading in the content that duplicates the hero's H1, and the spacer block right after it; running it again changes nothing.
0.5.9 - 2026-10-14 (batch LP5)
wp iga page-fixes: step 6 adds a vertical image focal point keyed by the image key (AI_FY) and swaps the image when the image or the focal point changes; step 7 updates theimageIdof the 6 stone cards on the home page to the newest AI image.
0.5.8 - 2026-10-14 (batch LP5)
wp iga page-fixesstep 7 (Lp5_Hero): places aniga/collection-gridblock of typestone"Bộ sưu tập theo loại đá" (Collection by stone type) in the spot of the old "Chọn theo loại tác phẩm" (Choose by type of piece) block on the home page and removes the old block (if the stone block is already elsewhere it is moved back); if the old block is gone it is inserted before the "Top sản phẩm nổi bật" (Top featured products) grid; skipped if it is already in the right place or the AI images are missing. Backup and rollback work like the earlier steps.
0.5.4 - 2026-10-14 (batch P3D)
- New
Portfolio\Portfolio: re-registers the post typefeatured_item, the categoryfeatured_item_category, the tagfeatured_item_tagand the Flatsome Portfolio URL rules (/bo-suu-tap/<category slug>/,/bo-suu-tap/<comma-joined category slugs>/<item slug>/,/featured_item/). It only runs when this post type does not exist yet; the URL prefix can be changed with the filteriga_core_portfolio_base. It writes nothing to the DB. After copying the plugin onto a site you must runwp rewrite flush(as the www-data user) once. Forms\Ask_Price: the price-inquiry keybst-<ID>for 3D jewelry models (publishedfeatured_itemposts);subject()andmessage()take an extra noun (default "sản phẩm" (product), for 3D models "mẫu trang sức 3D" (3D jewelry model)). WooCommerce products are unchanged.- Test:
tests/phpunit/Portfolio/PortfolioTest.php.
0.5.3 - 2026-10-14 (batch Q2)
Editor\Searchkeepstype=post, shows the date/category and only warns when a post has no featured image (the 1200 px warning was removed).wp iga page-fixes: task 1c adjusts theiga/news-feedblock of /kien-thuc/ (showBreadcrumb, showHeading, showFeatured, showChips = false; excludeCategories = the category with slugkhuyen-mai) and theexcludeCategoriesof the banner. Backup/rollback like the earlier tasks.
0.5.2 - 2026-10-14 (batch Q2)
Editor\Search: a new kindtype=postfor the post picker of theiga/news-bannerblock (diacritic-insensitive filter on the titles of published posts, cached for 5 minutes; returns the date, category, a small image, and a warning when the post has no featured image or the image is under 1200 px).wp iga page-fixes: added task 1b: if thekien-thucpage already has aniga/news-feedblock but no banner, it inserts aniga/news-bannerblock (the newest posts with a large image, 5 posts, 7 seconds) at the very start of the content, leaving the rest as it is. It has a backup outside the webroot, a hash check and a rollback like the earlier tasks; running it again changes nothing.
0.5.1 - 2026-10-13 (batch Q1)
assets/js/iga-filter.js(+.min.js): the state class on<html>when the drawer opens was renamed tois-filter-open/is-sort-open(the old name matched the open button's class, so the button CSS was applied to<html>and broke the page at 1100px and below).- New WP-CLI command
wp iga page-fixes [--dry-run|--apply --backup-dir=<dir>|--rollback=<dir>]: (1) thekien-thucpage is rebuilt with a singleiga/news-feedblock (only written while it still shows signs of the old version: alinear-gradientbanner origa/news-teaser, and noiga/news-feedyet); (2) aniga/product-gridblock whoseheadingrepeats the page title text has itsheadingremoved (the H1 and H2 are no longer the same). Backs up JSON +ROLLBACK.sqloutside the webroot, checks the hash of each file, checks that slug/title/status/meta are unchanged after writing, and rolls back with--rollback. Reusable at go-live.
Chưa phát hành (Unreleased)
Fixed (batch L8, security and SEO)
Ip::client: behind Cloudflare it only trustsCF-Connecting-IP(before, it also readX-Forwarded-For/X-Real-IPthat a visitor could send through Cloudflare, so changing IP could dodge the rate limit); behind an internal proxy it takes the right-most untrusted hop ofX-Forwarded-For. AddedIp::bucket(): IPv6 is grouped by /64 inRate_Limiter(also applied toForms\Throttle).Filter\Seo::print_canonical: filter/sort URLs are both noindex,follow and carry a canonical to the clean URL (Rank Math drops the canonical tag on noindex pages, so the old canonical filter had no effect).Editor\Search: the routeiga/v1/editor-searchdoes not return unpublished products to a person who has onlyedit_posts(checksread_post).- Tests:
IpTest(CF ignores XFF/X-Real-IP, right-most hop, IPv6 /64),RateLimiterTest(the same /64 shares one counter).
0.5.0 - 2026-10-13
Batch LP3: landing pages that can be edited the hungphat way (the right column shows the right settings panel for each section).
Added
wp iga blocks-upgrade --dry-run | --apply --backup-dir=<dir> | --rollback=<dir>: upgrades raw block pages (core/group, core/cover, core/heading, core/spacer produced bywp iga ux2blocks) intoiga/*blocks. It only recognizes known patterns and leaves unknown ones alone; the home page (page_on_front) is never touched.- A. A pair of banner covers (one
iga-hide-smfor desktop, oneiga-show-smfor phones) becomes a one-slideiga/hero-slider(desktop image, phone image, image focal point, "Thấp" (Low) style). If the phone text equals the desktop text they are merged; if it differs it is kept asmobileTitle. A desktop banner standing alone is left as it is (converting it would make it also appear on phones). - B. An h2 heading (with its spacing) placed before an
iga/product-gridthat has no heading yet becomes the grid'sheadingattribute ("Gọn" (Compact) style); a heading with nested tags, h3 or deeper, or an unknown class is not merged. - C. A plain
iga-sec/iga-colgroup that, after B, holds onlyiga/*blocks loses its wrapper. - It only writes a page that passes every check: text matches 100% (using the text extractor of
Ux\Text), the set of links, image IDs and URLs, product grid settings, the H1 count unchanged, block names registered, and the block sequence re-reads stably. After writing it checks that slug, title, status and meta (including Rank Math and template) are unchanged; on an error it restores that page by itself. - Backup OUTSIDE the webroot before writing:
pages/<ID>.json(content and all meta, with a hash),ROLLBACK.sql,posts-rows.sql.gz(a mysqldumpREPLACE INTOof exactly the post rows to be written, read back to verify),manifest.json,report.json. The DB password for mysqldump goes through a temporary option file with chmod 600, deleted immediately. The old version is still in the revisions. IGA\Core\Upgrade\Transformer(pure PHP on the block array),IGA\Core\Upgrade\Checker,IGA\Core\CLI\Blocks_Upgrade_Command. PHPUnittests/phpunit/Upgrade.
- A. A pair of banner covers (one
Ux\Text: the attribute keymobileTitlecounts as display text.
Changed
Ux\Runner::meta_hashdoes not countrank_math_og_content_image(Rank Math's OG image cache recomputes itself when the page is viewed, which skewed the meta hash on rollback).
0.4.1 - 2026-10-13
Batch L6: public forms (contact, price inquiry, checkout). Writes nothing to the DB, and no data command is needed when going to production.
Added
IGA\Core\Forms\Phone: correct Vietnamese phone number validation (mobile 03x 05x 07x 08x 09x according to the prefix ranges currently issued, landlines 02x of 11 digits, the forms +84, 84, 0084,+84 (0) ..., accepting spaces, dots, hyphens and parentheses). It replaces the broken regex/.../gof the old Flatsome child theme (the g flag does not exist in PCRE, so the function always returned false). Attached towpcf7_validate_telandwpcf7_validate_tel*with a clear error message that gives an example.IGA\Core\Forms\Turnstile: Cloudflare Turnstile for CF7 through CF7 6.x's built-in integration. The keys come from the CONSTANTSIGA_TURNSTILE_SITEKEYandIGA_TURNSTILE_SECRETin wp-config.php (filterswpcf7_turnstile_sitekey,wpcf7_turnstile_secret); the real keys are not stored in the DB. When the keys exist, the form drops the "10 x 5 = ?" quiz block and adds[turnstile theme:dark size:flexible]before the submit button (the form template is rewritten at runtime, without editing the form in the DB); when there are no keys the quiz stays as before.IGA\Core\Forms\Throttle: rate limiting by IP and by group (contact 5 times/10 minutes, orders 12 times/10 minutes, logins 10 times/10 minutes; filterable throughiga_core_throttle_limits), reusingFilter\Rate_LimiterandFilter\Ip(it only trusts the Cloudflare headers when the connection comes from a Cloudflare/internal range). The counter: persistent object cache if available (Redis), otherwise an expiring transient. The contact form counts after the field checks pass and before the Cloudflare call; checkout only counts a valid order attempt (it does not block a customer who mistypes).IGA\Core\Forms\Ask_Price:/lien-he/?hoi-gia=<product code>pre-fills the subjectHỏi giá sản phẩm <name> (<code>)(Price inquiry for product<name>(<code>)) and the message... <link>for the CF7 form (default field namesyour-subject,your-message; changeable throughiga_core_ask_fields) together with a line "Bạn đang hỏi giá" (You are asking for the price of). The GET parameter is only a lookup key (letters, digits, . _ -, at most 64 characters); the text filled in comes from the DB, so there is no XSS path. A page with the parameter does not go into the page cache.IGA\Core\Forms\Comment_Guard: comments posted straight to wp-comments-post.php are limited to 3 times/10 minutes/IP (returns 429).IGA\Core\Forms\Woo_Guard: rate limiting for Woo orders and logins. No captcha is used at checkout (every captcha failure loses an order).- CF7 only loads its JS (and the Turnstile api.js) on pages that have a form (
wpcf7_load_js, the filteriga_core_cf7_neededfor other places). - The phone field of the form raises
maxlengthfrom 11 to 20 (numbers with spaces or +84), keepingminlength. - PHPUnit tests
tests/phpunit/Forms(47 phone number cases, rate limiting, form rewriting, price inquiry).
0.4.0 - 2026-10-13
Batch LP v2: live search for the block editor.
Added
IGA\Core\Editor\Search: RESTGET /iga/v1/editor-search?type=product|product_cat|product_tag|category|term[&tax=pa_*]&q=..|ids=..|slugs=..&limit<=20, with theedit_postscapability. Products use tandoanh-code's diacritic-insensitive Vietnamese search engine (index, cache) when available, otherwise a name and SKU query that ignores diacritics; it returns a small image, name, code, price or "Giá liên hệ" (Contact for price), and stock. Terms are filtered in place without diacritics (cached for 5 minutes). Returned text does not use long dashes. Verified with PHPUnittests/phpunit/Editor.
Chưa phát hành (Unreleased)
Fixed (batch L8, security and SEO)
Ip::client: behind Cloudflare it only trustsCF-Connecting-IP(before, it also readX-Forwarded-For/X-Real-IPthat a visitor could send through Cloudflare, so changing IP could dodge the rate limit); behind an internal proxy it takes the right-most untrusted hop ofX-Forwarded-For. AddedIp::bucket(): IPv6 is grouped by /64 inRate_Limiter.Filter\Seo::print_canonical: filter/sort URLs are both noindex,follow and carry a canonical to the clean URL (Rank Math drops the canonical tag on noindex pages, so the old canonical filter had no effect).Editor\Search: the routeiga/v1/editor-searchdoes not return unpublished products to a person who has onlyedit_posts(checksread_post).- Tests:
IpTest(CF ignores XFF/X-Real-IP, right-most hop, IPv6 /64),RateLimiterTest(the same /64 shares one counter).
Changed
- Filter JS: added
assets/js/iga-filter.min.js(esbuild; 22.2 KB down to about 11 KB), with no behavior change.Assets::enqueueloads the minified build when the file exists andSCRIPT_DEBUGis off; the file name in uploads carries the mtime so cache-busting is automatic.
Added
IGA\Core\Schema\Product_Schema(batch L3): adds Product schema for Rank Math, replacing the hard-coded snippet of the old Flatsome child theme. MerchantReturnPolicy (3-day return, applicableCountry VN) and OfferShippingDetails are attached to every Offer; a product with a price of 0 or empty outputs no Offer; adds a VideoObject for products that have a YouTube video, withProduct.videopointing to it. Filtersiga/schema/return_policy,iga/schema/shipping.
0.2.1 - 2026-10-13
Batch L2 (category page UI) needs a few small things in the filter module.
Added
- Filter
iga/filter/mobile_breakpoint(default 959): the point where it switches to a drawer; the renderer writesdata-iga-bpinto the filter column and the JS reads it instead of a fixed number. - Filter
iga/filter/grid_html: the theme changes the empty state and prioritizes loading the first card image (LCP) on both the page-load path and the AJAX path. - Filter
iga/filter/card_version: a card markup version stamp that goes into the card cache key, so when the theme/card changes the old cards are no longer used. - JS: supports multiple chip areas
[data-iga-chips](the filter column and above the grid) and an error frame supplied by the theme ([data-iga-error],[data-iga-error-msg],[data-iga-retry]), shown right in the results area even when the drawer is closed. - A selected chip has a group name prefix ("Loại đá: Sapphire" (Stone type: Sapphire)).
Fixed
- The lightweight endpoint does not load Rank Math, so product and category URLs in the AJAX response were WooCommerce's original URLs (
/sản phẩm/slug/,/danh-muc-san-pham/cha/con/), different from the URLs on the page and needing a 301. The endpoint now rebuilds the same/slug/URLs according to Rank Math's settings (it only reads the optionrank-math-options-general); the JS changes the base part of the response URLs (pushState, pagination links) to the page'sdata-iga-base. - The counts in the filter column and the number of ticked options no longer have parentheses (the theme styles them itself).
0.3.0 - 2026-10-13
Batch LP: converting UX Builder to blocks and a Flatsome compatibility layer.
Added
wp iga ux2blocks --dry-run|--apply [--ids=..] [--backup-dir=..] [--out-dir=..] [--no-home] [--home-file=..]and--rollback=<folder> [--force]. It parses Flatsome shortcodes (nested several levels, double-quoted/single-quoted/unquoted attributes,]inside values, NBSP between attributes, unclosed shortcodes, stray closing tags) and converts them to core blocks (heading, paragraph, list, quote, image, gallery, columns/column, group, cover, buttons, details, spacer, separator, YouTube embed, html, shortcode) andiga/*blocks (ux_products becomesiga/product-gridwith the same source, ux_slider becomes a banner-styleiga/hero-slider, blog_posts becomesiga/news-teaser).- Text is kept word for word: the text is compared before/after on every page, and only written when it matches 100% (ignoring whitespace) and the blocks are valid.
[block id=N]becomes a reusablewp_blockblock (meta_iga_ux_block) so editing in one place changes every page. The home page (page_on_front) is replaced bythemes/iga/data/pages/home.blocks.html. - Safety: backs up post_content + all meta of each page to a folder OUTSIDE the webroot and checks hashes before writing, writes
post_content/post_modifieddirectly (with a revision of the old version; it does not change the slug, title, template or Rank Math meta), checks again after writing and restores a failed page by itself,ROLLBACK.sql+--rollbackwith a hash check after restoring. IGA\Core\Compat\FlatsomeShortcodes: minimal rendering of section/row/col/gap/title/button/ux_image/ux_banner/ux_slider/ux_products/ux_gallery/block/blog_posts... only when the shortcode does not exist (Flatsome is not running), so a raw shortcode is never shown; the filteriga_core_flatsome_compat_tags.- The action
iga_core_ux2blocks_page_writtenafter each page is written (to clear the cache).
0.2.0 - 2026-10-13
Batch F1: writing filter data and the AJAX filter module.
Added
wp iga extract --apply [--batch=200] [--only=<group>] --backup-dir=<folder>: writes the taxonomiespa_loai-san-pham,pa_loai-da,pa_nhom-da,pa_che-tac,pa_hinh-tuong,pa_menh,pa_xuat-xu,pa_chat-lieu(registering the WooCommerce attribute if it does not exist) and the measurement meta_iga_*. Idempotent, in batches inside a transaction, does not changepost_modified, does not touch descriptions, only writes high and medium confidence, skips attributes already used as variations, and keeps existing unknown terms.- A backup before writing (compressed file, a manifest with hashes and row counts, table checksums), a
ROLLBACK.sqlbuilt before writing;wp iga extract --rollback=<folder>runs in one transaction and then compares the checksums. - The element-by-stone-color table is editable in the admin (option
iga_core_menh_overrides, D-12) and is read by the extractor. - The
IGA\Core\Filterfilter module (forked from hungphat product-filter v6.12.1): the tables{prefix}iga_filter_indexand{prefix}iga_product_sort, an indexer that rebuilds in batches without TRUNCATE, reindexes on save, stock change or price change (including direct meta writes), an hourly reconcile, and a rebuilding flag together with DONOTCACHEPAGE. - A shared
Query_Enginefor page loads through URLs (replacing the main query) and AJAX; AND between groups, OR within a group, and counts calculated excluding the current group so choosing one value does not hide other values in the same group. - Filter groups: the
pa_*above, price in the 7 ranges of D-09 and a "Giá liên hệ" (Contact for price) group (D-02, always placed last when sorting by price),g-*measurement ranges (height, weight, carat, bead size), stock, Has video; configured per product type indata/facets.json(D-10). - A lightweight endpoint
runtime/iga-filter.php(copied to the webroot) + the mu-pluginruntime/iga-fast-filter.php: a limit of 60 requests per minute per IP (429), Redis response cache with a site prefix and version, and no nonce needed for visitors. assets/js/iga-filter.js(no jQuery): pushState, popstate, AJAX pagination that updates the URL, a drawer with a focus trap, a "Xem N kết quả" (View N results) button; sorting is a button.- Canonical to the clean URL and
noindex,followfor filter URLs (Rank Math and wp_robots). - Admin page iga-core > Bộ lọc (Filters): index status, a rebuild button, data coverage, unmapped values, a list needing manual fixes (D-14), the element table, broken YouTube links, settings.
IGA\Core\Video\ProductVideo::get()andiga_product_video(): YouTube video from the Flatsome metawc_productdata_options, independent of the theme; the filteriga/product_video.- Template tags for the theme:
iga_filter_toolbar(),iga_filter_sidebar(),iga_filter_results(),iga_filter_pagination(); the filteriga/filter/card_htmllets the theme replace the product card. - The command
wp iga filter rebuild|reindex|reconcile|status|query.
Notes
- Measurement meta names:
_iga_cao_cm,_iga_trong_luong_g,_iga_carat,_iga_size_hat_ly... - The index tables are created when the plugin is activated and only deleted when the plugin is uninstalled (uninstall.php); deactivating does not delete data.
0.1.0 - 2026-10-13
- Plugin skeleton, the attribute extractor (
wp iga extract --dry-run), mapping tables for stone type, element, motif, origin and material.