Changelog iga-core

Lịch sử thay đổi plugin iga-core.

IGA (Infinity Gems)Cập nhật 21/10/2026

Changelog iga-core

0.5.33 - 2026-10-17

  • /chon-menh/: khi mọi loại đá đã chọn, trang mở ra hiện đủ 62 loại (lọc "Tất cả") để nhân viên xem lại và sửa; còn loại chưa chọn thì vẫn chỉ hiện loại đó.

0.5.32 - 2026-10-16

  • data/menh.json + data/stones.json: Thạch anh (chưa rõ loại), Thạch anh xanh, Thạch anh tóc (chưa rõ màu) gán đủ 5 mệnh theo nhân viên tư vấn (không rõ màu thì chọn đủ).

0.5.31 - 2026-10-16

  • /chon-menh/: mặc định chỉ hiện loại đá chưa chọn (nút lọc "Chưa chọn" đứng đầu), chọn xong loại nào thì loại đó tự ẩn; hết thì báo đã chọn xong.

0.5.30 - 2026-10-16

  • data/menh.json + data/stones.json: áp lựa chọn của nhân viên tư vấn (ảnh chụp trang /chon-menh/ 23:12) cho 23 loại đá khác bảng màu: Topaz (Kim, Thủy); Thạch anh tóc vàng, Thạch anh vàng (Kim, Thủy, Thổ); Thạch anh mắt chim ưng, Đá giông bão (Mộc, Thủy); Đá mắt rồng, Fluorit (Mộc, Hỏa, Thổ); Ngọc đế quang (Mộc, Hỏa); Pyrite (Kim, Thủy); đủ 5 mệnh: Opal, Zircon, Beryl, Ametrine, Thạch anh tóc tam tài, ngũ sắc, Super 23, cầu vồng, Super Seven, Chalcedony, Băng ngọc thủy tảo, Amazonite, Mica, Mix.

0.5.29 - 2026-10-16

  • /chon-menh/: bấm chọn mệnh là lưu ngay (ghi chú tự lưu sau 1,2 giây), không còn bước Xác nhận dễ bị quên; thiếu tên thì báo rõ và tự lưu khi nhập tên; lựa chọn chưa lưu được giữ trong trình duyệt (localStorage) và tự gửi khi mở lại trang.

0.5.28 - 2026-10-16

  • data/menh.json + data/stones.json: Thạch anh ưu linh, Thạch anh super seven gán đủ 5 mệnh (không kỵ mệnh, theo cửa hàng).

0.5.27 - 2026-10-16

  • data/menh.json + data/stones.json: Ruby, Sapphire, Kim cương, Cẩm thạch, Hổ phách, Tourmaline, Thạch anh ưu linh gán đủ 5 mệnh (trường menh + moi_menh: true) theo cửa hàng: đá quý không kỵ mệnh. Ghi đè thắng cả màu trong tên sản phẩm (vd Sapphire vàng).

0.5.26 - 2026-10-16

  • Trang nội bộ /chon-menh/ (lớp Tools\Menh_Vote, assets/js/chon-menh.js, assets/css/chon-menh.css): nhân viên tư vấn chọn mệnh hợp cho từng loại đá trong data/menh.json, có ô ghi chú và tên người chọn. Lưu qua GET/POST /wp-json/iga/v1/chon-menh, mỗi loại đá một option iga_chon_menh__<slug> (autoload no, giữ 10 bản cũ). Chỉ bật khi WP_ENVIRONMENT_TYPE là staging hoặc IGA_CHON_MENH = true; trang noindex, không cache.

0.5.25 - 2026-10-16

  • Data\Menh::for_product: áp dụng ghi đè menh của loại đá khi gán cho từng sản phẩm (trước đó chỉ stone_menh đọc trường này nên 0.5.24 không đổi sản phẩm nào); ghi đè thắng cả màu viết trong tên sản phẩm.

0.5.24 - 2026-10-16

  • data/menh.json: loại đá có thể ghi đè mệnh bằng trường menh (Data\Menh::stone_menh); cẩm thạch chỉ gán Mộc (bỏ Hỏa) theo ý kiến nhân viên tư vấn.

