Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 131
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
CTA
0.00% covered (danger)
0.00%
0 / 131
0.00% covered (danger)
0.00%
0 / 9
702
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 maybe_init_options
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 get_option_name_for_screen
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 handle_ajax_toggle
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
20
 add_screen_option_html
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 1
6
 add_toggle_script
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 add_cta_to_add_new_button
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
12
 add_style
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 1
2
 is_core_post_type
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
72
1<?php
2namespace NewfoldLabs\WP\Module\Patterns\Admin;
3
4/**
5 * CTA for the edit pages screen.
6 */
7class CTA {
8
9    /**
10     * WordPress option name for posts
11     */
12    const POST_OPTION_NAME = 'nfd_wb_cta_enabled_posts';
13
14    /**
15     * WordPress option name for pages
16     */
17    const PAGE_OPTION_NAME = 'nfd_wb_cta_enabled_pages';
18
19    /**
20     * Nonce action name
21     */
22    const NONCE_ACTION = 'nfd_wonderblocks_toggle';
23
24    /**
25     * Constructor.
26     */
27    public function __construct() {
28
29        $this->maybe_init_options();
30
31        \add_action( 'wp_ajax_toggle_wonderblocks_cta', array( $this, 'handle_ajax_toggle' ) );
32
33        if ( ! $this->is_core_post_type() ) {
34            return;
35        }
36
37        \add_action( 'admin_head', array( $this, 'add_style' ) );
38        \add_action( 'admin_footer', array( $this, 'add_cta_to_add_new_button' ), 10 );
39        \add_action( 'admin_footer', array( $this, 'add_toggle_script' ), 20 );
40        \add_filter( 'screen_settings', array( $this, 'add_screen_option_html' ), 10, 2 );
41    }
42
43    /**
44     * Initialize options if they don't exist
45     */
46    private function maybe_init_options() {
47        if ( false === get_option( self::POST_OPTION_NAME ) ) {
48            add_option( self::POST_OPTION_NAME, true );
49        }
50        if ( false === get_option( self::PAGE_OPTION_NAME ) ) {
51            add_option( self::PAGE_OPTION_NAME, true );
52        }
53    }
54
55    /**
56     * Get the appropriate option name based on screen
57     *
58     * @param string $screen_id The screen ID
59     */
60    private function get_option_name_for_screen( $screen_id ) {
61        return 'edit-page' === $screen_id ? self::PAGE_OPTION_NAME : self::POST_OPTION_NAME;
62    }
63
64    /**
65     * Handle the AJAX toggle
66     */
67    public function handle_ajax_toggle() {
68        \check_ajax_referer( self::NONCE_ACTION, 'nonce' );
69
70        if ( ! \current_user_can( 'manage_options' ) ) {
71            \wp_die( '', '', 403 );
72        }
73
74        $screen_id   = isset( $_POST['screen_id'] ) ? \sanitize_text_field( $_POST['screen_id'] ) : 'edit-post';
75        $enabled     = (bool) ( 'true' === $_POST['nfd_wba_cta_button'] );
76        $option_name = $this->get_option_name_for_screen( $screen_id );
77
78        $updated = \update_option( $option_name, $enabled );
79
80        if ( $updated ) {
81            \wp_send_json_success(
82                array(
83                    'enabled'     => $enabled,
84                    'option_name' => $option_name,
85                    'screen_id'   => $screen_id,
86                )
87            );
88        } else {
89            \wp_send_json_error( 'Failed to update option' );
90        }
91    }
92
93    /**
94     * Add the screen option HTML
95     *
96     * @param string $settings The current screen settings
97     * @param object $screen The current screen object
98     */
99    public function add_screen_option_html( $settings, $screen ) {
100        if ( ! \in_array( $screen->id, array( 'edit-page', 'edit-post' ), true ) ) {
101            return $settings;
102        }
103
104        $option_name = $this->get_option_name_for_screen( $screen->id );
105        $enabled     = \get_option( $option_name, true );
106
107        $html  = '<fieldset class="metabox-prefs wonderblocks">';
108        $html .= '<legend>' . esc_html__( 'WonderBlocks', 'nfd-wonder-blocks' ) . '</legend>';
109        $html .= '<label>';
110        $html .= '<input type="checkbox" id="wonderblocks-toggle"' . checked( $enabled, true, false ) . ' /> ';
111        $html .= esc_html__( 'Show WonderBlocks Button', 'nfd-wonder-blocks' );
112        $html .= '</label>';
113        $html .= '</fieldset>';
114        $html .= \wp_nonce_field( self::NONCE_ACTION, 'wonderblocks_nonce', true, false );
115
116        $allowed_html = array(
117            'fieldset' => array(
118                'class' => true,
119            ),
120            'legend'   => array(),
121            'label'    => array(),
122            'input'    => array(
123                'type'    => true,
124                'id'      => true,
125                'checked' => true,
126                'name'    => true,
127                'value'   => true,
128            ),
129        );
130
131        return \wp_kses( $settings . $html, $allowed_html );
132    }
133
134    /**
135     * Add toggle script to footer
136     */
137    public function add_toggle_script() {
138        $screen = \get_current_screen();
139        ?>
140        <script>
141            document.addEventListener("DOMContentLoaded", function () {
142                const toggle = document.getElementById("wonderblocks-toggle");
143                const nonceField = document.getElementById("wonderblocks_nonce");
144                const ajaxUrl = "<?php echo esc_js( admin_url( 'admin-ajax.php' ) ); ?>";
145                const screenId = "<?php echo esc_js( $screen->id ); ?>";
146                const ctaButton = document.querySelector('.nfd-wba-cta-edit-screen');
147
148                if (!toggle || !nonceField || !ctaButton) return;
149
150                toggle.addEventListener("change", function () {
151                    const enabled = this.checked ? "true" : "false";
152                    const data = new FormData();
153                    data.append("action", "toggle_wonderblocks_cta");
154                    data.append("nonce", nonceField.value);
155                    data.append("nfd_wba_cta_button", enabled);
156                    data.append("screen_id", screenId);
157
158                    fetch(
159                        ajaxUrl,
160                        {
161                            method: "POST",
162                            body: data,
163                        }
164                    )
165                    .then((response) => response.json())
166                    .then((response) => {
167                        if (response.success) {
168                            ctaButton.style.display = enabled === "true" ? "inline-flex" : "none";
169                        } else {
170                            toggle.checked = !toggle.checked;
171                            console.error("Failed to update WonderBlocks setting:", response);
172                        }
173                    })
174                    .catch((error) => {
175                        toggle.checked = !toggle.checked;
176                        console.error("AJAX request failed:", error);
177                    });
178                });
179            });
180        </script>
181        <?php
182    }
183
184    /**
185     * Add CTA to Add New button
186     */
187    public function add_cta_to_add_new_button() {
188        $screen      = \get_current_screen();
189        $option_name = $this->get_option_name_for_screen( $screen->id );
190        $enabled     = \get_option( $option_name, true );
191
192        // Set the URL based on screen type
193        if ( 'edit-post' === $screen->id ) {
194            $url = \admin_url( 'post-new.php?wb-library=patterns&wb-category=text' );
195        } elseif ( 'edit-page' === $screen->id ) {
196            $url = \admin_url( 'post-new.php?post_type=page&wb-library=patterns&wb-category=features' );
197        } else {
198            // Default fallback URL
199            $url = \admin_url( 'post-new.php?wb-library=patterns' );
200        }
201
202        $svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="2.25 6 19.5 13.5" width="19px" height="19px"><path stroke-linecap="round" stroke-linejoin="round" d="M3.34 7.754c0-.552.447-.999.999-.999h5.328a1 1 0 0 1 .999.999v3.329a1 1 0 0 1-.999.999H4.339a.999.999 0 0 1-.999-.999V7.754Zm10.655 1.331a1 1 0 0 1 .999-.998h4.662c.552 0 .999.447.999.998v7.326a.999.999 0 0 1-.999.999h-4.662a1 1 0 0 1-.999-.999V9.085Zm-9.323 6.66a1 1 0 0 1 .998-.999h4.662a1 1 0 0 1 .999.999v1.998a1 1 0 0 1-.999.999H5.67a1 1 0 0 1-.998-.999v-1.998Z" style="fill:none;stroke-width:1.5px;paint-order:stroke;stroke:currentColor"/></svg>';
203
204        $cta_text = sprintf(
205            /* translators: %1$s: opening anchor tag, %2$s: closing anchor tag */
206            _x(
207                '%1$sAdd With WonderBlocks%2$s',
208                'Button label',
209                'nfd-wonder-blocks'
210            ),
211            '<a class="page-title-action" href="' . \esc_url( $url ) . '">' . $svg . '<span class="text">',
212            '</span></a>'
213        );
214
215        ?>
216        <script>
217            document.addEventListener('DOMContentLoaded', function() {
218                const pageTitleAction = document.querySelector('.wrap .page-title-action');
219                if (pageTitleAction) {
220                    const newElement = document.createElement('div');
221                    newElement.innerHTML = <?php echo \wp_json_encode( $cta_text ); ?>;
222                    newElement.classList.add('nfd-wba-cta-edit-screen');
223                    if (!<?php echo json_encode( $enabled ); ?>) {
224                        newElement.style.display = 'none';
225                    }
226                    pageTitleAction.insertAdjacentElement('afterend', newElement);
227                }
228            });
229        </script>
230        <?php
231    }
232
233    /**
234     * Add styles for the CTA
235     */
236    public function add_style() {
237        ?>
238        <style>
239            .nfd-wba-cta-edit-screen {
240                display: inline-flex;
241            }
242            .nfd-wba-cta-edit-screen .page-title-action {
243                align-items: center;
244                border-color: #3d7e29;
245                background-color: #3d7e29;
246                color: #ffffff;
247            }
248            .nfd-wba-cta-edit-screen .page-title-action:hover,
249            .nfd-wba-cta-edit-screen .page-title-action:active {
250                border-color: #3F8F3C;
251                background-color: #3F8F3C;
252                color: #ffffff;
253            }
254            .nfd-wba-cta-edit-screen .page-title-action:focus {
255                border-color: #2f621f;
256                background-color: #2f621f;
257                box-shadow: 0 0 0 1px #2f621f;
258            }
259
260            .nfd-wba-cta-edit-screen svg {
261                margin-right: 5px;
262                position: relative;
263                top: 4px;
264            }
265            @media only screen and (max-width: 433px) {
266                .nfd-wba-cta-edit-screen {
267                    margin-top: 6px;
268                }
269            }
270        </style>
271        <?php
272    }
273
274    /**
275     * Check if we're on the edit pages screen
276     */
277    private function is_core_post_type() {
278        if ( ! \is_admin() || ! \is_user_logged_in() ) {
279            return false;
280        }
281
282        global $pagenow, $typenow;
283
284        $is_core_post_type = ( 'edit.php' === $pagenow && (
285            // Default post type (no post_type parameter)
286            ( ! isset( $_GET['post_type'] ) && empty( $typenow ) ) ||
287            // Explicit post type check
288            ( isset( $_GET['post_type'] ) && in_array( $_GET['post_type'], array( 'post', 'page' ), true ) ) ||
289            // Fallback to typenow
290            in_array( $typenow, array( 'post', 'page' ), true )
291        ) );
292
293        return $is_core_post_type;
294    }
295}