PluginProbe
GutSlider – All in One Slider and Carousel Blocks for Gutenberg / 2.13.2
GutSlider – All in One Slider and Carousel Blocks for Gutenberg v2.13.2
3.1.0 3.0.0 2.13.2 2.13.1 2.13.0 trunk 1.0.0 2.1.0 2.10.0 2.10.1 2.11.0 2.11.1 2.11.2 2.11.3 2.11.4 2.12.0 2.2.1 2.2.2 2.3.0 2.4.0 2.5.1 2.5.3 2.5.4 2.5.5 2.6.1 All 56 releases
slider-blocks / includes / Blocks / Pattern.php

Pattern.php in GutSlider – All in One Slider and Carousel Blocks for Gutenberg 2.13.2, at includes/Blocks/Pattern.php

212 lines 7.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace GutSlider\Blocks;
3
4 use WP_REST_Request;
5 use WP_REST_Server;
6 use WP_Error;
7
8 if (!defined('ABSPATH')) {
9 exit;
10 }
11
12 if (!class_exists('Pattern')) {
13
14 class Pattern
15 {
16
17 const TRANSIENT_KEY = 'gutslider_patterns_cache';
18 const TRANSIENT_EXPIRY = HOUR_IN_SECONDS * 12;
19 const REMOTE_URL = 'https://gutslider.com/wp-json/divo-patterns/v1/patterns';
20 const NAMESPACE = 'gutslider/v1';
21 const ROUTE = '/patterns';
22
23 private static $instance = null;
24
25 public function __construct()
26 {
27 add_action('rest_api_init', [$this, 'register_routes']);
28 }
29
30 public function register_routes()
31 {
32 register_rest_route(
33 self::NAMESPACE,
34 self::ROUTE,
35 [
36 'methods' => WP_REST_Server::READABLE,
37 'callback' => [$this, 'get_patterns'],
38 'permission_callback' => [$this, 'permissions_check'],
39 'args' => [
40 'refresh' => [
41 'type' => 'boolean',
42 'default' => false,
43 ],
44 'page' => [
45 'type' => 'integer',
46 'default' => 1,
47 ],
48 'per_page' => [
49 'type' => 'integer',
50 'default' => 20,
51 ],
52 'category' => [
53 'type' => 'string',
54 'default' => '',
55 ],
56 ],
57 ]
58 );
59 }
60
61 /**
62 * Only logged-in users with edit_posts capability can call this endpoint.
63 */
64 public function permissions_check()
65 {
66 return current_user_can('edit_posts');
67 }
68
69 public function get_patterns(WP_REST_Request $request)
70 {
71 $force_refresh = (bool) $request->get_param('refresh');
72 $page = (int) $request->get_param('page');
73 $per_page = (int) $request->get_param('per_page');
74 $category = (string) $request->get_param('category');
75
76 if (!$force_refresh) {
77 $cached = get_transient(self::TRANSIENT_KEY);
78 if (false !== $cached) {
79 return $this->paginate_patterns($cached, $page, $per_page, $category);
80 }
81 }
82
83 $chunks = [];
84 $remote_page = 1;
85 $per_page_remote = 100;
86 $max_pages = 5;
87
88 while ($remote_page <= $max_pages) {
89 $url = add_query_arg(
90 ['page' => $remote_page, 'per_page' => $per_page_remote],
91 self::REMOTE_URL
92 );
93
94 $response = wp_remote_get($url, [
95 'timeout' => 10,
96 'user-agent' => 'GutSliderBlocks/' . GUTSLIDER_VERSION,
97 ]);
98
99 if (is_wp_error($response)) {
100 if (!empty($chunks)) break;
101 return new WP_Error('gutslider_fetch_failed', $response->get_error_message(), ['status' => 502]);
102 }
103
104 $code = wp_remote_retrieve_response_code($response);
105 if (200 !== (int) $code) {
106 if (!empty($chunks)) break;
107 return new WP_Error('gutslider_fetch_failed', sprintf('Remote API returned HTTP %d.', $code), ['status' => 502]);
108 }
109
110 $data = json_decode(wp_remote_retrieve_body($response), true);
111
112 if (!is_array($data) || empty($data)) {
113 break;
114 }
115
116 $chunks[] = $data;
117
118 if (count($data) < $per_page_remote) {
119 break;
120 }
121
122 $remote_page++;
123 }
124
125 $all_patterns = $chunks ? array_merge(...$chunks) : [];
126 $categories = $this->extract_categories($all_patterns);
127 $all_total = count($all_patterns);
128
129 $cache_data = [
130 'patterns' => $all_patterns,
131 'categories' => $categories,
132 'all_total' => $all_total,
133 ];
134
135 set_transient(self::TRANSIENT_KEY, $cache_data, self::TRANSIENT_EXPIRY);
136
137 return $this->paginate_patterns($cache_data, $page, $per_page, $category);
138 }
139
140 /**
141 * Paginate patterns array, optionally filtering by category.
142 * Accepts either the structured cache array or a raw patterns array (backwards compat).
143 */
144 private function paginate_patterns($cache, $page = 1, $per_page = 20, $category = '')
145 {
146 if (isset($cache['patterns'])) {
147 $all_patterns = $cache['patterns'];
148 $categories = $cache['categories'];
149 $all_total = $cache['all_total'];
150 } else {
151 $all_patterns = $cache;
152 $categories = $this->extract_categories($all_patterns);
153 $all_total = count($all_patterns);
154 }
155
156 if ($category !== '') {
157 $cat_lower = strtolower($category);
158 $all_patterns = array_values(array_filter($all_patterns, function ($p) use ($cat_lower) {
159 foreach ((array) ($p['categories'] ?? []) as $cat) {
160 $name = is_array($cat) ? ($cat['name'] ?? '') : (string) $cat;
161 if (strtolower(trim($name)) === $cat_lower) {
162 return true;
163 }
164 }
165 return false;
166 }));
167 }
168
169 $total = count($all_patterns);
170 $offset = ($page - 1) * $per_page;
171 $items = array_slice($all_patterns, $offset, $per_page);
172
173 return rest_ensure_response([
174 'items' => $items,
175 'total' => $total,
176 'all_total' => $all_total,
177 'page' => $page,
178 'per_page' => $per_page,
179 'total_pages' => $total > 0 ? (int) ceil($total / $per_page) : 1,
180 'categories' => $categories,
181 ]);
182 }
183
184 private function extract_categories($patterns)
185 {
186 $counts = [];
187 foreach ($patterns as $pattern) {
188 foreach ((array) ($pattern['categories'] ?? []) as $cat) {
189 $name = is_array($cat) ? ($cat['name'] ?? '') : (string) $cat;
190 $name = trim($name);
191 if ($name !== '') {
192 $counts[$name] = ($counts[$name] ?? 0) + 1;
193 }
194 }
195 }
196 $result = [];
197 foreach ($counts as $name => $count) {
198 $result[] = ['name' => $name, 'count' => $count];
199 }
200 usort($result, fn($a, $b) => strcmp($a['name'], $b['name']));
201 return $result;
202 }
203
204 public static function instance()
205 {
206 if (is_null(self::$instance)) {
207 self::$instance = new self();
208 }
209 return self::$instance;
210 }
211 }
212 }