PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.1-beta.2
Jetpack – WP Security, Backup, Speed, & Growth v16.1-beta.2
16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / jetpack_vendor / automattic / jetpack-forms / src / service / class-post-to-url.php
jetpack / jetpack_vendor / automattic / jetpack-forms / src / service Last commit date
class-form-webhooks.php 1 month ago class-google-drive.php 9 months ago class-hostinger-reach-integration.php 9 months ago class-mailpoet-integration.php 1 month ago class-post-to-url.php 1 month ago
class-post-to-url.php
195 lines
1 <?php
2 /**
3 * Post to URL using Jetpack Contact Forms.
4 *
5 * @package automattic/jetpack
6 */
7
8 namespace Automattic\Jetpack\Forms\Service;
9
10 use WP_Error;
11
12 /**
13 * Class Post_To_Url
14 *
15 * Hooks on Jetpack's Contact form to post form data to some URL.
16 */
17 class Post_To_Url {
18 /**
19 * Singleton instance
20 *
21 * @var Post_To_Url
22 */
23 private static $instance = null;
24
25 /**
26 * Initialize and return singleton instance.
27 *
28 * @return Post_To_Url
29 */
30 public static function init() {
31 if ( null === self::$instance ) {
32 self::$instance = new self();
33 }
34
35 return self::$instance;
36 }
37
38 /**
39 * Post_To_Url class constructor.
40 * Hooks on `grunion_after_feedback_post_inserted` action to send form data to specified URL.
41 * NOTE: As a singleton, this constructor is private and only callable from ::init, which will return the singleton instance,
42 * effectively preventing multiple instances of this class (hence, multiple hooks triggering the POST request).
43 */
44 private function __construct() {
45 add_action( 'grunion_after_feedback_post_inserted', array( $this, 'feedback_post_hook' ), 10, 4 );
46 }
47
48 /**
49 * Get the setup for the post to URL.
50 *
51 * Salesforce-only: posts to the fixed Salesforce Web-to-Lead endpoint when
52 * the form has a salesforceData.organizationId attribute. The legacy
53 * postToUrl override is intentionally NOT honored here — postToUrl is
54 * deprecated and the new pipeline (Form_Webhooks) already handles it with
55 * proper URL validation. Honoring it here too would let an Editor with
56 * Salesforce enabled override the destination to an arbitrary URL,
57 * including internal/cloud-metadata endpoints (SSRF).
58 *
59 * @param array $attributes - the attributes of the contact form.
60 * @return array|bool Array setup, or false if Salesforce isn't configured.
61 */
62 private function get_setup( $attributes = array() ) {
63 if ( empty( $attributes['salesforceData']['organizationId'] ) ) {
64 return false;
65 }
66
67 return array(
68 'url' => 'https://webto.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8',
69 'format' => 'urlencoded',
70 );
71 }
72
73 /**
74 * Hook on `grunion_after_feedback_post_inserted` action to send form data to specified URL.
75 *
76 * @param int $post_id - the post_id for the CPT that is created.
77 * @param array $fields - a collection of Automattic\Jetpack\Forms\ContactForm\Contact_Form_Field instances.
78 * @param bool $is_spam - marked as spam by Akismet(?).
79 * @param array $entry_values - extra fields added to from the contact form.
80 *
81 * @return null|void
82 */
83 public function feedback_post_hook( $post_id, $fields, $is_spam, $entry_values ) {
84 // Try and get the form from any of the fields
85 $form = null;
86 foreach ( $fields as $field ) {
87 if ( ! empty( $field->form ) ) {
88 $form = $field->form;
89 break;
90 }
91 }
92 if ( ! $form || ! is_a( $form, 'Automattic\Jetpack\Forms\ContactForm\Contact_Form' ) ) {
93 return;
94 }
95
96 // if spam (hinted by akismet?), don't process
97 if ( $is_spam ) {
98 return;
99 }
100
101 $setup = $this->get_setup( $form->attributes );
102
103 if ( ! $setup ) {
104 return;
105 }
106
107 $form_data = $this->get_form_data( $form, $entry_values );
108
109 $result = $this->post_to_url( $form_data, $setup );
110
111 if ( is_wp_error( $result ) ) {
112 // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- figuring out what to do with the error.
113 $message = sprintf(
114 'JETPACK %s - Jetpack Forms: POSTing to URL failed: "%s" at %s',
115 constant( 'JETPACK__VERSION' ),
116 $result->get_error_message(),
117 $entry_values['entry_permalink']
118 );
119 // TODO: not sure what to do with the error. Is not useful at frontend and it would be difficult to
120 // solve for a non tech-savvy user. We should log it somewhere, but it could turn messy.
121 // Maybe email the owner?
122 }
123 }
124
125 /**
126 * POST to URL
127 *
128 * @param array $data The data key/value pairs to send in POST.
129 * @param array $options Options for POST.
130 *
131 * @return array|WP_Error The result value from wp_safe_remote_post
132 *
133 * TODO: do complex fields (MC, etc) need to be handled differently? JSON should be fine, but URLencoded might need to be serialized.
134 */
135 private function post_to_url( $data, $options = array() ) {
136 global $wp_version;
137
138 $user_agent = "WordPress/{$wp_version} | Jetpack/" . constant( 'JETPACK__VERSION' ) . '; ' . get_bloginfo( 'url' );
139 $format = $options['format'] === 'urlencoded' ? 'application/x-www-form-urlencoded' : 'application/json';
140 $args = array(
141 'body' => $data,
142 'headers' => array(
143 'Content-Type' => $format,
144 'user-agent' => $user_agent,
145 ),
146 );
147 return wp_safe_remote_post( $options['url'], $args );
148 }
149
150 /**
151 * Gather fields key/value pairs from the form
152 * Sanitizes the hidden fields values
153 *
154 * @param \Automattic\Jetpack\Forms\ContactForm\Contact_Form $form The form instance being processed/submitted.
155 * @param array $entry_values The feedback entry values.
156 */
157 private function get_form_data( $form, $entry_values ) {
158 $fields = array();
159 foreach ( $form->fields as $field ) {
160 $fields[ $field->get_attribute( 'id' ) ] = $field->value;
161 }
162
163 // Right in the middle, backwards compatibility for salesforceData implementation.
164 $salesforce_data = (array) ( $form->attributes['salesforceData'] ?? array() );
165 if ( ! empty( $salesforce_data['organizationId'] ) ) {
166 $fields['oid'] = sanitize_text_field( $salesforce_data['organizationId'] );
167 $fields['lead_source'] = $entry_values['entry_permalink'];
168 }
169
170 // `hiddenFields` is a legacy attribute that may appear in a few shapes on forms
171 // in the wild: an array of `{ name, value }` objects (its original design), an
172 // associative `name => value` map, or a JSON-encoded string. Iterating it blindly
173 // and accessing `['name']`/`['value']` on a non-array element fatals on PHP 8 with
174 // "Cannot access offset of type string on string", so normalize defensively.
175 $hidden_fields = $form->attributes['hiddenFields'] ?? array();
176 if ( is_string( $hidden_fields ) ) {
177 $decoded = json_decode( $hidden_fields, true );
178 $hidden_fields = is_array( $decoded ) ? $decoded : array();
179 }
180 foreach ( (array) $hidden_fields as $key => $hidden_field ) {
181 if ( is_array( $hidden_field ) ) {
182 // Original `{ name, value }` object shape.
183 if ( isset( $hidden_field['name'] ) ) {
184 $fields[ $hidden_field['name'] ] = sanitize_text_field( $hidden_field['value'] ?? '' );
185 }
186 } elseif ( ! is_int( $key ) ) {
187 // Associative `name => value` shape.
188 $fields[ $key ] = sanitize_text_field( (string) $hidden_field );
189 }
190 }
191
192 return $fields;
193 }
194 }
195