0.5.23 - 2026-10-16

  • Lệnh mới wp iga menh (lớp CLI\Menh_Command, logic thuần ở Data\Menh, bảng ở data/menh.json): gán lại thuộc tính "Mệnh hợp" (pa_menh) theo MÀU CHỦ ĐẠO của loại đá. Đá màu hành E hợp mệnh E và hành mà E sinh ra (Ruby đỏ: Hỏa, Thổ; thạch anh tóc vàng: Kim, Thổ; Sapphire xanh dương/đen: Mộc, Thủy). Loại đá nhiều màu đọc màu viết ngay sau tên đá trong tên sản phẩm; không có thì lấy mệnh ghi rõ trong mô tả shop; không có nữa thì để trống (không đoán).
    • apply --dry-run [--report=<file>] thống kê và liệt kê sản phẩm có mô tả shop ghi mệnh khác bảng mới; apply --backup-dir=<dir> sao lưu rows.jsonl + rows.sha256 (đọc ngược kiểm) rồi ghi term_relationships trong một giao dịch, tính lại số đếm term, reindex bộ lọc đúng các sản phẩm đổi, đổi phiên bản cache lọc, đọc lại so khớp; rollback --dir=<dir> [--force]; table in bảng. Chạy lại không đổi gì thêm.
  • data/stones.json: cột menh của 62 loại đá đổi theo menh.json (32 loại đổi; menh_source chỉ còn rule hoặc none; nhiều màu để trống thay vì ghi đủ 5 mệnh), có kiểm thử so khớp (tests/phpunit/Data/MenhTest.php).
  • Schema\Product_Schema: bỏ hasMerchantReturnPolicy (MerchantReturnPolicy đổi trả 3 ngày) và shippingDetails (OfferShippingDetails giao hàng miễn phí 0 VND) khỏi mọi Offer của Product/ProductGroup. Shop không cam kết đổi trả, và prod chỉ miễn phí giao hàng khi khách chuyển khoản trước nên schema không được hứa thay shop. Offer không có giá vẫn bị bỏ như cũ.

0.5.22 - 2026-10-16

  • Content\Desc_Fix (sapo của wp iga desc-fix) bỏ hai chỗ tự suy: (1) "tự nhiên" chỉ ghi khi TÊN sản phẩm hoặc một dòng RIÊNG của mô tả ngắn nói là tự nhiên/thiên nhiên (dòng cam kết chung của shop bắt đầu bằng dấu tick hoặc có chữ "cam kết" nằm ở hầu hết mô tả ngắn nên không còn tính; hàm mới excerpt_product_text()); (2) xuất xứ chỉ ghi "có xuất xứ từ X" đúng dữ liệu (ưu tiên mục cụ thể nhất như "Phan Thiết (Bình Thuận)"), không còn "khai thác tại" vì prod chỉ có nhãn "Xuất xứ". Kiểm thử cập nhật + thêm test_sapo_natural_only_from_product_own_text.
  • CLI\Lp5_Hero (trang tổng /bo-suu-tap/): câu giới thiệu 3 nhóm lấy nguyên văn prod hoặc chỉ liệt kê tên mẫu ("Phật giáo mật tông đưa ra thuyết 8 vị Bản Tôn chủ quản 12 con giáp"; bỏ "được thờ và đeo nhiều nhất" và "mang may mắn, tài lộc và bình an" vì prod không có).

0.5.21 - 2026-10-16

  • wp iga desc-fix v2 (cấu trúc mô tả chuẩn hơn, chạy đúng trên nội dung GỐC): SAPO dựng từ dữ liệu thật của sản phẩm (tên không "#mã" in đậm, loại sản phẩm, hình tượng, loại đá, xuất xứ, nhóm đá + một câu giới thiệu dễ hiểu từ mô tả ngắn); H2 MỚI đặt tên cho cụm đoạn mở đầu cũ ("Ý nghĩa {hình tượng}", "Ý nghĩa Phật bản mệnh ...", "Ý nghĩa đá ...", "Giới thiệu {tên}"); heading phần A (hình tượng) là H3 dưới H2 mở đầu, heading mở phần B (loại đá) là H2 và các mục sau là H3/H4; không có phần B thì heading cũ là H3 (giữ H2 khi từ 3 chủ đề lớn cùng bậc). Mô tả đã mở đầu bằng sapo thì bỏ qua (idempotent).
  • Tuỳ chọn --original-from=<thư mục sao lưu> (chỉ cùng --dry-run): xem trước trên nội dung gốc lấy từ rows.jsonl của một lần chạy trước mà không phải hoàn tác DB.
  • Content\Headings::retag()/step() tách ra để lập bậc heading theo luật riêng; kiểm thử tests/phpunit/Content/DescFixTest.php viết lại cho v2.

