PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.12.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.12.0
2.12.0 2.11.0 2.10.0 2.9.0 2.8.0 2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk All 53 releases
← All changes | includes/admin/importers/class-export-controller.php +22 -6 2.3.0 → 2.12.0 View file →
@@ -429,14 +429,21 @@
429 429 // The slug becomes an option name via Snapshot_Store, so a hand-edited
430 430 // file must not be able to steer where the chunks land. The original key
431 431 // is carried alongside it because that is what the records are filed
432 432 // under in `data` — sanitising is for the destination, not the lookup.
433 - $present = [];
433 + //
434 + // sanitize_key() bounds the alphabet but not the length, and an
435 + // over-long slug makes a chunk key MySQL truncates on write and cannot
436 + // find on read: from the next request on, the type counts records that
437 + // read back as nothing. Keeping such a type would only preserve a
438 + // promise the storage cannot honour, so it is dropped at the door.
439 + $max_slug = Snapshot_Store::max_type_length(Thinkrank_Exporter::SLUG);
440 + $present = [];
434 441 foreach (array_keys((array) $payload['data']) as $key) {
435 442 $slug = sanitize_key((string) $key);
436 443 // First one wins, so two keys that sanitise alike cannot have the
437 444 // second silently overwrite the first's chunks.
438 - if ($slug !== '' && !isset($present[$slug])) {
445 + if ($slug !== '' && strlen($slug) <= $max_slug && !isset($present[$slug])) {
439 446 $present[$slug] = (string) $key;
440 447 }
441 448 }
442 449
@@ -575,17 +582,26 @@
575 582 * not the count), so an untouched type shows up as
576 583 * `total_records: 0, total_chunks: 1`. Writing that into the file would
577 584 * advertise data the file does not contain.
578 585 *
586 + * The question is what the *snapshot* holds, not what this build can
587 + * produce. Filtering on get_exportable_types() here conflated the two and
588 + * broke the guarantee payload_types() makes on the way in: a file exported
589 + * with Pro active and uploaded to a free build keeps its Pro records so a
590 + * later Pro activation can drain the same snapshot — and then re-downloading
591 + * it on that free build stripped exactly those records from both `types` and
592 + * `data` (#589). A fresh export cannot smuggle a type in this way: the run
593 + * starts from an empty slot (the `reset` flag on the first chunk) and the
594 + * manifest only ever gains the types that build actually exported.
595 + *
596 + * @since 2.12.0 Tests the stored payload rather than the running build's
597 + * exportable types.
598 + *
579 599 * @param array $manifest Snapshot manifest
580 600 * @param string $type Data type
581 601 * @return bool
582 602 */
583 603 private function type_has_records(array $manifest, string $type): bool {
584 - if (!in_array($type, Thinkrank_Exporter::get_exportable_types(), true)) {
585 - return false;
586 - }
587 -
588 604 return (int) ($manifest['types'][$type]['total_records'] ?? 0) > 0;
589 605 }
590 606
591 607 /**