JezK
Edit File: Controller.php
<?php namespace GeminiLabs\SiteReviews\Integrations\Breakdance; use Breakdance\Elements\PresetSections\PresetSectionsController; use GeminiLabs\SiteReviews\Contracts\ShortcodeContract; use GeminiLabs\SiteReviews\Controllers\AbstractController; use GeminiLabs\SiteReviews\Database\ShortcodeOptionManager; use GeminiLabs\SiteReviews\Helpers\Arr; use GeminiLabs\SiteReviews\Helpers\Str; use GeminiLabs\SiteReviews\Helpers\Svg; use function Breakdance\Elements\PresetSections\getPresetSection; class Controller extends AbstractController { /** * @filter site-reviews/modal_wrapped_by */ public function filterModalWrappedBy(array $builders): array { $builders[] = 'breakdance'; return $builders; } /** * @filter site-reviews/breakdance/design_controls */ public function filterTransformedDesignControls(array $controls, ShortcodeContract $shortcode): array { if ('site_reviews' === $shortcode->tag) { if (isset(PresetSectionsController::getInstance()->presets['GLSR\ButtonDesign'])) { $controls[] = getPresetSection('GLSR\ButtonDesign', 'Load More Button', 'load_more_button', [ 'type' => 'popout', ]); } } if ('site_reviews_form' === $shortcode->tag) { if (isset(PresetSectionsController::getInstance()->presets['GLSR\FormDesign'])) { $controls[] = getPresetSection('GLSR\FormDesign', 'Form', 'form', [ 'type' => 'popout', ]); } } return $controls; } /** * Breakdance does not provide a way to create a multi-select dropdown * populated by an AJAX callback on init AND search. To get around this, * we use the post_chooser control and override the callback with our own. * * @action breakdance_ajax_breakdance_get_posts:1 * @action wp_ajax_breakdance_get_posts:1 * @action wp_ajax_nopriv_breakdance_get_posts:1 */ public function interceptGetPostsQuery(): void { if (!$this->verifyRequest()) { return; } $pseudoPostTypeMap = [ 'assigned_posts' => 'assigned_posts', 'assigned_terms' => 'assigned_terms', 'assigned_users' => 'assigned_users', 'review_authors' => 'author', ]; $option = (string) filter_input(\INPUT_POST, 'postType'); $option = $pseudoPostTypeMap[$option] ?? Str::removePrefix($option, glsr()->prefix); $search = filter_input(\INPUT_POST, 'search'); $results = glsr(ShortcodeOptionManager::class)->get($option, compact('search')); $replacements = [ // the post_chooser control requires integer keys 'post_id' => -10, 'parent_id' => -20, 'user_id' => -30, 'author_id' => -40, 'profile_id' => -50, ]; if (in_array($option, ['assigned_posts'])) { $thumbnailFn = fn ($id) => get_the_post_thumbnail_url($id, 'thumbnail'); } elseif (in_array($option, ['assigned_users', 'author'])) { $thumbnailFn = fn ($id) => get_avatar_url($id); } $data = $this->prepareResponse($results, $thumbnailFn ?? null, $replacements); if (in_array($option, ['post_id'])) { array_walk($data, function (&$item) { $item['title'] = "{$item['id']}: {$item['title']}"; }); } wp_send_json_success($data, 200); glsr_exit(); // @phpstan-ignore-line } /** * @action unofficial_i_am_kevin_geary_master_of_all_things_css_and_html */ public function printInlineStyles(): void { $icon = Svg::encoded('assets/images/icon-static.svg'); echo '<style>'. '.breakdance-control-wrapper:has(+ .breakdance-control-wrapper [data-test-id$="_description_alert"] .breakdance-alert-box) {'. 'margin-bottom: 7px;'. '}'. '[data-test-id$="_description_alert"] .breakdance-alert-box {'. 'padding-inline: 13px;', '.v-alert__border--left {'. 'display: none;'. '}', '}', '.breakdance-add-panel__element:has(.glsr-icon) .breakdance-element-badge {'. 'padding: 0;'. '&::after {'. 'background-image: url("'.$icon.'");'. 'content: "";'. 'height: var(--text-lg);'. 'width: var(--text-lg);'. '}'. '}'. '</style>'; } /** * Breakdance loads an element by filtering the result of get_declared_classes * and checking if the class is_subclass_of \Breakdance\Elements\Element. * * Controls are added dynamically from the Shortcode class to enforce consistancy * across builder integrations. As a result, we need to bypass the Element Studio * and we do this by setting the save location to $onlyForAdvancedUsers and * $excludeFromElementStudio. * * @action breakdance_loaded:5 */ public function registerElements(): void { $pluginDir = dirname(plugin_basename(glsr()->file)); \Breakdance\Elements\registerCategory(glsr()->id, glsr()->name); \Breakdance\ElementStudio\registerSaveLocation( "{$pluginDir}/assets/breakdance/elements", 'GLSR_Breakdance', 'element', 'Site Reviews Elements', true, // onlyForAdvancedUsers true // excludeFromElementStudio ); \Breakdance\ElementStudio\registerSaveLocation( "{$pluginDir}/assets/breakdance/presets", 'GLSR_Breakdance', 'preset', 'Site Reviews Presets', true, // onlyForAdvancedUsers true // excludeFromElementStudio ); } /** * @action init */ public function registerPresets(): void { $presets = [ 'GLSR\ButtonDesign' => glsr()->path('assets/breakdance/presets/button-design.php'), 'GLSR\FormDesign' => glsr()->path('assets/breakdance/presets/form-design.php'), ]; $presets = glsr()->filterArray('breakdance/presets', $presets); foreach ($presets as $presetName => $presetPath) { if (file_exists($presetPath)) { $controls = include $presetPath; PresetSectionsController::getInstance()->register($presetName, $controls, true); } } } /** * Unfortunately we can't use this yet because Breakdance does not support * AJAX searching in multiselect controls. * * @action breakdance_loaded */ public function registerRoutes(): void { return; // We can't use this yet... // $input = filter_input_array(INPUT_POST, [ // 'requestData' => [ // 'context' => [ // 'filter' => fn ($value) => is_numeric($value) // ? intval($value) // : strip_tags($value), // 'flags' => FILTER_REQUIRE_ARRAY, // ], // ], // ]); // $include = Arr::consolidate($input['requestData']['context'] ?? []); // \Breakdance\AJAX\register_handler( // glsr()->prefix.'breakdance_assigned_posts', // fn () => $this->fetchAssignedPosts($include), // 'edit' // ); // \Breakdance\AJAX\register_handler( // glsr()->prefix.'breakdance_assigned_terms', // fn () => $this->fetchAssignedTerms($include), // 'edit' // ); // \Breakdance\AJAX\register_handler( // glsr()->prefix.'breakdance_assigned_users', // fn () => $this->fetchAssignedUsers($include), // 'edit' // ); // \Breakdance\AJAX\register_handler( // glsr()->prefix.'breakdance_author', // fn () => $this->fetchAuthor($include), // 'edit' // ); // \Breakdance\AJAX\register_handler( // glsr()->prefix.'breakdance_post_id', // fn () => $this->fetchPostId($include), // 'edit' // ); } /** * Unfortunately we can't use this yet because Breakdance does not support * AJAX searching in multiselect controls. */ protected function fetchAssignedPosts(array $include): array { // $include = array_filter($include, fn ($id) => is_numeric($id) || in_array($id, [ // 'parent_id', // 'post_id', // ])); return []; } /** * Unfortunately we can't use this yet because Breakdance does not support * AJAX searching in multiselect controls. */ protected function fetchAssignedTerms(array $include): array { return []; } /** * Unfortunately we can't use this yet because Breakdance does not support * AJAX searching in multiselect controls. */ protected function fetchAssignedUsers(array $include): array { // $include = array_filter($include, fn ($id) => is_numeric($id) || in_array($id, [ // 'author_id', // 'profile_id', // 'user_id', // ])); return []; } /** * Unfortunately we can't use this yet because Breakdance does not support * AJAX searching in multiselect controls. */ protected function fetchAuthor(array $include): array { // $include = array_filter($include, fn ($id) => is_numeric($id) || in_array($id, [ // 'user_id', // ])); return []; } /** * Unfortunately we can't use this yet because Breakdance does not support * AJAX searching in multiselect controls. */ protected function fetchPostId(array $include): array { return []; } protected function prepareResponse(array $results, ?callable $thumbnailFn = null, array $replacements = []): array { if (!$thumbnailFn) { $thumbnailFn = fn () => false; } $callback = fn ($id, $title) => [ 'id' => $replacements[$id] ?? $id, 'title' => $title, 'thumbnail' => $thumbnailFn($id), ]; return array_map($callback, array_keys($results), $results); } protected function verifyRequest(): bool { $checkFor = [ glsr()->prefix, 'assigned_posts', 'assigned_terms', 'assigned_users', 'review_authors', ]; $postType = filter_input(\INPUT_POST, 'postType'); if (empty(array_filter($checkFor, fn ($prefix) => str_starts_with($postType, $prefix)))) { return false; } if (!\Breakdance\Permissions\hasMinimumPermission('edit')) { return false; } $nonceTick = check_ajax_referer(\Breakdance\AJAX\get_nonce_key_for_ajax_requests(), false, false); if (!$nonceTick) { return false; } if (2 === $nonceTick) { $refreshNonce = \Breakdance\AJAX\get_nonce_for_ajax_requests(); header('Breakdance-Refresh-Nonce:'.$refreshNonce); } return true; } }