Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 108
0.00% covered (danger)
0.00%
0 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
Yoast
0.00% covered (danger)
0.00%
0 / 108
0.00% covered (danger)
0.00%
0 / 12
1806
0.00% covered (danger)
0.00%
0 / 1
 register_hooks
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 site_representation_updated
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 social_profiles_updated
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
42
 tracking_updated
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 maybe_push_site_representation_event
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
90
 maybe_push_social_profiles_event
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
30
 push_other_social_profiles
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
6
 map_params_names_to_hiive_names
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 map_failures_to_hiive_names
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 is_param_empty
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 get_base_url
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 clean_social_profiles_failures
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2
3namespace NewfoldLabs\WP\Module\Data\Listeners;
4
5/**
6 * Monitors Yoast events
7 */
8class Yoast extends Listener {
9    // We don't want to track these fields
10    private $site_representation_skip_fields = array( 'company_logo_id', 'person_logo_id', 'description' );
11
12    // The names used for Hiive events tracking are different from the names used for the Yoast options
13    private $site_representation_map = array(
14        'company_or_person'         => 'site_representation',
15        'company_name'              => 'organization_name',
16        'company_logo'              => 'organization_logo',
17        'person_logo'               => 'logo',
18        'company_or_person_user_id' => 'name',
19        'website_name'              => 'website_name',
20    );
21
22    private $social_profiles_map = array(
23        'facebook_site'     => 'facebook_profile',
24        'twitter_site'      => 'twitter_profile',
25        'other_social_urls' => 'other_profiles',
26    );
27
28    /**
29     * Register the hooks for the listener
30     *
31     * @return void
32     */
33    public function register_hooks() {
34        // First time configuration
35        add_action( 'wpseo_ftc_post_update_site_representation', array( $this, 'site_representation_updated' ), 10, 3 );
36        add_action( 'wpseo_ftc_post_update_social_profiles', array( $this, 'social_profiles_updated' ), 10, 3 );
37        add_action( 'wpseo_ftc_post_update_enable_tracking', array( $this, 'tracking_updated' ), 10, 3 );
38    }
39
40    /**
41     * The user just updated their site representation
42     *
43     * @param array $new_values The new values for the options related to the site representation
44     * @param array $old_values The old values for the options related to the site representation
45     * @param array $failures   The failures that occurred during the update
46     *
47     * @return void
48     */
49    public function site_representation_updated( $new_values, $old_values, $failures ) {
50        // All the options are unchanged, opt out
51        if ( $new_values === $old_values ) {
52            return;
53        }
54
55        $mapped_new_values = $this->map_params_names_to_hiive_names( $new_values, $this->site_representation_map, $this->site_representation_skip_fields );
56        $mapped_old_values = $this->map_params_names_to_hiive_names( $old_values, $this->site_representation_map, $this->site_representation_skip_fields );
57        $mapped_failures   = $this->map_failures_to_hiive_names( $failures, $this->site_representation_map, $this->site_representation_skip_fields );
58
59        foreach ( $mapped_new_values as $key => $value ) {
60            $this->maybe_push_site_representation_event( $key, $value, $mapped_old_values[ $key ], \in_array( $key, $mapped_failures ) );
61        }
62    }
63
64    /**
65     * The user just updated their personal profiles
66     *
67     * @param array $new_values The new values for the options related to the site representation
68     * @param array $old_values The old values for the options related to the site representation
69     * @param array $failures   The failures that occurred during the update
70     *
71     * @return void
72     */
73    public function social_profiles_updated( $new_values, $old_values, $failures ) {
74        // Yoast stores only twitter username, and $new_values stores the pre-processed values
75        if ( strpos( $new_values['twitter_site'], 'twitter.com/' ) !== false ) {
76            $new_values['twitter_site'] = ( explode( 'twitter.com/', $new_values['twitter_site'] )[1] );
77        }
78
79        // All the options are unchanged, opt out
80        if ( $new_values === $old_values ) {
81            return;
82        }
83
84        // Remove multiple occurences of other_social_urls;
85        $cleaned_failures = $this->clean_social_profiles_failures( $failures );
86
87        $mapped_values     = $this->map_params_names_to_hiive_names( $new_values, $this->social_profiles_map );
88        $mapped_old_values = $this->map_params_names_to_hiive_names( $old_values, $this->social_profiles_map );
89        $mapped_failures   = $this->map_failures_to_hiive_names( $cleaned_failures, $this->social_profiles_map );
90
91        foreach ( $mapped_values as $key => $value ) {
92            // The option update failed
93            if ( \in_array( $key, $mapped_failures ) ) {
94                $this->push( "failed_$key", array( 'category' => 'ftc_personal_profiles' ) );
95                return;
96            }
97
98            if ( $value !== $mapped_old_values[ $key ] ) {
99                $this->maybe_push_social_profiles_event( $key, $value, $mapped_old_values[ $key ], \in_array( $key, $mapped_failures ) );
100            }
101        }
102    }
103
104    /**
105     * The user updated their tracking preferences
106     *
107     * @param string $new_value The new value for the option related to tracking
108     * @param string $old_value The old value for the option related to tracking
109     * @param bool   $failed    Whether the option update failed
110     *
111     * @return void
112     */
113    public function tracking_updated( $new_value, $old_value, $failed ) {
114        // Option unchanged, opt out
115        if ( $new_value === $old_value ) {
116            return;
117        }
118
119        $failed ? $this->push( 'failed_usage_tracking', array( 'category' => 'ftc_tracking' ) ) : $this->push( 'changed_usage_tracking', array( 'category' => 'ftc_tracking' ) );
120    }
121
122    /**
123     * A method used to (maybe) push a site representation-related event to the queue.
124     *
125     * @param string $key       The option key
126     * @param string $value     The new option value
127     * @param string $old_value The old option value
128     * @param bool   $failure   Whether the option update failed
129     *
130     * @return void
131     */
132    private function maybe_push_site_representation_event( $key, $value, $old_value, $failure ) {
133        $category = 'ftc_site_representation';
134
135        // The option update failed
136        if ( $failure ) {
137            $this->push( "failed_$key", array( 'category' => $category ) );
138            return;
139        }
140
141        // The option value changed
142        if ( $value !== $old_value ) {
143            // The option was set for the first time
144
145            // name is a special case, because it represents the company_or_person_user_id which is initialised to false, and the first time the user saves the site representation step
146            // is set either to 0 if the site represents an organisation, or to an integer > 0 if the site represents a person
147            if ( $key === 'name' ) {
148                if ( $old_value === false && $value === 0 ) {
149                    return;
150                }
151            }
152
153            // Again, name is a special case, because if its old value was 0 and a value different that 0 is being received, it means that the user
154            // switched from organisation to person, and then the person id is being set.
155            // Once the name is assigned an integer > 0, it can never go back to 0, even if the user switches back to organisation
156            // ( it "caches" the last user id that was set)
157            if ( ( $this->is_param_empty( $old_value ) ) || ( $key === 'name' && $old_value === 0 ) ) {
158                $this->push( "set_$key", array( 'category' => $category ) );
159                return;
160            }
161
162            // The option was updated
163            $data = array(
164                'category' => $category,
165                'data'     => array(
166                    'label_key' => $key,
167                    'new_value' => $value,
168                ),
169            );
170
171            $this->push(
172                "changed_$key",
173                $data
174            );
175        }
176    }
177
178    /**
179     * A method used to (maybe) push a social profile-related event to the queue.
180     *
181     * @param string $key       The option key
182     * @param string $value     The new option value
183     * @param string $old_value The old option value
184     * @param bool   $failure   Whether the option update failed
185     *
186     * @return void
187     */
188    private function maybe_push_social_profiles_event( $key, $value, $old_value, $failure ) {
189        $category = 'ftc_personal_profiles';
190
191        // The option update failed
192        if ( $failure ) {
193            $this->push( "failed_$key", array( 'category' => $category ) );
194            return;
195        }
196
197        // The option value changed
198        if ( $value !== $old_value ) {
199            if ( $key === 'other_profiles' ) {
200                $this->push_other_social_profiles( $key, $value, $old_value, $category );
201                return;
202            }
203
204            // The option was set for the first time
205            if ( $this->is_param_empty( $old_value ) ) {
206                $this->push( "set_$key", array( 'category' => $category ) );
207                return;
208            }
209
210            // The option was updated
211            $this->push( "changed_$key", array( 'category' => $category ) );
212        }
213    }
214
215
216    /**
217     * A method used to (maybe) push the other_profiles-related event to the queue.
218     *
219     * @param string $key       The option key (other_profiles)
220     * @param array  $new_value The array of new social profiles
221     * @param array  $old_value The array of old social profiles
222     * @param string $category  The category of the event
223     *
224     * @return void
225     */
226    private function push_other_social_profiles( $key, $new_value, $old_value, $category ) {
227        // The option was set for the first time
228        if ( $this->is_param_empty( $old_value ) ) {
229            $this->push( "set_$key", array( 'category' => $category ) );
230            return;
231        }
232
233        $changed_profiles = \array_map(
234            function ( $value ) {
235                return $this->get_base_url( \wp_unslash( $value ) );
236            },
237            $new_value
238        );
239
240        // The option was updated
241        $data = array(
242            'category' => $category,
243            'data'     => array(
244                'label_key' => $key,
245                'new_value' => $changed_profiles,
246            ),
247        );
248
249        $this->push( 'changed_other_profiles', $data );
250    }
251
252    /**
253     * Maps the param names to the names used for Hiive events tracking.
254     *
255     * @param array $params      The params to map.
256     * @param array $map         The map to use.
257     * @param array $skip_fields The fields to skip.
258     *
259     * @return array The mapped params.
260     */
261    private function map_params_names_to_hiive_names( $params, $map, $skip_fields = array() ) {
262        $mapped_params = array();
263
264        foreach ( $params as $param_name => $param_value ) {
265            if ( in_array( $param_name, $skip_fields, true ) ) {
266                continue;
267            }
268
269            $new_name                   = $map[ $param_name ];
270            $mapped_params[ $new_name ] = $param_value;
271        }
272
273        return $mapped_params;
274    }
275
276    /**
277     * Maps the names of the params which failed the update to the names used for Hiive events tracking.
278     *
279     * @param array $failures    The params names to map.
280     * @param array $map         The map to use.
281     * @param array $skip_fields The fields to skip.
282     *
283     * @return array The mapped params names.
284     */
285    private function map_failures_to_hiive_names( $failures, $map, $skip_fields = array() ) {
286        $mapped_failures = array();
287
288        foreach ( $failures as $failed_field_name ) {
289            if ( in_array( $failed_field_name, $skip_fields, true ) ) {
290                continue;
291            }
292
293            $mapped_failures[] = $map[ $failed_field_name ];
294        }
295
296        return $mapped_failures;
297    }
298
299    /**
300     * Checks whether a param is empty.
301     *
302     * @param mixed $param The param to check.
303     *
304     * @return bool Whether the param is empty.
305     */
306    private function is_param_empty( $param ) {
307        if ( is_array( $param ) ) {
308            return ( count( $param ) === 0 );
309        }
310
311        return ( strlen( $param ) === 0 );
312    }
313
314    /**
315     * Gets the base url of a given url.
316     *
317     * @param string $url The url.
318     *
319     * @return string The base url.
320     */
321    private function get_base_url( $url ) {
322        $parts = \parse_url( $url );
323
324        return $parts['scheme'] . '://' . $parts['host'];
325    }
326
327    /**
328     * Removes multiple occurences of other_social_urls from the failures array
329     *
330     * @param array $failures The failures array
331     *
332     * @return array The cleaned failures array
333     */
334    private function clean_social_profiles_failures( $failures ) {
335        $cleaned_failures             = array();
336        $other_social_profiles_failed = false;
337
338        foreach ( $failures as $failure ) {
339            if ( strpos( $failure, 'other_social_urls' ) === 0 ) {
340                $other_social_profiles_failed = true;
341                continue;
342            }
343            $cleaned_failures[] = $failure;
344        }
345
346        if ( $other_social_profiles_failed ) {
347            $cleaned_failures[] = 'other_social_urls';
348        }
349
350        return $cleaned_failures;
351    }
352}