PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.3-beta
16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 All 507 releases
jetpack / jetpack_vendor / automattic / jetpack-sharing-likes / src / settings / class-settings-form.php

class-settings-form.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-beta, at jetpack_vendor/automattic/jetpack-sharing-likes/src/settings/class-settings-form.php

155 lines 4.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * The one form every setting on Settings > Sharing saves through.
4 *
5 * @package automattic/jetpack-sharing-likes
6 */
7
8 declare( strict_types = 1 );
9
10 namespace Automattic\Jetpack\Sharing_Likes\Settings;
11
12 /**
13 * One Save button for the whole screen.
14 *
15 * Sections cannot sit inside a single `<form>`: the services list nests the
16 * legacy forms its script submits over AJAX. So the form renders empty at the
17 * end of the screen, and each section's fields join it through the HTML `form`
18 * attribute instead, wherever they sit.
19 */
20 final class Settings_Form {
21
22 /**
23 * `id` of the form element, which fields name in their `form` attribute.
24 */
25 public const ID = 'jetpack-sharing-settings';
26
27 /**
28 * Nonce action for the form.
29 *
30 * Deliberately not sharedaddy's `sharing-options`: `Services_Config::process_requests()`
31 * answers to that one, and on Simple so does a Likes save that turns Likes back
32 * on when its own fields are missing from the request.
33 */
34 public const NONCE_ACTION = 'jetpack-sharing-settings';
35
36 /**
37 * Field listing which sections put fields on the form, so a save leaves the rest alone.
38 */
39 public const SECTIONS_FIELD = 'jetpack_sharing_sections';
40
41 /**
42 * The services list's own settings: button style, label, and what hangs off them.
43 */
44 public const SECTION_SHARING = 'sharing';
45
46 /**
47 * The Like buttons settings, or just their sitewide default when `Comment_Likes_Section` renders it.
48 */
49 public const SECTION_LIKES = 'likes';
50
51 /**
52 * Comment Likes, which save on their own whatever the Like buttons are doing.
53 */
54 public const SECTION_COMMENT_LIKES = 'comment-likes';
55
56 /**
57 * Where the buttons appear.
58 */
59 public const SECTION_PLACEMENT = 'placement';
60
61 /**
62 * The rows that close the services table, whenever that table is hidden.
63 */
64 public const SECTION_EXTRAS = 'extras';
65
66 /**
67 * Sections that have put fields on the form during this render.
68 *
69 * @var string[]
70 */
71 private static $sections = array();
72
73 /**
74 * Print a section's fields, attached to the form.
75 *
76 * @param string $section One of the SECTION_* constants.
77 * @param string $markup The fields, escaped by whoever rendered them.
78 */
79 public static function render_fields( string $section, string $markup ): void {
80 self::$sections[] = $section;
81
82 $markup .= sprintf(
83 '<input type="hidden" name="%1$s[]" value="%2$s" />',
84 esc_attr( self::SECTIONS_FIELD ),
85 esc_attr( $section )
86 );
87
88 echo self::attach( $markup ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped by whoever rendered it; attach() only adds an attribute.
89 }
90
91 /**
92 * Print the form and its Save button, if any section put fields on it.
93 */
94 public static function render(): void {
95 if ( array() === self::$sections ) {
96 return;
97 }
98
99 self::$sections = array();
100 ?>
101 <form method="post" action="" id="<?php echo esc_attr( self::ID ); ?>">
102 <p class="submit">
103 <input type="submit" name="submit" class="button-primary" value="<?php esc_attr_e( 'Save Changes', 'jetpack-sharing-likes' ); ?>" />
104 <?php
105 Post_Handler::render_action_field( 'save-settings' );
106 wp_nonce_field( self::NONCE_ACTION );
107 ?>
108 </p>
109 </form>
110 <?php
111 }
112
113 /**
114 * Sections the submitted form carried fields for. Callers verify the nonce.
115 *
116 * @return string[]
117 */
118 public static function posted_sections(): array {
119 // phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified by the caller.
120 if ( ! isset( $_POST[ self::SECTIONS_FIELD ] ) || ! is_array( $_POST[ self::SECTIONS_FIELD ] ) ) {
121 return array();
122 }
123
124 $known = array(
125 self::SECTION_SHARING,
126 self::SECTION_LIKES,
127 self::SECTION_COMMENT_LIKES,
128 self::SECTION_PLACEMENT,
129 self::SECTION_EXTRAS,
130 );
131
132 // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput -- verified by the caller; checked against an allowlist.
133 $posted = array_filter( wp_unslash( $_POST[ self::SECTIONS_FIELD ] ), 'is_string' );
134
135 return array_values( array_intersect( $known, $posted ) );
136 }
137
138 /**
139 * Point every field in the markup at the form, leaving any that already name one.
140 *
141 * @param string $markup Field markup.
142 */
143 private static function attach( string $markup ): string {
144 $tags = new \WP_HTML_Tag_Processor( $markup );
145
146 while ( $tags->next_tag() ) {
147 if ( in_array( $tags->get_tag(), array( 'INPUT', 'SELECT', 'TEXTAREA' ), true ) && null === $tags->get_attribute( 'form' ) ) {
148 $tags->set_attribute( 'form', self::ID );
149 }
150 }
151
152 return $tags->get_updated_html();
153 }
154 }
155