Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
74.73% covered (warning)
74.73%
68 / 91
71.43% covered (warning)
71.43%
10 / 14
CRAP
0.00% covered (danger)
0.00%
0 / 1
Feedback_Source
74.73% covered (warning)
74.73%
68 / 91
71.43% covered (warning)
71.43%
10 / 14
115.20
0.00% covered (danger)
0.00%
0 / 1
 __construct
86.96% covered (warning)
86.96%
20 / 23
0.00% covered (danger)
0.00%
0 / 1
13.38
 from_submission
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
5
 get_source_title
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
56
 get_current
66.67% covered (warning)
66.67%
6 / 9
0.00% covered (danger)
0.00%
0 / 1
8.81
 from_serialized
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 get_permalink
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 get_edit_form_url
69.23% covered (warning)
69.23%
9 / 13
0.00% covered (danger)
0.00%
0 / 1
19.71
 get_relative_permalink
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 get_page_number
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_title
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_id
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_test
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 set_is_test
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 serialize
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3 * Feedback Entry
4 *
5 * @package automattic/jetpack-forms
6 */
7
8namespace Automattic\Jetpack\Forms\ContactForm;
9
10/**
11 * Class Feedback_Source
12 *
13 * Represents where a feedback was created from, feedback entry with an ID, title, permalink, and page number.
14 */
15class Feedback_Source {
16
17    /**
18     * The ID of the post or page that the feedback was created on.
19     *
20     * @var string
21     */
22    private $id = '';
23
24    /**
25     * The title of the  post or page that the feedback was created on.
26     *
27     * @var string
28     */
29    private $title = '';
30
31    /**
32     * The permalink of the feedback entry.
33     *
34     * @var string
35     */
36    private $permalink = '';
37
38    /**
39     * The page number of the feedback post or page that the feedback was created on.
40     * This is used to determine the page number in a paginated view of page or post.
41     *
42     * @var int
43     */
44    private $page_number = 1;
45
46    /**
47     * The source type of the feedback entry.
48     * Possible values: single, widget, block_template, block_template_part
49     *
50     * @var string
51     */
52    private $source_type = 'single';
53
54    /**
55     * The request URL of the feedback entry.
56     *
57     * @var string
58     */
59    private $request_url = '';
60
61    /**
62     * Whether this feedback was created from a form preview submission.
63     *
64     * Test feedback is stored normally in the inbox but is distinguished in the
65     * notification email, excluded from the default CSV export, and skips the
66     * spam/Akismet pipeline.
67     *
68     * @var bool
69     */
70    private $is_test = false;
71
72    /**
73     * Constructor for Feedback_Source.
74     *
75     * @param string|int $id          The Source ID = post ID, widget ID, block template ID, or 0 for homepage or non-post/page.
76     * @param string     $title       The title of the feedback entry.
77     * @param int        $page_number The page number of the feedback entry, default is 1.
78     * @param string     $source_type The source type of the feedback entry, default is 'single'.
79     * @param string     $request_url The request URL of the feedback entry.
80     * @param bool       $is_test     Whether the feedback was submitted from a form preview.
81     */
82    public function __construct( $id = 0, $title = '', $page_number = 1, $source_type = 'single', $request_url = '', $is_test = false ) {
83
84        if ( is_numeric( $id ) ) {
85            $this->id = $id > 0 ? $id : 0;
86        } else {
87            $this->id = $id;
88        }
89
90        if ( is_numeric( $page_number ) ) {
91            $this->page_number = $page_number > 0 ? $page_number : 1;
92        } else {
93            $this->page_number = 1;
94        }
95
96        $this->title       = html_entity_decode( $title, ENT_QUOTES | ENT_HTML5, 'UTF-8' );
97        $this->permalink   = empty( $request_url ) ? home_url() : $request_url;
98        $this->source_type = $source_type; // possible source types: single, widget, block_template, block_template_part
99        $this->request_url = $request_url;
100        $this->is_test     = (bool) $is_test;
101
102        if ( is_numeric( $id ) && ! empty( $id ) ) {
103            $entry_post = get_post( (int) $id );
104            if ( $entry_post && $entry_post->post_status === 'publish' ) {
105                $this->permalink = get_permalink( $entry_post );
106                $this->title     = html_entity_decode( get_the_title( $entry_post ), ENT_QUOTES | ENT_HTML5, 'UTF-8' );
107            } elseif ( $entry_post ) {
108                $this->permalink = '';
109
110                if ( $entry_post->post_status === 'trash' ) {
111                    /* translators: %s is the post title */
112                    $this->title = sprintf( __( '(trashed) %s', 'jetpack-forms' ), $this->title );
113                }
114            }
115            if ( empty( $entry_post ) ) {
116                /* translators: %s is the post title */
117                $this->title     = sprintf( __( '(deleted) %s', 'jetpack-forms' ), $this->title );
118                $this->permalink = '';
119            }
120        }
121    }
122
123    /**
124     * Creates a Feedback_Source instance from a submission.
125     *
126     * @param \WP_Post|null $current_post The current post object.
127     * @param int           $current_page_number The current page number, default is 1.
128     * @return Feedback_Source Returns an instance of Feedback_Source.
129     */
130    public static function from_submission( $current_post, int $current_page_number = 1 ) {
131        $id = isset( $current_post->ID ) ? (int) $current_post->ID : 0;
132
133        if ( ! $current_post instanceof \WP_Post || $id === 0 ) {
134            return new self( 0, '', $current_page_number );
135        }
136
137        $title = isset( $current_post->post_title ) ? html_entity_decode( $current_post->post_title, ENT_QUOTES | ENT_HTML5, 'UTF-8' ) : __( '(no title)', 'jetpack-forms' );
138
139        return new self( $id, $title, $current_page_number );
140    }
141
142    /**
143     * Get the title of the current page. That we can then use to display in the feedback entry.
144     *
145     * @return string The title of the current page. That we want to show to the user. To tell them where the feedback was left.
146     */
147    private static function get_source_title() {
148        if ( is_front_page() ) {
149            return get_bloginfo( 'name' );
150        }
151        if ( is_home() ) {
152            return get_the_title( get_option( 'page_for_posts', true ) );
153        }
154        if ( is_singular() ) {
155            return get_the_title();
156        }
157        if ( is_archive() ) {
158            return get_the_archive_title();
159        }
160        if ( is_search() ) {
161            /* translators: %s is the search term */
162            return sprintf( __( 'Search results for: %s', 'jetpack-forms' ), get_search_query() );
163        }
164        if ( is_404() ) {
165            return __( '404 Not Found', 'jetpack-forms' );
166        }
167        return get_bloginfo( 'name' );
168    }
169
170    /**
171     * Creates a Feedback_Source instance for a block template.
172     *
173     * @param array $attributes Form Shortcode attributes.
174     *
175     * @return Feedback_Source Returns an instance of Feedback_Source.
176     */
177    public static function get_current( $attributes ) {
178        global $wp, $page;
179        $current_url = home_url( add_query_arg( array(), $wp->request ) );
180
181        // When a form is rendered inside the server-side preview
182        // (Form_Preview::maybe_render_preview), flag the source as a test
183        // submission. The flag travels with the signed JWT and is read back
184        // at submission time to branch the response into the test pipeline.
185        $is_test = Form_Preview::is_preview_mode();
186
187        if ( isset( $attributes['widget'] ) && ! empty( $attributes['widget'] ) ) {
188            return new self( $attributes['widget'], self::get_source_title(), 1, 'widget', $current_url, $is_test );
189        }
190
191        if ( isset( $attributes['block_template'] ) && ! empty( $attributes['block_template'] ) ) {
192            global $_wp_current_template_id;
193            return new self( $_wp_current_template_id, self::get_source_title(), $page, 'block_template', $current_url, $is_test );
194        }
195
196        if ( isset( $attributes['block_template_part'] ) && ! empty( $attributes['block_template_part'] ) ) {
197            return new self( $attributes['block_template_part'], self::get_source_title(), $page, 'block_template_part', $current_url, $is_test );
198        }
199
200        return new Feedback_Source( \get_the_ID(), \get_the_title(), $page, 'single', $current_url, $is_test );
201    }
202
203    /**
204     * Creates a Feedback_Source instance from serialized data.
205     *
206     * @param array $data The serialized data.
207     * @return Feedback_Source Returns an instance of Feedback_Source.
208     */
209    public static function from_serialized( $data ) {
210        $id          = $data['source_id'] ?? 0;
211        $title       = $data['entry_title'] ?? '';
212        $page_number = $data['entry_page'] ?? 1;
213        $source_type = $data['source_type'] ?? 'single';
214        $request_url = $data['request_url'] ?? '';
215        $is_test     = ! empty( $data['is_test'] );
216
217        return new self( $id, $title, $page_number, $source_type, $request_url, $is_test );
218    }
219
220    /**
221     * Get the permalink of the feedback entry.
222     *
223     * @return string The permalink of the feedback entry.
224     */
225    public function get_permalink() {
226        if ( $this->page_number > 1 && ! empty( $this->permalink ) ) {
227            return add_query_arg( 'page', $this->page_number, $this->permalink );
228        }
229        return wp_validate_redirect( $this->permalink, home_url() );
230    }
231
232    /**
233     * Get the edit URL of the form or page where the feedback was submitted from.
234     *
235     * @return string The edit URL of the form or page.
236     */
237    public function get_edit_form_url() {
238
239        if ( current_user_can( 'edit_theme_options' ) ) {
240            if ( $this->source_type === 'block_template' && \wp_is_block_theme() ) {
241                return admin_url( 'site-editor.php?p=' . esc_attr( '/wp_template/' . addslashes( $this->id ) ) . '&canvas=edit' );
242            }
243
244            if ( $this->source_type === 'block_template_part' && \wp_is_block_theme() ) {
245                return admin_url( 'site-editor.php?p=' . esc_attr( '/wp_template_part/' . addslashes( $this->id ) ) . '&canvas=edit' );
246            }
247
248            if ( $this->source_type === 'widget' && current_theme_supports( 'widgets' ) ) {
249                return admin_url( 'widgets.php' );
250            }
251        }
252
253        if ( $this->id && is_numeric( $this->id ) && $this->id > 0 && current_user_can( 'edit_post', (int) $this->id ) ) {
254            $entry_post = get_post( (int) $this->id );
255            if ( $entry_post && $entry_post->post_status === 'trash' ) {
256                return ''; // No edit link is possible for trashed posts. They need to be restored first.
257            }
258            return \get_edit_post_link( (int) $this->id, 'url' );
259        }
260
261        return '';
262    }
263
264    /**
265     * Get the relative permalink of the feedback entry.
266     *
267     * @return string The relative permalink of the feedback entry.
268     */
269    public function get_relative_permalink() {
270        if ( ! empty( $this->permalink ) ) {
271            return wp_make_link_relative( $this->get_permalink() );
272        }
273        return '';
274    }
275
276    /**
277     * Get the page number of the feedback entry.
278     *
279     * @return int The page number of the feedback entry.
280     */
281    public function get_page_number() {
282        return $this->page_number;
283    }
284    /**
285     * Get the title of the feedback entry.
286     *
287     * @return string The title of the feedback entry.
288     */
289    public function get_title() {
290        return $this->title;
291    }
292    /**
293     * Get the post id of the feedback entry.
294     *
295     * @return int|string The ID of the feedback entry.
296     */
297    public function get_id() {
298        return $this->id;
299    }
300
301    /**
302     * Whether this feedback was submitted from a form preview (test submission).
303     *
304     * @return bool
305     */
306    public function is_test() {
307        return $this->is_test;
308    }
309
310    /**
311     * Flag this feedback as a test submission coming from form preview.
312     *
313     * @param bool $is_test Whether the feedback is a test submission.
314     * @return void
315     */
316    public function set_is_test( $is_test ) {
317        $this->is_test = (bool) $is_test;
318    }
319
320    /**
321     * Get the page number of the entry title.
322     *
323     * @return array
324     */
325    public function serialize() {
326        $data = array(
327            'entry_title' => $this->title,
328            'entry_page'  => $this->page_number,
329            'source_id'   => $this->id,
330            'source_type' => $this->source_type,
331            'request_url' => $this->request_url,
332        );
333
334        if ( $this->is_test ) {
335            $data['is_test'] = true;
336        }
337
338        return $data;
339    }
340}