0.5.20 - 2026-10-16

  • Lệnh mới wp iga desc-fix (lớp CLI\Desc_Fix_Command, logic thuần ở Content\Desc_Fix): chuẩn hoá mô tả sản phẩm (post_content) thẳng trong DB.
    • Bỏ dòng tiêu đề lặp tên sản phẩm ở đầu (mọi h1 chữ thuần; h2/h3 chỉ khi lặp tên; tiêu đề mục như "Ý nghĩa ..." hoặc "... là gì?" được giữ).
    • Dựng sapo: khối kế tiếp là <blockquote> thì chuyển thành các <p> (p đầu class="sapo"); là <p>/chữ trần từ 60 ký tự thì gắn class="sapo"; còn lại lấy dòng giới thiệu đầu tiên (không phải "Nhãn: giá trị", không phải dòng cam kết có dấu tick, từ 40 ký tự) của mô tả ngắn làm <p class="sapo">.
    • Heading: h1 thành h2, cấp cao nhất là h2, không nhảy cấp (h3 mở đầu thành h2, h4 sau h2 thành h3).
  • Lệnh mới wp iga headings-fix [--type=page,post] (lớp CLI\Headings_Fix_Command, logic thuần ở Content\Headings): cùng luật heading cho trang và bài viết đang xuất bản. Trang dựng bằng khối: đổi đồng bộ level trong chú thích wp:heading và thẻ HTML, kiểm bằng parse_blocks trước/sau (cùng số khối, bậc khớp thẻ). Bỏ qua trang WooCommerce; heading rỗng hoặc chỉ ảnh không đổi (chỉ ghi chú).
  • Cả hai lệnh dùng chung CLI\Content_Fix_Base: --dry-run (thống kê + mẫu trước/sau), sao lưu rows.jsonl ngoài webroot (kiểm đọc ngược + băm) trước khi ghi, ghi bằng $wpdb->update trong một giao dịch rồi đọc lại so băm, clean_post_cache từng ID, --rollback=<thư mục> (kiểm băm từng hàng), chạy lại không đổi gì thêm. Mỗi hàng qua bước kiểm chữ trước/sau; hàng không đạt bị bỏ nguyên.
  • Kiểm thử: tests/phpunit/Content/HeadingsTest.php, DescFixTest.php.

0.5.19 - 2026-10-15 (lô G1)

  • Lệnh mới wp iga image-swap (lớp CLI\Image_Swap_Command, logic thuần ở Media\Image_Swap): thay ảnh lấy từ mạng bằng ảnh AI trong post_content và post_excerpt của bài/sản phẩm.
    • import --dir=<thư mục>: nhập ảnh mới vào Thư viện bằng wp media import (tiêu đề, alt tiếng Việt, chú thích "Ảnh AI tạo (G1)"), gắn meta _iga_g1_job; chạy lại không nhập trùng.
    • swap [--dry-run|--apply --backup-dir=<ngoài webroot>]: đổi URL cỡ gốc và mọi cỡ -WxH (src, srcset, dạng mã hoá phần trăm), lớp wp-image-<id> và "id":<id> của khối wp:image. Banner dùng products trong manifest để chỉ đổi ở đúng nhóm sản phẩm. Sao lưu rows.jsonl (kiểm đọc ngược + băm), ghi trong một giao dịch, đọc lại so băm rồi mới COMMIT; không xoá ảnh cũ, không đổi post_modified.
    • rollback --dir=<thư mục sao lưu> [--force]: khôi phục và kiểm băm từng hàng.
    • Job có supersedes (bản làm lại) thay luôn ảnh AI của bản trước; job có featured_posts đổi cả ảnh đại diện (_thumbnail_id) của các bài đó (sao lưu thumbs.json, hoàn tác kiểm giá trị).
    • Danh sách job: data/image-swap/g1.json (cộng dồn các đợt). Kiểm thử: tests/phpunit/Media/ImageSwapTest.php.

0.5.18 - 2026-10-15

  • Sửa nút "Xem thêm" không bấm được ở nhóm có ô tìm (nút lấy nhầm dòng "Không có lựa chọn phù hợp" làm danh sách).

0.5.17 - 2026-10-15

  • Mức giá: nhãn "nghìn" đổi thành "k" (data/price-buckets.json); không hiện lựa chọn "Giá liên hệ" (vẫn giữ khi URL cũ đang chọn, vẫn tính tỉ lệ phủ giá).

0.5.16 - 2026-10-15

  • Nhóm lọc có hơn 10 lựa chọn có ô tìm (gõ không dấu vẫn ra, đang gõ thì hiện cả mục sau "Xem thêm"; giữ chữ đã gõ khi nhóm lọc được vẽ lại).
  • sort: "alpha" (A-Z tiếng Việt qua Collator vi_VN) cho Loại đá và Hình tượng.
  • Bỏ nhóm lọc Xuất xứ khỏi mọi bộ type_facets (giữ định nghĩa để URL cũ có xuat-xu vẫn lọc được).

0.5.15 - 2026-10-15

  • Bộ lọc Loại đá, Chế tác, Hình tượng hiện danh sách phẳng (ui list) thay cây nhóm; Query_Engine::term_options() bỏ các term nhóm thuần g-* khỏi danh sách phẳng (trừ khi đang chọn qua URL). Xuất xứ giữ dạng cây.

0.5.14 - 2026-10-14 (lô LP5)

  • wp iga page-fixes bước 9 (Lp5_Hero::enable_load_more): bật loadMore cho các khối iga/product-grid, giữ cột và hàng; chạy lại không đổi gì.

0.5.13 - 2026-10-14 (lô LH1)

  • wp iga page-fixes bước 10 (Lh1_Map): trang lien-he thay khối HTML chứa iframe Google Maps (kèm khối đệm 15px ngay sau) bằng khối iga/store-map của theme (toạ độ, tên, địa chỉ, place_id của hồ sơ Google "Infinity Gems An Thịnh"). Chỉ ghi khi đúng một khung Google Maps và chưa có khối bản đồ; chạy lại không đổi gì. Sao lưu ngoài webroot, kiểm băm, hoàn tác bằng --rollback.

