Changeset 3356445
- Timestamp:
- 09/05/2025 05:31:39 AM (6 months ago)
- Location:
- hizzle-downloads
- Files:
-
- 18 edited
- 1 copied
-
tags/1.2.2 (copied) (copied from hizzle-downloads/trunk)
-
tags/1.2.2/hizzle-downloads.php (modified) (2 diffs)
-
tags/1.2.2/readme.txt (modified) (5 diffs)
-
tags/1.2.2/vendor/composer/installed.json (modified) (3 diffs)
-
tags/1.2.2/vendor/composer/installed.php (modified) (3 diffs)
-
tags/1.2.2/vendor/hizzle/store/example-plugin.php (modified) (1 diff)
-
tags/1.2.2/vendor/hizzle/store/src/Collection.php (modified) (16 diffs)
-
tags/1.2.2/vendor/hizzle/store/src/Query.php (modified) (6 diffs)
-
tags/1.2.2/vendor/hizzle/store/src/REST_Controller.php (modified) (4 diffs)
-
tags/1.2.2/vendor/hizzle/store/src/Record.php (modified) (3 diffs)
-
trunk/hizzle-downloads.php (modified) (2 diffs)
-
trunk/readme.txt (modified) (5 diffs)
-
trunk/vendor/composer/installed.json (modified) (3 diffs)
-
trunk/vendor/composer/installed.php (modified) (3 diffs)
-
trunk/vendor/hizzle/store/example-plugin.php (modified) (1 diff)
-
trunk/vendor/hizzle/store/src/Collection.php (modified) (16 diffs)
-
trunk/vendor/hizzle/store/src/Query.php (modified) (6 diffs)
-
trunk/vendor/hizzle/store/src/REST_Controller.php (modified) (4 diffs)
-
trunk/vendor/hizzle/store/src/Record.php (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
hizzle-downloads/tags/1.2.2/hizzle-downloads.php
r3287466 r3356445 4 4 * Plugin URI: https://hizzle.co/download-manager/ 5 5 * Description: A lightweight download manager plugin. 6 * Version: 1.2. 16 * Version: 1.2.2 7 7 * Author: Hizzle 8 8 * Author URI: https://hizzle.co … … 22 22 23 23 if ( ! defined( 'HIZZLE_DOWNLOADS_VERSION' ) ) { 24 define( 'HIZZLE_DOWNLOADS_VERSION', '1.2. 1' );24 define( 'HIZZLE_DOWNLOADS_VERSION', '1.2.2' ); 25 25 } 26 26 -
hizzle-downloads/tags/1.2.2/readme.txt
r3287466 r3356445 1 1 === Simple Download Manager - Hizzle Downloads === 2 2 Contributors: picocodes, mutendebrian 3 Tags: files, downloads, digital downloads 3 Tags: files, downloads, digital downloads, download manager, restrict downloads 4 4 Requires at least: 4.9 5 5 Tested up to: 6.7 6 6 Requires PHP: 5.6 7 Version: 1.2. 18 Stable tag: 1.2. 17 Version: 1.2.2 8 Stable tag: 1.2.2 9 9 License: GPLv3 10 10 License URI: https://www.gnu.org/licenses/gpl-3.0.html 11 11 Donate link: https://noptin.com/products/?utm_source=wp-repo&utm_medium=donate&utm_campaign=readme 12 12 13 Allow your website visitors to download files from your site. Optionally restrict downloads by user role, ip address, etc 13 Easily add, restrict, and track digital downloads in WordPress — protect files with passwords, user roles, IPs, or subscriber access. 14 14 15 15 == Description == 16 16 17 This plugin allows you to:- 17 **A simple WordPress download manager for secure file sharing, access control, and download tracking — perfect for digital products.** 18 ★★★★★<br> 18 19 19 - Add unlimited downloadable files. 20 - Optionally protect each downloadable file with a password. 21 - Restrict downloads to specific user roles. 22 - Restrict downloads to specific ip addresses. 23 - Restrict downloads to specific users. 24 - Restrict downloads to newsletter subscribers. 25 - Track file downloads. 20 Do you need a simple yet powerful way to manage file downloads on your WordPress site? This plugin makes it easy to upload, organize, and control access to downloadable files of any type. Whether you are sharing free resources, selling digital products, or delivering private documents, this plugin gives you full control over who can download your files and when. 21 22 With unlimited downloads, flexible restrictions, and detailed tracking, you can confidently provide files to your audience while keeping them secure. 23 24 = Key Features = 25 26 - **Add unlimited downloadable files** – Add and manage as many downloadable files as you need, with no limits. 27 - **Password Protection** – Protect individual files with custom passwords so only authorized users can access them. 28 - **Restrict downloads to specific user roles** – Control file access based on WordPress user roles, ensuring that only administrators, editors, subscribers, or custom roles can download. 29 - **Restrict downloads to specific IP addresses** – Restrict downloads to specific IP addresses to prevent abuse or unauthorized sharing. 30 - **Restrict downloads to specific users** – Assign downloads to specific registered users for secure, private file delivery. 31 - **Restrict downloads to newsletter subscribers** – Restrict downloads to Noptin newsletter subscriber, making it an excellent tool for lead generation. 32 - **Track file downloads** – Track every file download with detailed statistics, helping you understand how your files are being accessed. 33 - **Simple Management** – A user-friendly interface makes uploading and managing files straightforward, even for beginners. 34 35 = Why Use This Plugin? = 36 37 Managing downloads manually in WordPress can be difficult. Links can be shared publicly, access can’t easily be restricted, and tracking is limited. This plugin solves those problems by giving you advanced tools to: 38 39 - Protect digital products such as **software, themes, and plugins**. 40 - Share private **PDF documents, contracts, or reports** securely with clients. 41 - Provide exclusive resources like **eBooks, whitepapers, and templates** to email subscribers. 42 - Control access to files for **membership sites and online courses**. 43 - Monitor and analyze download activity to make better business decisions. 44 45 = Benefits for Your Website = 46 47 By installing this plugin, you’ll be able to: 48 49 - Grow your email list by offering subscriber-only downloads. 50 - Monetize your website by controlling access to premium resources. 51 - Increase security by preventing unauthorized downloads and link sharing. 52 - Gain insights into how your downloads are performing. 53 54 Whether you’re a blogger, developer, marketer, educator, or business owner, this plugin gives you all the tools you need to manage file downloads effectively in WordPress. 55 56 Take control of your downloads today and provide a seamless, secure experience for your users. 26 57 27 58 == Installation == … … 37 68 = Can I see how many times each file has been downloaded? = 38 69 39 Yes, Noptin allows you to view how many times each file has been downloaded, as well as which users have downloaded the file. This information is available in the Noptindashboard, where you can see a list of all your downloadable files and their download stats. You can also see which users have downloaded the files, and view their information and activity in your mailing list. This can be useful for tracking the popularity of your files and understanding which users are interested in them.70 Yes, Hizzle Downloads allows you to view how many times each file has been downloaded, as well as which users have downloaded the file. This information is available in the Hizzle Downloads dashboard, where you can see a list of all your downloadable files and their download stats. You can also see which users have downloaded the files, and view their information and activity in your mailing list. This can be useful for tracking the popularity of your files and understanding which users are interested in them. 40 71 41 72 = Can I restrict downloads by user role or newsletter subscription status? = 42 73 43 Yes, Noptin allows you to restrict downloads by user role or newsletter subscription status. This means that you can choose which users are able to download your files, based on their user role on your website or their status as a newsletter subscriber. For example, you can make a file available only to users who are registered as members on your website, or only to users who have subscribed to your newsletter. This can be useful for creating exclusive content or offers for your users, and for encouraging people to sign up for your newsletter. You can set these restrictions for each of your downloadable files in the Noptinsettings.74 Yes, Hizzle Downloads allows you to restrict downloads by user role or newsletter subscription status. This means that you can choose which users are able to download your files, based on their user role on your website or their status as a newsletter subscriber. For example, you can make a file available only to users who are registered as members on your website, or only to users who have subscribed to your newsletter. This can be useful for creating exclusive content or offers for your users, and for encouraging people to sign up for your newsletter. You can set these restrictions for each of your downloadable files in the Hizzle Downloads settings. 44 75 45 76 = How do I display downloadable files? = … … 49 80 = How can I get in touch? = 50 81 51 Use the [contact form on our website](https://hizzle .co/contact-us/).82 Use the [contact form on our website](https://hizzlewp.com/contact/). 52 83 53 84 = How can I contribute? = … … 64 95 65 96 == Changelog == 97 98 = 1.2.2 = 99 - Update composer packages. 66 100 67 101 = 1.2.1 = … … 100 134 101 135 = 1.0.3 = 102 - Restrict downloads to newsletter subscribers.136 - Restrict downloads to Noptin newsletter subscribers. 103 137 - Add support for password protected downloads. 104 138 - Automatically update download files when there is a GitHub release. -
hizzle-downloads/tags/1.2.2/vendor/composer/installed.json
r3287466 r3356445 54 54 { 55 55 "name": "hizzle/store", 56 "version": "0.2. 7",57 "version_normalized": "0.2. 7.0",56 "version": "0.2.11", 57 "version_normalized": "0.2.11.0", 58 58 "source": { 59 59 "type": "git", 60 60 "url": "https://github.com/hizzle-co/datastore.git", 61 "reference": " 6620679060f7b64e0a417758a328173e8b5ec729"61 "reference": "a6e17c589443de791e9bf9eb9c2536c7401f09c2" 62 62 }, 63 63 "dist": { 64 64 "type": "zip", 65 "url": "https://api.github.com/repos/hizzle-co/datastore/zipball/ 6620679060f7b64e0a417758a328173e8b5ec729",66 "reference": " 6620679060f7b64e0a417758a328173e8b5ec729",65 "url": "https://api.github.com/repos/hizzle-co/datastore/zipball/a6e17c589443de791e9bf9eb9c2536c7401f09c2", 66 "reference": "a6e17c589443de791e9bf9eb9c2536c7401f09c2", 67 67 "shasum": "" 68 68 }, … … 70 70 "php": ">=5.3.0" 71 71 }, 72 "time": "2025-0 5-03T10:20:52+00:00",72 "time": "2025-08-20T05:32:06+00:00", 73 73 "type": "library", 74 74 "installation-source": "dist", … … 99 99 "support": { 100 100 "issues": "https://github.com/hizzle-co/datastore/issues", 101 "source": "https://github.com/hizzle-co/datastore/tree/0.2. 7"101 "source": "https://github.com/hizzle-co/datastore/tree/0.2.11" 102 102 }, 103 103 "install-path": "../hizzle/store" -
hizzle-downloads/tags/1.2.2/vendor/composer/installed.php
r3287466 r3356445 4 4 'pretty_version' => 'dev-main', 5 5 'version' => 'dev-main', 6 'reference' => ' eacb4c22f68cba9ad5d53a20b1db45c4f10bad5a',6 'reference' => '54e05e6de537c0de0a2c4e916ec2394d169ccd55', 7 7 'type' => 'library', 8 8 'install_path' => __DIR__ . '/../../', … … 14 14 'pretty_version' => 'dev-main', 15 15 'version' => 'dev-main', 16 'reference' => ' eacb4c22f68cba9ad5d53a20b1db45c4f10bad5a',16 'reference' => '54e05e6de537c0de0a2c4e916ec2394d169ccd55', 17 17 'type' => 'library', 18 18 'install_path' => __DIR__ . '/../../', … … 30 30 ), 31 31 'hizzle/store' => array( 32 'pretty_version' => '0.2. 7',33 'version' => '0.2. 7.0',34 'reference' => ' 6620679060f7b64e0a417758a328173e8b5ec729',32 'pretty_version' => '0.2.11', 33 'version' => '0.2.11.0', 34 'reference' => 'a6e17c589443de791e9bf9eb9c2536c7401f09c2', 35 35 'type' => 'library', 36 36 'install_path' => __DIR__ . '/../hizzle/store', -
hizzle-downloads/tags/1.2.2/vendor/hizzle/store/example-plugin.php
r3287466 r3356445 4 4 Plugin URI: https://hizzle.co/ 5 5 Description: Hizzle Data Stores 6 Version: 0.2. 76 Version: 0.2.11 7 7 Author: picocodes 8 8 Author URI: https://github.com/picocodes/ -
hizzle-downloads/tags/1.2.2/vendor/hizzle/store/src/Collection.php
r3287466 r3356445 427 427 $schema .= "PRIMARY KEY ($cols),\n"; // Maintain 2 spaces between key and opening bracket. 428 428 } elseif ( 'unique' === $index ) { 429 430 429 foreach ( $cols as $prop => $index ) { 431 430 $schema .= "UNIQUE KEY $prop ($index),\n"; … … 727 726 728 727 /** 728 * Returns a prop's cache key. 729 * 730 * @param string $prop The prop to get the cache key for. 731 * @return string The cache key. 732 */ 733 private function get_prop_cache_key( $prop ) { 734 $current_version = 'v2'; // Update this version when the cache key structure changes. 735 return $this->hook_prefix( $current_version . '_ids_by_' . $prop, true ); 736 } 737 738 /** 729 739 * Retrieves an ID by a given prop. 730 740 * … … 741 751 742 752 // Try the cache. 743 $value = trim( $value ); 744 $id = wp_cache_get( $value, $this->hook_prefix( 'ids_by_' . $prop, true ) ); 753 $value = trim( $value ); 754 $cache_key = $this->get_prop_cache_key( $prop ); 755 $id = wp_cache_get( $value, $cache_key ); 745 756 746 757 // Maybe retrieve from the db. … … 753 764 754 765 // Update the cache. 755 wp_cache_set( $value, $id, $ this->hook_prefix( 'ids_by_' . $prop, true ));766 wp_cache_set( $value, $id, $cache_key, empty( $id ) ? MINUTE_IN_SECONDS : HOUR_IN_SECONDS ); 756 767 } 757 768 … … 778 789 // Date fields. 779 790 if ( $value instanceof Date_Time ) { 780 781 791 if ( ! empty( $this->props[ $key ] ) && 'date' === strtolower( $this->props[ $key ]->type ) ) { 782 792 $value = $value->utc( 'Y-m-d' ); … … 846 856 $record->apply_changes(); 847 857 848 // Clear the cache.849 $this->clear_cache( $record ->get_data());858 // Clear any stale cache entries. 859 $this->clear_cache( $record ); 850 860 851 861 // Fires after creating a record. … … 915 925 apply_filters( $this->hook_prefix( 'insert_formats', true ), $formats, $record ) 916 926 ); 927 928 if ( empty( $result ) && ! empty( $wpdb->last_error ) ) { 929 noptin_error_log( 'Error creating record: ' . $wpdb->last_error ); 930 } 917 931 918 932 return $result ? $wpdb->insert_id : 0; … … 1013 1027 1014 1028 if ( $prop->is_meta_key_multiple ) { 1015 1016 1029 $new = (array) $new; 1017 1030 $to_delete = array_diff( $current, $new ); … … 1108 1121 $this->save_defaults( $record ); 1109 1122 $raw_data = $record->get_data(); 1110 1111 1123 } elseif ( empty( $raw_data ) ) { 1112 1124 $this->not_found(); … … 1124 1136 // Cache the record data. 1125 1137 $this->update_cache( $data ); 1126 1127 1138 } 1128 1139 … … 1203 1214 // Fires before updating a record. 1204 1215 do_action( $this->hook_prefix( 'before_update', true ), $record ); 1216 1217 // Clear cache early to prevent race conditions and stale data 1218 // Do this before any database operations 1219 $this->clear_cache( $record ); 1205 1220 1206 1221 $raw_changes = array_keys( $record->get_changes() ); … … 1235 1250 $record->apply_changes(); 1236 1251 1237 // Clear the cache.1238 $this->clear_cache( $record->get_data() );1239 1240 1252 if ( $has_changes ) { 1241 1253 … … 1264 1276 1265 1277 // Invalidate cache. 1266 $this->clear_cache( $record ->get_data());1278 $this->clear_cache( $record ); 1267 1279 1268 1280 // If this is a CPT, delete the post. … … 1517 1529 1518 1530 // Ensure we have an array. 1519 if ( ! is_array( $record ) ) {1531 if ( ! is_array( $record ) || empty( $record['id'] ) ) { 1520 1532 return; 1521 1533 } 1522 1534 1535 // Cache lookup values for faster queries 1523 1536 foreach ( $this->get_cache_keys() as $key ) { 1524 1537 if ( isset( $record[ $key ] ) && ! empty( $record[ $key ] ) ) { 1525 wp_cache_set( $record[ $key ], $record['id'], $this-> hook_prefix( 'ids_by_' . $key, true ), WEEK_IN_SECONDS );1538 wp_cache_set( $record[ $key ], $record['id'], $this->get_prop_cache_key( $key ), HOUR_IN_SECONDS ); 1526 1539 } 1527 1540 } 1528 1541 1529 1542 // Cache the entire record. 1530 wp_cache_set( $record['id'], $record, $this->get_full_name(), DAY_IN_SECONDS );1543 wp_cache_set( $record['id'], $record, $this->get_full_name(), HOUR_IN_SECONDS ); 1531 1544 } 1532 1545 … … 1534 1547 * Clean caches. 1535 1548 * 1536 * @param object$record The raw db record.1549 * @param Record $record The raw db record. 1537 1550 */ 1538 1551 public function clear_cache( $record ) { 1539 1552 1540 $record = (object) $record;1541 1542 1553 foreach ( $this->get_cache_keys() as $key ) { 1543 if ( ! is_null( $record->$key ) && '' !== $record->$key ) { 1544 wp_cache_delete( $record->$key, $this->hook_prefix( 'ids_by_' . $key, true ) ); 1545 } 1546 } 1547 1548 wp_cache_delete( $record->id, $this->get_full_name() ); 1554 $value = $record->get( $key ); 1555 if ( is_string( $value ) && '' !== $value ) { 1556 wp_cache_delete( $value, $this->get_prop_cache_key( $key ) ); 1557 } 1558 } 1559 1560 // Bail early if the record doesn't exist. 1561 if ( ! $record->exists() ) { 1562 return; 1563 } 1564 1565 // Clear the main record cache. 1566 wp_cache_delete( $record->get_id(), $this->get_full_name() ); 1567 1568 // Clear any potential old cache entries by attempting to fetch and clear them 1569 // This handles cases where cache keys have changed due to updates 1570 $this->clear_stale_cache_entries( $record->get_id() ); 1571 } 1572 1573 /** 1574 * Clears potentially stale cache entries for a record. 1575 * 1576 * @param int $record_id The record ID. 1577 */ 1578 private function clear_stale_cache_entries( $record_id ) { 1579 global $wpdb; 1580 1581 // For updates, we need to clear cache for old values that might have changed 1582 if ( ! empty( $record_id ) ) { 1583 1584 // Get the current database values to clear any old cached entries 1585 $table_name = $this->get_db_table_name(); 1586 $current_data = $wpdb->get_row( 1587 $wpdb->prepare( "SELECT * FROM $table_name WHERE id = %d", $record_id ), // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 1588 ARRAY_A 1589 ); 1590 1591 if ( $current_data ) { 1592 foreach ( $this->get_cache_keys() as $key ) { 1593 $value = $current_data[ $key ] ?? null; 1594 if ( is_string( $value ) && '' !== $value ) { 1595 // Clear cache for database values (in case they differ from the record object) 1596 wp_cache_delete( $value, $this->get_prop_cache_key( $key ) ); 1597 } 1598 } 1599 } 1600 } 1549 1601 } 1550 1602 … … 1597 1649 * @param string $default The default label. 1598 1650 */ 1599 public function get_label( $key, $default ) {1600 return isset( $this->labels[ $key ] ) ? $this->labels[ $key ] : $default;1651 public function get_label( $key, $default_value ) { 1652 return $this->labels[ $key ] ?? $default_value; 1601 1653 } 1602 1654 } -
hizzle-downloads/tags/1.2.2/vendor/hizzle/store/src/Query.php
r3245426 r3356445 306 306 307 307 // Prepare aggregate fields. 308 foreach ( $aggregate_fields as $field => $function ) { 308 foreach ( $aggregate_fields as $field => $aggregate ) { 309 310 if ( ! is_array( $aggregate ) ) { 311 $aggregate = wp_parse_list( $aggregate ); 312 } 309 313 310 314 // Handle CASE expressions 311 if ( is_array( $ function ) && isset( $function['case'] ) ) {312 $case_field = $this->prefix_field( esc_sql( sanitize_key( $ function['case']['field'] ) ) );315 if ( is_array( $aggregate ) && isset( $aggregate['case'] ) ) { 316 $case_field = $this->prefix_field( esc_sql( sanitize_key( $aggregate['case']['field'] ) ) ); 313 317 if ( empty( $case_field ) ) { 314 318 throw new Store_Exception( 'query_invalid_field', 'Invalid case field.' ); … … 316 320 317 321 $case_sql = "CASE $case_field"; 318 foreach ( $ function['case']['when'] as $when => $then ) {319 $when = esc_sql( $when );320 $then_sql = $this->prepare_case_then( $then );322 foreach ( $aggregate['case']['when'] as $when => $then ) { 323 $when = esc_sql( $when ); 324 $then_sql = $this->prepare_case_then( $then ); 321 325 $case_sql .= " WHEN '$when' THEN $then_sql"; 322 326 } 323 327 324 if ( isset( $ function['case']['else'] ) ) {325 $else_sql = $this->prepare_case_then( $function['case']['else'] );328 if ( isset( $aggregate['case']['else'] ) ) { 329 $else_sql = $this->prepare_case_then( $aggregate['case']['else'] ); 326 330 $case_sql .= " ELSE $else_sql"; 327 331 } 328 332 329 $case_sql .= " END";333 $case_sql .= ' END'; 330 334 331 335 // Handle optional aggregate function wrapper 332 if ( isset( $ function['function'] ) ) {333 $agg_function = strtoupper( $ function['function'] );336 if ( isset( $aggregate['function'] ) ) { 337 $agg_function = strtoupper( $aggregate['function'] ); 334 338 if ( ! in_array( $agg_function, array( 'AVG', 'COUNT', 'MAX', 'MIN', 'SUM' ), true ) ) { 335 339 throw new Store_Exception( 'query_invalid_function', 'Invalid aggregate function.' ); … … 339 343 340 344 // Handle optional math operations 341 if ( isset( $ function['math'] ) ) {342 $math_op = $this->prepare_math_expression( $function['math'] );345 if ( isset( $aggregate['math'] ) ) { 346 $math_op = $this->prepare_math_expression( $aggregate['math'] ); 343 347 $case_sql = "($case_sql $math_op)"; 344 348 } … … 356 360 } 357 361 358 foreach ( wp_parse_list( $function ) as $function ) { 362 foreach ( array_filter( $aggregate ) as $function ) { 363 364 if ( is_array( $function ) ) { 365 if ( ! isset( $function['function'] ) ) { 366 throw new Store_Exception( 'query_invalid_function', 'Invalid aggregate function configuration.' ); 367 } 368 369 $as = isset( $function['as'] ) ? esc_sql( sanitize_key( $function['as'] ) ) : strtolower( $function['function'] ) . '_' . $field; 370 $query_field = isset( $function['expression'] ) ? $this->prepare_math_expression( $function['expression'], $field ) : $table_field; 371 $function = $function['function']; 372 } else { 373 $as = strtolower( $function ) . '_' . $field; 374 $query_field = $table_field; 375 } 359 376 360 377 // Ensure the function is supported. … … 364 381 } 365 382 366 $function = strtolower( $function ); 367 $this->query_fields[] = "$function_upper($table_field) AS {$function}_{$field}"; 383 $this->query_fields[] = "$function_upper($query_field) AS $as"; 368 384 } 369 385 } … … 480 496 } 481 497 482 // Split the expression into parts. 483 $parts = preg_split( '/([+\-*\/])/', $expression, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY ); 484 485 // Process each part. 498 // Handle parentheses by processing inner expressions first 499 while ( preg_match( '/\(([^()]+)\)/', $expression, $matches ) ) { 500 $inner_result = $this->process_simple_expression( $matches[1] ); 501 $expression = str_replace( $matches[0], $inner_result, $expression ); 502 } 503 504 // Process the remaining expression 505 return $this->process_simple_expression( $expression ); 506 } 507 508 /** 509 * Processes a simple math expression without parentheses 510 * 511 * @param string $expression The simple math expression 512 * @return string 513 */ 514 protected function process_simple_expression( $expression ) { 515 // Enhanced regex to handle operators, negative numbers, decimals, and functions 516 $pattern = '/([+\-*\/])|(\w+\s*\()|(\))|(-?\d*\.?\d+)|([a-zA-Z_][a-zA-Z0-9_]*)/'; 517 518 preg_match_all( $pattern, $expression, $matches, PREG_OFFSET_CAPTURE ); 519 486 520 $processed_parts = array(); 487 foreach ( $parts as $part ) { 488 $part = trim( $part ); 489 490 // If it's an operator, add it directly 491 if ( in_array( $part, array( '+', '-', '*', '/' ), true ) ) { 492 $processed_parts[] = $part; 521 $i = 0; 522 $total = count( $matches[0] ); 523 524 while ( $i < $total ) { 525 $match = $matches[0][ $i ][0]; 526 $match = trim( $match ); 527 ++$i; 528 529 if ( empty( $match ) ) { 493 530 continue; 494 531 } 495 532 496 // Numbers.497 if ( is_numeric( $part) ) {498 $processed_parts[] = esc_sql( (float) $part );533 // Operators 534 if ( preg_match( '/^[+\-*\/]$/', $match ) ) { 535 $processed_parts[] = $match; 499 536 continue; 500 537 } 501 538 502 // If it's a field reference, prefix it 503 $prefixed_field = $this->prefix_field( esc_sql( sanitize_key( $part ) ) ); 504 505 if ( empty( $prefixed_field ) ) { 506 throw new Store_Exception( 'query_invalid_field', 'Invalid field in math expression.' ); 507 } 508 509 if ( ! empty( $prefixed_field ) ) { 539 // SQL Functions (e.g., ABS, ROUND, etc.) 540 if ( preg_match( '/^(\w+)\s*\($/', $match ) ) { 541 $function = trim( str_replace( '(', '', $match ) ); 542 543 // Validate allowed functions 544 $allowed_functions = array( 'ABS', 'ROUND', 'CEIL', 'FLOOR', 'SQRT', 'POW' ); 545 if ( ! in_array( strtoupper( $function ), $allowed_functions, true ) ) { 546 throw new Store_Exception( 'query_invalid_function', 'Invalid function in math expression.' ); 547 } 548 549 $processed_parts[] = strtoupper( $function ) . '('; 550 continue; 551 } 552 553 // Closing parenthesis 554 if ( ')' === $match ) { 555 $processed_parts[] = ')'; 556 continue; 557 } 558 559 // Numbers (including negative and decimals) 560 if ( is_numeric( $match ) ) { 561 $processed_parts[] = esc_sql( (float) $match ); 562 continue; 563 } 564 565 // Field references 566 if ( preg_match( '/^[a-zA-Z_][a-zA-Z0-9_]*$/', $match ) ) { 567 $prefixed_field = $this->prefix_field( esc_sql( sanitize_key( $match ) ) ); 568 569 if ( empty( $prefixed_field ) ) { 570 throw new Store_Exception( 'query_invalid_field', 'Invalid field in math expression: ' . $match ); 571 } 572 510 573 $processed_parts[] = $prefixed_field; 511 574 continue; 512 575 } 576 577 // If we get here, it's an unrecognized token 578 throw new Store_Exception( 'query_invalid_expression', 'Invalid token in math expression: ' . $match ); 513 579 } 514 580 -
hizzle-downloads/tags/1.2.2/vendor/hizzle/store/src/REST_Controller.php
r3287466 r3356445 79 79 } 80 80 81 $collection_params = $this->get_collection_params(); 82 81 83 // METHODS to CREATE new records, READ the entire collection, or DELETE the entire collection. 82 84 register_rest_route( … … 90 92 'callback' => array( $this, 'get_items' ), 91 93 'permission_callback' => array( $this, 'get_items_permissions_check' ), 92 'args' => $ this->get_collection_params(),94 'args' => $collection_params, 93 95 ), 94 96 … … 108 110 'permission_callback' => array( $this, 'create_item_permissions_check' ), 109 111 'args' => array_merge( 110 $this->get_collection_params(), 112 array_diff_key( 113 $collection_params, 114 array( 115 'paged' => true, 116 'per_page' => true, 117 'offset' => true, 118 'order' => true, 119 'orderby' => true, 120 ) 121 ), 111 122 array( 112 123 'bulk_update' => array( … … 341 352 'permission_callback' => array( $this, 'get_items_permissions_check' ), 342 353 'args' => array_merge( 343 $ this->get_collection_params(),354 $collection_params, 344 355 array( 345 356 'aggregate' => array( -
hizzle-downloads/tags/1.2.2/vendor/hizzle/store/src/Record.php
r3287466 r3356445 253 253 254 254 try { 255 256 255 $this->get_collection()->delete( $this, $force_delete ); 257 256 return true; 258 259 257 } catch ( Store_Exception $e ) { 260 258 return new \WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() ); … … 511 509 // If this is an enum or boolean, record the change. 512 510 if ( $object->is_boolean() || $object->is_tokens || ! empty( $object->enum ) ) { 513 514 511 if ( ! $this->exists() || $value !== $this->data[ $prop ] ) { 515 512 $this->enum_transition[ $prop ] = array( … … 603 600 604 601 if ( ! is_array( $string_or_array ) ) { 605 606 602 if ( $strict ) { 607 603 $string_or_array = preg_split( '/,+/', $string_or_array, -1, PREG_SPLIT_NO_EMPTY ); -
hizzle-downloads/trunk/hizzle-downloads.php
r3287466 r3356445 4 4 * Plugin URI: https://hizzle.co/download-manager/ 5 5 * Description: A lightweight download manager plugin. 6 * Version: 1.2. 16 * Version: 1.2.2 7 7 * Author: Hizzle 8 8 * Author URI: https://hizzle.co … … 22 22 23 23 if ( ! defined( 'HIZZLE_DOWNLOADS_VERSION' ) ) { 24 define( 'HIZZLE_DOWNLOADS_VERSION', '1.2. 1' );24 define( 'HIZZLE_DOWNLOADS_VERSION', '1.2.2' ); 25 25 } 26 26 -
hizzle-downloads/trunk/readme.txt
r3287466 r3356445 1 1 === Simple Download Manager - Hizzle Downloads === 2 2 Contributors: picocodes, mutendebrian 3 Tags: files, downloads, digital downloads 3 Tags: files, downloads, digital downloads, download manager, restrict downloads 4 4 Requires at least: 4.9 5 5 Tested up to: 6.7 6 6 Requires PHP: 5.6 7 Version: 1.2. 18 Stable tag: 1.2. 17 Version: 1.2.2 8 Stable tag: 1.2.2 9 9 License: GPLv3 10 10 License URI: https://www.gnu.org/licenses/gpl-3.0.html 11 11 Donate link: https://noptin.com/products/?utm_source=wp-repo&utm_medium=donate&utm_campaign=readme 12 12 13 Allow your website visitors to download files from your site. Optionally restrict downloads by user role, ip address, etc 13 Easily add, restrict, and track digital downloads in WordPress — protect files with passwords, user roles, IPs, or subscriber access. 14 14 15 15 == Description == 16 16 17 This plugin allows you to:- 17 **A simple WordPress download manager for secure file sharing, access control, and download tracking — perfect for digital products.** 18 ★★★★★<br> 18 19 19 - Add unlimited downloadable files. 20 - Optionally protect each downloadable file with a password. 21 - Restrict downloads to specific user roles. 22 - Restrict downloads to specific ip addresses. 23 - Restrict downloads to specific users. 24 - Restrict downloads to newsletter subscribers. 25 - Track file downloads. 20 Do you need a simple yet powerful way to manage file downloads on your WordPress site? This plugin makes it easy to upload, organize, and control access to downloadable files of any type. Whether you are sharing free resources, selling digital products, or delivering private documents, this plugin gives you full control over who can download your files and when. 21 22 With unlimited downloads, flexible restrictions, and detailed tracking, you can confidently provide files to your audience while keeping them secure. 23 24 = Key Features = 25 26 - **Add unlimited downloadable files** – Add and manage as many downloadable files as you need, with no limits. 27 - **Password Protection** – Protect individual files with custom passwords so only authorized users can access them. 28 - **Restrict downloads to specific user roles** – Control file access based on WordPress user roles, ensuring that only administrators, editors, subscribers, or custom roles can download. 29 - **Restrict downloads to specific IP addresses** – Restrict downloads to specific IP addresses to prevent abuse or unauthorized sharing. 30 - **Restrict downloads to specific users** – Assign downloads to specific registered users for secure, private file delivery. 31 - **Restrict downloads to newsletter subscribers** – Restrict downloads to Noptin newsletter subscriber, making it an excellent tool for lead generation. 32 - **Track file downloads** – Track every file download with detailed statistics, helping you understand how your files are being accessed. 33 - **Simple Management** – A user-friendly interface makes uploading and managing files straightforward, even for beginners. 34 35 = Why Use This Plugin? = 36 37 Managing downloads manually in WordPress can be difficult. Links can be shared publicly, access can’t easily be restricted, and tracking is limited. This plugin solves those problems by giving you advanced tools to: 38 39 - Protect digital products such as **software, themes, and plugins**. 40 - Share private **PDF documents, contracts, or reports** securely with clients. 41 - Provide exclusive resources like **eBooks, whitepapers, and templates** to email subscribers. 42 - Control access to files for **membership sites and online courses**. 43 - Monitor and analyze download activity to make better business decisions. 44 45 = Benefits for Your Website = 46 47 By installing this plugin, you’ll be able to: 48 49 - Grow your email list by offering subscriber-only downloads. 50 - Monetize your website by controlling access to premium resources. 51 - Increase security by preventing unauthorized downloads and link sharing. 52 - Gain insights into how your downloads are performing. 53 54 Whether you’re a blogger, developer, marketer, educator, or business owner, this plugin gives you all the tools you need to manage file downloads effectively in WordPress. 55 56 Take control of your downloads today and provide a seamless, secure experience for your users. 26 57 27 58 == Installation == … … 37 68 = Can I see how many times each file has been downloaded? = 38 69 39 Yes, Noptin allows you to view how many times each file has been downloaded, as well as which users have downloaded the file. This information is available in the Noptindashboard, where you can see a list of all your downloadable files and their download stats. You can also see which users have downloaded the files, and view their information and activity in your mailing list. This can be useful for tracking the popularity of your files and understanding which users are interested in them.70 Yes, Hizzle Downloads allows you to view how many times each file has been downloaded, as well as which users have downloaded the file. This information is available in the Hizzle Downloads dashboard, where you can see a list of all your downloadable files and their download stats. You can also see which users have downloaded the files, and view their information and activity in your mailing list. This can be useful for tracking the popularity of your files and understanding which users are interested in them. 40 71 41 72 = Can I restrict downloads by user role or newsletter subscription status? = 42 73 43 Yes, Noptin allows you to restrict downloads by user role or newsletter subscription status. This means that you can choose which users are able to download your files, based on their user role on your website or their status as a newsletter subscriber. For example, you can make a file available only to users who are registered as members on your website, or only to users who have subscribed to your newsletter. This can be useful for creating exclusive content or offers for your users, and for encouraging people to sign up for your newsletter. You can set these restrictions for each of your downloadable files in the Noptinsettings.74 Yes, Hizzle Downloads allows you to restrict downloads by user role or newsletter subscription status. This means that you can choose which users are able to download your files, based on their user role on your website or their status as a newsletter subscriber. For example, you can make a file available only to users who are registered as members on your website, or only to users who have subscribed to your newsletter. This can be useful for creating exclusive content or offers for your users, and for encouraging people to sign up for your newsletter. You can set these restrictions for each of your downloadable files in the Hizzle Downloads settings. 44 75 45 76 = How do I display downloadable files? = … … 49 80 = How can I get in touch? = 50 81 51 Use the [contact form on our website](https://hizzle .co/contact-us/).82 Use the [contact form on our website](https://hizzlewp.com/contact/). 52 83 53 84 = How can I contribute? = … … 64 95 65 96 == Changelog == 97 98 = 1.2.2 = 99 - Update composer packages. 66 100 67 101 = 1.2.1 = … … 100 134 101 135 = 1.0.3 = 102 - Restrict downloads to newsletter subscribers.136 - Restrict downloads to Noptin newsletter subscribers. 103 137 - Add support for password protected downloads. 104 138 - Automatically update download files when there is a GitHub release. -
hizzle-downloads/trunk/vendor/composer/installed.json
r3287466 r3356445 54 54 { 55 55 "name": "hizzle/store", 56 "version": "0.2. 7",57 "version_normalized": "0.2. 7.0",56 "version": "0.2.11", 57 "version_normalized": "0.2.11.0", 58 58 "source": { 59 59 "type": "git", 60 60 "url": "https://github.com/hizzle-co/datastore.git", 61 "reference": " 6620679060f7b64e0a417758a328173e8b5ec729"61 "reference": "a6e17c589443de791e9bf9eb9c2536c7401f09c2" 62 62 }, 63 63 "dist": { 64 64 "type": "zip", 65 "url": "https://api.github.com/repos/hizzle-co/datastore/zipball/ 6620679060f7b64e0a417758a328173e8b5ec729",66 "reference": " 6620679060f7b64e0a417758a328173e8b5ec729",65 "url": "https://api.github.com/repos/hizzle-co/datastore/zipball/a6e17c589443de791e9bf9eb9c2536c7401f09c2", 66 "reference": "a6e17c589443de791e9bf9eb9c2536c7401f09c2", 67 67 "shasum": "" 68 68 }, … … 70 70 "php": ">=5.3.0" 71 71 }, 72 "time": "2025-0 5-03T10:20:52+00:00",72 "time": "2025-08-20T05:32:06+00:00", 73 73 "type": "library", 74 74 "installation-source": "dist", … … 99 99 "support": { 100 100 "issues": "https://github.com/hizzle-co/datastore/issues", 101 "source": "https://github.com/hizzle-co/datastore/tree/0.2. 7"101 "source": "https://github.com/hizzle-co/datastore/tree/0.2.11" 102 102 }, 103 103 "install-path": "../hizzle/store" -
hizzle-downloads/trunk/vendor/composer/installed.php
r3287466 r3356445 4 4 'pretty_version' => 'dev-main', 5 5 'version' => 'dev-main', 6 'reference' => ' eacb4c22f68cba9ad5d53a20b1db45c4f10bad5a',6 'reference' => '54e05e6de537c0de0a2c4e916ec2394d169ccd55', 7 7 'type' => 'library', 8 8 'install_path' => __DIR__ . '/../../', … … 14 14 'pretty_version' => 'dev-main', 15 15 'version' => 'dev-main', 16 'reference' => ' eacb4c22f68cba9ad5d53a20b1db45c4f10bad5a',16 'reference' => '54e05e6de537c0de0a2c4e916ec2394d169ccd55', 17 17 'type' => 'library', 18 18 'install_path' => __DIR__ . '/../../', … … 30 30 ), 31 31 'hizzle/store' => array( 32 'pretty_version' => '0.2. 7',33 'version' => '0.2. 7.0',34 'reference' => ' 6620679060f7b64e0a417758a328173e8b5ec729',32 'pretty_version' => '0.2.11', 33 'version' => '0.2.11.0', 34 'reference' => 'a6e17c589443de791e9bf9eb9c2536c7401f09c2', 35 35 'type' => 'library', 36 36 'install_path' => __DIR__ . '/../hizzle/store', -
hizzle-downloads/trunk/vendor/hizzle/store/example-plugin.php
r3287466 r3356445 4 4 Plugin URI: https://hizzle.co/ 5 5 Description: Hizzle Data Stores 6 Version: 0.2. 76 Version: 0.2.11 7 7 Author: picocodes 8 8 Author URI: https://github.com/picocodes/ -
hizzle-downloads/trunk/vendor/hizzle/store/src/Collection.php
r3287466 r3356445 427 427 $schema .= "PRIMARY KEY ($cols),\n"; // Maintain 2 spaces between key and opening bracket. 428 428 } elseif ( 'unique' === $index ) { 429 430 429 foreach ( $cols as $prop => $index ) { 431 430 $schema .= "UNIQUE KEY $prop ($index),\n"; … … 727 726 728 727 /** 728 * Returns a prop's cache key. 729 * 730 * @param string $prop The prop to get the cache key for. 731 * @return string The cache key. 732 */ 733 private function get_prop_cache_key( $prop ) { 734 $current_version = 'v2'; // Update this version when the cache key structure changes. 735 return $this->hook_prefix( $current_version . '_ids_by_' . $prop, true ); 736 } 737 738 /** 729 739 * Retrieves an ID by a given prop. 730 740 * … … 741 751 742 752 // Try the cache. 743 $value = trim( $value ); 744 $id = wp_cache_get( $value, $this->hook_prefix( 'ids_by_' . $prop, true ) ); 753 $value = trim( $value ); 754 $cache_key = $this->get_prop_cache_key( $prop ); 755 $id = wp_cache_get( $value, $cache_key ); 745 756 746 757 // Maybe retrieve from the db. … … 753 764 754 765 // Update the cache. 755 wp_cache_set( $value, $id, $ this->hook_prefix( 'ids_by_' . $prop, true ));766 wp_cache_set( $value, $id, $cache_key, empty( $id ) ? MINUTE_IN_SECONDS : HOUR_IN_SECONDS ); 756 767 } 757 768 … … 778 789 // Date fields. 779 790 if ( $value instanceof Date_Time ) { 780 781 791 if ( ! empty( $this->props[ $key ] ) && 'date' === strtolower( $this->props[ $key ]->type ) ) { 782 792 $value = $value->utc( 'Y-m-d' ); … … 846 856 $record->apply_changes(); 847 857 848 // Clear the cache.849 $this->clear_cache( $record ->get_data());858 // Clear any stale cache entries. 859 $this->clear_cache( $record ); 850 860 851 861 // Fires after creating a record. … … 915 925 apply_filters( $this->hook_prefix( 'insert_formats', true ), $formats, $record ) 916 926 ); 927 928 if ( empty( $result ) && ! empty( $wpdb->last_error ) ) { 929 noptin_error_log( 'Error creating record: ' . $wpdb->last_error ); 930 } 917 931 918 932 return $result ? $wpdb->insert_id : 0; … … 1013 1027 1014 1028 if ( $prop->is_meta_key_multiple ) { 1015 1016 1029 $new = (array) $new; 1017 1030 $to_delete = array_diff( $current, $new ); … … 1108 1121 $this->save_defaults( $record ); 1109 1122 $raw_data = $record->get_data(); 1110 1111 1123 } elseif ( empty( $raw_data ) ) { 1112 1124 $this->not_found(); … … 1124 1136 // Cache the record data. 1125 1137 $this->update_cache( $data ); 1126 1127 1138 } 1128 1139 … … 1203 1214 // Fires before updating a record. 1204 1215 do_action( $this->hook_prefix( 'before_update', true ), $record ); 1216 1217 // Clear cache early to prevent race conditions and stale data 1218 // Do this before any database operations 1219 $this->clear_cache( $record ); 1205 1220 1206 1221 $raw_changes = array_keys( $record->get_changes() ); … … 1235 1250 $record->apply_changes(); 1236 1251 1237 // Clear the cache.1238 $this->clear_cache( $record->get_data() );1239 1240 1252 if ( $has_changes ) { 1241 1253 … … 1264 1276 1265 1277 // Invalidate cache. 1266 $this->clear_cache( $record ->get_data());1278 $this->clear_cache( $record ); 1267 1279 1268 1280 // If this is a CPT, delete the post. … … 1517 1529 1518 1530 // Ensure we have an array. 1519 if ( ! is_array( $record ) ) {1531 if ( ! is_array( $record ) || empty( $record['id'] ) ) { 1520 1532 return; 1521 1533 } 1522 1534 1535 // Cache lookup values for faster queries 1523 1536 foreach ( $this->get_cache_keys() as $key ) { 1524 1537 if ( isset( $record[ $key ] ) && ! empty( $record[ $key ] ) ) { 1525 wp_cache_set( $record[ $key ], $record['id'], $this-> hook_prefix( 'ids_by_' . $key, true ), WEEK_IN_SECONDS );1538 wp_cache_set( $record[ $key ], $record['id'], $this->get_prop_cache_key( $key ), HOUR_IN_SECONDS ); 1526 1539 } 1527 1540 } 1528 1541 1529 1542 // Cache the entire record. 1530 wp_cache_set( $record['id'], $record, $this->get_full_name(), DAY_IN_SECONDS );1543 wp_cache_set( $record['id'], $record, $this->get_full_name(), HOUR_IN_SECONDS ); 1531 1544 } 1532 1545 … … 1534 1547 * Clean caches. 1535 1548 * 1536 * @param object$record The raw db record.1549 * @param Record $record The raw db record. 1537 1550 */ 1538 1551 public function clear_cache( $record ) { 1539 1552 1540 $record = (object) $record;1541 1542 1553 foreach ( $this->get_cache_keys() as $key ) { 1543 if ( ! is_null( $record->$key ) && '' !== $record->$key ) { 1544 wp_cache_delete( $record->$key, $this->hook_prefix( 'ids_by_' . $key, true ) ); 1545 } 1546 } 1547 1548 wp_cache_delete( $record->id, $this->get_full_name() ); 1554 $value = $record->get( $key ); 1555 if ( is_string( $value ) && '' !== $value ) { 1556 wp_cache_delete( $value, $this->get_prop_cache_key( $key ) ); 1557 } 1558 } 1559 1560 // Bail early if the record doesn't exist. 1561 if ( ! $record->exists() ) { 1562 return; 1563 } 1564 1565 // Clear the main record cache. 1566 wp_cache_delete( $record->get_id(), $this->get_full_name() ); 1567 1568 // Clear any potential old cache entries by attempting to fetch and clear them 1569 // This handles cases where cache keys have changed due to updates 1570 $this->clear_stale_cache_entries( $record->get_id() ); 1571 } 1572 1573 /** 1574 * Clears potentially stale cache entries for a record. 1575 * 1576 * @param int $record_id The record ID. 1577 */ 1578 private function clear_stale_cache_entries( $record_id ) { 1579 global $wpdb; 1580 1581 // For updates, we need to clear cache for old values that might have changed 1582 if ( ! empty( $record_id ) ) { 1583 1584 // Get the current database values to clear any old cached entries 1585 $table_name = $this->get_db_table_name(); 1586 $current_data = $wpdb->get_row( 1587 $wpdb->prepare( "SELECT * FROM $table_name WHERE id = %d", $record_id ), // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 1588 ARRAY_A 1589 ); 1590 1591 if ( $current_data ) { 1592 foreach ( $this->get_cache_keys() as $key ) { 1593 $value = $current_data[ $key ] ?? null; 1594 if ( is_string( $value ) && '' !== $value ) { 1595 // Clear cache for database values (in case they differ from the record object) 1596 wp_cache_delete( $value, $this->get_prop_cache_key( $key ) ); 1597 } 1598 } 1599 } 1600 } 1549 1601 } 1550 1602 … … 1597 1649 * @param string $default The default label. 1598 1650 */ 1599 public function get_label( $key, $default ) {1600 return isset( $this->labels[ $key ] ) ? $this->labels[ $key ] : $default;1651 public function get_label( $key, $default_value ) { 1652 return $this->labels[ $key ] ?? $default_value; 1601 1653 } 1602 1654 } -
hizzle-downloads/trunk/vendor/hizzle/store/src/Query.php
r3245426 r3356445 306 306 307 307 // Prepare aggregate fields. 308 foreach ( $aggregate_fields as $field => $function ) { 308 foreach ( $aggregate_fields as $field => $aggregate ) { 309 310 if ( ! is_array( $aggregate ) ) { 311 $aggregate = wp_parse_list( $aggregate ); 312 } 309 313 310 314 // Handle CASE expressions 311 if ( is_array( $ function ) && isset( $function['case'] ) ) {312 $case_field = $this->prefix_field( esc_sql( sanitize_key( $ function['case']['field'] ) ) );315 if ( is_array( $aggregate ) && isset( $aggregate['case'] ) ) { 316 $case_field = $this->prefix_field( esc_sql( sanitize_key( $aggregate['case']['field'] ) ) ); 313 317 if ( empty( $case_field ) ) { 314 318 throw new Store_Exception( 'query_invalid_field', 'Invalid case field.' ); … … 316 320 317 321 $case_sql = "CASE $case_field"; 318 foreach ( $ function['case']['when'] as $when => $then ) {319 $when = esc_sql( $when );320 $then_sql = $this->prepare_case_then( $then );322 foreach ( $aggregate['case']['when'] as $when => $then ) { 323 $when = esc_sql( $when ); 324 $then_sql = $this->prepare_case_then( $then ); 321 325 $case_sql .= " WHEN '$when' THEN $then_sql"; 322 326 } 323 327 324 if ( isset( $ function['case']['else'] ) ) {325 $else_sql = $this->prepare_case_then( $function['case']['else'] );328 if ( isset( $aggregate['case']['else'] ) ) { 329 $else_sql = $this->prepare_case_then( $aggregate['case']['else'] ); 326 330 $case_sql .= " ELSE $else_sql"; 327 331 } 328 332 329 $case_sql .= " END";333 $case_sql .= ' END'; 330 334 331 335 // Handle optional aggregate function wrapper 332 if ( isset( $ function['function'] ) ) {333 $agg_function = strtoupper( $ function['function'] );336 if ( isset( $aggregate['function'] ) ) { 337 $agg_function = strtoupper( $aggregate['function'] ); 334 338 if ( ! in_array( $agg_function, array( 'AVG', 'COUNT', 'MAX', 'MIN', 'SUM' ), true ) ) { 335 339 throw new Store_Exception( 'query_invalid_function', 'Invalid aggregate function.' ); … … 339 343 340 344 // Handle optional math operations 341 if ( isset( $ function['math'] ) ) {342 $math_op = $this->prepare_math_expression( $function['math'] );345 if ( isset( $aggregate['math'] ) ) { 346 $math_op = $this->prepare_math_expression( $aggregate['math'] ); 343 347 $case_sql = "($case_sql $math_op)"; 344 348 } … … 356 360 } 357 361 358 foreach ( wp_parse_list( $function ) as $function ) { 362 foreach ( array_filter( $aggregate ) as $function ) { 363 364 if ( is_array( $function ) ) { 365 if ( ! isset( $function['function'] ) ) { 366 throw new Store_Exception( 'query_invalid_function', 'Invalid aggregate function configuration.' ); 367 } 368 369 $as = isset( $function['as'] ) ? esc_sql( sanitize_key( $function['as'] ) ) : strtolower( $function['function'] ) . '_' . $field; 370 $query_field = isset( $function['expression'] ) ? $this->prepare_math_expression( $function['expression'], $field ) : $table_field; 371 $function = $function['function']; 372 } else { 373 $as = strtolower( $function ) . '_' . $field; 374 $query_field = $table_field; 375 } 359 376 360 377 // Ensure the function is supported. … … 364 381 } 365 382 366 $function = strtolower( $function ); 367 $this->query_fields[] = "$function_upper($table_field) AS {$function}_{$field}"; 383 $this->query_fields[] = "$function_upper($query_field) AS $as"; 368 384 } 369 385 } … … 480 496 } 481 497 482 // Split the expression into parts. 483 $parts = preg_split( '/([+\-*\/])/', $expression, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY ); 484 485 // Process each part. 498 // Handle parentheses by processing inner expressions first 499 while ( preg_match( '/\(([^()]+)\)/', $expression, $matches ) ) { 500 $inner_result = $this->process_simple_expression( $matches[1] ); 501 $expression = str_replace( $matches[0], $inner_result, $expression ); 502 } 503 504 // Process the remaining expression 505 return $this->process_simple_expression( $expression ); 506 } 507 508 /** 509 * Processes a simple math expression without parentheses 510 * 511 * @param string $expression The simple math expression 512 * @return string 513 */ 514 protected function process_simple_expression( $expression ) { 515 // Enhanced regex to handle operators, negative numbers, decimals, and functions 516 $pattern = '/([+\-*\/])|(\w+\s*\()|(\))|(-?\d*\.?\d+)|([a-zA-Z_][a-zA-Z0-9_]*)/'; 517 518 preg_match_all( $pattern, $expression, $matches, PREG_OFFSET_CAPTURE ); 519 486 520 $processed_parts = array(); 487 foreach ( $parts as $part ) { 488 $part = trim( $part ); 489 490 // If it's an operator, add it directly 491 if ( in_array( $part, array( '+', '-', '*', '/' ), true ) ) { 492 $processed_parts[] = $part; 521 $i = 0; 522 $total = count( $matches[0] ); 523 524 while ( $i < $total ) { 525 $match = $matches[0][ $i ][0]; 526 $match = trim( $match ); 527 ++$i; 528 529 if ( empty( $match ) ) { 493 530 continue; 494 531 } 495 532 496 // Numbers.497 if ( is_numeric( $part) ) {498 $processed_parts[] = esc_sql( (float) $part );533 // Operators 534 if ( preg_match( '/^[+\-*\/]$/', $match ) ) { 535 $processed_parts[] = $match; 499 536 continue; 500 537 } 501 538 502 // If it's a field reference, prefix it 503 $prefixed_field = $this->prefix_field( esc_sql( sanitize_key( $part ) ) ); 504 505 if ( empty( $prefixed_field ) ) { 506 throw new Store_Exception( 'query_invalid_field', 'Invalid field in math expression.' ); 507 } 508 509 if ( ! empty( $prefixed_field ) ) { 539 // SQL Functions (e.g., ABS, ROUND, etc.) 540 if ( preg_match( '/^(\w+)\s*\($/', $match ) ) { 541 $function = trim( str_replace( '(', '', $match ) ); 542 543 // Validate allowed functions 544 $allowed_functions = array( 'ABS', 'ROUND', 'CEIL', 'FLOOR', 'SQRT', 'POW' ); 545 if ( ! in_array( strtoupper( $function ), $allowed_functions, true ) ) { 546 throw new Store_Exception( 'query_invalid_function', 'Invalid function in math expression.' ); 547 } 548 549 $processed_parts[] = strtoupper( $function ) . '('; 550 continue; 551 } 552 553 // Closing parenthesis 554 if ( ')' === $match ) { 555 $processed_parts[] = ')'; 556 continue; 557 } 558 559 // Numbers (including negative and decimals) 560 if ( is_numeric( $match ) ) { 561 $processed_parts[] = esc_sql( (float) $match ); 562 continue; 563 } 564 565 // Field references 566 if ( preg_match( '/^[a-zA-Z_][a-zA-Z0-9_]*$/', $match ) ) { 567 $prefixed_field = $this->prefix_field( esc_sql( sanitize_key( $match ) ) ); 568 569 if ( empty( $prefixed_field ) ) { 570 throw new Store_Exception( 'query_invalid_field', 'Invalid field in math expression: ' . $match ); 571 } 572 510 573 $processed_parts[] = $prefixed_field; 511 574 continue; 512 575 } 576 577 // If we get here, it's an unrecognized token 578 throw new Store_Exception( 'query_invalid_expression', 'Invalid token in math expression: ' . $match ); 513 579 } 514 580 -
hizzle-downloads/trunk/vendor/hizzle/store/src/REST_Controller.php
r3287466 r3356445 79 79 } 80 80 81 $collection_params = $this->get_collection_params(); 82 81 83 // METHODS to CREATE new records, READ the entire collection, or DELETE the entire collection. 82 84 register_rest_route( … … 90 92 'callback' => array( $this, 'get_items' ), 91 93 'permission_callback' => array( $this, 'get_items_permissions_check' ), 92 'args' => $ this->get_collection_params(),94 'args' => $collection_params, 93 95 ), 94 96 … … 108 110 'permission_callback' => array( $this, 'create_item_permissions_check' ), 109 111 'args' => array_merge( 110 $this->get_collection_params(), 112 array_diff_key( 113 $collection_params, 114 array( 115 'paged' => true, 116 'per_page' => true, 117 'offset' => true, 118 'order' => true, 119 'orderby' => true, 120 ) 121 ), 111 122 array( 112 123 'bulk_update' => array( … … 341 352 'permission_callback' => array( $this, 'get_items_permissions_check' ), 342 353 'args' => array_merge( 343 $ this->get_collection_params(),354 $collection_params, 344 355 array( 345 356 'aggregate' => array( -
hizzle-downloads/trunk/vendor/hizzle/store/src/Record.php
r3287466 r3356445 253 253 254 254 try { 255 256 255 $this->get_collection()->delete( $this, $force_delete ); 257 256 return true; 258 259 257 } catch ( Store_Exception $e ) { 260 258 return new \WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() ); … … 511 509 // If this is an enum or boolean, record the change. 512 510 if ( $object->is_boolean() || $object->is_tokens || ! empty( $object->enum ) ) { 513 514 511 if ( ! $this->exists() || $value !== $this->data[ $prop ] ) { 515 512 $this->enum_transition[ $prop ] = array( … … 603 600 604 601 if ( ! is_array( $string_or_array ) ) { 605 606 602 if ( $strict ) { 607 603 $string_or_array = preg_split( '/,+/', $string_or_array, -1, PREG_SPLIT_NO_EMPTY );
Note: See TracChangeset
for help on using the changeset viewer.