PluginProbe ʕ •ᴥ•ʔ
Kirki – Freeform Page Builder, Website Builder & Customizer / 6.0.12
Kirki – Freeform Page Builder, Website Builder & Customizer v6.0.12
6.0.14 6.0.13 6.0.12 6.0.11 6.0.10 6.0.9 6.0.8 6.0.7 6.0.6 6.0.5 6.0.4 6.0.3 6.0.2 6.0.1 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 4.0.19 4.0.20 4.0.21 4.0.22 4.0.23 4.0.24 4.1 4.2.0 5.0.0 5.1.0 5.1.1 5.2.0 5.2.1 5.2.2 5.2.3 6.0.0 trunk 3.0.40 3.0.41 3.0.42 3.0.43 3.0.44 3.0.45 3.1.0 3.1.1 3.1.2
kirki / ComponentLibrary / controller / ElementGenerator.php
kirki / ComponentLibrary / controller Last commit date
CompLibFormHandler.php 3 weeks ago ElementGenerator.php 3 weeks ago ShowUserMetadata.php 2 months ago
ElementGenerator.php
266 lines
1 <?php
2
3
4 namespace KirkiComponentLib;
5
6 if ( ! defined( 'ABSPATH' ) ) {
7 exit; // Exit if accessed directly.
8 }
9
10 class ElementGenerator {
11
12 private $element = array();
13 private $elements = array();
14 private $attributes = array();
15 private $setting = array();
16 private $options = array();
17 private $generate_child_element = null;
18 private $get_data_and_styles_from_root = null;
19 private $style_blocks = array();
20 private $properties = array();
21 public $component_lib_forms = array();
22 private $exceptional_elements = array(
23 'kirki-logout',
24 'kirki-comment',
25 );
26
27 public function __construct( $props ) {
28 $this->element = $props['element'];
29 $this->elements = $props['elements'];
30 $this->attributes = $props['attributes'];
31 $this->options = $props['options'];
32 $this->generate_child_element = $props['generate_child_element'];
33 $this->properties = $this->element['properties'];
34 $this->setting = $this->properties['settings'];
35 $this->component_lib_forms = $props['component_lib_forms'];
36 $this->get_data_and_styles_from_root = $props['get_data_and_styles_from_root'];
37 $this->style_blocks = $props['style_blocks'];
38 $this->add_element_config();
39 }
40
41
42 private function add_element_config() {
43 $id = $this->element['id'];
44 if (
45 $this->element['name'] === 'kirki-login' || $this->element['name'] === 'kirki-register' ||
46 $this->element['name'] === 'kirki-forgot-password' || $this->element['name'] === 'kirki-change-password' ||
47 $this->element['name'] === 'kirki-retrieve-username' || $this->element['name'] === 'kirki-comment'
48 ) {
49 $nonce = $this->add_nonce_to_element( $this->element );
50 $config = array_merge(
51 $this->properties['attributes'],
52 $this->setting,
53 array(
54 'name' => $this->element['name'],
55 'nonce' => $nonce,
56 )
57 );
58
59 // SECURITY FIX: Sign the email template so the REST handler can verify
60 // it was not tampered with — without any extra DB queries.
61 // Always generate a signature for these element types, even when
62 // settings are empty, so the client always has a value to send.
63 if ( in_array( $this->element['name'], array( 'kirki-forgot-password', 'kirki-retrieve-username' ), true ) ) {
64 if ( ! isset( $config['emailSubject'] ) ) {
65 $config['emailSubject'] = '';
66 }
67 if ( ! isset( $config['emailBody'] ) ) {
68 $config['emailBody'] = array();
69 }
70 $config['emailSignature'] = $this->sign_email_template(
71 $config['emailSubject'],
72 $config['emailBody']
73 );
74 }
75
76 $this->component_lib_forms[ $id ] = $config;
77 }
78 }
79
80 /**
81 * Produce an HMAC signature over the admin-configured email template.
82 * Uses WordPress AUTH_KEY + AUTH_SALT so it is server-secret and
83 * never reproducible by an external attacker.
84 *
85 * @param string $subject
86 * @param array|string $body
87 * @return string Hex HMAC-SHA256 signature.
88 */
89 private function sign_email_template( $subject, $body ) {
90 $body_string = is_array( $body ) ? wp_json_encode( $body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) : (string) $body;
91 $payload = $subject . '|' . $body_string;
92 $secret = AUTH_KEY . AUTH_SALT;
93 return hash_hmac( 'sha256', $payload, $secret );
94 }
95
96 public function generate_common_element( $hide = false, $children_html = false ) {
97 if ( in_array( $this->element['name'], $this->exceptional_elements, true ) ) {
98 return $this->generate_exceptional_element( $this->element['name'], $hide, $children_html );
99 }
100
101 $extra_attributes = '';
102 if ( $hide ) {
103 $extra_attributes .= ' data-element_hide="true"';
104 }
105
106 $html = '';
107 $tag = isset( $this->properties['tag'] ) ? $this->properties['tag'] : 'div';
108 $name = $this->element['name'];
109 $can_register = get_option( 'users_can_register' );
110
111 if ( $name === 'kirki-register' && $can_register !== '1' ) {
112 return '';
113 }
114
115 if ( ! $children_html ) {
116 $children_html = $this->generate_child_elements();
117 }
118 $html = "<$tag $this->attributes data-ele_name='$name' $extra_attributes>$children_html</$tag>";
119 return $html;
120 }
121
122 private function generate_child_elements() {
123 $html = '';
124 $child_count = isset( $this->element['children'] ) ? count( $this->element['children'] ) : 0;
125 for ( $i = 0; $i < $child_count; $i++ ) {
126 $html .= call_user_func( $this->generate_child_element, $this->element['children'][ $i ], $this->options );
127 }
128 return $html;
129 }
130
131 private function generate_exceptional_element( $name, $hide = false, $children_html = false ) {
132 $extra_attributes = '';
133 if ( $hide ) {
134 $extra_attributes .= ' data-element_hide="true"';
135 }
136
137 if ( ! $children_html ) {
138 $children_html = $this->generate_child_elements();
139 }
140
141 $tag = isset( $this->properties['tag'] ) ? $this->properties['tag'] : 'div';
142 $name = $this->element['name'];
143
144 switch ( $name ) {
145 case 'kirki-logout': {
146 $user = wp_get_current_user();
147 if ( $user->ID === 0 ) {
148 return '';
149 }
150
151 $href = '';
152 $attr = $this->attributes;
153 if (
154 isset(
155 $this->element,
156 $this->element['properties'],
157 $this->element['properties']['settings'],
158 $this->element['properties']['settings']['redirect_url']
159 ) &&
160 strlen( $this->element['properties']['settings']['redirect_url'] ) > 0
161 ) {
162 $href = wp_logout_url( $this->element['properties']['settings']['redirect_url'] );
163 $attr = preg_replace( '/href="([^"]+")/i', '', $attr );
164 $attr = $attr . 'href=' . $href;
165 }
166 return "<$tag $attr data-ele_name='$name' $extra_attributes>$children_html</$tag>";
167 }
168 case 'kirki-comment': {
169 $post_id = get_the_ID();
170 if ( isset( $this->options['post'] ) && isset( $this->options['post']->ID ) ) {
171 $post_id = $this->options['post']->ID;
172 }
173 if ( isset( $this->options['comment'] ) && isset( $this->options['comment']['comment_post_ID'] ) ) {
174 $post_id = $this->options['comment']['comment_post_ID'];
175 }
176
177 $comment_parent = 0;
178 $comment_id = 0;
179 if ( isset( $this->options['comment'] ) ) {
180 $comment = $this->options['comment'];
181 if ( isset( $comment['id'] ) ) {
182 $comment_parent = $comment['id'];
183 // $comment_id = $comment['id'];
184 }
185 }
186
187 $parent_id = $this->element['parentId'];
188 while ( isset( $this->elements[ $parent_id ] ) && $this->elements[ $parent_id ]['name'] !== 'collection' ) {
189 if ( $this->elements[ $parent_id ]['name'] === 'body' ) {
190 $parent_id = false;
191 break;
192 }
193 $parent_id = $this->elements[ $parent_id ]['parentId'];
194 }
195 $collection_type = '';
196 if ( $parent_id && isset( $this->elements[ $parent_id ]['properties']['dynamicContent'] ) ) {
197 $collection_type = $this->elements[ $parent_id ]['properties']['dynamicContent']['type'];
198 }
199
200 $kirki_data = '';
201 if ( isset( $this->elements[ $this->element['parentId'] ] ) ) {
202 $data_n_styles = array(
203 'blocks' => array(),
204 'styles' => array(),
205 'root' => $this->element['parentId'],
206 );
207 call_user_func_array( $this->get_data_and_styles_from_root, array( $this->element['parentId'], &$data_n_styles, &$this->elements, &$this->style_blocks ) );
208 $encoded_data = json_encode( $data_n_styles );
209 $kirki_data .= "<textarea data-type='kirki_data' style='display: none'>" . esc_textarea( $encoded_data ) . '</textarea>';
210 }
211
212 $limit_per_user = isset( $this->properties['settings']['limit_per_user'] ) ? $this->properties['settings']['limit_per_user'] : false;
213 if ( $limit_per_user ) {
214 $user = wp_get_current_user();
215 if ( $user->ID === 0 ) {
216 return '';
217 }
218 $comment_type = $collection_type;
219 $type = explode( '-', $collection_type );
220 if ( isset( $type[1] ) ) {
221 $comment_type = $type[1];
222 }
223 global $wpdb;
224 $comment_count = $wpdb->get_var(
225 $wpdb->prepare(
226 "SELECT COUNT(*) FROM $wpdb->comments WHERE comment_post_ID = %d AND user_id = %d AND comment_parent = %d AND comment_type = %s",
227 $post_id,
228 $user->ID,
229 $comment_parent,
230 $comment_type
231 )
232 );
233 if ( $comment_count >= $limit_per_user ) {
234 return '';
235 }
236 }
237
238 if ( ! $children_html ) {
239 $children_html = $this->generate_child_elements();
240 }
241
242 $hidden_data_html = "<input type='hidden' name='post_id' value='" . esc_attr( $post_id ) . "' />";
243 $hidden_data_html .= "<input type='hidden' name='comment_parent' value='" . esc_attr( $comment_parent ) . "' />";
244 $hidden_data_html .= "<input type='hidden' name='comment_id' value='" . esc_attr( $comment_id ) . "' />";
245 $hidden_data_html .= "<input type='hidden' name='collection_type' value='" . esc_attr( $collection_type ) . "' />";
246 $hidden_data_html .= "<input type='hidden' name='collection_id' value='" . esc_attr( $parent_id ) . "' />";
247 $children_html = $hidden_data_html . $kirki_data . $children_html;
248 $html = "<$tag $this->attributes data-ele_name='$name' $extra_attributes>$children_html</$tag>";
249 return $html;
250 }
251 }
252 }
253
254 private function add_nonce_to_element( $element ) {
255 if ( empty( $element['name'] ) ) {
256 return false;
257 }
258
259 $action = KIRKI_COMPONENT_LIBRARY_APP_PREFIX . '_' . $element['name'];
260
261 // Always returns consistent nonce for same user + action for ~12 hours.
262 return wp_create_nonce( $action );
263 }
264
265 }
266