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