PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks for WordPress Block Editor / 2.0.2
Blockenberg — 600+ Advanced Gutenberg Blocks for WordPress Block Editor v2.0.2
2.0.12 2.0.11 2.0.10 2.0.9 trunk 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8
blockenberg / blockenberg.php

blockenberg.php in Blockenberg — 600+ Advanced Gutenberg Blocks for WordPress Block Editor 2.0.2, at blockenberg.php

357 lines 13.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: Blockenberg
4 * Description: Advanced Gutenberg Blocks (Extensions for WordPress Editor)
5 * Version: 2.0.2
6 * Author: Blockenberg
7 * Text Domain: blockenberg
8 * Domain Path: /languages
9 * License: GPLv2 or later
10 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
11 */
12
13 defined( 'ABSPATH' ) || exit;
14
15 /**
16 * Enqueue common editor styles and scripts for all Blockenberg blocks
17 */
18 add_action( 'enqueue_block_editor_assets', function() {
19 wp_enqueue_style( 'dashicons' );
20 // Ensure Media Library modal assets are available for blocks using MediaUpload.
21 wp_enqueue_media();
22
23 // Common editor scripts (branded icons)
24 $editor_js = __DIR__ . '/assets/js/editor.js';
25 if ( file_exists( $editor_js ) ) {
26 wp_enqueue_script(
27 'bkbg-editor-common',
28 plugins_url( 'assets/js/editor.js', __FILE__ ),
29 array( 'wp-blocks', 'wp-dom-ready', 'wp-element' ),
30 filemtime( $editor_js ),
31 true
32 );
33 }
34 });
35
36 /**
37 * Enqueue editor styles in a way compatible with the iframe-based editor canvas.
38 */
39 add_action( 'enqueue_block_assets', function () {
40 // Avoid loading editor-only CSS on the frontend.
41 if ( ! is_admin() ) {
42 return;
43 }
44
45 // Layout system CSS (shared variables and utilities)
46 $layout_css = __DIR__ . '/assets/css/layout.css';
47 if ( file_exists( $layout_css ) ) {
48 wp_enqueue_style(
49 'bkbg-layout-system',
50 plugins_url( 'assets/css/layout.css', __FILE__ ),
51 array(),
52 filemtime( $layout_css )
53 );
54 }
55
56 // Common editor styles for all blocks
57 $editor_css = __DIR__ . '/assets/css/editor.css';
58 if ( file_exists( $editor_css ) ) {
59 wp_enqueue_style(
60 'bkbg-editor-common',
61 plugins_url( 'assets/css/editor.css', __FILE__ ),
62 array( 'bkbg-layout-system' ),
63 filemtime( $editor_css )
64 );
65 }
66 } );
67
68 /**
69 * Register block scripts with dependencies (but don't enqueue them yet)
70 */
71 add_action( 'init', function () {
72 $blocks_dir = __DIR__ . '/blocks/';
73
74 // Standard WordPress script dependencies for blocks
75 $script_dependencies = array(
76 'wp-blocks',
77 'wp-element',
78 'wp-i18n',
79 'wp-block-editor',
80 'wp-components',
81 'wp-dom-ready',
82 'wp-data'
83 );
84
85 // Standard WordPress style dependencies for blocks
86 $style_dependencies = array(
87 'dashicons'
88 );
89
90 // Register layout system CSS for frontend
91 $layout_css = __DIR__ . '/assets/css/layout.css';
92 if ( file_exists( $layout_css ) ) {
93 wp_register_style(
94 'bkbg-layout-system',
95 plugins_url( 'assets/css/layout.css', __FILE__ ),
96 array(),
97 filemtime( $layout_css )
98 );
99 }
100
101 // Automatically register scripts for all blocks
102 foreach ( glob( $blocks_dir . '*', GLOB_ONLYDIR ) as $block_dir ) {
103 $block_name = basename( $block_dir );
104 $script_file = $block_dir . '/index.js';
105
106 $style_file = $block_dir . '/style.css';
107 $frontend_file = $block_dir . '/frontend.js';
108
109 // Check if block has a JavaScript file
110 if ( file_exists( $script_file ) ) {
111 wp_register_script(
112 'bkbg-' . $block_name . '-editor',
113 plugins_url( 'blocks/' . $block_name . '/index.js', __FILE__ ),
114 $script_dependencies,
115 filemtime( $script_file ),
116 true
117 );
118 }
119
120 // Check if block has a CSS file
121 if ( file_exists( $style_file ) ) {
122 // Layout blocks need the layout system CSS
123 $block_style_deps = $style_dependencies;
124 if ( in_array( $block_name, array( 'section', 'row', 'column' ), true ) ) {
125 $block_style_deps[] = 'bkbg-layout-system';
126 }
127
128 wp_register_style(
129 'bkbg-' . $block_name . '-style',
130 plugins_url( 'blocks/' . $block_name . '/style.css', __FILE__ ),
131 $block_style_deps,
132 filemtime( $style_file )
133 );
134 }
135
136 // Check if block has a frontend JavaScript file
137 if ( file_exists( $frontend_file ) ) {
138 wp_register_script(
139 'bkbg-' . $block_name . '-frontend',
140 plugins_url( 'blocks/' . $block_name . '/frontend.js', __FILE__ ),
141 array(),
142 filemtime( $frontend_file ),
143 true
144 );
145 }
146 }
147
148 // Automatically register all blocks in the /blocks directory
149 foreach ( glob( __DIR__ . '/blocks/*/block.json' ) as $metadata ) {
150 register_block_type( dirname( $metadata ) );
151 }
152 } );
153
154 // Register custom block category and ensure Blockenberg blocks appear in it.
155 add_filter( 'block_categories_all', function( $categories, $block_editor_context ) {
156 // Prepend our custom category so it appears first.
157 array_unshift( $categories, array(
158 'slug' => 'blockenberg',
159 'title' => __( 'Blockenberg Blocks', 'blockenberg' ),
160 'icon' => null,
161 ) );
162 return $categories;
163 }, 10, 2 );
164
165 /**
166 * Register REST API endpoint for Post Grid block.
167 *
168 * Route: /wp-json/blockenberg/v1/post-grid
169 * Method: GET
170 * Params:
171 * - type: string (posts|pages|any CPT)
172 * - orderby: string (date|title|comment_count|...)
173 * - order: string (asc|desc)
174 * - per_page: int
175 * - offset: int
176 * - page: int (1-based)
177 * - excerpt_len: int (optional)
178 */
179 add_action( 'rest_api_init', function () {
180 register_rest_route( 'blockenberg/v1', '/post-grid', array(
181 'methods' => 'GET',
182 'permission_callback' => '__return_true',
183 'args' => array(
184 'type' => array( 'sanitize_callback' => 'sanitize_key' ),
185 'orderby' => array( 'sanitize_callback' => 'sanitize_key' ),
186 'order' => array( 'sanitize_callback' => 'sanitize_text_field' ),
187 'per_page' => array( 'sanitize_callback' => 'absint' ),
188 'offset' => array( 'sanitize_callback' => 'absint' ),
189 'page' => array( 'sanitize_callback' => 'absint' ),
190 'excerpt_len' => array( 'sanitize_callback' => 'absint' ),
191 ),
192 'callback' => function ( WP_REST_Request $request ) {
193 $requested_type = sanitize_key( $request->get_param( 'type' ) ?: 'post' );
194 if ( 'posts' === $requested_type ) {
195 $requested_type = 'post';
196 } elseif ( 'pages' === $requested_type ) {
197 $requested_type = 'page';
198 }
199
200 $public_post_types = get_post_types( array( 'public' => true ), 'names' );
201 $post_type = in_array( $requested_type, $public_post_types, true ) ? $requested_type : 'post';
202
203 $requested_orderby = sanitize_key( $request->get_param( 'orderby' ) ?: 'date' );
204 $allowed_orderby = array( 'date', 'title', 'modified', 'comment_count', 'rand', 'menu_order' );
205 $orderby = in_array( $requested_orderby, $allowed_orderby, true ) ? $requested_orderby : 'date';
206 $order = strtolower( (string) $request->get_param( 'order' ) ) === 'asc' ? 'ASC' : 'DESC';
207 $per_page = max( 1, absint( $request->get_param( 'per_page' ) ?: 6 ) );
208 $per_page = min( 50, $per_page );
209 $offset = max( 0, absint( $request->get_param( 'offset' ) ?: 0 ) );
210 $offset = min( 5000, $offset );
211 $page = max( 1, absint( $request->get_param( 'page' ) ?: 1 ) );
212 $page = min( 200, $page );
213 $excerpt_len = max( 5, absint( $request->get_param( 'excerpt_len' ) ?: 18 ) );
214 $excerpt_len = min( 80, $excerpt_len );
215
216 // Short cache for public non-product queries.
217 $is_product_query = ( 'product' === $post_type );
218 $cache_key = '';
219 if ( ! $is_product_query ) {
220 $cache_key = 'bkbg_post_grid_' . md5( wp_json_encode( array(
221 'type' => $post_type,
222 'orderby' => $orderby,
223 'order' => $order,
224 'per_page' => $per_page,
225 'offset' => $offset,
226 'page' => $page,
227 'excerpt_len' => $excerpt_len,
228 ) ) );
229 $cached = get_transient( $cache_key );
230 if ( is_array( $cached ) ) {
231 return rest_ensure_response( $cached );
232 }
233 }
234
235 $q = new WP_Query( array(
236 'post_type' => $post_type,
237 'post_status' => 'publish',
238 'orderby' => $orderby,
239 'order' => $order,
240 'posts_per_page' => $per_page,
241 'offset' => $offset + ( ( $page - 1 ) * $per_page ),
242 'ignore_sticky_posts' => true,
243 'no_found_rows' => true,
244 ) );
245
246 $posts = array();
247 foreach ( $q->posts as $p ) {
248 $post_id = $p->ID;
249 $title = wp_strip_all_tags( get_the_title( $post_id ) );
250 $link = esc_url_raw( get_permalink( $post_id ) );
251 $image = esc_url_raw( (string) get_the_post_thumbnail_url( $post_id, 'medium_large' ) );
252 $date = wp_strip_all_tags( (string) get_the_date( '', $post_id ) );
253 $author = sanitize_text_field( (string) get_the_author_meta( 'display_name', $p->post_author ) );
254 $meta = trim( $date . ' · ' . $author );
255 $raw = get_post_field( 'post_excerpt', $post_id );
256 if ( '' === $raw ) {
257 $raw = get_post_field( 'post_content', $post_id );
258 }
259 $excerpt = wp_strip_all_tags( wp_trim_words( wp_strip_all_tags( $raw ), $excerpt_len, '' ) );
260
261 $item = array(
262 'id' => $post_id,
263 'title' => $title,
264 'link' => $link,
265 'image' => $image,
266 'meta' => $meta,
267 'excerpt' => $excerpt,
268 );
269
270 if ( 'product' === $post_type && function_exists( 'wc_get_product' ) ) {
271 $product = wc_get_product( $post_id );
272 if ( $product ) {
273 $item['price_html'] = wp_kses_post( $product->get_price_html() );
274 $item['add_to_cart'] = esc_url_raw( $product->add_to_cart_url() );
275 }
276 }
277
278 $posts[] = $item;
279 }
280
281 $payload = array( 'posts' => $posts );
282
283 if ( ! $is_product_query && '' !== $cache_key ) {
284 set_transient( $cache_key, $payload, 60 );
285 }
286
287 return rest_ensure_response( $payload );
288 },
289 ) );
290 } );
291
292 /**
293 * Simple local newsletter subscribe endpoint.
294 * Stores emails in an option array; extend as needed (e.g., to a custom table).
295 */
296 add_action( 'rest_api_init', function () {
297 register_rest_route( 'blockenberg/v1', '/subscribe', array(
298 'methods' => 'POST',
299 'permission_callback' => '__return_true',
300 'args' => array(
301 'email' => array( 'sanitize_callback' => 'sanitize_email' ),
302 'website' => array( 'sanitize_callback' => 'sanitize_text_field' ),
303 ),
304 'callback' => function ( WP_REST_Request $request ) {
305 $payload = $request->get_json_params();
306 if ( ! is_array( $payload ) ) {
307 $payload = array();
308 }
309
310 // Basic rate limiting by IP to reduce spam / abuse.
311 $ip = '';
312 if ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
313 $ip = sanitize_text_field( wp_unslash( (string) $_SERVER['REMOTE_ADDR'] ) );
314 }
315 if ( '' !== $ip ) {
316 $key = 'bkbg_subscribe_' . md5( $ip );
317 $rl = get_transient( $key );
318 if ( is_array( $rl ) && isset( $rl['count'], $rl['start'] ) ) {
319 if ( $rl['count'] >= 5 ) {
320 return new WP_Error( 'rate_limited', __( 'Too many requests. Please try again later.', 'blockenberg' ), array( 'status' => 429 ) );
321 }
322 $rl['count']++;
323 set_transient( $key, $rl, 60 );
324 } else {
325 set_transient( $key, array( 'count' => 1, 'start' => time() ), 60 );
326 }
327 }
328
329 // Optional honeypot field (bots tend to fill it).
330 $honeypot = isset( $payload['website'] ) ? sanitize_text_field( (string) $payload['website'] ) : '';
331 if ( '' !== $honeypot ) {
332 return rest_ensure_response( array( 'ok' => true ) );
333 }
334
335 $email = isset( $payload['email'] ) ? sanitize_email( (string) $payload['email'] ) : '';
336 if ( empty( $email ) || ! is_email( $email ) ) {
337 return new WP_Error( 'invalid_email', __( 'Invalid email address', 'blockenberg' ), array( 'status' => 400 ) );
338 }
339
340 $list = get_option( 'blockenberg_newsletter_subscribers', array() );
341 if ( ! is_array( $list ) ) {
342 $list = array();
343 }
344
345 // Prevent unbounded option growth.
346 if ( count( $list ) >= 5000 ) {
347 return new WP_Error( 'storage_full', __( 'Subscriber list is full.', 'blockenberg' ), array( 'status' => 503 ) );
348 }
349
350 if ( ! in_array( $email, $list, true ) ) {
351 $list[] = $email;
352 update_option( 'blockenberg_newsletter_subscribers', $list, false );
353 }
354 return rest_ensure_response( array( 'ok' => true ) );
355 },
356 ) );
357 } );