| 1 |
<?php |
| 2 |
/** |
| 3 |
* Blocks |
| 4 |
* |
| 5 |
* @package GamiPress\Blocks |
| 6 |
* @author GamiPress <contact@gamipress.com>, Ruben Garcia <rubengcdev@gmail.com> |
| 7 |
* @since 1.6.0 |
| 8 |
*/ |
| 9 |
// Exit if accessed directly |
| 10 |
if( !defined( 'ABSPATH' ) ) exit; |
| 11 |
|
| 12 |
/** |
| 13 |
* Select the right "block_categories" filter according to WP version |
| 14 |
* |
| 15 |
* @since 2.1.9 |
| 16 |
*/ |
| 17 |
function gamipress_init_block_category() { |
| 18 |
|
| 19 |
global $wp_version; |
| 20 |
|
| 21 |
if( version_compare( $wp_version, '5.8', '<' ) ) { |
| 22 |
// WP version is less than 5.8 |
| 23 |
add_filter( 'block_categories', 'gamipress_register_block_categories', 10, 2 ); |
| 24 |
} else { |
| 25 |
// WP version is 5.8 or higher |
| 26 |
add_filter( 'block_categories_all', 'gamipress_register_block_categories', 10, 2 ); |
| 27 |
} |
| 28 |
|
| 29 |
} |
| 30 |
add_action( 'gamipress_init', 'gamipress_init_block_category' ); |
| 31 |
|
| 32 |
/** |
| 33 |
* Register GamiPress as block category |
| 34 |
* |
| 35 |
* @since 1.6.0 |
| 36 |
*/ |
| 37 |
function gamipress_register_block_categories( $categories, $post ) { |
| 38 |
|
| 39 |
// Register GamiPress as block category |
| 40 |
$categories[] = array( |
| 41 |
'title' => 'GamiPress', |
| 42 |
'slug' => 'gamipress', |
| 43 |
'icon' => 'gamipress', |
| 44 |
); |
| 45 |
|
| 46 |
return $categories; |
| 47 |
|
| 48 |
} |
| 49 |
|
| 50 |
/** |
| 51 |
* GamiPress block icons |
| 52 |
* |
| 53 |
* @since 1.6.1 |
| 54 |
*/ |
| 55 |
function gamipress_get_block_icons() { |
| 56 |
|
| 57 |
$icons = array( |
| 58 |
'gamipress' => |
| 59 |
'<svg width="24" height="24" viewBox="0 0 167.548 167.548" xmlns="http://www.w3.org/2000/svg" > |
| 60 |
<path d="M 131.973,136.242 H 35.596 L 8.108,57.111 42.507,82.363 c -0.172,0.873 -0.264,1.727 -0.264,2.584 0,7.815 6.359,14.175 14.175,14.175 7.812,0 14.175,-6.359 14.175,-14.175 0,-6.091 -3.797,-11.274 -9.273,-13.255 l 22.465,-47.363 22.396,47.239 c -5.627,1.898 -9.629,7.193 -9.629,13.384 0,7.815 6.359,14.175 14.175,14.175 7.81,0 14.175,-6.359 14.175,-14.175 0,-1.036 -0.121,-2.077 -0.364,-3.103 l 34.984,-24.922 z"/> |
| 61 |
</svg>', |
| 62 |
'rank' => |
| 63 |
'<svg width="24" height="24" viewBox="0 0 92.275001 92.275002" xmlns="http://www.w3.org/2000/svg" > |
| 64 |
<polygon points="50.1,96.2 76.2,81.3 76.2,66.5 50.1,81.3 24.1,66.5 24.1,81.3" transform="translate(-4.1,-3.9250045)"/> |
| 65 |
<polygon points="50.1,74.5 76.2,59.7 76.2,44.8 50.1,59.7 24.1,44.8 24.1,59.7" transform="translate(-4.1,-3.9250045)"/> |
| 66 |
<path d="M 47,0.67499541 52.6,11.974994 c 0.2,0.3 0.5,0.5 0.8,0.6 l 12.5,1.8 c 0.9,0.1 1.3,1.2 0.6,1.9 l -9,8.8 c -0.3,0.3 -0.4,0.6 -0.3,1 l 2.1,12.5 c 0.2,0.9 -0.8,1.6 -1.6,1.2 l -11.2,-5.9 c -0.3,-0.2 -0.7,-0.2 -1,0 l -11.2,5.9 c -0.8,0.4 -1.7,-0.3 -1.6,-1.2 l 2.2,-12.5 c 0.1,-0.4 -0.1,-0.7 -0.3,-1 l -9.1,-8.8 c -0.7,-0.6 -0.3,-1.7 0.6,-1.9 l 12.5,-1.8 c 0.4,-0.1 0.7,-0.3 0.8,-0.6 L 45,0.67499541 c 0.5,-0.9 1.6,-0.9 2,0 z"/> |
| 67 |
</svg>', |
| 68 |
); |
| 69 |
|
| 70 |
/** |
| 71 |
* Filter to register custom GamiPress block icons |
| 72 |
* |
| 73 |
* @since 1.6.1 |
| 74 |
* |
| 75 |
* @param array $icons Icons already registered |
| 76 |
* |
| 77 |
* @return array |
| 78 |
*/ |
| 79 |
return apply_filters( 'gamipress_block_icons', $icons ); |
| 80 |
|
| 81 |
} |
| 82 |
|
| 83 |
/** |
| 84 |
* Register GamiPress blocks |
| 85 |
* |
| 86 |
* Important: Priority is set to 20 to let every add-on register their shortcodes first |
| 87 |
* |
| 88 |
* @since 1.6.0 |
| 89 |
*/ |
| 90 |
function gamipress_register_block_types() { |
| 91 |
|
| 92 |
// Bail if Gutenberg is not enabled |
| 93 |
if( ! function_exists( 'register_block_type' ) ) { |
| 94 |
return; |
| 95 |
} |
| 96 |
|
| 97 |
foreach( GamiPress()->shortcodes as $shortcode ) { |
| 98 |
|
| 99 |
// Block name requires to be a prefix/block-slug |
| 100 |
// So let's turn gamipress_points_types to gamipress/points-types |
| 101 |
$block_name = str_replace( '_', '-', str_replace( 'gamipress_', 'gamipress/', $shortcode->slug ) ); |
| 102 |
|
| 103 |
// Register block and attributes |
| 104 |
register_block_type( $block_name, array( |
| 105 |
'attributes' => gamipress_get_block_attributes( $shortcode ), |
| 106 |
'render_callback' => $shortcode->output_callback, |
| 107 |
) ); |
| 108 |
|
| 109 |
// Filter to remove null attributes passed by Gutenberg |
| 110 |
add_filter( "shortcode_atts_{$shortcode->slug}", 'gamipress_remove_null_block_attributes', 10, 4 ); |
| 111 |
|
| 112 |
} |
| 113 |
|
| 114 |
} |
| 115 |
add_action( 'init', 'gamipress_register_block_types', 20 ); |
| 116 |
|
| 117 |
/** |
| 118 |
* Setup block fields from give shortcode object |
| 119 |
* |
| 120 |
* @since 1.6.0 |
| 121 |
* |
| 122 |
* @param GamiPress_Shortcode $shortcode |
| 123 |
* |
| 124 |
* @return array |
| 125 |
*/ |
| 126 |
function gamipress_get_block_fields( $shortcode ) { |
| 127 |
|
| 128 |
$fields = array(); |
| 129 |
|
| 130 |
foreach( $shortcode->fields as $field_id => $field ) { |
| 131 |
|
| 132 |
// Parse options callbacks |
| 133 |
if( isset( $field['options_cb'] ) ) { |
| 134 |
$field['options'] = gamipress_blocks_parse_options_cb( $field ); |
| 135 |
} |
| 136 |
|
| 137 |
// Move tooltip to desc |
| 138 |
if( isset( $field['tooltip'] ) ) { |
| 139 |
|
| 140 |
if( ! isset( $field['desc'] ) ) { |
| 141 |
$field['desc'] = ''; |
| 142 |
} |
| 143 |
|
| 144 |
if( ! is_array( $field['tooltip'] ) ) { |
| 145 |
// tooltip => description |
| 146 |
$field['desc'] .= $field['tooltip']; |
| 147 |
} else { |
| 148 |
// tooltip => array() |
| 149 |
$field['desc'] .= ( isset( $field['tooltip']['desc'] ) ? $field['tooltip']['desc'] : '' ); |
| 150 |
} |
| 151 |
|
| 152 |
} |
| 153 |
|
| 154 |
// Set the definitive object field |
| 155 |
$fields[$field_id] = $field; |
| 156 |
} |
| 157 |
|
| 158 |
/** |
| 159 |
* Filter to override specific block fields |
| 160 |
* |
| 161 |
* @since 1.6.0 |
| 162 |
* |
| 163 |
* @param array $fields |
| 164 |
* @param GamiPress_Shortcode $shortcode |
| 165 |
* |
| 166 |
* @return array |
| 167 |
*/ |
| 168 |
$fields = apply_filters( "gamipress_get_{$shortcode->slug}_block_fields", $fields, $shortcode ); |
| 169 |
|
| 170 |
/** |
| 171 |
* Filter to override block fields |
| 172 |
* |
| 173 |
* @since 1.6.0 |
| 174 |
* |
| 175 |
* @param array $fields |
| 176 |
* @param GamiPress_Shortcode $shortcode |
| 177 |
* |
| 178 |
* @return array |
| 179 |
*/ |
| 180 |
return apply_filters( 'gamipress_get_block_fields', $fields, $shortcode ); |
| 181 |
|
| 182 |
} |
| 183 |
|
| 184 |
/** |
| 185 |
* Turn select2 fields into 'post' or 'user' field types |
| 186 |
* |
| 187 |
* @since 1.6.0 |
| 188 |
* |
| 189 |
* @param array $fields |
| 190 |
* @param GamiPress_Shortcode $shortcode |
| 191 |
* |
| 192 |
* @return array |
| 193 |
*/ |
| 194 |
function gamipress_blocks_selector_fields( $fields, $shortcode ) { |
| 195 |
|
| 196 |
switch ( $shortcode->slug ) { |
| 197 |
case 'gamipress_achievement': |
| 198 |
case 'gamipress_inline_achievement': |
| 199 |
// Achievement ID |
| 200 |
$fields['id']['type'] = 'post'; |
| 201 |
$fields['id']['post_type'] = gamipress_get_achievement_types_slugs(); |
| 202 |
break; |
| 203 |
case 'gamipress_achievements': |
| 204 |
// Include |
| 205 |
$fields['include']['type'] = 'post'; |
| 206 |
$fields['include']['post_type'] = gamipress_get_achievement_types_slugs(); |
| 207 |
// Exclude |
| 208 |
$fields['exclude']['type'] = 'post'; |
| 209 |
$fields['exclude']['post_type'] = gamipress_get_achievement_types_slugs(); |
| 210 |
break; |
| 211 |
case 'gamipress_rank': |
| 212 |
case 'gamipress_inline_rank': |
| 213 |
// Rank ID |
| 214 |
$fields['id']['type'] = 'post'; |
| 215 |
$fields['id']['post_type'] = gamipress_get_rank_types_slugs(); |
| 216 |
break; |
| 217 |
case 'gamipress_ranks': |
| 218 |
// Include |
| 219 |
$fields['include']['type'] = 'post'; |
| 220 |
$fields['include']['post_type'] = gamipress_get_rank_types_slugs(); |
| 221 |
// Exclude |
| 222 |
$fields['exclude']['type'] = 'post'; |
| 223 |
$fields['exclude']['post_type'] = gamipress_get_rank_types_slugs(); |
| 224 |
break; |
| 225 |
case 'gamipress_points': |
| 226 |
case 'gamipress_user_points': |
| 227 |
case 'gamipress_site_points': |
| 228 |
// Period start and end visibility |
| 229 |
$fields['period_start']['conditions'] = array( 'period' => 'custom' ); |
| 230 |
$fields['period_end']['conditions'] = array( 'period' => 'custom' ); |
| 231 |
// Inline conditions |
| 232 |
$inline_condition = array( |
| 233 |
'field_id' => 'inline', |
| 234 |
'value' => true, |
| 235 |
'compare' => '!=', |
| 236 |
); |
| 237 |
$fields['columns']['conditions'] = array( $inline_condition ); |
| 238 |
$fields['columns_small']['conditions'] = array( $inline_condition ); |
| 239 |
$fields['layout']['conditions'] = array( $inline_condition ); |
| 240 |
$fields['align']['conditions'] = array( $inline_condition ); |
| 241 |
break; |
| 242 |
case 'gamipress_points_types': |
| 243 |
// Points Types attributes |
| 244 |
$fields['toggle']['conditions'] = array( 'awards' => true, 'deducts' => true, 'relation' => 'OR' ); |
| 245 |
$fields['heading']['conditions'] = array( 'awards' => true, 'deducts' => true, 'relation' => 'OR' ); |
| 246 |
$fields['heading_size']['conditions'] = array( 'heading' => true ); |
| 247 |
break; |
| 248 |
} |
| 249 |
|
| 250 |
// Common attributes |
| 251 |
switch ( $shortcode->slug ) { |
| 252 |
case 'gamipress_achievement': |
| 253 |
case 'gamipress_achievements': |
| 254 |
case 'gamipress_last_achievements_earned': |
| 255 |
// Achievement common attributes |
| 256 |
$fields['title_size']['conditions'] = array( 'title' => true ); |
| 257 |
$fields['thumbnail_size']['conditions'] = array( 'thumbnail' => true ); |
| 258 |
$fields['points_awarded_thumbnail']['conditions'] = array( 'points_awarded' => true ); |
| 259 |
$fields['toggle']['conditions'] = array( 'steps' => true ); |
| 260 |
$fields['heading']['conditions'] = array( 'steps' => true ); |
| 261 |
$fields['heading_size']['conditions'] = array( 'steps' => true, 'heading' => true ); |
| 262 |
$fields['earners_limit']['conditions'] = array( 'earners' => true ); |
| 263 |
break; |
| 264 |
case 'gamipress_inline_achievement': |
| 265 |
case 'gamipress_inline_last_achievements_earned': |
| 266 |
// Inline achievement common attributes |
| 267 |
$fields['thumbnail_size']['conditions'] = array( 'thumbnail' => true ); |
| 268 |
break; |
| 269 |
case 'gamipress_rank': |
| 270 |
case 'gamipress_ranks': |
| 271 |
case 'gamipress_user_rank': |
| 272 |
// Rank common attributes |
| 273 |
$fields['title_size']['conditions'] = array( 'title' => true ); |
| 274 |
$fields['thumbnail_size']['conditions'] = array( 'thumbnail' => true ); |
| 275 |
$fields['toggle']['conditions'] = array( 'requirements' => true ); |
| 276 |
$fields['heading']['conditions'] = array( 'requirements' => true ); |
| 277 |
$fields['heading_size']['conditions'] = array( 'requirements' => true, 'heading' => true ); |
| 278 |
$fields['earners_limit']['conditions'] = array( 'earners' => true ); |
| 279 |
break; |
| 280 |
case 'gamipress_inline_rank': |
| 281 |
case 'gamipress_inline_user_rank': |
| 282 |
// Inline rank common attributes |
| 283 |
$fields['thumbnail_size']['conditions'] = array( 'thumbnail' => true ); |
| 284 |
break; |
| 285 |
case 'gamipress_points': |
| 286 |
case 'gamipress_user_points': |
| 287 |
case 'gamipress_site_points': |
| 288 |
case 'gamipress_points_types': |
| 289 |
// Points common attributes |
| 290 |
$fields['thumbnail_size']['conditions'] = array( 'thumbnail' => true ); |
| 291 |
break; |
| 292 |
} |
| 293 |
|
| 294 |
// Common fields to all shortcodes |
| 295 |
|
| 296 |
// User ID |
| 297 |
if( isset( $fields['user_id'] ) ) { |
| 298 |
// User ID |
| 299 |
$fields['user_id']['type'] = 'user'; |
| 300 |
} |
| 301 |
|
| 302 |
// User ID visibility when current user field is in form |
| 303 |
if( isset( $fields['user_id'] ) && isset( $fields['current_user'] ) ) { |
| 304 |
// Setup as display condition that current_user needs to be false (unchecked) |
| 305 |
$fields['user_id']['conditions'] = array( |
| 306 |
'current_user' => false, |
| 307 |
); |
| 308 |
} |
| 309 |
|
| 310 |
return $fields; |
| 311 |
} |
| 312 |
add_filter( 'gamipress_get_block_fields', 'gamipress_blocks_selector_fields', 10, 2 ); |
| 313 |
|
| 314 |
/** |
| 315 |
* Setup block attributes from give shortcode object |
| 316 |
* |
| 317 |
* @since 1.6.0 |
| 318 |
* |
| 319 |
* @param GamiPress_Shortcode $shortcode |
| 320 |
* |
| 321 |
* @return array |
| 322 |
*/ |
| 323 |
function gamipress_get_block_attributes( $shortcode ) { |
| 324 |
|
| 325 |
$attributes = array(); |
| 326 |
|
| 327 |
// Turn fields into attributes |
| 328 |
foreach( $shortcode->fields as $field_id => $field ) { |
| 329 |
|
| 330 |
if( $field['type'] === 'checkbox' ) { |
| 331 |
// Checkboxes as boolean |
| 332 |
$attributes[$field_id] = array( |
| 333 |
'type' => 'boolean', |
| 334 |
); |
| 335 |
} else { |
| 336 |
// String is the default type |
| 337 |
$attributes[$field_id] = array( |
| 338 |
'type' => 'string', |
| 339 |
); |
| 340 |
} |
| 341 |
|
| 342 |
} |
| 343 |
|
| 344 |
/** |
| 345 |
* Filter to override specific block attributes |
| 346 |
* |
| 347 |
* @since 1.6.0 |
| 348 |
* |
| 349 |
* @param array $attributes |
| 350 |
* @param GamiPress_Shortcode $shortcode |
| 351 |
* |
| 352 |
* @return array |
| 353 |
*/ |
| 354 |
$attributes = apply_filters( "gamipress_gutenberg_blocks_get_{$shortcode->slug}_block_attributes", $attributes, $shortcode ); |
| 355 |
|
| 356 |
/** |
| 357 |
* Filter to override block attributes |
| 358 |
* |
| 359 |
* @since 1.6.0 |
| 360 |
* |
| 361 |
* @param array $attributes |
| 362 |
* @param GamiPress_Shortcode $shortcode |
| 363 |
* |
| 364 |
* @return array |
| 365 |
*/ |
| 366 |
return apply_filters( 'gamipress_gutenberg_blocks_get_block_attributes', $attributes, $shortcode ); |
| 367 |
|
| 368 |
} |
| 369 |
|
| 370 |
/** |
| 371 |
* Removes null attributes passed by Gutenberg |
| 372 |
* |
| 373 |
* @since 1.6.0 |
| 374 |
* |
| 375 |
* @param array $out The output array of shortcode attributes |
| 376 |
* @param array $pairs The supported attributes and their defaults |
| 377 |
* @param array $atts The user defined shortcode attributes |
| 378 |
* @param string $shortcode The shortcode name |
| 379 |
* |
| 380 |
* @return array |
| 381 |
*/ |
| 382 |
function gamipress_remove_null_block_attributes( $out, $pairs, $atts, $shortcode ) { |
| 383 |
|
| 384 |
// Bail if shortcode is not registered |
| 385 |
if( ! isset( GamiPress()->shortcodes[$shortcode] ) ) |
| 386 |
return $out; |
| 387 |
|
| 388 |
// Setup the shortcode object |
| 389 |
$shortcode_obj = GamiPress()->shortcodes[$shortcode]; |
| 390 |
|
| 391 |
foreach( $out as $name => $value ) { |
| 392 |
|
| 393 |
// Gutenberg pass default values and empty as null |
| 394 |
// So let's force default value instead of null |
| 395 |
if( $value === null ) { |
| 396 |
$out[$name] = $pairs[$name]; |
| 397 |
} |
| 398 |
|
| 399 |
if( isset( $shortcode_obj->fields[$name] ) ) { |
| 400 |
$field = $shortcode_obj->fields[$name]; |
| 401 |
|
| 402 |
if( $field['type'] === 'checkbox' && is_bool( $out[$name] ) ) { |
| 403 |
// Turn booleans into yes|no |
| 404 |
$out[$name] = ( $out[$name] === true ? 'yes' : 'no' ); |
| 405 |
} |
| 406 |
} |
| 407 |
} |
| 408 |
|
| 409 |
return $out; |
| 410 |
|
| 411 |
} |
| 412 |
|
| 413 |
/** |
| 414 |
* Filter block per-rendering to setup the renderer for error messages |
| 415 |
* |
| 416 |
* @since 1.7.6 |
| 417 |
* |
| 418 |
* @param string $pre_render The pre-rendered content. Default null. |
| 419 |
* @param array $block The block being rendered. |
| 420 |
* |
| 421 |
* @return string |
| 422 |
*/ |
| 423 |
function gamipress_pre_render_block( $pre_render, $block ) { |
| 424 |
|
| 425 |
global $gamipress_renderer; |
| 426 |
|
| 427 |
// Setup renderer |
| 428 |
$gamipress_renderer = 'block'; |
| 429 |
|
| 430 |
return $pre_render; |
| 431 |
} |
| 432 |
add_filter( 'pre_render_block', 'gamipress_pre_render_block', 10, 2 ); |
| 433 |
|
| 434 |
/** |
| 435 |
* Filter block rendering to reset the renderer for error messages |
| 436 |
* |
| 437 |
* @since 1.7.6 |
| 438 |
* |
| 439 |
* @param string $block_content The block content about to be appended. |
| 440 |
* @param array $block The full block, including name and attributes. |
| 441 |
* |
| 442 |
* @return string |
| 443 |
*/ |
| 444 |
function gamipress_render_block( $block_content, $block ) { |
| 445 |
|
| 446 |
global $gamipress_renderer; |
| 447 |
|
| 448 |
// Reset renderer |
| 449 |
$gamipress_renderer = false; |
| 450 |
|
| 451 |
return $block_content; |
| 452 |
} |
| 453 |
add_filter( 'render_block', 'gamipress_render_block', 10, 2 ); |
| 454 |
|
| 455 |
/** |
| 456 |
* Simulates CMB2 options_cb processing |
| 457 |
* |
| 458 |
* @since 1.6.0 |
| 459 |
* |
| 460 |
* @param array $field |
| 461 |
* |
| 462 |
* @return array |
| 463 |
*/ |
| 464 |
function gamipress_blocks_parse_options_cb( $field ) { |
| 465 |
|
| 466 |
$field_options = ( isset( $field['options'] ) ? (array) $field['options'] : array() ); |
| 467 |
|
| 468 |
if ( isset( $field['options_cb'] ) && is_callable( $field['options_cb'] ) ) { |
| 469 |
$options = call_user_func( $field['options_cb'], gamipress_blocks_cmb_field_object( $field ) ); |
| 470 |
|
| 471 |
if ( $options && is_array( $options ) ) { |
| 472 |
$field_options = $options + $field_options; |
| 473 |
} |
| 474 |
} |
| 475 |
|
| 476 |
return $field_options; |
| 477 |
|
| 478 |
} |
| 479 |
|
| 480 |
/** |
| 481 |
* Builds a false CMB2 field object |
| 482 |
* |
| 483 |
* @since 1.6.0 |
| 484 |
* |
| 485 |
* @param array $field |
| 486 |
* |
| 487 |
* @return stdClass |
| 488 |
*/ |
| 489 |
function gamipress_blocks_cmb_field_object( $field ) { |
| 490 |
return (object) array( |
| 491 |
'args' => $field, |
| 492 |
'value' => '', |
| 493 |
'escaped_value' => '', |
| 494 |
); |
| 495 |
} |