0.5.12 - 2026-10-14 (lô F2)

  • Bỏ nhóm lọc ton-kho (Tình trạng): data/facets.json, Query_Engine (kind stock), Index_Builder (không còn ghi dòng ton-kho). Archive::redirect_legacy_params() (template_redirect) 301 URL có ton-kho về URL không có tham số đó; Request bỏ qua tham số.
  • Index_Builder::PRIORITY_AVAILABLE/PRIORITY_SOLD: stock_priority chỉ còn 1 (còn bán, gồm onbackorder) và 3 (outofstock); Indexer::reconcile() sửa lệch theo quy tắc mới.
  • Schema::DB_VERSION 2: bước nâng cấp xoá dòng index ton-kho, đổi stock_priority 2 và 4 thành 1, tăng phiên bản cache bộ lọc.
  • Lớp mới Filter\Stock_Order: biến truy vấn iga_sold_last; posts_clauses thêm LEFT JOIN bảng iga_product_sort (khoá chính product_id; chưa có index thì dùng meta _stock_status) và chèn biểu thức đã-bán vào đầu ORDER BY (phân trang đúng). Đo: JOIN postmeta chậm gấp 10-40 lần (postmeta không có chỉ mục post_id + meta_key), JOIN bảng sắp xếp thì không; tự bật cho truy vấn chính của sản phẩm (tìm kiếm, thẻ, thuộc tính, danh mục, cửa hàng); posts_pre_query xếp lại kết quả tìm kiếm của engine tandoanh-code.
  • Shortcode lưới sản phẩm cũ (Compat\FlatsomeShortcodes::sc_products) bật iga_sold_last.

0.5.11 - 2026-10-14 (lô LP5)

  • wp iga page-fixes bước 8 (Lp5_Hero::strip_dup_heading): bỏ heading đầu nội dung trùng H1 của hero và khối spacer ngay sau; chạy lại không đổi gì.

0.5.9 - 2026-10-14 (lô LP5)

  • wp iga page-fixes: bước 6 thêm điểm giữ ảnh dọc theo khoá ảnh (AI_FY) và đổi ảnh khi ảnh hoặc điểm giữ đổi; bước 7 cập nhật imageId của 6 thẻ stone ở trang chủ theo ảnh AI mới nhất.

0.5.8 - 2026-10-14 (lô LP5)

  • wp iga page-fixes bước 7 (Lp5_Hero): đặt khối iga/collection-grid kiểu stone "Bộ sưu tập theo loại đá" vào chỗ khối cũ "Chọn theo loại tác phẩm" của trang chủ và gỡ khối cũ (nếu khối stone đã nằm chỗ khác thì dời về); không còn khối cũ thì chèn trước lưới "Top sản phẩm nổi bật"; bỏ qua nếu đã đúng chỗ hoặc thiếu ảnh AI. Sao lưu và hoàn tác như các bước cũ.

0.5.4 - 2026-10-14 (lô P3D)

  • Mới Portfolio\Portfolio: đăng ký lại kiểu bài featured_item, danh mục featured_item_category, thẻ featured_item_tag và luật địa chỉ của Flatsome Portfolio (/bo-suu-tap/<slug danh mục>/, /bo-suu-tap/<slug danh mục nối dấu phẩy>/<slug mẫu>/, /featured_item/). Chỉ chạy khi chưa có kiểu bài này; phần đầu địa chỉ đổi được bằng filter iga_core_portfolio_base. Không ghi DB. Sau khi chép plugin lên một site phải chạy wp rewrite flush (người dùng www-data) một lần.
  • Forms\Ask_Price: khoá hỏi giá bst-<ID> cho mẫu trang sức 3D (bài featured_item đã đăng); subject() và message() nhận thêm từ gọi đối tượng (mặc định "sản phẩm", mẫu 3D là "mẫu trang sức 3D"). Sản phẩm Woo không đổi.
  • Test: tests/phpunit/Portfolio/PortfolioTest.php.

0.5.3 - 2026-10-14 (lô Q2)

  • Editor\Search giữ type=post, hiển thị ngày/chuyên mục và chỉ cảnh báo khi bài chưa có ảnh đại diện (bỏ cảnh báo 1200 px).
  • wp iga page-fixes: việc 1c chỉnh khối iga/news-feed của /kien-thuc/ (showBreadcrumb, showHeading, showFeatured, showChips = false; excludeCategories = chuyên mục slug khuyen-mai) và excludeCategories của banner. Sao lưu/hoàn tác như các việc cũ.

