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