Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
76.36% covered (warning)
76.36%
42 / 55
40.00% covered (danger)
40.00%
2 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
PlayerStyle
76.36% covered (warning)
76.36%
42 / 55
40.00% covered (danger)
40.00%
2 / 5
16.59
0.00% covered (danger)
0.00%
0 / 1
 init
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 element
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
2
 save
78.57% covered (warning)
78.57%
11 / 14
0.00% covered (danger)
0.00%
0 / 1
7.48
 restApiInit
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 playerStylesRestApiResponse
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3declare(strict_types=1);
4
5/**
6 * BeyondWords Component: Player style
7 *
8 * @package Beyondwords\Wordpress
9 * @author  Stuart McAlpine <stu@beyondwords.io>
10 * @since   4.1.0
11 */
12
13namespace Beyondwords\Wordpress\Component\Post\PlayerStyle;
14
15use Beyondwords\Wordpress\Component\Post\PostMetaUtils;
16use Beyondwords\Wordpress\Component\Settings\Fields\PlayerStyle\PlayerStyle as PlayerStyleSetting;
17use Beyondwords\Wordpress\Component\Settings\SettingsUtils;
18
19/**
20 * PlayerStyle
21 *
22 * @since 4.1.0
23 */
24class PlayerStyle
25{
26    /**
27     * Player styles.
28     *
29     * @var array Arry of player styles.
30     */
31    public const PLAYER_STYLES = [
32        'small',
33        'standard',
34        'large',
35        'screen',
36        'video',
37    ];
38
39    /**
40     * Constructor
41     */
42    public function init()
43    {
44        add_action('rest_api_init', array($this, 'restApiInit'));
45
46        add_action('wp_loaded', function () {
47            $postTypes = SettingsUtils::getCompatiblePostTypes();
48
49            if (is_array($postTypes)) {
50                foreach ($postTypes as $postType) {
51                    add_action("save_post_{$postType}", array($this, 'save'), 10);
52                }
53            }
54        });
55    }
56
57    /**
58     * HTML output for this component.
59     *
60     * @since 4.1.0
61     * @since 4.5.1 Hide element if no language data exists.
62     *
63     * @param WP_Post $post The post object.
64     *
65     * @return string|null
66     */
67    public function element($post)
68    {
69        $playerStyle     = PostMetaUtils::getPlayerStyle($post->ID);
70        $allPlayerStyles = PlayerStyleSetting::getOptions();
71
72        wp_nonce_field('beyondwords_player_style', 'beyondwords_player_style_nonce');
73        ?>
74        <p
75            id="beyondwords-metabox-player-style"
76            class="post-attributes-label-wrapper page-template-label-wrapper"
77        >
78            <label class="post-attributes-label" for="beyondwords_player_style">
79                <?php esc_html_e('Player style', 'speechkit'); ?>
80            </label>
81        </p>
82        <select id="beyondwords_player_style" name="beyondwords_player_style" style="width: 100%;">
83            <option value=""></option>
84            <?php
85            foreach ($allPlayerStyles as $item) {
86                printf(
87                    '<option value="%s" %s %s>%s</option>',
88                    esc_attr($item['value']),
89                    selected(strval($item['value']), $playerStyle),
90                    disabled($item['disabled'] ?? false, true),
91                    esc_html($item['label'])
92                );
93            }
94            ?>
95        </select>
96        <?php
97    }
98
99    /**
100     * Save the meta when the post is saved.
101     *
102     * @since 4.1.0
103     *
104     * @param int $postId The ID of the post being saved.
105     */
106    public function save($postId)
107    {
108        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
109            return $postId;
110        }
111
112        // "save_post" can be triggered at other times, so verify this request came from the our component
113        if (! isset($_POST['beyondwords_player_style']) || ! isset($_POST['beyondwords_player_style_nonce'])) {
114            return $postId;
115        }
116
117        // "save_post" can be triggered at other times, so verify this request came from the our component
118        if (
119            ! wp_verify_nonce(
120                sanitize_key($_POST['beyondwords_player_style_nonce']),
121                'beyondwords_player_style'
122            )
123        ) {
124            return $postId;
125        }
126
127        $playerStyle = sanitize_text_field(wp_unslash($_POST['beyondwords_player_style']));
128
129        if (! empty($playerStyle)) {
130            update_post_meta($postId, 'beyondwords_player_style', $playerStyle);
131        } else {
132            delete_post_meta($postId, 'beyondwords_player_style');
133        }
134
135        return $postId;
136    }
137
138    /**
139     * Register WP REST API route
140     *
141     * @since 4.1.0
142     *
143     * @return void
144     */
145    public function restApiInit()
146    {
147        // Player styles endpoint
148        register_rest_route('beyondwords/v1', '/projects/(?P<projectId>[0-9]+)/player-styles', array(
149            'methods'  => \WP_REST_Server::READABLE,
150            'callback' => array($this, 'playerStylesRestApiResponse'),
151            'permission_callback' => function () {
152                return current_user_can('edit_posts');
153            },
154        ));
155    }
156
157    /**
158     * "Player styles" WP REST API response (required for the Gutenberg editor).
159     *
160     * @since 4.1.0
161     * @since 5.0.0 Stop saving a dedicated player styles transient for each project ID.
162     */
163    public function playerStylesRestApiResponse()
164    {
165        $response = PlayerStyleSetting::getOptions();
166
167        // Convert from object to array so we can use find() in Block Editor JS.
168        $response = array_values($response);
169
170        return new \WP_REST_Response($response);
171    }
172}