| @@ -12,8 +12,14 @@ | ||
| 12 | 12 | const NONCE_ACTION = 'wpc-f-set-nonce'; |
| 13 | 13 | |
| 14 | 14 | const FIELD_NAME_PREFIX = 'wpc_set_fields'; |
| 15 | 15 | |
| 16 | + const FREE_LIMIT_NEW = 2; | |
| 17 | + | |
| 18 | + const FREE_LIMIT_LEGACY = 3; | |
| 19 | + | |
| 20 | + const FREE_LIMIT_CUTOFF_DATE = '2026-06-15 00:00:00'; | |
| 21 | + | |
| 16 | 22 | private $defaultFields = []; |
| 17 | 23 | |
| 18 | 24 | private $hooksRegistered = false; |
| 19 | 25 | |
| @@ -307,8 +313,19 @@ | ||
| 307 | 313 | 'defaultCustomMetaKeys' => wpc_default_custom_meta_keys_filter(), |
| 308 | 314 | 'selectMetaKeyPlaceholder' => esc_html__( 'Type to search for a custom field key', 'filter-everything' ), |
| 309 | 315 | 'metaKeySearchText' => esc_html__( 'Searching', 'filter-everything' ), |
| 310 | 316 | 'isLimitFilterSet' => $this->under_limit_filter_set($post_id), |
| 317 | + 'rangeListValueToRemove' => esc_html__('Remove', 'filter-everything'), | |
| 318 | + 'limitForRangeList' => (int)(defined('FLRT_RANGE_LIST_LIMIT') && FLRT_RANGE_LIST_LIMIT) ? FLRT_RANGE_LIST_LIMIT : 20, | |
| 319 | + 'rangeListTexts' => [ | |
| 320 | + 'min_value' => esc_html__('Min Value', 'filter-everything'), | |
| 321 | + 'max_value' => esc_html__('Max Value', 'filter-everything'), | |
| 322 | + 'label' => esc_html__('Label', 'filter-everything'), | |
| 323 | + 'up_to' => esc_html__('Up to', 'filter-everything'), | |
| 324 | + 'and_up' => esc_html__('And up', 'filter-everything'), | |
| 325 | + 'error' => esc_html__('Error: No fields are filled in. Please fill in at least one field.', 'filter-everything'), | |
| 326 | + 'value_error' => esc_html__('Error: The minimum value cannot be greater than or equal to the maximum value.', 'filter-everything'), | |
| 327 | + ], | |
| 311 | 328 | ); |
| 312 | 329 | |
| 313 | 330 | wp_localize_script( 'wpc-filters-admin-filter-set', 'wpcSetVars', $l10n ); |
| 314 | 331 | } |
| @@ -426,8 +443,9 @@ | ||
| 426 | 443 | |
| 427 | 444 | if( ! empty( $filterSets ) ){ |
| 428 | 445 | foreach ( $filterSets as $set ){ |
| 429 | 446 | if( isset( $set['show_on_the_page'] ) && $set['show_on_the_page'] === true ){ |
| 447 | + $this->addFilterSetsToJsonData($filterSets); | |
| 430 | 448 | return $filterSets; |
| 431 | 449 | } |
| 432 | 450 | } |
| 433 | 451 | } |
| @@ -443,8 +461,10 @@ | ||
| 443 | 461 | } |
| 444 | 462 | |
| 445 | 463 | $filterSets = apply_filters( 'wpc_return_relevant_set_ids', $filterSets, $queriedObject ); |
| 446 | 464 | |
| 465 | + $this->addFilterSetsToJsonData($filterSets); | |
| 466 | + | |
| 447 | 467 | return $filterSets; |
| 448 | 468 | } |
| 449 | 469 | |
| 450 | 470 | /** |
| @@ -574,8 +594,9 @@ | ||
| 574 | 594 | |
| 575 | 595 | public function preSaveSet( $post_id, $data ) |
| 576 | 596 | { |
| 577 | 597 | $postData = Container::instance()->getThePost(); |
| 598 | + $postData = prepare_wpc_filter_set_json_data($postData); | |
| 578 | 599 | |
| 579 | 600 | if( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) { |
| 580 | 601 | return $post_id; |
| 581 | 602 | } |
| @@ -623,8 +644,10 @@ | ||
| 623 | 644 | |
| 624 | 645 | public function saveSet( $post_id, $post ) |
| 625 | 646 | { |
| 626 | 647 | $postData = Container::instance()->getThePost(); |
| 648 | + $postData = prepare_wpc_filter_set_json_data($postData); | |
| 649 | + | |
| 627 | 650 | if( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) { |
| 628 | 651 | return $post_id; |
| 629 | 652 | } |
| 630 | 653 | |
| @@ -1089,10 +1112,12 @@ | ||
| 1089 | 1112 | } |
| 1090 | 1113 | |
| 1091 | 1114 | public function getPostTypeField( $post_id ) |
| 1092 | 1115 | { |
| 1116 | + // getSet() returns [] for an empty ID (e.g. a Filters block/widget with | |
| 1117 | + // no Filter Set chosen) — report "no post type" instead of a warning. | |
| 1093 | 1118 | $set = $this->getSet( $post_id ); |
| 1094 | - $field['post_type'] = ( $set['post_type'] ) ? $set['post_type'] : NULL; | |
| 1119 | + $field['post_type'] = ! empty( $set['post_type'] ) ? $set['post_type'] : NULL; | |
| 1095 | 1120 | return $field; |
| 1096 | 1121 | } |
| 1097 | 1122 | |
| 1098 | 1123 | public function getSettingsTypeFields( $post_id ) |
| @@ -1363,8 +1388,65 @@ | ||
| 1363 | 1388 | $content = maybe_unserialize($raw); |
| 1364 | 1389 | return is_array($content) ? $content : []; |
| 1365 | 1390 | } |
| 1366 | 1391 | |
| 1392 | + /** | |
| 1393 | + * Returns the effective free-version Filter Set limit for a given post type. | |
| 1394 | + * | |
| 1395 | + * The historical free limit was {@see self::FREE_LIMIT_LEGACY} (3) Filter Sets per post type. | |
| 1396 | + * From {@see self::FREE_LIMIT_CUTOFF_DATE} onwards the new free limit is {@see self::FREE_LIMIT_NEW} (2). | |
| 1397 | + * To avoid breaking existing free-version installs that already relied on the old limit, any post type | |
| 1398 | + * that had at least one published Filter Set created before the cutoff is grandfathered at the legacy | |
| 1399 | + * limit; only post types whose Filter Sets are all post-cutoff (or have none yet) get the new lower limit. | |
| 1400 | + * | |
| 1401 | + * @param string $post_type Target post type (stored in wp_posts.post_excerpt for Filter Sets). | |
| 1402 | + * @return int Effective max number of published Filter Sets allowed for this post type. | |
| 1403 | + */ | |
| 1404 | + public function getFreeLimitForPostType($post_type) : int | |
| 1405 | + { | |
| 1406 | + if (empty($post_type)) { | |
| 1407 | + return self::FREE_LIMIT_NEW; | |
| 1408 | + } | |
| 1409 | + | |
| 1410 | + global $wpdb; | |
| 1411 | + | |
| 1412 | + $legacy_count = (int) $wpdb->get_var( | |
| 1413 | + $wpdb->prepare( | |
| 1414 | + "SELECT COUNT(ID) | |
| 1415 | + FROM {$wpdb->posts} | |
| 1416 | + WHERE post_type = %s | |
| 1417 | + AND post_excerpt = %s | |
| 1418 | + AND post_status = %s | |
| 1419 | + AND post_date < %s", | |
| 1420 | + FLRT_FILTERS_SET_POST_TYPE, | |
| 1421 | + $post_type, | |
| 1422 | + 'publish', | |
| 1423 | + self::FREE_LIMIT_CUTOFF_DATE | |
| 1424 | + ) | |
| 1425 | + ); | |
| 1426 | + | |
| 1427 | + return $legacy_count > 0 ? self::FREE_LIMIT_LEGACY : self::FREE_LIMIT_NEW; | |
| 1428 | + } | |
| 1429 | + | |
| 1430 | + /** | |
| 1431 | + * Checks whether the free-version limit of published Filter Sets per post type has been reached. | |
| 1432 | + * | |
| 1433 | + * In the PRO version (FLRT_FILTERS_PRO is defined and true) the limit does not apply and the method | |
| 1434 | + * always returns false. In the free version the effective limit comes from {@see self::getFreeLimitForPostType()} | |
| 1435 | + * — {@see self::FREE_LIMIT_LEGACY} (3) for post types grandfathered by Filter Sets created before | |
| 1436 | + * {@see self::FREE_LIMIT_CUTOFF_DATE}, and {@see self::FREE_LIMIT_NEW} (2) otherwise. The method then: | |
| 1437 | + * - resolves the post type from $post_id (or $default_post_type if provided), | |
| 1438 | + * - returns true (over the limit) when there are already enough Filter Sets for that post type and | |
| 1439 | + * the current $post_id is not among the first allowed ones, when switching an existing set to a | |
| 1440 | + * post type that already has enough sets, or when no Filter Set context exists yet and the | |
| 1441 | + * post type already has enough sets, | |
| 1442 | + * - returns false otherwise, i.e. the user is still under the limit and may proceed. | |
| 1443 | + * | |
| 1444 | + * @param int|string $post_id ID of the Filter Set being edited, or empty when creating a new one. | |
| 1445 | + * @param string $default_post_type Post type explicitly chosen in the UI; overrides the value stored on the set. | |
| 1446 | + * @param bool $update Reserved flag for update flows (currently unused inside the method). | |
| 1447 | + * @return bool True when the free-version limit is reached and the action must be blocked, false otherwise. | |
| 1448 | + */ | |
| 1367 | 1449 | public function under_limit_filter_set($post_id = '', $default_post_type = '', $update = false) : bool |
| 1368 | 1450 | { |
| 1369 | 1451 | |
| 1370 | 1452 | if( defined('FLRT_FILTERS_PRO') && FLRT_FILTERS_PRO ){ |
| @@ -1390,14 +1472,16 @@ | ||
| 1390 | 1472 | $post_type = $default_post_type; |
| 1391 | 1473 | } |
| 1392 | 1474 | |
| 1393 | 1475 | if (!empty($post_type)) { |
| 1476 | + $limit = $this->getFreeLimitForPostType($post_type); | |
| 1477 | + | |
| 1394 | 1478 | $count = $wpdb->get_var( |
| 1395 | 1479 | $wpdb->prepare( |
| 1396 | - "SELECT COUNT(ID) | |
| 1397 | - FROM {$wpdb->posts} | |
| 1398 | - WHERE post_type = %s | |
| 1399 | - AND post_excerpt = %s | |
| 1480 | + "SELECT COUNT(ID) | |
| 1481 | + FROM {$wpdb->posts} | |
| 1482 | + WHERE post_type = %s | |
| 1483 | + AND post_excerpt = %s | |
| 1400 | 1484 | AND post_status = %s", |
| 1401 | 1485 | FLRT_FILTERS_SET_POST_TYPE, |
| 1402 | 1486 | $post_type, |
| 1403 | 1487 | 'publish', |
| @@ -1405,23 +1489,24 @@ | ||
| 1405 | 1489 | ); |
| 1406 | 1490 | |
| 1407 | 1491 | $post_ids = $wpdb->get_col( |
| 1408 | 1492 | $wpdb->prepare( |
| 1409 | - "SELECT ID | |
| 1410 | - FROM {$wpdb->posts} | |
| 1411 | - WHERE post_type = %s | |
| 1412 | - AND post_excerpt = %s | |
| 1493 | + "SELECT ID | |
| 1494 | + FROM {$wpdb->posts} | |
| 1495 | + WHERE post_type = %s | |
| 1496 | + AND post_excerpt = %s | |
| 1413 | 1497 | AND post_status = %s |
| 1414 | 1498 | ORDER BY ID ASC |
| 1415 | - LIMIT 3", | |
| 1499 | + LIMIT %d", | |
| 1416 | 1500 | FLRT_FILTERS_SET_POST_TYPE, |
| 1417 | 1501 | $post_type, |
| 1418 | - 'publish' | |
| 1502 | + 'publish', | |
| 1503 | + $limit | |
| 1419 | 1504 | ) |
| 1420 | 1505 | ); |
| 1421 | 1506 | |
| 1422 | 1507 | if (!empty($post_id)) { |
| 1423 | - if ($count >= 3 && !in_array($post_id, $post_ids)) { | |
| 1508 | + if ($count >= $limit && !in_array($post_id, $post_ids)) { | |
| 1424 | 1509 | return true; |
| 1425 | 1510 | } |
| 1426 | 1511 | } |
| 1427 | 1512 | |
| @@ -1432,15 +1517,15 @@ | ||
| 1432 | 1517 | } |
| 1433 | 1518 | |
| 1434 | 1519 | if(!empty($default_post_type) && !empty($get_post_type['post_type']['value'])){ |
| 1435 | 1520 | if($default_post_type !== $get_post_type['post_type']['value']){ |
| 1436 | - if($count >= 3){ | |
| 1521 | + if($count >= $limit){ | |
| 1437 | 1522 | return true; |
| 1438 | 1523 | } |
| 1439 | 1524 | } |
| 1440 | 1525 | } |
| 1441 | 1526 | |
| 1442 | - if(empty($get_post_type['post_type']['value']) && $count >= 3){ | |
| 1527 | + if(empty($get_post_type['post_type']['value']) && $count >= $limit){ | |
| 1443 | 1528 | return true; |
| 1444 | 1529 | } |
| 1445 | 1530 | |
| 1446 | 1531 | } |
| @@ -1449,6 +1534,24 @@ | ||
| 1449 | 1534 | } |
| 1450 | 1535 | |
| 1451 | 1536 | public function getErrors() { |
| 1452 | 1537 | return $this->errors; |
| 1538 | + } | |
| 1539 | + | |
| 1540 | + private function addFilterSetsToJsonData($filterSets) | |
| 1541 | + { | |
| 1542 | + $flrt_json_data = &Container::instance()->getFilterContext()->jsonData(); | |
| 1543 | + foreach ($filterSets as $set) { | |
| 1544 | + $setId = $set['ID']; | |
| 1545 | + $setSettings = $this->getSet($setId); | |
| 1546 | + | |
| 1547 | + if(!isset($flrt_json_data[$setId])){ | |
| 1548 | + $flrt_json_data[$setId] = []; | |
| 1549 | + } | |
| 1550 | + | |
| 1551 | + $flrt_json_data[$setId]['settings'] = array_map( | |
| 1552 | + fn($item) => is_array($item) ? $item['value'] : $item, | |
| 1553 | + $setSettings | |
| 1554 | + ); | |
| 1555 | + } | |
| 1453 | 1556 | } |
| 1454 | 1557 | } |