Skip to content

Commit f69d1f0

Browse files
authored
Forms: Fix feedback source data (#45231)
* Forms: Add edit form URL to feedback source and dashboard Introduces an 'edit_form_url' property to the feedback source, allowing users to quickly access the edit page for forms, widgets, or block templates from the dashboard. Updates backend logic to serialize and expose this URL via the REST API, and adds a new dashboard action for editing forms. Includes related test updates to ensure source serialization and form source matching. * changelog * Fixing tests * Match Core * Update actions.js * Update to match core style * Fix the trashed post case. * Always pass an int * Fix typo
1 parent 516d24b commit f69d1f0

File tree

12 files changed

+296
-48
lines changed

12 files changed

+296
-48
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: patch
2+
Type: fixed
3+
4+
Forms: Store the feedback source info with more context

projects/packages/forms/src/contact-form/class-contact-form-endpoint.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,16 @@ public function get_item_schema() {
533533
'readonly' => true,
534534
);
535535

536+
$schema['properties']['edit_form_url'] = array(
537+
'description' => __( 'The URL to edit the form.', 'jetpack-forms' ),
538+
'type' => 'string',
539+
'context' => array( 'view', 'edit', 'embed' ),
540+
'arg_options' => array(
541+
'sanitize_callback' => 'sanitize_text_field',
542+
),
543+
'readonly' => true,
544+
);
545+
536546
$schema['properties']['subject'] = array(
537547
'description' => __( 'The subject line of the form submission.', 'jetpack-forms' ),
538548
'type' => 'string',
@@ -756,6 +766,9 @@ public function prepare_item_for_response( $item, $request ) {
756766
if ( rest_is_field_included( 'entry_permalink', $fields ) ) {
757767
$data['entry_permalink'] = $feedback_response->get_entry_permalink();
758768
}
769+
if ( rest_is_field_included( 'edit_form_url', $fields ) ) {
770+
$data['edit_form_url'] = $feedback_response->get_edit_form_url();
771+
}
759772
if ( rest_is_field_included( 'subject', $fields ) ) {
760773
$data['subject'] = $feedback_response->get_subject();
761774
}

projects/packages/forms/src/contact-form/class-contact-form.php

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,13 @@ class Contact_Form extends Contact_Form_Shortcode {
121121
*/
122122
public $has_verified_jwt = false;
123123

124+
/**
125+
* The source of the feedback entry.
126+
*
127+
* @var Feedback_Source
128+
*/
129+
private $source;
130+
124131
/**
125132
* Construction function.
126133
*
@@ -251,6 +258,7 @@ public static function get_instance_from_jwt( $jwt_token ) {
251258
}
252259

253260
$form = new self( $data['attributes'], $data['content'], empty( $data['attributes']['id'] ) );
261+
$form->source = Feedback_Source::from_serialized( $data['source'] );
254262
$form->hash = $data['hash'];
255263
$form->has_verified_jwt = true;
256264
return $form;
@@ -362,17 +370,32 @@ public function get_attributes() {
362370
* @return string The JWT token.
363371
*/
364372
public function get_jwt() {
365-
$attributes = $this->attributes;
373+
$attributes = $this->attributes;
374+
$this->source = Feedback_Source::get_current( $attributes );
366375
return JWT::encode(
367376
array(
368377
'attributes' => $attributes,
369378
'content' => $this->content,
370379
'hash' => $this->hash,
380+
'source' => $this->source->serialize(),
371381
),
372382
self::get_secret()
373383
);
374384
}
375385

386+
/**
387+
* Get the current source obejct. That is relevent to the form and there current request.
388+
*
389+
* @return Feedback_Source Return the current feedback source object.
390+
*/
391+
public function get_source() {
392+
if ( ! $this->source ) {
393+
$attributes = $this->attributes;
394+
$this->source = Feedback_Source::get_current( $attributes );
395+
}
396+
return $this->source;
397+
}
398+
376399
/**
377400
* Get the count of forms.
378401
*
@@ -837,10 +860,6 @@ class='" . esc_attr( $form_classes ) . "' $form_aria_label
837860
$r .= "\t\t<input type='hidden' name='action' value='grunion-contact-form' />\n";
838861
$r .= "\t\t<input type='hidden' name='contact-form-hash' value='" . esc_attr( $form->hash ) . "' />\n";
839862

840-
if ( $page && $page > 1 ) {
841-
$r .= "\t\t<input type='hidden' name='page' value='$page' />\n";
842-
}
843-
844863
if ( ! $has_submit_button_block ) {
845864
$r .= "\t</p>\n";
846865
}
@@ -1663,15 +1682,9 @@ public function get_field_ids() {
16631682
* Stores feedback. Sends email.
16641683
*/
16651684
public function process_submission() {
1666-
$page_number = 1;
1667-
1668-
// We skip the nonce verification for since nonce earlier in process_form_submission.
1669-
if ( isset( $_POST['page'] ) ) { // phpcs:Ignore WordPress.Security.NonceVerification.Missing
1670-
$page_number = absint( wp_unslash( $_POST['page'] ) ); // phpcs:Ignore WordPress.Security.NonceVerification.Missing
1671-
}
1672-
1673-
$response = Feedback::from_submission( $_POST, $this, $this->current_post, $page_number ); // phpcs:Ignore WordPress.Security.NonceVerification.Missing
16741685

1686+
$response = Feedback::from_submission( $_POST, $this ); // phpcs:Ignore WordPress.Security.NonceVerification.Missing
1687+
$response->set_source( $this->get_source() );
16751688
$plugin = Contact_Form_Plugin::init();
16761689

16771690
$id = $this->get_attribute( 'id' );

projects/packages/forms/src/contact-form/class-feedback-source.php

Lines changed: 156 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ class Feedback_Source {
1717
/**
1818
* The ID of the post or page that the feedback was created on.
1919
*
20-
* @var int
20+
* @var string
2121
*/
22-
private $id = 0;
22+
private $id = '';
2323

2424
/**
2525
* The title of the post or page that the feedback was created on.
@@ -43,29 +43,62 @@ class Feedback_Source {
4343
*/
4444
private $page_number = 1;
4545

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+
4661
/**
4762
* Constructor for Feedback_Source.
4863
*
49-
* @param int $id The ID of the feedback entry.
50-
* @param string $title The title of the feedback entry.
51-
* @param int $page_number The page number of the feedback entry, default is 1.
64+
* @param string|int $id The Source ID = post ID, widget ID, block template ID, or 0 for homepage or non-post/page.
65+
* @param string $title The title of the feedback entry.
66+
* @param int $page_number The page number of the feedback entry, default is 1.
67+
* @param string $source_type The source type of the feedback entry, default is 'single'.
68+
* @param string $request_url The request URL of the feedback entry.
5269
*/
53-
public function __construct( $id, $title, $page_number = 1 ) {
70+
public function __construct( $id = 0, $title = '', $page_number = 1, $source_type = 'single', $request_url = '' ) {
71+
72+
if ( is_numeric( $id ) ) {
73+
$this->id = $id > 0 ? $id : 0;
74+
} else {
75+
$this->id = $id;
76+
}
5477

55-
$this->id = $id > 0 ? (int) $id : 0;
5678
$this->title = $title;
5779
$this->page_number = $page_number;
58-
$this->permalink = $this->id === 0 ? home_url() : '';
59-
60-
if ( $id <= 0 ) {
61-
return;
62-
}
80+
$this->permalink = empty( $request_url ) ? home_url() : $request_url;
81+
$this->source_type = $source_type; // possible source types: single, widget, block_template, block_template_part
82+
$this->request_url = $request_url;
6383

64-
$entry_post = get_post( $id );
84+
if ( is_numeric( $id ) && ! empty( $id ) ) {
85+
$entry_post = get_post( (int) $id );
86+
if ( $entry_post && $entry_post->post_status === 'publish' ) {
87+
$this->permalink = get_permalink( $entry_post );
88+
$this->title = get_the_title( $entry_post );
89+
} elseif ( $entry_post ) {
90+
$this->permalink = '';
6591

66-
if ( $entry_post && $entry_post->post_status === 'publish' ) {
67-
$this->permalink = get_permalink( $entry_post );
68-
$this->title = get_the_title( $entry_post );
92+
if ( $entry_post->post_status === 'trash' ) {
93+
/* translators: %s is the post title */
94+
$this->title = sprintf( __( '(trashed) %s', 'jetpack-forms' ), $this->title );
95+
}
96+
}
97+
if ( empty( $entry_post ) ) {
98+
/* translators: %s is the post title */
99+
$this->title = sprintf( __( '(deleted) %s', 'jetpack-forms' ), $this->title );
100+
$this->permalink = '';
101+
}
69102
}
70103
}
71104

@@ -83,11 +116,81 @@ public static function from_submission( $current_post, int $current_page_number
83116
return new self( 0, '', $current_page_number );
84117
}
85118

86-
$title = $current_post->post_title ?? '';
119+
$title = $current_post->post_title ?? __( '(no title)', 'jetpack-forms' );
87120

88121
return new self( $id, $title, $current_page_number );
89122
}
90123

124+
/**
125+
* Get the title of the current page. That we can then use to display in the feedback entry.
126+
*
127+
* @return string The title of the current page. That we want to show to the user. To tell them where the feedback was left.
128+
*/
129+
private static function get_source_title() {
130+
if ( is_front_page() ) {
131+
return get_bloginfo( 'name' );
132+
}
133+
if ( is_home() ) {
134+
return get_the_title( get_option( 'page_for_posts', true ) );
135+
}
136+
if ( is_singular() ) {
137+
return get_the_title();
138+
}
139+
if ( is_archive() ) {
140+
return get_the_archive_title();
141+
}
142+
if ( is_search() ) {
143+
/* translators: %s is the search term */
144+
return sprintf( __( 'Search results for: %s', 'jetpack-forms' ), get_search_query() );
145+
}
146+
if ( is_404() ) {
147+
return __( '404 Not Found', 'jetpack-forms' );
148+
}
149+
return get_bloginfo( 'name' );
150+
}
151+
152+
/**
153+
* Creates a Feedback_Source instance for a block template.
154+
*
155+
* @param array $attributes Form Shortcode attributes.
156+
*
157+
* @return Feedback_Source Returns an instance of Feedback_Source.
158+
*/
159+
public static function get_current( $attributes ) {
160+
global $wp, $page;
161+
$current_url = home_url( add_query_arg( array(), $wp->request ) );
162+
if ( isset( $attributes['widget'] ) && ! empty( $attributes['widget'] ) ) {
163+
return new self( $attributes['widget'], self::get_source_title(), 1, 'widget', $current_url );
164+
}
165+
166+
if ( isset( $attributes['block_template'] ) && ! empty( $attributes['block_template'] ) ) {
167+
global $_wp_current_template_id;
168+
return new self( $_wp_current_template_id, self::get_source_title(), $page, 'block_template', $current_url );
169+
}
170+
171+
if ( isset( $attributes['block_template_part'] ) && ! empty( $attributes['block_template_part'] ) ) {
172+
return new self( $attributes['block_template_part'], self::get_source_title(), $page, 'block_template_part', $current_url );
173+
}
174+
175+
return new Feedback_Source( \get_the_ID(), \get_the_title(), $page, 'single', $current_url );
176+
}
177+
178+
/**
179+
* Creates a Feedback_Source instance from serialized data.
180+
*
181+
* @param array $data The serialized data.
182+
* @return Feedback_Source Returns an instance of Feedback_Source.
183+
*/
184+
public static function from_serialized( $data ) {
185+
$id = $data['source_id'] ?? 0;
186+
$title = $data['entry_title'] ?? '';
187+
$page_number = $data['entry_page'] ?? 1;
188+
$source_type = $data['source_type'] ?? 'single';
189+
$request_url = $data['request_url'] ?? '';
190+
191+
return new self( $id, $title, $page_number, $source_type, $request_url );
192+
}
193+
91194
/**
92195
* Get the permalink of the feedback entry.
93196
*
@@ -100,6 +203,38 @@ public function get_permalink() {
100203
return $this->permalink;
101204
}
102205

206+
/**
207+
* Get the edit URL of the form or page where the feedback was submitted from.
208+
*
209+
* @return string The edit URL of the form or page.
210+
*/
211+
public function get_edit_form_url() {
212+
213+
if ( current_user_can( 'edit_theme_options' ) ) {
214+
if ( $this->source_type === 'block_template' && \wp_is_block_theme() ) {
215+
return admin_url( 'site-editor.php?p=' . esc_attr( '/wp_template/' . addslashes( $this->id ) ) . '&canvas=edit' );
216+
}
217+
218+
if ( $this->source_type === 'block_template_part' && \wp_is_block_theme() ) {
219+
return admin_url( 'site-editor.php?p=' . esc_attr( '/wp_template_part/' . addslashes( $this->id ) ) . '&canvas=edit' );
220+
}
221+
222+
if ( $this->source_type === 'widget' && current_theme_supports( 'widgets' ) ) {
223+
return admin_url( 'widgets.php' );
224+
}
225+
}
226+
227+
if ( $this->id && is_numeric( $this->id ) && $this->id > 0 && current_user_can( 'edit_post', (int) $this->id ) ) {
228+
$entry_post = get_post( (int) $this->id );
229+
if ( $entry_post && $entry_post->post_status === 'trash' ) {
230+
return ''; // No edit link is possible for trashed posts. They need to be restored first.
231+
}
232+
return \get_edit_post_link( (int) $this->id, 'url' );
233+
}
234+
235+
return '';
236+
}
237+
103238
/**
104239
* Get the relative permalink of the feedback entry.
105240
*
@@ -131,7 +266,7 @@ public function get_title() {
131266
/**
132267
* Get the post id of the feedback entry.
133268
*
134-
* @return int The ID of the feedback entry.
269+
* @return int|string The ID of the feedback entry.
135270
*/
136271
public function get_id() {
137272
return $this->id;
@@ -146,6 +281,9 @@ public function serialize() {
146281
return array(
147282
'entry_title' => $this->title,
148283
'entry_page' => $this->page_number,
284+
'source_id' => $this->id,
285+
'source_type' => $this->source_type,
286+
'request_url' => $this->request_url,
149287
);
150288
}
151289
}

0 commit comments

Comments
 (0)