Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
76.42% covered (warning)
76.42%
81 / 106
55.56% covered (danger)
55.56%
5 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
Metabox
76.19% covered (warning)
76.19%
80 / 105
55.56% covered (danger)
55.56%
5 / 9
26.95
0.00% covered (danger)
0.00%
0 / 1
 init
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 adminEnqueueScripts
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 addMetaBox
92.86% covered (success)
92.86%
13 / 14
0.00% covered (danger)
0.00%
0 / 1
2.00
 renderMetaBoxContent
94.44% covered (success)
94.44%
17 / 18
0.00% covered (danger)
0.00%
0 / 1
4.00
 pendingReviewNotice
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
6
 playerEmbed
81.25% covered (success)
81.25%
13 / 16
0.00% covered (danger)
0.00%
0 / 1
5.16
 errors
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
2
 help
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 regenerateInstructions
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5/**
6 * BeyondWords Post Metabox.
7 *
8 * @package Beyondwords\Wordpress
9 * @author  Stuart McAlpine <stu@beyondwords.io>
10 * @since   3.0.0
11 */
12
13namespace Beyondwords\Wordpress\Component\Post\Metabox;
14
15use Beyondwords\Wordpress\Component\Post\GenerateAudio\GenerateAudio;
16use Beyondwords\Wordpress\Component\Post\DisplayPlayer\DisplayPlayer;
17use Beyondwords\Wordpress\Component\Post\PostMetaUtils;
18use Beyondwords\Wordpress\Component\Post\SelectVoice\SelectVoice;
19use Beyondwords\Wordpress\Component\Post\PlayerContent\PlayerContent;
20use Beyondwords\Wordpress\Component\Post\PlayerStyle\PlayerStyle;
21use Beyondwords\Wordpress\Component\Settings\SettingsUtils;
22use Beyondwords\Wordpress\Core\Environment;
23
24/**
25 * PostMetabox
26 *
27 * @since 3.0.0
28 */
29defined('ABSPATH') || exit;
30
31class Metabox
32{
33    /**
34     * Init.
35     *
36     * @since 4.0.0
37     * @since 6.0.0 Make static.
38     */
39    public static function init()
40    {
41        add_action('admin_enqueue_scripts', [self::class, 'adminEnqueueScripts']);
42        add_action("add_meta_boxes", [self::class, 'addMetaBox']);
43    }
44
45    /**
46     * Enque JS for Bulk Edit feature.
47     *
48     * @since 6.0.0 Make static.
49     */
50    public static function adminEnqueueScripts($hook)
51    {
52        // Only enqueue for Post screens
53        if ($hook === 'post.php' || $hook === 'post-new.php') {
54            // Register the Classic Editor "Metabox" CSS
55            wp_enqueue_style(
56                'beyondwords-Metabox',
57                BEYONDWORDS__PLUGIN_URI . 'src/Component/Post/Metabox/Metabox.css',
58                false,
59                BEYONDWORDS__PLUGIN_VERSION
60            );
61        }
62    }
63
64    /**
65     * Adds the meta box container.
66     *
67     * @since 6.0.0 Make static.
68     *
69     * @param string $postType
70     */
71    public static function addMetaBox($postType)
72    {
73        $postTypes = SettingsUtils::getCompatiblePostTypes();
74
75        if (! in_array($postType, $postTypes)) {
76            return;
77        }
78
79        add_meta_box(
80            'beyondwords',
81            __('BeyondWords', 'speechkit'),
82            [self::class, 'renderMetaBoxContent'],
83            $postType,
84            'side',
85            'default',
86            [
87                '__back_compat_meta_box' => true,
88            ]
89        );
90    }
91
92    /**
93     * Render Meta Box content.
94     *
95     * @param int|WP_Post $post The WordPress post ID, or post object.
96     *
97     * @since 3.0.0
98     * @since 3.7.0 Show "Pending review" notice for posts with status of "pending"
99     * @since 4.0.0 Content ID is no longer an int
100     * @since 4.1.0 Add "Player style" and update component display conditions
101     * @since 6.0.0 Make static and add Magic Embed support.
102     */
103    public static function renderMetaBoxContent($post)
104    {
105        $post = get_post($post);
106
107        if (!($post instanceof \WP_Post)) {
108            return;
109        }
110
111        // Show errors for posts with/without audio
112        self::errors($post);
113
114        $hasContent = PostMetaUtils::hasContent($post->ID);
115
116        if ($hasContent) {
117            // Enable these components for posts with audio
118            if (get_post_status($post) === 'pending') {
119                self::pendingReviewNotice($post);
120            } else {
121                self::playerEmbed($post);
122            }
123            echo '<hr />';
124            (new DisplayPlayer())::element($post);
125        } else {
126            self::errors($post);
127            // Enable these components for posts without audio
128            (new GenerateAudio())::element($post);
129        }
130
131        // Enable these components for posts with/without audio
132        (new SelectVoice())::element($post);
133        (new PlayerStyle())::element($post);
134        (new PlayerContent())::element($post);
135
136        echo '<hr />';
137        self::help();
138    }
139
140
141    /**
142     * The "Pending review" message, shown instead of the audio player
143     * if the post status in WordPress is "pending".
144     *
145     * This message is displayed instead of the player because the player
146     * cannot be rendered for audio which has been created
147     * with { published: false }.
148     *
149     * @since 3.7.0
150     * @since 6.0.0 Make static.
151     *
152     * @var int|\WP_Post $post The WordPress post ID, or post object.
153     */
154    public static function pendingReviewNotice($post)
155    {
156        $post = get_post($post);
157
158        if (!($post instanceof \WP_Post)) {
159            return;
160        }
161
162        $projectUrl = sprintf(
163            '%s/dashboard/project/%d/content',
164            Environment::getDashboardUrl(),
165            PostMetaUtils::getProjectId($post->ID)
166        );
167
168        ?>
169        <div id="beyondwords-pending-review-message">
170            <?php
171            printf(
172                /* translators: %s is replaced with the link to the BeyondWords dashboard */
173                esc_html__('Listen to content saved as “Pending” in the %s.', 'speechkit'),
174                sprintf(
175                    '<a href="%s" target="_blank" rel="nofollow">%s</a>',
176                    esc_url($projectUrl),
177                    esc_html__('BeyondWords dashboard', 'speechkit')
178                )
179            );
180            ?>
181        </div>
182        <?php
183    }
184
185    /**
186     * Embed a player for a WordPress post.
187     *
188     * @param int|WP_Post (Optional) Post ID or WP_Post object. Default is global $post.
189     *
190     * @since 3.x   Introduced
191     * @since 4.0.1 Admin player init is now all in this one function.
192     * @since 6.0.0 Make static and add Magic Embed support.
193     */
194    public static function playerEmbed($post = null)
195    {
196        $post = get_post($post);
197
198        if (!($post instanceof \WP_Post)) {
199            return;
200        }
201
202        $projectId  = PostMetaUtils::getProjectId($post->ID);
203        $hasContent = PostMetaUtils::hasContent($post->ID);
204
205        if (! $projectId || ! $hasContent) {
206            return;
207        }
208
209        $contentId    = PostMetaUtils::getContentId($post->ID);
210        $previewToken = PostMetaUtils::getPreviewToken($post->ID);
211
212        // phpcs:disable WordPress.WP.EnqueuedResources.NonEnqueuedScript
213        ?>
214        <script async defer
215            src='<?php echo esc_url(Environment::getJsSdkUrl()); ?>'
216            onload='const player = new BeyondWords.Player({
217                target: this,
218                projectId: <?php echo esc_attr($projectId); ?>,
219                <?php if (! empty($contentId)) : ?>
220                contentId: "<?php echo esc_attr($contentId); ?>",
221                <?php else : ?>
222                sourceId: "<?php echo esc_attr($post->ID); ?>",
223                <?php endif; ?>
224                previewToken: "<?php echo esc_attr($previewToken); ?>",
225                adverts: [],
226                analyticsConsent: "none",
227                introsOutros: [],
228                playerStyle: "small",
229                widgetStyle: "none",
230            });'
231        >
232        </script>
233        <?php
234        // phpcs:enable WordPress.WP.EnqueuedResources.NonEnqueuedScript
235    }
236
237    /**
238     * Display errors for the post.
239     *
240     * @since 6.0.0 Make static.
241     */
242    public static function errors($post)
243    {
244        $error = PostMetaUtils::getErrorMessage($post->ID);
245
246        if ($error) :
247            ?>
248            <div id="beyondwords-metabox-errors">
249                <div class="beyondwords-error">
250                    <p>
251                        <?php echo esc_html($error); ?>
252                    </p>
253                </div>
254                <?php self::regenerateInstructions(); ?>
255            </div>
256            <?php
257        endif;
258    }
259
260    /**
261     * Display help text for the metabox.
262     *
263     * @since 6.0.0 Make static.
264     */
265    public static function help()
266    {
267        ?>
268        <p id="beyondwords-metabox-help">
269            <?php
270            printf(
271                /* translators: %s is replaced with the link to the support email address */
272                esc_html__('Need help? Email our support team on %s', 'speechkit'),
273                sprintf('<a href="%s">%s</a>', 'mailto:support@beyondwords.io', 'support@beyondwords.io')
274            );
275            ?>
276        </p>
277        <?php
278    }
279
280    /**
281     * Display instructions for regenerating audio.
282     *
283     * @since 6.0.0 Make static.
284     */
285    public static function regenerateInstructions()
286    {
287        ?>
288        <!-- Update/regenerate -->
289        <p>
290            <?php
291            esc_html_e(
292                'To create audio, resolve the error above then select ‘Update’ with ‘Generate audio’ checked.', // phpcs:ignore Generic.Files.LineLength.TooLong
293                'speechkit'
294            );
295            ?>
296        </p>
297        <?php
298    }
299}