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