0.5.2 - 2026-10-14 (lô Q2)

  • Editor\Search: kiểu mới type=post cho ô chọn bài của khối iga/news-banner (lọc không dấu trên tiêu đề các bài đã đăng, cache 5 phút; trả kèm ngày, chuyên mục, ảnh nhỏ và cảnh báo khi bài chưa có ảnh đại diện hoặc ảnh dưới 1200 px).
  • wp iga page-fixes: thêm việc 1b, trang kien-thuc đã có khối iga/news-feed mà chưa có banner thì chèn khối iga/news-banner (bài mới nhất có ảnh lớn, 5 bài, 7 giây) ngay đầu nội dung, giữ nguyên phần còn lại. Có sao lưu ngoài webroot, kiểm băm, hoàn tác như các việc cũ; chạy lại không đổi gì.

0.5.1 - 2026-10-13 (lô Q1)

  • assets/js/iga-filter.js (+ .min.js): class trạng thái trên <html> khi mở ngăn kéo đổi thành is-filter-open / is-sort-open (tên cũ trùng class của nút mở nên CSS nút áp lên <html> và làm vỡ trang từ 1100px trở xuống).
  • Lệnh WP-CLI mới wp iga page-fixes [--dry-run|--apply --backup-dir=<dir>|--rollback=<dir>]: (1) trang kien-thuc dựng lại bằng một khối iga/news-feed (chỉ ghi khi còn dấu hiệu bản cũ: banner linear-gradient hoặc iga/news-teaser, chưa có iga/news-feed); (2) khối iga/product-grid có heading trùng chữ tiêu đề trang thì bỏ heading (H1 và H2 không còn giống nhau). Sao lưu JSON + ROLLBACK.sql ngoài webroot, kiểm băm từng file, kiểm slug/tiêu đề/trạng thái/meta không đổi sau khi ghi, hoàn tác bằng --rollback. Dùng lại được khi go-live.

Chưa phát hành

Sửa (lô L8, bảo mật và SEO)

  • Ip::client: qua Cloudflare chỉ tin CF-Connecting-IP (trước đây còn đọc X-Forwarded-For/X-Real-IP khách tự gửi xuyên Cloudflare nên đổi IP né được giới hạn tần suất); qua proxy nội bộ lấy hop phải nhất không tin cậy của X-Forwarded-For. Thêm Ip::bucket(): IPv6 gộp theo /64 trong Rate_Limiter (cũng áp cho Forms\Throttle).
  • Filter\Seo::print_canonical: URL lọc/sắp xếp vừa noindex,follow vừa có canonical về URL sạch (Rank Math bỏ thẻ canonical trên trang noindex nên filter canonical cũ không có tác dụng).
  • Editor\Search: route iga/v1/editor-search không trả sản phẩm chưa công khai cho người chỉ có edit_posts (kiểm read_post).
  • Test: IpTest (CF bỏ qua XFF/X-Real-IP, hop phải nhất, IPv6 /64), RateLimiterTest (cùng /64 chung bộ đếm).

0.5.0 - 2026-10-13

Lô LP3: trang landing sửa được kiểu hungphat (cột phải hiện đúng bảng thiết lập của từng phần).

Thêm

  • wp iga blocks-upgrade --dry-run | --apply --backup-dir=<dir> | --rollback=<dir>: nâng cấp trang block thô (core/group, core/cover, core/heading, core/spacer do wp iga ux2blocks sinh ra) thành khối iga/*. Chỉ nhận ra khuôn đã biết, khuôn lạ để nguyên; trang chủ (page_on_front) không bao giờ bị đụng.
    • A. Cặp cover băng rôn (một cái iga-hide-sm cho máy tính, một cái iga-show-sm cho điện thoại) thành một iga/hero-slider một slide (ảnh máy tính, ảnh điện thoại, điểm giữ ảnh, kiểu "Thấp"). Chữ điện thoại trùng chữ máy tính thì gộp; khác thì giữ làm mobileTitle. Băng rôn máy tính đứng một mình giữ nguyên (chuyển sẽ làm nó hiện thêm trên điện thoại).
    • B. Tiêu đề h2 (kèm khoảng cách) đứng trước iga/product-grid chưa có tiêu đề thành thuộc tính heading của lưới (kiểu "Gọn"); tiêu đề có thẻ lồng, h3 trở đi hoặc lớp lạ không gộp.
    • C. Nhóm iga-sec/iga-col trơn mà sau B chỉ còn khối iga/* thì bỏ lớp bọc.
    • Chỉ ghi trang đạt mọi phép kiểm: chữ khớp 100% (bộ trích chữ của Ux\Text), tập liên kết, ID và URL ảnh, thiết lập lưới sản phẩm, số H1 không đổi, tên khối đã đăng ký, chuỗi khối đọc lại ổn định. Sau ghi kiểm slug, tiêu đề, trạng thái, meta (gồm Rank Math, template) không đổi; lỗi thì tự khôi phục trang đó.
    • Sao lưu NGOÀI webroot trước khi ghi: pages/<ID>.json (nội dung và toàn bộ meta, có băm), ROLLBACK.sql, posts-rows.sql.gz (mysqldump REPLACE INTO đúng các hàng bài viết sẽ ghi, đọc lại kiểm), manifest.json, report.json. Mật khẩu DB cho mysqldump đi qua file tuỳ chọn tạm chmod 600, xoá ngay. Bản cũ còn trong revision.
    • IGA\Core\Upgrade\Transformer (thuần PHP trên mảng khối), IGA\Core\Upgrade\Checker, IGA\Core\CLI\Blocks_Upgrade_Command. PHPUnit tests/phpunit/Upgrade.
  • Ux\Text: khoá thuộc tính mobileTitle tính là chữ hiển thị.

Đổi

  • Ux\Runner::meta_hash không tính rank_math_og_content_image (bộ nhớ đệm ảnh OG Rank Math tự tính lại khi trang được xem, làm lệch băm meta khi hoàn tác).

0.4.1 - 2026-10-13

Lô L6: form công khai (liên hệ, hỏi giá, thanh toán). Không ghi gì vào DB, không cần chạy lệnh dữ liệu khi lên prod.

Thêm

  • IGA\Core\Forms\Phone: kiểm tra số điện thoại Việt Nam đúng (di động 03x 05x 07x 08x 09x theo dải đầu số đang cấp, số bàn 02x 11 số, dạng +84, 84, 0084, +84 (0) ..., chấp nhận khoảng trắng, chấm, gạch ngang, ngoặc). Thay đoạn regex hỏng /.../g của child theme Flatsome cũ (cờ g không có trong PCRE nên hàm luôn trả false). Gắn vào wpcf7_validate_tel và wpcf7_validate_tel* với câu lỗi rõ ví dụ.
  • IGA\Core\Forms\Turnstile: Cloudflare Turnstile cho CF7 qua tích hợp sẵn của CF7 6.x. Khoá lấy từ HẰNG SỐ IGA_TURNSTILE_SITEKEY và IGA_TURNSTILE_SECRET trong wp-config.php (filter wpcf7_turnstile_sitekey, wpcf7_turnstile_secret), không lưu khoá thật vào DB. Có khoá thì form bỏ khối quiz "10 x 5 = ?" và thêm [turnstile theme:dark size:flexible] trước nút gửi (viết lại mẫu form lúc chạy, không sửa form trong DB); chưa có khoá thì giữ quiz như cũ.
  • IGA\Core\Forms\Throttle: giới hạn tần suất theo IP và theo nhóm (liên hệ 5 lần/10 phút, đặt hàng 12 lần/10 phút, đăng nhập 10 lần/10 phút; lọc qua iga_core_throttle_limits), dùng lại Filter\Rate_Limiter và Filter\Ip (chỉ tin header Cloudflare khi kết nối đến từ dải Cloudflare/nội bộ). Bộ đếm: object cache bền vững nếu có (Redis), không thì transient có hạn. Form liên hệ đếm sau khi qua kiểm tra trường và trước bước gọi Cloudflare; thanh toán chỉ đếm lần đặt hợp lệ (không chặn khách gõ sai).
  • IGA\Core\Forms\Ask_Price: /lien-he/?hoi-gia=<mã sp> điền sẵn chủ đề Hỏi giá sản phẩm <tên> (<mã>) và nội dung ... <link> cho form CF7 (tên trường mặc định your-subject, your-message; đổi qua iga_core_ask_fields) kèm dòng "Bạn đang hỏi giá". Tham số GET chỉ là khoá tra cứu (chữ, số, . _ -, tối đa 64 ký tự); chữ điền vào lấy từ DB nên không có đường XSS. Trang có tham số không vào cache trang.
  • IGA\Core\Forms\Comment_Guard: bình luận gửi thẳng tới wp-comments-post.php giới hạn 3 lần/10 phút/IP (trả 429).
  • IGA\Core\Forms\Woo_Guard: giới hạn đặt hàng và đăng nhập Woo. Không dùng captcha ở thanh toán (mỗi lần captcha lỗi là mất một đơn).
  • CF7 chỉ nạp JS (và api.js của Turnstile) ở trang có form (wpcf7_load_js, filter iga_core_cf7_needed cho vị trí khác).
  • Ô điện thoại của form nới maxlength 11 lên 20 (số có khoảng trắng hoặc +84), giữ minlength.
  • Test PHPUnit tests/phpunit/Forms (số điện thoại 47 ca, giới hạn tần suất, viết lại form, hỏi giá).

0.4.0 - 2026-10-13

Lô LP v2: ô tìm trực tiếp cho trình soạn thảo block.

Thêm

  • IGA\Core\Editor\Search: REST GET /iga/v1/editor-search?type=product|product_cat|product_tag|category|term[&tax=pa_*]&q=..|ids=..|slugs=..&limit<=20, quyền edit_posts. Sản phẩm dùng engine tìm tiếng Việt không dấu của tandoanh-code (chỉ mục, cache) khi có, không thì truy vấn tên và SKU không phân biệt dấu; trả ảnh nhỏ, tên, mã, giá hoặc "Giá liên hệ", tồn kho. Term lọc tại chỗ không dấu (cache 5 phút). Chữ trả về không dùng gạch dài. Kiểm bằng PHPUnit tests/phpunit/Editor.

Chưa phát hành

Sửa (lô L8, bảo mật và SEO)

  • Ip::client: qua Cloudflare chỉ tin CF-Connecting-IP (trước đây còn đọc X-Forwarded-For/X-Real-IP khách tự gửi xuyên Cloudflare nên đổi IP né được giới hạn tần suất); qua proxy nội bộ lấy hop phải nhất không tin cậy của X-Forwarded-For. Thêm Ip::bucket(): IPv6 gộp theo /64 trong Rate_Limiter.
  • Filter\Seo::print_canonical: URL lọc/sắp xếp vừa noindex,follow vừa có canonical về URL sạch (Rank Math bỏ thẻ canonical trên trang noindex nên filter canonical cũ không có tác dụng).
  • Editor\Search: route iga/v1/editor-search không trả sản phẩm chưa công khai cho người chỉ có edit_posts (kiểm read_post).
  • Test: IpTest (CF bỏ qua XFF/X-Real-IP, hop phải nhất, IPv6 /64), RateLimiterTest (cùng /64 chung bộ đếm).

Đổi

  • JS bộ lọc: thêm assets/js/iga-filter.min.js (esbuild; 22,2 KB xuống còn khoảng 11 KB), không đổi hành vi. Assets::enqueue nạp bản rút gọn khi có file và không bật SCRIPT_DEBUG; tên file trong uploads mang mtime nên cache-bust tự động.

Thêm

  • IGA\Core\Schema\Product_Schema (lô L3): bổ sung schema Product cho Rank Math, thay đoạn hard-code của child theme Flatsome cũ. MerchantReturnPolicy (đổi trả 3 ngày, applicableCountry VN) và OfferShippingDetails gắn vào mọi Offer; sản phẩm giá 0 hoặc trống không xuất Offer; thêm VideoObject cho sản phẩm có video YouTube, Product.video trỏ tới nó. Filter iga/schema/return_policy, iga/schema/shipping.

0.2.1 - 2026-10-13

Lô L2 (giao diện danh mục) cần vài chỗ nhỏ trong module bộ lọc.

Thêm

  • Filter iga/filter/mobile_breakpoint (mặc định 959): điểm chuyển sang drawer; renderer ghi data-iga-bp vào cột lọc, JS đọc thay cho số cố định.
  • Filter iga/filter/grid_html: theme đổi trạng thái rỗng và ưu tiên tải ảnh thẻ đầu (LCP) trên cả đường tải trang lẫn AJAX.
  • Filter iga/filter/card_version: dấu phiên bản markup thẻ đưa vào khoá cache thẻ, đổi theme/thẻ thì thẻ cũ không còn được dùng.
  • JS: hỗ trợ nhiều vùng chip [data-iga-chips] (cột lọc và trên lưới) và khung lỗi do theme cấp ([data-iga-error], [data-iga-error-msg], [data-iga-retry]), hiện ngay trên vùng kết quả kể cả khi drawer đóng.
  • Chip đang chọn có tiền tố tên nhóm ("Loại đá: Sapphire").

Sửa

  • Endpoint nhẹ không nạp Rank Math nên URL sản phẩm và danh mục trong phản hồi AJAX là URL gốc của WooCommerce (/sản phẩm/slug/, /danh-muc-san-pham/cha/con/), khác URL trên trang và phải qua 301. Endpoint nay dựng lại cùng URL /slug/ theo cài đặt của Rank Math (chỉ đọc option rank-math-options-general); JS đổi phần gốc URL phản hồi (pushState, liên kết phân trang) về data-iga-base của trang.
  • Số đếm trong cột lọc và số lựa chọn đang tick không còn dấu ngoặc (theme tự định kiểu).

0.3.0 - 2026-10-13

Lô LP: chuyển UX Builder sang block và lớp tương thích Flatsome.

Thêm

  • wp iga ux2blocks --dry-run|--apply [--ids=..] [--backup-dir=..] [--out-dir=..] [--no-home] [--home-file=..] và --rollback=<thư mục> [--force]. Phân tích shortcode Flatsome (lồng nhiều cấp, thuộc tính nháy kép/đơn/không nháy, ] trong giá trị, NBSP giữa thuộc tính, shortcode không đóng, thẻ đóng thừa) và chuyển sang khối core (heading, paragraph, list, quote, image, gallery, columns/column, group, cover, buttons, details, spacer, separator, embed YouTube, html, shortcode) và khối iga/* (ux_products thành iga/product-grid cùng nguồn, ux_slider thành iga/hero-slider kiểu banner, blog_posts thành iga/news-teaser).
  • Giữ nguyên văn chữ: so chữ trước/sau từng trang, chỉ ghi khi khớp 100% (không tính khoảng trắng) và khối hợp lệ. [block id=N] thành khối dùng lại wp_block (meta _iga_ux_block) để sửa một chỗ đổi mọi trang. Trang chủ (page_on_front) được thay bằng themes/iga/data/pages/home.blocks.html.
  • An toàn: sao lưu post_content + toàn bộ meta từng trang ra thư mục NGOÀI webroot và kiểm băm trước khi ghi, ghi trực tiếp post_content/post_modified (kèm revision của bản cũ; không đổi slug, tiêu đề, template, meta Rank Math), kiểm lại sau ghi và tự khôi phục trang lỗi, ROLLBACK.sql + --rollback kiểm băm sau khôi phục.
  • IGA\Core\Compat\FlatsomeShortcodes: render tối thiểu section/row/col/gap/title/button/ux_image/ux_banner/ux_slider/ux_products/ux_gallery/block/blog_posts... chỉ khi shortcode chưa tồn tại (Flatsome không chạy), để không bao giờ hiện shortcode thô; filter iga_core_flatsome_compat_tags.
  • Action iga_core_ux2blocks_page_written sau mỗi trang được ghi (để xoá cache).

0.2.0 - 2026-10-13

Lô F1: ghi dữ liệu lọc và module bộ lọc AJAX.

Thêm

  • wp iga extract --apply [--batch=200] [--only=<nhóm>] --backup-dir=<thư mục>: ghi taxonomy pa_loai-san-pham, pa_loai-da, pa_nhom-da, pa_che-tac, pa_hinh-tuong, pa_menh, pa_xuat-xu, pa_chat-lieu (đăng ký attribute WooCommerce nếu chưa có) và meta số đo _iga_*. Idempotent, theo lô trong giao dịch, không đổi post_modified, không đụng mô tả, chỉ ghi độ tin cậy high và medium, bỏ qua thuộc tính đang dùng làm biến thể, giữ term lạ sẵn có.
  • Sao lưu trước khi ghi (file nén, manifest có hash và số dòng, checksum bảng), ROLLBACK.sql dựng trước khi ghi; wp iga extract --rollback=<thư mục> chạy trong một giao dịch rồi so checksum.
  • Bảng mệnh theo màu đá sửa được trong admin (option iga_core_menh_overrides, D-12) và được bộ trích xuất đọc.
  • Module bộ lọc IGA\Core\Filter (fork từ hungphat product-filter v6.12.1): bảng {prefix}iga_filter_index và {prefix}iga_product_sort, indexer rebuild theo lô không TRUNCATE, reindex khi lưu, đổi tồn kho hoặc giá (kể cả ghi meta trực tiếp), reconcile theo giờ, cờ đang rebuild kèm DONOTCACHEPAGE.
  • Query_Engine dùng chung cho tải trang qua URL (thay truy vấn chính) và AJAX; AND giữa nhóm, OR trong nhóm, số đếm tính trừ nhóm hiện tại nên chọn một giá trị không ẩn giá trị khác cùng nhóm.
  • Nhóm lọc: các pa_* ở trên, giá theo 7 mốc D-09 và nhóm Giá liên hệ (D-02, luôn xếp cuối khi sắp theo giá), khoảng số đo g-* (chiều cao, trọng lượng, carat, size hạt), tồn kho, Có video; cấu hình theo loại sản phẩm trong data/facets.json (D-10).
  • Endpoint nhẹ runtime/iga-filter.php (chép ra webroot) + mu-plugin runtime/iga-fast-filter.php: giới hạn 60 yêu cầu mỗi phút mỗi IP (429), cache phản hồi Redis có tiền tố site và phiên bản, không cần nonce với khách.
  • assets/js/iga-filter.js (không jQuery): pushState, popstate, phân trang AJAX cập nhật URL, drawer có bẫy focus, nút Xem N kết quả; sắp xếp là nút.
  • Canonical về URL sạch và noindex,follow cho URL lọc (Rank Math và wp_robots).
  • Trang admin iga-core > Bộ lọc: tình trạng index, nút dựng lại, phủ dữ liệu, giá trị chưa map, danh sách cần sửa tay (D-14), bảng mệnh, link YouTube hỏng, cài đặt.
  • IGA\Core\Video\ProductVideo::get() và iga_product_video(): video YouTube từ meta Flatsome wc_productdata_options, không phụ thuộc theme; filter iga/product_video.
  • Template tag cho theme: iga_filter_toolbar(), iga_filter_sidebar(), iga_filter_results(), iga_filter_pagination(); filter iga/filter/card_html để theme thay thẻ sản phẩm.
  • Lệnh wp iga filter rebuild|reindex|reconcile|status|query.

Ghi chú

  • Tên meta số đo: _iga_cao_cm, _iga_trong_luong_g, _iga_carat, _iga_size_hat_ly...
  • Bảng index được tạo khi kích hoạt plugin và chỉ bị xoá khi gỡ plugin (uninstall.php); vô hiệu hoá không xoá dữ liệu.

0.1.0 - 2026-10-13

  • Khung plugin, bộ trích xuất thuộc tính (wp iga extract --dry-run), bảng ánh xạ loại đá, mệnh, hình tượng, xuất xứ, chất liệu.