Version Description
- Fixed #2380, #2397, #2212
Download this release
Release Info
Developer | Unyson |
Plugin | Unyson |
Version | 2.6.15 |
Comparing to | |
See all releases |
Code changes from version 2.6.14 to 2.6.15
- framework/autoload.php +2 -0
- framework/helpers/class-fw-flash-messages.php +1 -1
- framework/helpers/class-fw-form.php +393 -321
- framework/helpers/database.php +2 -1
- framework/helpers/exceptions/class-fw-form-invalid-submission-exception.php +22 -0
- framework/helpers/exceptions/class-fw-form-not-found-exception.php +7 -0
- framework/includes/option-types/color-picker/class-fw-option-type-color-picker.php +1 -1
- framework/includes/option-types/color-picker/static/js/scripts.js +1 -1
- framework/includes/option-types/gradient/view.php +1 -1
- framework/includes/option-types/multi-select/class-fw-option-type-multi-select.php +26 -5
- framework/includes/option-types/rgba-color-picker/class-fw-option-type-rgba-color-picker.php +1 -1
- framework/includes/option-types/rgba-color-picker/static/js/scripts.js +1 -1
- framework/includes/option-types/typography-v2/class-fw-option-type-typography-v2.php +2 -2
- framework/includes/option-types/typography/class-fw-option-type-typography.php +1 -1
- framework/includes/option-types/wp-editor/static/scripts.js +62 -50
- framework/manifest.php +1 -1
- readme.txt +5 -2
- unyson.php +1 -1
framework/autoload.php
CHANGED
@@ -107,6 +107,8 @@ function _fw_autoload_helper_classes($class) {
|
|
107 |
'FW_Access_Key' => 'class-fw-access-key',
|
108 |
'FW_WP_Filesystem' => 'class-fw-wp-filesystem',
|
109 |
'FW_Form' => 'class-fw-form',
|
|
|
|
|
110 |
'FW_Settings_Form' => 'class-fw-settings-form',
|
111 |
'FW_Request' => 'class-fw-request',
|
112 |
'FW_Session' => 'class-fw-session',
|
107 |
'FW_Access_Key' => 'class-fw-access-key',
|
108 |
'FW_WP_Filesystem' => 'class-fw-wp-filesystem',
|
109 |
'FW_Form' => 'class-fw-form',
|
110 |
+
'FW_Form_Not_Found_Exception' => 'exceptions/class-fw-form-not-found-exception',
|
111 |
+
'FW_Form_Invalid_Submission_Exception' => 'exceptions/class-fw-form-invalid-submission-exception',
|
112 |
'FW_Settings_Form' => 'class-fw-settings-form',
|
113 |
'FW_Request' => 'class-fw-request',
|
114 |
'FW_Session' => 'class-fw-session',
|
framework/helpers/class-fw-flash-messages.php
CHANGED
@@ -210,7 +210,7 @@ class FW_Flash_Messages
|
|
210 |
/**
|
211 |
* Clear the FW_Flash_Messages messages
|
212 |
*
|
213 |
-
* @since 2.6.
|
214 |
*/
|
215 |
public static function _clear() {
|
216 |
self::set_messages(array());
|
210 |
/**
|
211 |
* Clear the FW_Flash_Messages messages
|
212 |
*
|
213 |
+
* @since 2.6.15
|
214 |
*/
|
215 |
public static function _clear() {
|
216 |
self::set_messages(array());
|
framework/helpers/class-fw-form.php
CHANGED
@@ -9,32 +9,42 @@ class FW_Form {
|
|
9 |
/**
|
10 |
* Store all form ids created with this class
|
11 |
* @var FW_Form[] {'form_id' => instance}
|
|
|
|
|
12 |
*/
|
13 |
protected static $forms = array();
|
14 |
|
15 |
/**
|
16 |
* The id of the submitted form id
|
17 |
* @var string
|
|
|
|
|
18 |
*/
|
19 |
protected static $submitted_id;
|
20 |
|
21 |
/**
|
22 |
* Hidden input name that stores the form id
|
23 |
* @var string
|
|
|
|
|
24 |
*/
|
25 |
protected static $id_input_name = 'fwf';
|
26 |
|
27 |
/**
|
28 |
* Form id
|
29 |
* @var string
|
|
|
|
|
30 |
*/
|
31 |
protected $id;
|
32 |
|
33 |
/**
|
34 |
* Html attributes for <form> tag
|
35 |
* @var array
|
|
|
|
|
36 |
*/
|
37 |
-
protected $attr;
|
38 |
|
39 |
/**
|
40 |
* Found validation errors
|
@@ -51,20 +61,31 @@ class FW_Form {
|
|
51 |
/**
|
52 |
* If current request is the submit of this form
|
53 |
* @var bool
|
|
|
|
|
54 |
*/
|
55 |
protected $is_submitted;
|
56 |
|
57 |
/**
|
58 |
* @var bool
|
|
|
|
|
59 |
*/
|
60 |
protected $validate_and_save_called = false;
|
61 |
|
|
|
|
|
|
|
|
|
|
|
62 |
protected $callbacks = array(
|
63 |
'render' => false,
|
64 |
'validate' => false,
|
65 |
'save' => false
|
66 |
);
|
67 |
|
|
|
|
|
68 |
/**
|
69 |
* @param string $id Unique
|
70 |
* @param array $data (optional)
|
@@ -76,50 +97,27 @@ class FW_Form {
|
|
76 |
* )
|
77 |
*/
|
78 |
public function __construct( $id, $data = array() ) {
|
79 |
-
|
|
|
80 |
trigger_error( sprintf( __( 'Form with id "%s" was already defined', 'fw' ), $id ), E_USER_ERROR );
|
81 |
-
}
|
82 |
-
|
83 |
-
$this->id = $id;
|
84 |
-
|
85 |
-
self::$forms[ $this->id ] =& $this;
|
86 |
-
|
87 |
-
// prepare $this->attr
|
88 |
-
{
|
89 |
-
if ( ! isset( $data['attr'] ) || ! is_array( $data['attr'] ) ) {
|
90 |
-
$data['attr'] = array();
|
91 |
-
}
|
92 |
-
|
93 |
-
$data['attr']['data-fw-form-id'] = $this->id;
|
94 |
-
|
95 |
-
/** @deprecated */
|
96 |
-
$data['attr']['class'] = 'fw_form_' . $this->id;
|
97 |
-
|
98 |
-
if ( isset( $data['attr']['method'] ) ) {
|
99 |
-
$data['attr']['method'] = strtolower( $data['attr']['method'] );
|
100 |
-
|
101 |
-
$data['attr']['method'] = in_array( $data['attr']['method'], array( 'get', 'post' ) )
|
102 |
-
? $data['attr']['method']
|
103 |
-
: 'post';
|
104 |
-
} else {
|
105 |
-
$data['attr']['method'] = 'post';
|
106 |
-
}
|
107 |
|
108 |
-
|
109 |
-
|
110 |
-
}
|
111 |
-
|
112 |
-
$this->attr = $data['attr'];
|
113 |
}
|
114 |
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
'
|
121 |
-
|
122 |
-
|
|
|
|
|
|
|
|
|
|
|
123 |
|
124 |
if ( did_action( 'wp_loaded' ) ) {
|
125 |
// in case if form instance was created after action
|
@@ -128,104 +126,40 @@ class FW_Form {
|
|
128 |
// attach to an action before 'send_headers' action, to be able to do redirects
|
129 |
add_action( 'wp_loaded', array( $this, '_validate_and_save' ), 101 );
|
130 |
}
|
131 |
-
}
|
132 |
-
|
133 |
-
protected function validate() {
|
134 |
-
if ( is_array( $this->errors ) ) {
|
135 |
-
trigger_error( __METHOD__ . ' already called', E_USER_WARNING );
|
136 |
-
|
137 |
-
return;
|
138 |
-
}
|
139 |
-
|
140 |
-
/**
|
141 |
-
* Errors array {'input[name]' => 'Error message'}
|
142 |
-
*/
|
143 |
-
$errors = array();
|
144 |
-
|
145 |
-
/**
|
146 |
-
* Call validate callback
|
147 |
-
*
|
148 |
-
* Callback must 'manually' extract input values from $_POST (or $_GET)
|
149 |
-
*/
|
150 |
-
if ( $this->callbacks['validate'] ) {
|
151 |
-
$errors = call_user_func_array( $this->callbacks['validate'], array( $errors ) );
|
152 |
-
|
153 |
-
if ( ! is_array( $errors ) ) {
|
154 |
-
|
155 |
-
$errors = array();
|
156 |
-
}
|
157 |
-
}
|
158 |
-
|
159 |
-
/**
|
160 |
-
* check nonce
|
161 |
-
*/
|
162 |
-
if ( $this->attr['method'] == 'post' ) {
|
163 |
-
$nonce_name = $this->get_nonce_name();
|
164 |
-
|
165 |
-
if (
|
166 |
-
! isset( $_REQUEST[ $nonce_name ] )
|
167 |
-
||
|
168 |
-
wp_verify_nonce( $_REQUEST[ $nonce_name ], 'submit_fwf' ) === false
|
169 |
-
) {
|
170 |
-
$errors[ $nonce_name ] = __( 'Nonce verification failed', 'fw' );
|
171 |
-
}
|
172 |
-
}
|
173 |
|
174 |
-
$this
|
175 |
}
|
176 |
|
177 |
/**
|
178 |
-
* Some forms (like Forms extension frontend form) uses the same FW_Form instance for all sub-forms
|
179 |
-
* and they must be differentiated somehow.
|
180 |
-
* Fixes https://github.com/ThemeFuse/Unyson/issues/2033
|
181 |
-
*
|
182 |
-
* @param array $render_data
|
183 |
-
*
|
184 |
* @return string
|
185 |
-
* @since 2.6.6
|
186 |
*/
|
187 |
-
|
188 |
-
return
|
189 |
}
|
190 |
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
*
|
200 |
-
* Callback must 'manually' extract input values from $_POST (or $_GET)
|
201 |
-
*/
|
202 |
-
if ( $this->callbacks['save'] ) {
|
203 |
-
$data = call_user_func_array( $this->callbacks['save'], array( $save_data ) );
|
204 |
-
|
205 |
-
if ( ! is_array( $data ) ) {
|
206 |
-
// fix if returned wrong data from callback
|
207 |
-
$data = $save_data;
|
208 |
-
}
|
209 |
-
|
210 |
-
$save_data = $data;
|
211 |
|
212 |
-
|
213 |
}
|
214 |
|
215 |
-
|
216 |
-
if ( isset( $save_data['redirect'] ) ) {
|
217 |
-
wp_redirect( $save_data['redirect'] );
|
218 |
-
exit;
|
219 |
-
}
|
220 |
-
}
|
221 |
|
222 |
-
return $
|
223 |
}
|
224 |
|
225 |
-
|
226 |
-
return
|
227 |
-
|
228 |
-
|
|
|
|
|
229 |
}
|
230 |
|
231 |
/**
|
@@ -233,94 +167,84 @@ class FW_Form {
|
|
233 |
*
|
234 |
* Note: This callback can abort script execution if save does redirect
|
235 |
*
|
236 |
-
* @return bool|null
|
237 |
* @internal
|
238 |
*/
|
239 |
public function _validate_and_save() {
|
240 |
-
if ( $this->validate_and_save_called ) {
|
241 |
-
trigger_error( __METHOD__ . ' already called', E_USER_WARNING );
|
242 |
|
243 |
-
|
244 |
-
|
245 |
-
$this->validate_and_save_called = true;
|
246 |
}
|
247 |
|
248 |
-
|
249 |
-
return null;
|
250 |
-
}
|
251 |
|
252 |
-
|
|
|
253 |
|
254 |
-
|
255 |
-
|
|
|
|
|
|
|
|
|
256 |
|
257 |
-
if ( $
|
258 |
-
$
|
259 |
-
|
260 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
261 |
}
|
|
|
|
|
262 |
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
271 |
|
272 |
-
|
273 |
-
|
274 |
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
|
280 |
-
|
281 |
-
}
|
282 |
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
*
|
287 |
-
* ob_start();
|
288 |
-
* $this->render();
|
289 |
-
* $json_data['html'] = ob_get_clean();
|
290 |
-
*
|
291 |
-
* because the render() method is not called within this class
|
292 |
-
* but by the code that created and owns the $form,
|
293 |
-
* and it's usually called with some custom data $this->render(array(...))
|
294 |
-
* that it's impossible to know here which data is that.
|
295 |
-
* If we will call $this->render(); without data, this may throw errors because
|
296 |
-
* the render callback may expect some custom data.
|
297 |
-
* Also it may be called or not, depending on the owner code inner logic.
|
298 |
-
*
|
299 |
-
* The only way to get the latest form html on ajax submit
|
300 |
-
* is to make a new ajax GET to current page and extract form html from the response.
|
301 |
-
*/
|
302 |
|
303 |
-
|
304 |
-
wp_send_json_success( $json_data );
|
305 |
-
} else {
|
306 |
-
wp_send_json_error( $json_data );
|
307 |
-
}
|
308 |
-
} else {
|
309 |
-
if ( ! $this->is_valid() ) {
|
310 |
-
return false;
|
311 |
-
}
|
312 |
|
313 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
314 |
}
|
315 |
|
316 |
-
|
317 |
-
}
|
318 |
-
|
319 |
-
/**
|
320 |
-
* @return string
|
321 |
-
*/
|
322 |
-
public function get_id() {
|
323 |
-
return $this->id;
|
324 |
}
|
325 |
|
326 |
/**
|
@@ -331,11 +255,9 @@ class FW_Form {
|
|
331 |
* @return array|string
|
332 |
*/
|
333 |
public function attr( $name = null ) {
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
return $this->attr;
|
338 |
-
}
|
339 |
}
|
340 |
|
341 |
/**
|
@@ -354,15 +276,15 @@ class FW_Form {
|
|
354 |
'html' => null,
|
355 |
),
|
356 |
'data' => $data,
|
357 |
-
'attr' => $this->attr,
|
358 |
);
|
359 |
|
360 |
-
|
361 |
|
362 |
-
if ( $
|
363 |
ob_start();
|
364 |
|
365 |
-
$data = call_user_func_array( $
|
366 |
|
367 |
$html = ob_get_clean();
|
368 |
|
@@ -372,71 +294,11 @@ class FW_Form {
|
|
372 |
}
|
373 |
|
374 |
$render_data = $data;
|
375 |
-
|
376 |
-
unset( $data );
|
377 |
}
|
378 |
|
379 |
-
|
380 |
-
do {
|
381 |
-
if ( is_admin() ) {
|
382 |
-
// errors in admin side are displayed by a script at the end of this file
|
383 |
-
break;
|
384 |
-
}
|
385 |
-
|
386 |
-
$submitted_form = FW_Form::get_submitted();
|
387 |
-
|
388 |
-
if ( ! $submitted_form ) {
|
389 |
-
break;
|
390 |
-
}
|
391 |
-
|
392 |
-
if ( $submitted_form->get_id() !== $this->get_id() ) {
|
393 |
-
// the submitted form is not current form
|
394 |
-
break;
|
395 |
-
}
|
396 |
-
|
397 |
-
unset( $submitted_form ); // not needed anymore, below will be used only with $this (because it's the same form)
|
398 |
-
|
399 |
-
if ( ! isset( $_POST[ $this->get_nonce_name( $render_data ) ] ) ) {
|
400 |
-
break;
|
401 |
-
}
|
402 |
-
|
403 |
-
if ( $this->is_valid() ) {
|
404 |
-
break;
|
405 |
-
}
|
406 |
-
|
407 |
-
/**
|
408 |
-
* Use this action to customize errors display in your theme
|
409 |
-
*/
|
410 |
-
do_action( 'fw_form_display_errors_frontend', $this );
|
411 |
-
|
412 |
-
if ( $this->errors_accessed() ) {
|
413 |
-
// already displayed, prevent/cancel default display
|
414 |
-
break;
|
415 |
-
}
|
416 |
-
|
417 |
-
$errors = $this->get_errors();
|
418 |
-
|
419 |
-
if ( empty( $errors ) ) {
|
420 |
-
break;
|
421 |
-
}
|
422 |
-
|
423 |
-
echo '<ul class="fw-form-errors">';
|
424 |
-
|
425 |
-
foreach ( $errors as $input_name => $error_message ) {
|
426 |
-
echo fw_html_tag(
|
427 |
-
'li',
|
428 |
-
array(
|
429 |
-
'data-input-name' => $input_name,
|
430 |
-
),
|
431 |
-
$error_message
|
432 |
-
);
|
433 |
-
}
|
434 |
-
|
435 |
-
echo '</ul>';
|
436 |
-
|
437 |
-
unset( $errors );
|
438 |
-
} while ( false );
|
439 |
|
|
|
440 |
echo '<form ' . fw_attr_to_html( $render_data['attr'] ) . ' >';
|
441 |
|
442 |
do_action( 'fw_form_display:before', $this );
|
@@ -444,13 +306,11 @@ class FW_Form {
|
|
444 |
echo fw_html_tag( 'input',
|
445 |
array(
|
446 |
'type' => 'hidden',
|
447 |
-
'name' => self
|
448 |
-
'value' => $this->
|
449 |
) );
|
450 |
|
451 |
-
|
452 |
-
wp_nonce_field( 'submit_fwf', $this->get_nonce_name( $render_data ) );
|
453 |
-
}
|
454 |
|
455 |
if ( ! empty( $render_data['attr']['action'] ) && $render_data['attr']['method'] == 'get' ) {
|
456 |
/**
|
@@ -487,6 +347,8 @@ class FW_Form {
|
|
487 |
do_action( 'fw_form_display:after', $this );
|
488 |
|
489 |
echo '</form>';
|
|
|
|
|
490 |
}
|
491 |
|
492 |
/**
|
@@ -494,61 +356,226 @@ class FW_Form {
|
|
494 |
* @return bool
|
495 |
*/
|
496 |
public function is_submitted() {
|
497 |
-
|
498 |
-
switch ( strtoupper( $this->attr( 'method' ) ) ) {
|
499 |
-
case 'POST':
|
500 |
-
$this->is_submitted = (
|
501 |
-
isset( $_POST[ self::$id_input_name ] )
|
502 |
-
&&
|
503 |
-
FW_Request::POST( self::$id_input_name ) === $this->id
|
504 |
-
);
|
505 |
-
break;
|
506 |
-
case 'GET':
|
507 |
-
$this->is_submitted = (
|
508 |
-
isset( $_GET[ self::$id_input_name ] )
|
509 |
-
&&
|
510 |
-
FW_Request::GET( self::$id_input_name ) === $this->id
|
511 |
-
);
|
512 |
-
break;
|
513 |
-
default:
|
514 |
-
$this->is_submitted = false;
|
515 |
-
}
|
516 |
-
}
|
517 |
-
|
518 |
-
return $this->is_submitted;
|
519 |
}
|
520 |
|
521 |
/**
|
522 |
* @return bool
|
523 |
*/
|
524 |
public function is_valid() {
|
525 |
-
if ( ! $this->
|
526 |
-
trigger_error( __METHOD__ . ' called before validation', E_USER_WARNING );
|
527 |
-
|
528 |
return null;
|
529 |
}
|
530 |
|
531 |
-
return
|
532 |
}
|
533 |
|
534 |
/**
|
535 |
-
*
|
536 |
-
*
|
|
|
|
|
|
|
537 |
*/
|
538 |
-
public function
|
539 |
-
|
540 |
-
|
|
|
541 |
|
542 |
-
|
|
|
|
|
|
|
543 |
}
|
544 |
|
545 |
-
$this->
|
|
|
546 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
547 |
return $this->errors;
|
548 |
}
|
549 |
|
550 |
-
|
551 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
552 |
}
|
553 |
|
554 |
/**
|
@@ -556,24 +583,69 @@ class FW_Form {
|
|
556 |
* @return FW_Form|false
|
557 |
*/
|
558 |
public static function get_submitted() {
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
self::$submitted_id = $form->get_id();
|
565 |
-
break 2;
|
566 |
-
}
|
567 |
-
}
|
568 |
|
569 |
-
|
570 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
571 |
}
|
572 |
|
573 |
-
if (
|
574 |
-
return
|
575 |
-
} else {
|
576 |
-
return false;
|
577 |
}
|
|
|
|
|
578 |
}
|
579 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
/**
|
10 |
* Store all form ids created with this class
|
11 |
* @var FW_Form[] {'form_id' => instance}
|
12 |
+
*
|
13 |
+
* @deprecated 2.6.15 Use FW_Form::get_forms()
|
14 |
*/
|
15 |
protected static $forms = array();
|
16 |
|
17 |
/**
|
18 |
* The id of the submitted form id
|
19 |
* @var string
|
20 |
+
*
|
21 |
+
* @deprecated 2.6.15
|
22 |
*/
|
23 |
protected static $submitted_id;
|
24 |
|
25 |
/**
|
26 |
* Hidden input name that stores the form id
|
27 |
* @var string
|
28 |
+
*
|
29 |
+
* @deprecated 2.6.15 Use self::get_form_id_name()
|
30 |
*/
|
31 |
protected static $id_input_name = 'fwf';
|
32 |
|
33 |
/**
|
34 |
* Form id
|
35 |
* @var string
|
36 |
+
*
|
37 |
+
* @deprecated 2.6.15 Use $this->get_id()
|
38 |
*/
|
39 |
protected $id;
|
40 |
|
41 |
/**
|
42 |
* Html attributes for <form> tag
|
43 |
* @var array
|
44 |
+
*
|
45 |
+
* @deprecated 2.6.15 Use $this->attr()
|
46 |
*/
|
47 |
+
protected $attr = array();
|
48 |
|
49 |
/**
|
50 |
* Found validation errors
|
61 |
/**
|
62 |
* If current request is the submit of this form
|
63 |
* @var bool
|
64 |
+
*
|
65 |
+
* @deprecated 2.6.15 Use $this->is_submitted()
|
66 |
*/
|
67 |
protected $is_submitted;
|
68 |
|
69 |
/**
|
70 |
* @var bool
|
71 |
+
*
|
72 |
+
* @deprecated 2.6.15
|
73 |
*/
|
74 |
protected $validate_and_save_called = false;
|
75 |
|
76 |
+
/**
|
77 |
+
* @var array
|
78 |
+
*
|
79 |
+
* @deprecated 2.6.15 Use $this->get_callbacks()
|
80 |
+
*/
|
81 |
protected $callbacks = array(
|
82 |
'render' => false,
|
83 |
'validate' => false,
|
84 |
'save' => false
|
85 |
);
|
86 |
|
87 |
+
private $request;
|
88 |
+
|
89 |
/**
|
90 |
* @param string $id Unique
|
91 |
* @param array $data (optional)
|
97 |
* )
|
98 |
*/
|
99 |
public function __construct( $id, $data = array() ) {
|
100 |
+
try {
|
101 |
+
self::get_form( $id );
|
102 |
trigger_error( sprintf( __( 'Form with id "%s" was already defined', 'fw' ), $id ), E_USER_ERROR );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
103 |
|
104 |
+
return;
|
105 |
+
} catch ( FW_Form_Not_Found_Exception $e ) {
|
|
|
|
|
|
|
106 |
}
|
107 |
|
108 |
+
$this
|
109 |
+
// set id
|
110 |
+
->set_id( $id )
|
111 |
+
// prepare callbacks
|
112 |
+
->set_callbacks( array(
|
113 |
+
'render' => fw_akg( 'render', $data, false ),
|
114 |
+
'validate' => fw_akg( 'validate', $data, false ),
|
115 |
+
'save' => fw_akg( 'save', $data, false ),
|
116 |
+
) )
|
117 |
+
// prepare attributes
|
118 |
+
->set_attr( (array) fw_akg( 'attr', $data, array() ) );
|
119 |
+
|
120 |
+
self::$forms[ $this->get_id() ] =& $this;
|
121 |
|
122 |
if ( did_action( 'wp_loaded' ) ) {
|
123 |
// in case if form instance was created after action
|
126 |
// attach to an action before 'send_headers' action, to be able to do redirects
|
127 |
add_action( 'wp_loaded', array( $this, '_validate_and_save' ), 101 );
|
128 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
129 |
|
130 |
+
add_action( 'fw_form_display:before_form', array( $this, '_action_fw_form_show_errors' ) );
|
131 |
}
|
132 |
|
133 |
/**
|
|
|
|
|
|
|
|
|
|
|
|
|
134 |
* @return string
|
|
|
135 |
*/
|
136 |
+
public function get_id() {
|
137 |
+
return $this->id;
|
138 |
}
|
139 |
|
140 |
+
/**
|
141 |
+
* Get validation errors
|
142 |
+
* @return array
|
143 |
+
*/
|
144 |
+
public function get_errors() {
|
145 |
+
if ( ! $this->validate_and_save_called ) {
|
146 |
+
fw_print( debug_backtrace() );
|
147 |
+
trigger_error( __METHOD__ . ' called before validation', E_USER_WARNING );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
148 |
|
149 |
+
return array( '~' => true );
|
150 |
}
|
151 |
|
152 |
+
$this->errors_accessed = true;
|
|
|
|
|
|
|
|
|
|
|
153 |
|
154 |
+
return $this->_get_errors();
|
155 |
}
|
156 |
|
157 |
+
public function get_callbacks() {
|
158 |
+
return $this->callbacks;
|
159 |
+
}
|
160 |
+
|
161 |
+
public function errors_accessed() {
|
162 |
+
return $this->errors_accessed;
|
163 |
}
|
164 |
|
165 |
/**
|
167 |
*
|
168 |
* Note: This callback can abort script execution if save does redirect
|
169 |
*
|
|
|
170 |
* @internal
|
171 |
*/
|
172 |
public function _validate_and_save() {
|
|
|
|
|
173 |
|
174 |
+
if ( ! self::is_form_submitted( $this->get_id() ) || $this->validate_and_save_called ) {
|
175 |
+
return;
|
|
|
176 |
}
|
177 |
|
178 |
+
$this->validate_and_save_called = true;
|
|
|
|
|
179 |
|
180 |
+
try {
|
181 |
+
$data = $this->submit( self::get_form_request( $this->get_id() ) );
|
182 |
|
183 |
+
if ( $this->_is_ajax() ) {
|
184 |
+
wp_send_json_success( array(
|
185 |
+
'save_data' => $data,
|
186 |
+
'flash_messages' => self::collect_flash_messages(),
|
187 |
+
) );
|
188 |
+
}
|
189 |
|
190 |
+
if ( ( $redirect = fw_akg( 'redirect', $data ) ) ) {
|
191 |
+
wp_redirect( $redirect );
|
192 |
+
exit;
|
193 |
+
}
|
194 |
+
} catch ( FW_Form_Invalid_Submission_Exception $e ) {
|
195 |
+
if ( $this->_is_ajax() ) {
|
196 |
+
wp_send_json_error( array(
|
197 |
+
'errors' => $this->get_errors(),
|
198 |
+
'flash_messages' => self::collect_flash_messages()
|
199 |
+
) );
|
200 |
}
|
201 |
+
}
|
202 |
+
}
|
203 |
|
204 |
+
/**
|
205 |
+
* @param FW_Form $form
|
206 |
+
*
|
207 |
+
* @internal
|
208 |
+
*
|
209 |
+
* You can overwrite it in case you do not need the errors to be shown for your form
|
210 |
+
*/
|
211 |
+
public function _action_fw_form_show_errors( $form ) {
|
212 |
+
if (
|
213 |
+
$form->get_id() != $this->get_id()
|
214 |
+
// errors in admin side are displayed by a script at the end of this file
|
215 |
+
|| is_admin()
|
216 |
+
|| ! $form->is_submitted()
|
217 |
+
|| $form->is_valid()
|
218 |
+
|| $form->errors_accessed()
|
219 |
+
) {
|
220 |
|
221 |
+
return;
|
222 |
+
}
|
223 |
|
224 |
+
/**
|
225 |
+
* Use this action to customize errors display in your theme
|
226 |
+
*/
|
227 |
+
do_action( 'fw_form_display_errors_frontend', $form );
|
228 |
|
229 |
+
$errors = $form->get_errors();
|
|
|
230 |
|
231 |
+
if ( empty( $errors ) ) {
|
232 |
+
return;
|
233 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
234 |
|
235 |
+
echo '<ul class="fw-form-errors">';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
236 |
|
237 |
+
foreach ( $errors as $input_name => $error_message ) {
|
238 |
+
echo fw_html_tag(
|
239 |
+
'li',
|
240 |
+
array(
|
241 |
+
'data-input-name' => $input_name,
|
242 |
+
),
|
243 |
+
$error_message
|
244 |
+
);
|
245 |
}
|
246 |
|
247 |
+
echo '</ul>';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
248 |
}
|
249 |
|
250 |
/**
|
255 |
* @return array|string
|
256 |
*/
|
257 |
public function attr( $name = null ) {
|
258 |
+
return $name !== null
|
259 |
+
? fw_akg( $name, $this->attr )
|
260 |
+
: $this->attr;
|
|
|
|
|
261 |
}
|
262 |
|
263 |
/**
|
276 |
'html' => null,
|
277 |
),
|
278 |
'data' => $data,
|
279 |
+
'attr' => $this->attr(),
|
280 |
);
|
281 |
|
282 |
+
$html = '';
|
283 |
|
284 |
+
if ( $render_callback = fw_akg( 'render', $this->get_callbacks() ) ) {
|
285 |
ob_start();
|
286 |
|
287 |
+
$data = call_user_func_array( $render_callback, array( $render_data, $this ) );
|
288 |
|
289 |
$html = ob_get_clean();
|
290 |
|
294 |
}
|
295 |
|
296 |
$render_data = $data;
|
|
|
|
|
297 |
}
|
298 |
|
299 |
+
do_action( 'fw_form_display:before_form', $this );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
300 |
|
301 |
+
// display form errors in frontend
|
302 |
echo '<form ' . fw_attr_to_html( $render_data['attr'] ) . ' >';
|
303 |
|
304 |
do_action( 'fw_form_display:before', $this );
|
306 |
echo fw_html_tag( 'input',
|
307 |
array(
|
308 |
'type' => 'hidden',
|
309 |
+
'name' => self::get_form_id_name(),
|
310 |
+
'value' => $this->get_id(),
|
311 |
) );
|
312 |
|
313 |
+
wp_nonce_field( $this->get_nonce_action(), $this->get_nonce_name( $render_data ) );
|
|
|
|
|
314 |
|
315 |
if ( ! empty( $render_data['attr']['action'] ) && $render_data['attr']['method'] == 'get' ) {
|
316 |
/**
|
347 |
do_action( 'fw_form_display:after', $this );
|
348 |
|
349 |
echo '</form>';
|
350 |
+
|
351 |
+
do_action( 'fw_form_display:after_form', $this );
|
352 |
}
|
353 |
|
354 |
/**
|
356 |
* @return bool
|
357 |
*/
|
358 |
public function is_submitted() {
|
359 |
+
return $this->request !== null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
360 |
}
|
361 |
|
362 |
/**
|
363 |
* @return bool
|
364 |
*/
|
365 |
public function is_valid() {
|
366 |
+
if ( ! $this->is_submitted() ) {
|
|
|
|
|
367 |
return null;
|
368 |
}
|
369 |
|
370 |
+
return count( $this->_get_errors() ) == 0;
|
371 |
}
|
372 |
|
373 |
/**
|
374 |
+
* @param array $request
|
375 |
+
*
|
376 |
+
* @throws FW_Form_Invalid_Submission_Exception
|
377 |
+
*
|
378 |
+
* @return mixed
|
379 |
*/
|
380 |
+
public function submit( array $request = array() ) {
|
381 |
+
$this->request = $request;
|
382 |
+
//Updated the deprecated member for those that extended the class and use it in code
|
383 |
+
$this->is_submitted = true;
|
384 |
|
385 |
+
$errors = $this->validate();
|
386 |
+
|
387 |
+
if ( ! empty( $errors ) ) {
|
388 |
+
throw new FW_Form_Invalid_Submission_Exception( $errors );
|
389 |
}
|
390 |
|
391 |
+
return $this->save();
|
392 |
+
}
|
393 |
|
394 |
+
protected function get_default_attr() {
|
395 |
+
return array(
|
396 |
+
'data-fw-form-id' => $this->get_id(),
|
397 |
+
'method' => 'post',
|
398 |
+
'action' => fw_current_url(),
|
399 |
+
'class' => 'fw_form_' . $this->get_id()
|
400 |
+
);
|
401 |
+
}
|
402 |
+
|
403 |
+
/**
|
404 |
+
* @param array $attr
|
405 |
+
*
|
406 |
+
* @return $this
|
407 |
+
*/
|
408 |
+
protected function set_attr( array $attr ) {
|
409 |
+
$this->attr = array_merge( $this->get_default_attr(), $attr );
|
410 |
+
|
411 |
+
return $this;
|
412 |
+
}
|
413 |
+
|
414 |
+
/**
|
415 |
+
* @param null $key
|
416 |
+
*
|
417 |
+
* @return array|mixed|null
|
418 |
+
*
|
419 |
+
* @since 2.6.15
|
420 |
+
*/
|
421 |
+
protected function get_request( $key = null ) {
|
422 |
+
return $key === null ? (array) $this->request : fw_akg( $key, $this->request );
|
423 |
+
}
|
424 |
+
|
425 |
+
/**
|
426 |
+
* @return string|null
|
427 |
+
*
|
428 |
+
* @since 2.6.15
|
429 |
+
*/
|
430 |
+
protected function get_nonce() {
|
431 |
+
return $this->get_request( $this->get_nonce_name() );
|
432 |
+
}
|
433 |
+
|
434 |
+
/**
|
435 |
+
* Returns forms errors without counting them as accessed
|
436 |
+
* @return array
|
437 |
+
*/
|
438 |
+
protected function _get_errors() {
|
439 |
return $this->errors;
|
440 |
}
|
441 |
|
442 |
+
/**
|
443 |
+
* @return string
|
444 |
+
*
|
445 |
+
* @since 2.6.15
|
446 |
+
*/
|
447 |
+
protected function get_nonce_action() {
|
448 |
+
return 'submit_fwf';
|
449 |
+
}
|
450 |
+
|
451 |
+
protected function check_nonce( $nonce ) {
|
452 |
+
return wp_verify_nonce( $nonce, $this->get_nonce_action() );
|
453 |
+
}
|
454 |
+
|
455 |
+
/**
|
456 |
+
* @return array
|
457 |
+
*/
|
458 |
+
protected function validate() {
|
459 |
+
/**
|
460 |
+
* Errors array {'input[name]' => 'Error message'}
|
461 |
+
*/
|
462 |
+
$errors = array();
|
463 |
+
|
464 |
+
if ( ! $this->check_nonce( $this->get_nonce() ) ) {
|
465 |
+
$errors[ $this->get_nonce_name() ] = __( 'Nonce verification failed', 'fw' );
|
466 |
+
}
|
467 |
+
|
468 |
+
/**
|
469 |
+
* Call validate callback
|
470 |
+
*
|
471 |
+
* Callback must 'manually' extract input values from $_POST (or $_GET)
|
472 |
+
*/
|
473 |
+
if ( ( $validate = fw_akg( 'validate', $this->get_callbacks() ) ) ) {
|
474 |
+
$errors = (array) call_user_func( $validate, $errors );
|
475 |
+
}
|
476 |
+
|
477 |
+
return $this->set_errors( $errors )->_get_errors();
|
478 |
+
}
|
479 |
+
|
480 |
+
/**
|
481 |
+
* @return array|mixed
|
482 |
+
*/
|
483 |
+
protected function save() {
|
484 |
+
$save_data = array(
|
485 |
+
// you can set here a url for redirect after save
|
486 |
+
'redirect' => null
|
487 |
+
);
|
488 |
+
|
489 |
+
/**
|
490 |
+
* Call save callback
|
491 |
+
*
|
492 |
+
* Callback must 'manually' extract input values from $_POST (or $_GET)
|
493 |
+
*/
|
494 |
+
if ( ( $save_callback = fw_akg( 'save', $this->get_callbacks() ) ) ) {
|
495 |
+
$data = call_user_func_array( $save_callback, array( $save_data ) );
|
496 |
+
|
497 |
+
if ( ! is_array( $data ) ) {
|
498 |
+
// fix if returned wrong data from callback
|
499 |
+
$data = $save_data;
|
500 |
+
}
|
501 |
+
|
502 |
+
$save_data = $data;
|
503 |
+
|
504 |
+
unset( $data );
|
505 |
+
}
|
506 |
+
|
507 |
+
return $save_data;
|
508 |
+
}
|
509 |
+
|
510 |
+
/**
|
511 |
+
* @return bool
|
512 |
+
*
|
513 |
+
* @deprecated 2.6.15
|
514 |
+
*/
|
515 |
+
protected function is_ajax() {
|
516 |
+
return self::_is_ajax();
|
517 |
+
}
|
518 |
+
|
519 |
+
protected function set_id( $id ) {
|
520 |
+
$this->id = $id;
|
521 |
+
|
522 |
+
return $this;
|
523 |
+
}
|
524 |
+
|
525 |
+
/**
|
526 |
+
* @param array $callbacks
|
527 |
+
*
|
528 |
+
* @return $this
|
529 |
+
*/
|
530 |
+
protected function set_callbacks( array $callbacks ) {
|
531 |
+
$this->callbacks = $callbacks;
|
532 |
+
|
533 |
+
return $this;
|
534 |
+
}
|
535 |
+
|
536 |
+
protected function set_errors( array $errors ) {
|
537 |
+
$this->errors = $errors;
|
538 |
+
|
539 |
+
return $this;
|
540 |
+
}
|
541 |
+
|
542 |
+
/**
|
543 |
+
* Some forms (like Forms extension frontend form) uses the same FW_Form instance for all sub-forms
|
544 |
+
* and they must be differentiated somehow.
|
545 |
+
* Fixes https://github.com/ThemeFuse/Unyson/issues/2033
|
546 |
+
*
|
547 |
+
* @param array $render_data
|
548 |
+
*
|
549 |
+
* @return string
|
550 |
+
* @since 2.6.6
|
551 |
+
*/
|
552 |
+
private function get_nonce_name( $render_data = array() ) {
|
553 |
+
return '_nonce_' . md5( $this->id . apply_filters( 'fw:form:nonce-name-data', '', $this, $render_data ) );
|
554 |
+
}
|
555 |
+
|
556 |
+
/**
|
557 |
+
* @return FW_Form[]
|
558 |
+
*
|
559 |
+
* @since 2.6.15
|
560 |
+
*/
|
561 |
+
public static function get_forms() {
|
562 |
+
return self::$forms;
|
563 |
+
}
|
564 |
+
|
565 |
+
/**
|
566 |
+
* @param $id
|
567 |
+
*
|
568 |
+
* @return FW_Form
|
569 |
+
* @throws FW_Form_Not_Found_Exception
|
570 |
+
*
|
571 |
+
* @since 2.6.15
|
572 |
+
*/
|
573 |
+
public static function get_form( $id ) {
|
574 |
+
if ( ! isset( self::$forms[ $id ] ) ) {
|
575 |
+
throw new FW_Form_Not_Found_Exception( "FW_Form $id was not defined" );
|
576 |
+
}
|
577 |
+
|
578 |
+
return self::$forms[ $id ];
|
579 |
}
|
580 |
|
581 |
/**
|
583 |
* @return FW_Form|false
|
584 |
*/
|
585 |
public static function get_submitted() {
|
586 |
+
foreach ( self::get_forms() as $id => $form ) {
|
587 |
+
if ( self::is_form_submitted( $id ) ) {
|
588 |
+
return $form;
|
589 |
+
}
|
590 |
+
}
|
|
|
|
|
|
|
|
|
591 |
|
592 |
+
return false;
|
593 |
+
}
|
594 |
+
|
595 |
+
/**
|
596 |
+
* @return bool
|
597 |
+
*
|
598 |
+
* @since 2.6.15
|
599 |
+
*/
|
600 |
+
public static function _is_ajax() {
|
601 |
+
return ( defined( 'DOING_AJAX' ) && DOING_AJAX )
|
602 |
+
||
|
603 |
+
strtolower( fw_akg( 'HTTP_X_REQUESTED_WITH', $_SERVER ) ) == 'xmlhttprequest';
|
604 |
+
}
|
605 |
+
|
606 |
+
public static function get_form_request( $id ) {
|
607 |
+
if ( FW_Request::POST( self::get_form_id_name() ) == $id ) {
|
608 |
+
return FW_Request::POST();
|
609 |
}
|
610 |
|
611 |
+
if ( FW_Request::GET( self::get_form_id_name() ) == $id ) {
|
612 |
+
return FW_Request::GET();
|
|
|
|
|
613 |
}
|
614 |
+
|
615 |
+
return null;
|
616 |
}
|
617 |
+
|
618 |
+
/**
|
619 |
+
* @return string
|
620 |
+
*
|
621 |
+
* @since 2.6.15
|
622 |
+
*/
|
623 |
+
protected static function get_form_id_name() {
|
624 |
+
return 'fwf';
|
625 |
+
}
|
626 |
+
|
627 |
+
/**
|
628 |
+
* @param $id
|
629 |
+
*
|
630 |
+
* @return bool
|
631 |
+
*
|
632 |
+
* @since 2.6.15
|
633 |
+
*/
|
634 |
+
protected static function is_form_submitted( $id ) {
|
635 |
+
return self::get_form_request( $id ) !== null;
|
636 |
+
}
|
637 |
+
|
638 |
+
private static function collect_flash_messages() {
|
639 |
+
$flash_messages = array();
|
640 |
+
|
641 |
+
foreach ( FW_Flash_Messages::_get_messages( true ) as $type => $messages ) {
|
642 |
+
$flash_messages[ $type ] = array();
|
643 |
+
|
644 |
+
foreach ( $messages as $id => $message_data ) {
|
645 |
+
$flash_messages[ $type ][ $id ] = $message_data['message'];
|
646 |
+
}
|
647 |
+
}
|
648 |
+
|
649 |
+
return $flash_messages;
|
650 |
+
}
|
651 |
+
}
|
framework/helpers/database.php
CHANGED
@@ -174,7 +174,8 @@ class FW_Db_Options_Model_Post extends FW_Db_Options_Model {
|
|
174 |
|
175 |
protected function _get_cache_key($key, $item_id, array $extra_data = array()) {
|
176 |
if ($key === 'options') {
|
177 |
-
|
|
|
178 |
} else {
|
179 |
return $this->get_post_id($item_id);
|
180 |
}
|
174 |
|
175 |
protected function _get_cache_key($key, $item_id, array $extra_data = array()) {
|
176 |
if ($key === 'options') {
|
177 |
+
// Cache options grouped by post-type, not by post id
|
178 |
+
return ($post_type = $this->get_post_type($item_id)) ? $post_type : '?';
|
179 |
} else {
|
180 |
return $this->get_post_id($item_id);
|
181 |
}
|
framework/helpers/exceptions/class-fw-form-invalid-submission-exception.php
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'FW' ) ) {
|
2 |
+
die( 'Forbidden' );
|
3 |
+
}
|
4 |
+
|
5 |
+
class FW_Form_Invalid_Submission_Exception extends Exception {
|
6 |
+
|
7 |
+
private $errors = array();
|
8 |
+
|
9 |
+
public function __construct( array $errors ) {
|
10 |
+
parent::__construct();
|
11 |
+
|
12 |
+
$this->set_errors( $errors );
|
13 |
+
}
|
14 |
+
|
15 |
+
public function get_errors() {
|
16 |
+
return $this->errors;
|
17 |
+
}
|
18 |
+
|
19 |
+
protected function set_errors( array $errors ) {
|
20 |
+
$this->errors = $errors;
|
21 |
+
}
|
22 |
+
}
|
framework/helpers/exceptions/class-fw-form-not-found-exception.php
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'FW' ) ) {
|
2 |
+
die( 'Forbidden' );
|
3 |
+
}
|
4 |
+
|
5 |
+
class FW_Form_Not_Found_Exception extends Exception {
|
6 |
+
|
7 |
+
}
|
framework/includes/option-types/color-picker/class-fw-option-type-color-picker.php
CHANGED
@@ -78,7 +78,7 @@ class FW_Option_Type_Color_Picker extends FW_Option_Type
|
|
78 |
// do not use `!is_null()` allow empty values https://github.com/ThemeFuse/Unyson/issues/2025
|
79 |
!empty($input_value)
|
80 |
&&
|
81 |
-
!preg_match('/^#[a-f0-9]{3}
|
82 |
)
|
83 |
) {
|
84 |
return (string)$option['value'];
|
78 |
// do not use `!is_null()` allow empty values https://github.com/ThemeFuse/Unyson/issues/2025
|
79 |
!empty($input_value)
|
80 |
&&
|
81 |
+
!preg_match('/^#([a-f0-9]{3}){1,2}$/i', $input_value)
|
82 |
)
|
83 |
) {
|
84 |
return (string)$option['value'];
|
framework/includes/option-types/color-picker/static/js/scripts.js
CHANGED
@@ -2,7 +2,7 @@ jQuery(document).ready(function($){
|
|
2 |
var helpers = {
|
3 |
optionClass: 'fw-option-type-color-picker',
|
4 |
eventNamespace: '.fwOptionTypeColorPicker',
|
5 |
-
colorRegex: /^#[a-f0-9]{3}
|
6 |
localized: window._fw_option_type_color_picker_localized,
|
7 |
/**
|
8 |
* Return true if color is dark
|
2 |
var helpers = {
|
3 |
optionClass: 'fw-option-type-color-picker',
|
4 |
eventNamespace: '.fwOptionTypeColorPicker',
|
5 |
+
colorRegex: /^#([a-f0-9]{3}){1,2}$/i,
|
6 |
localized: window._fw_option_type_color_picker_localized,
|
7 |
/**
|
8 |
* Return true if color is dark
|
framework/includes/option-types/gradient/view.php
CHANGED
@@ -15,7 +15,7 @@
|
|
15 |
);
|
16 |
}
|
17 |
|
18 |
-
$color_regex = '/^#[a-f0-9]{
|
19 |
|
20 |
?>
|
21 |
<div <?php echo fw_attr_to_html($div_attr) ?> >
|
15 |
);
|
16 |
}
|
17 |
|
18 |
+
$color_regex = '/^#([a-f0-9]{3}){1,2}$/i';
|
19 |
|
20 |
?>
|
21 |
<div <?php echo fw_attr_to_html($div_attr) ?> >
|
framework/includes/option-types/multi-select/class-fw-option-type-multi-select.php
CHANGED
@@ -79,6 +79,8 @@ if ( ! class_exists( 'FW_Option_Type_Multi_Select' ) ):
|
|
79 |
'limit' => 100,
|
80 |
), $limits);
|
81 |
|
|
|
|
|
82 |
/** @var WPDB $wpdb */
|
83 |
global $wpdb;
|
84 |
|
@@ -112,7 +114,12 @@ if ( ! class_exists( 'FW_Option_Type_Multi_Select' ) ):
|
|
112 |
|
113 |
$sql .= " LIMIT ". intval($limits['limit']);
|
114 |
|
115 |
-
return $wpdb->get_results(
|
|
|
|
|
|
|
|
|
|
|
116 |
}
|
117 |
|
118 |
private static function query_terms(array $limits) {
|
@@ -125,6 +132,8 @@ if ( ! class_exists( 'FW_Option_Type_Multi_Select' ) ):
|
|
125 |
'limit' => 100,
|
126 |
), $limits);
|
127 |
|
|
|
|
|
128 |
/** @var WPDB $wpdb */
|
129 |
global $wpdb;
|
130 |
|
@@ -157,7 +166,12 @@ if ( ! class_exists( 'FW_Option_Type_Multi_Select' ) ):
|
|
157 |
|
158 |
$sql .= " LIMIT ". intval($limits['limit']);
|
159 |
|
160 |
-
return $wpdb->get_results(
|
|
|
|
|
|
|
|
|
|
|
161 |
}
|
162 |
|
163 |
private static function query_users(array $limits) {
|
@@ -170,10 +184,12 @@ if ( ! class_exists( 'FW_Option_Type_Multi_Select' ) ):
|
|
170 |
'limit' => 100,
|
171 |
), $limits);
|
172 |
|
|
|
|
|
173 |
/** @var WPDB $wpdb */
|
174 |
global $wpdb;
|
175 |
|
176 |
-
$sql = "SELECT DISTINCT users.
|
177 |
." FROM $wpdb->users AS users, $wpdb->usermeta AS usermeta"
|
178 |
." WHERE usermeta.user_id = users.ID";
|
179 |
|
@@ -181,7 +197,7 @@ if ( ! class_exists( 'FW_Option_Type_Multi_Select' ) ):
|
|
181 |
$prepare = array();
|
182 |
|
183 |
if ($limits['id']) {
|
184 |
-
$sql .= " AND users.
|
185 |
. implode( ', ', array_fill( 1, count( $limits['id'] ), '%d' ) )
|
186 |
. " ) ";
|
187 |
$prepare = array_merge($prepare, $limits['id']);
|
@@ -207,7 +223,12 @@ if ( ! class_exists( 'FW_Option_Type_Multi_Select' ) ):
|
|
207 |
|
208 |
$sql .= " LIMIT ". intval($limits['limit']);
|
209 |
|
210 |
-
return $wpdb->get_results(
|
|
|
|
|
|
|
|
|
|
|
211 |
}
|
212 |
|
213 |
/**
|
79 |
'limit' => 100,
|
80 |
), $limits);
|
81 |
|
82 |
+
$limits['limit'] = max($limits['limit'], 1);
|
83 |
+
|
84 |
/** @var WPDB $wpdb */
|
85 |
global $wpdb;
|
86 |
|
114 |
|
115 |
$sql .= " LIMIT ". intval($limits['limit']);
|
116 |
|
117 |
+
return $wpdb->get_results(
|
118 |
+
$prepare
|
119 |
+
? $wpdb->prepare($sql, $prepare)
|
120 |
+
: $sql,
|
121 |
+
ARRAY_A
|
122 |
+
);
|
123 |
}
|
124 |
|
125 |
private static function query_terms(array $limits) {
|
132 |
'limit' => 100,
|
133 |
), $limits);
|
134 |
|
135 |
+
$limits['limit'] = max($limits['limit'], 1);
|
136 |
+
|
137 |
/** @var WPDB $wpdb */
|
138 |
global $wpdb;
|
139 |
|
166 |
|
167 |
$sql .= " LIMIT ". intval($limits['limit']);
|
168 |
|
169 |
+
return $wpdb->get_results(
|
170 |
+
$prepare
|
171 |
+
? $wpdb->prepare($sql, $prepare)
|
172 |
+
: $sql,
|
173 |
+
ARRAY_A
|
174 |
+
);
|
175 |
}
|
176 |
|
177 |
private static function query_users(array $limits) {
|
184 |
'limit' => 100,
|
185 |
), $limits);
|
186 |
|
187 |
+
$limits['limit'] = max($limits['limit'], 1);
|
188 |
+
|
189 |
/** @var WPDB $wpdb */
|
190 |
global $wpdb;
|
191 |
|
192 |
+
$sql = "SELECT DISTINCT users.ID AS val, users.user_nicename AS title"
|
193 |
." FROM $wpdb->users AS users, $wpdb->usermeta AS usermeta"
|
194 |
." WHERE usermeta.user_id = users.ID";
|
195 |
|
197 |
$prepare = array();
|
198 |
|
199 |
if ($limits['id']) {
|
200 |
+
$sql .= " AND users.ID IN ( "
|
201 |
. implode( ', ', array_fill( 1, count( $limits['id'] ), '%d' ) )
|
202 |
. " ) ";
|
203 |
$prepare = array_merge($prepare, $limits['id']);
|
223 |
|
224 |
$sql .= " LIMIT ". intval($limits['limit']);
|
225 |
|
226 |
+
return $wpdb->get_results(
|
227 |
+
$prepare
|
228 |
+
? $wpdb->prepare($sql, $prepare)
|
229 |
+
: $sql,
|
230 |
+
ARRAY_A
|
231 |
+
);
|
232 |
}
|
233 |
|
234 |
/**
|
framework/includes/option-types/rgba-color-picker/class-fw-option-type-rgba-color-picker.php
CHANGED
@@ -78,7 +78,7 @@ class FW_Option_Type_Rgba_Color_Picker extends FW_Option_Type {
|
|
78 |
!empty($input_value)
|
79 |
&&
|
80 |
!(
|
81 |
-
preg_match( '/^#[a-f0-9]{3}
|
82 |
||
|
83 |
preg_match( '/^rgba\( *([01]?\d\d?|2[0-4]\d|25[0-5]) *\, *([01]?\d\d?|2[0-4]\d|25[0-5]) *\, *([01]?\d\d?|2[0-4]\d|25[0-5]) *\, *(1|0|0?.\d+) *\)$/', $input_value )
|
84 |
)
|
78 |
!empty($input_value)
|
79 |
&&
|
80 |
!(
|
81 |
+
preg_match( '/^#([a-f0-9]{3}){1,2}$/i', $input_value )
|
82 |
||
|
83 |
preg_match( '/^rgba\( *([01]?\d\d?|2[0-4]\d|25[0-5]) *\, *([01]?\d\d?|2[0-4]\d|25[0-5]) *\, *([01]?\d\d?|2[0-4]\d|25[0-5]) *\, *(1|0|0?.\d+) *\)$/', $input_value )
|
84 |
)
|
framework/includes/option-types/rgba-color-picker/static/js/scripts.js
CHANGED
@@ -35,7 +35,7 @@ jQuery(function($){
|
|
35 |
var helpers = {
|
36 |
optionClass: 'fw-option-type-rgba-color-picker',
|
37 |
eventNamespace: '.fwOptionTypeRgbaColorPicker',
|
38 |
-
hexColorRegex: /^#[a-f0-9]{3}
|
39 |
localized: window._fw_option_type_rgba_color_picker_localized,
|
40 |
increment: 0,
|
41 |
isColorDark: function(rgbaColor) {
|
35 |
var helpers = {
|
36 |
optionClass: 'fw-option-type-rgba-color-picker',
|
37 |
eventNamespace: '.fwOptionTypeRgbaColorPicker',
|
38 |
+
hexColorRegex: /^#([a-f0-9]{3}){1,2}$/i,
|
39 |
localized: window._fw_option_type_rgba_color_picker_localized,
|
40 |
increment: 0,
|
41 |
isColorDark: function(rgbaColor) {
|
framework/includes/option-types/typography-v2/class-fw-option-type-typography-v2.php
CHANGED
@@ -101,8 +101,8 @@ class FW_Option_Type_Typography_v2 extends FW_Option_Type {
|
|
101 |
$default = $this->get_defaults();
|
102 |
$values = array_merge( $default['value'], $option['value'], is_array($input_value) ? $input_value : array());
|
103 |
|
104 |
-
if ( ! preg_match( '/^#[a-f0-9]{
|
105 |
-
$values =
|
106 |
}
|
107 |
|
108 |
$components = array_merge( $default['components'], $option['components'] );
|
101 |
$default = $this->get_defaults();
|
102 |
$values = array_merge( $default['value'], $option['value'], is_array($input_value) ? $input_value : array());
|
103 |
|
104 |
+
if ( ! empty($values['color']) && ! preg_match( '/^#([a-f0-9]{3}){1,2}$/i', $values['color'] ) ) {
|
105 |
+
$values['color'] = isset( $option['value']['color'] ) ? $option['value']['color'] : $default['value']['color'];
|
106 |
}
|
107 |
|
108 |
$components = array_merge( $default['components'], $option['components'] );
|
framework/includes/option-types/typography/class-fw-option-type-typography.php
CHANGED
@@ -112,7 +112,7 @@ class FW_Option_Type_Typography extends FW_Option_Type
|
|
112 |
'size' => ( ! empty( $components['size'] ) ) ? ( isset( $input_value['size'] ) ) ? intval( $input_value['size'] ) : intval( $option['value']['size'] ) : false,
|
113 |
'family' => ( ! empty( $components['family'] ) ) ? ( isset( $input_value['family'] ) ) ? $input_value['family'] : $option['value']['family'] : false,
|
114 |
'style' => ( ! empty( $components['family'] ) ) ? ( isset( $input_value['style'] ) ) ? $input_value['style'] : $option['value']['style'] : false,
|
115 |
-
'color' => ( ! empty( $components['color'] ) ) ? ( isset( $input_value['color'] ) && preg_match( '/^#[a-f0-9]{
|
116 |
);
|
117 |
|
118 |
return $values;
|
112 |
'size' => ( ! empty( $components['size'] ) ) ? ( isset( $input_value['size'] ) ) ? intval( $input_value['size'] ) : intval( $option['value']['size'] ) : false,
|
113 |
'family' => ( ! empty( $components['family'] ) ) ? ( isset( $input_value['family'] ) ) ? $input_value['family'] : $option['value']['family'] : false,
|
114 |
'style' => ( ! empty( $components['family'] ) ) ? ( isset( $input_value['style'] ) ) ? $input_value['style'] : $option['value']['style'] : false,
|
115 |
+
'color' => ( ! empty( $components['color'] ) ) ? ( isset( $input_value['color'] ) && preg_match( '/^#([a-f0-9]{3}){1,2}$/i', $input_value['color'] ) ) ? $input_value['color'] : $option['value']['color'] : false,
|
116 |
);
|
117 |
|
118 |
return $values;
|
framework/includes/option-types/wp-editor/static/scripts.js
CHANGED
@@ -1,37 +1,61 @@
|
|
1 |
(function ($, fwe) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
var init = function () {
|
4 |
var $option = $(this),
|
5 |
$textarea = $option.find('.wp-editor-area:first'),
|
6 |
-
id = $option.attr('data-fw-editor-id'),
|
7 |
$wrap = $textarea.closest('.wp-editor-wrap'),
|
8 |
-
|
9 |
-
? ($option.attr('data-mode') == 'tinymce')
|
10 |
-
: $wrap.hasClass('tmce-active'),
|
11 |
-
hasAutoP = $option.attr('data-fw-has-autop') === 'true';
|
12 |
|
13 |
/**
|
14 |
* Dynamically set tinyMCEPreInit.mceInit and tinyMCEPreInit.qtInit
|
15 |
* based on the data-fw-mce-settings and data-fw-qt-settings
|
16 |
*/
|
17 |
-
|
18 |
-
|
19 |
|
20 |
-
|
21 |
-
tinyMCEPreInit.qtInit[ id ] = qtSettings;
|
22 |
-
|
23 |
-
/**
|
24 |
-
* Set width
|
25 |
-
*/
|
26 |
$option.closest('.fw-backend-option-input-type-wp-editor').addClass(
|
27 |
'width-type-'+ ($option.attr('data-size') == 'large' ? 'full' : 'fixed')
|
28 |
);
|
29 |
|
30 |
-
|
31 |
-
|
32 |
-
* http://stackoverflow.com/a/21519323/1794248
|
33 |
-
*/
|
34 |
-
if (mceSettings) {
|
35 |
if (typeof tinyMCEPreInit.mceInit[ id ] == 'undefined') {
|
36 |
console.error('Can\'t find "'+ id +'" in tinyMCEPreInit.mceInit');
|
37 |
return;
|
@@ -40,6 +64,8 @@
|
|
40 |
tinymce.execCommand('mceRemoveEditor', false, id);
|
41 |
|
42 |
tinyMCEPreInit.mceInit[ id ].setup = function(ed) {
|
|
|
|
|
43 |
ed.once('init', function (e) {
|
44 |
var editor = e.target,
|
45 |
id = editor.id;
|
@@ -49,10 +75,13 @@
|
|
49 |
$textarea.trigger('change'); // fixes https://github.com/ThemeFuse/Unyson/issues/2273
|
50 |
});
|
51 |
|
52 |
-
|
53 |
-
* Fixes when wpautop is false
|
54 |
-
*/
|
55 |
if (!editor.getParam('wpautop')) {
|
|
|
|
|
|
|
|
|
|
|
56 |
editor
|
57 |
.on('SaveContent', function(event){
|
58 |
// Remove <p> in Visual mode
|
@@ -68,32 +97,23 @@
|
|
68 |
});
|
69 |
}
|
70 |
|
71 |
-
|
72 |
-
* Quick Tags
|
73 |
-
* http://stackoverflow.com/a/21519323/1794248
|
74 |
-
*/
|
75 |
-
if (tinyMCEPreInit.qtInit[ id ]) {
|
76 |
-
new QTags( tinyMCEPreInit.qtInit[ id ] );
|
77 |
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
if (!hasAutoP && !visualMode) {
|
87 |
-
$textarea.val(wp.editor.removep(editor.getContent()));
|
88 |
}
|
89 |
}
|
|
|
|
|
90 |
});
|
91 |
};
|
92 |
|
93 |
-
if (!tinyMCEPreInit.mceInit[ id ].wpautop) {
|
94 |
-
$textarea.val( wp.editor.autop($textarea.val()) );
|
95 |
-
}
|
96 |
-
|
97 |
try {
|
98 |
tinymce.init( tinyMCEPreInit.mceInit[ id ] );
|
99 |
|
@@ -122,15 +142,7 @@
|
|
122 |
}
|
123 |
}
|
124 |
} else {
|
125 |
-
|
126 |
-
* Quick Tags
|
127 |
-
* http://stackoverflow.com/a/21519323/1794248
|
128 |
-
*/
|
129 |
-
if (tinyMCEPreInit.qtInit[ id ]) {
|
130 |
-
new QTags( tinyMCEPreInit.qtInit[ id ] );
|
131 |
-
|
132 |
-
QTags._buttonsInit();
|
133 |
-
}
|
134 |
}
|
135 |
};
|
136 |
|
1 |
(function ($, fwe) {
|
2 |
+
var activeVisualMode = {},
|
3 |
+
/**
|
4 |
+
* Quick Tags
|
5 |
+
* http://stackoverflow.com/a/21519323/1794248
|
6 |
+
*/
|
7 |
+
qTagsInit = function (id, $option, $wrap, $textarea, editor) {
|
8 |
+
if (!tinyMCEPreInit.qtInit[ id ]) {
|
9 |
+
return;
|
10 |
+
}
|
11 |
+
|
12 |
+
new QTags( tinyMCEPreInit.qtInit[ id ] );
|
13 |
+
|
14 |
+
QTags._buttonsInit();
|
15 |
+
|
16 |
+
if ($wrap.hasClass('html-active')) { // fixes glitch on init
|
17 |
+
$wrap.find('.switch-html:first').trigger('click');
|
18 |
+
}
|
19 |
+
|
20 |
+
var visualMode = (typeof activeVisualMode[ id ] != 'undefined')
|
21 |
+
? activeVisualMode[ id ]
|
22 |
+
: (
|
23 |
+
(typeof $option.attr('data-mode') != 'undefined')
|
24 |
+
? ($option.attr('data-mode') == 'tinymce')
|
25 |
+
: $wrap.hasClass('tmce-active')
|
26 |
+
);
|
27 |
+
|
28 |
+
$wrap.on('click', '.wp-switch-editor', function () {
|
29 |
+
activeVisualMode[ id ] = $(this).hasClass('switch-tmce');
|
30 |
+
});
|
31 |
+
|
32 |
+
$wrap.find('.switch-'+ (visualMode ? 'tmce' : 'html') +':first').trigger('click');
|
33 |
+
|
34 |
+
if (editor && !visualMode) {
|
35 |
+
$textarea.val(wp.editor.removep(editor.getContent()));
|
36 |
+
}
|
37 |
+
};
|
38 |
|
39 |
var init = function () {
|
40 |
var $option = $(this),
|
41 |
$textarea = $option.find('.wp-editor-area:first'),
|
|
|
42 |
$wrap = $textarea.closest('.wp-editor-wrap'),
|
43 |
+
id = $option.attr('data-fw-editor-id');
|
|
|
|
|
|
|
44 |
|
45 |
/**
|
46 |
* Dynamically set tinyMCEPreInit.mceInit and tinyMCEPreInit.qtInit
|
47 |
* based on the data-fw-mce-settings and data-fw-qt-settings
|
48 |
*/
|
49 |
+
tinyMCEPreInit.mceInit[ id ] = JSON.parse($option.attr('data-fw-mce-settings'));
|
50 |
+
tinyMCEPreInit.qtInit[ id ] = JSON.parse($option.attr('data-fw-qt-settings'));
|
51 |
|
52 |
+
// Set width
|
|
|
|
|
|
|
|
|
|
|
53 |
$option.closest('.fw-backend-option-input-type-wp-editor').addClass(
|
54 |
'width-type-'+ ($option.attr('data-size') == 'large' ? 'full' : 'fixed')
|
55 |
);
|
56 |
|
57 |
+
// TinyMCE Editor http://stackoverflow.com/a/21519323/1794248
|
58 |
+
if (tinyMCEPreInit.mceInit[ id ]) {
|
|
|
|
|
|
|
59 |
if (typeof tinyMCEPreInit.mceInit[ id ] == 'undefined') {
|
60 |
console.error('Can\'t find "'+ id +'" in tinyMCEPreInit.mceInit');
|
61 |
return;
|
64 |
tinymce.execCommand('mceRemoveEditor', false, id);
|
65 |
|
66 |
tinyMCEPreInit.mceInit[ id ].setup = function(ed) {
|
67 |
+
var initialContent = $textarea.val(); // before \r\n were replaced
|
68 |
+
|
69 |
ed.once('init', function (e) {
|
70 |
var editor = e.target,
|
71 |
id = editor.id;
|
75 |
$textarea.trigger('change'); // fixes https://github.com/ThemeFuse/Unyson/issues/2273
|
76 |
});
|
77 |
|
78 |
+
// Fixes when wpautop is false
|
|
|
|
|
79 |
if (!editor.getParam('wpautop')) {
|
80 |
+
if (initialContent.indexOf('<p>') !== -1) {
|
81 |
+
initialContent = wp.editor.removep(initialContent);
|
82 |
+
}
|
83 |
+
editor.setContent(initialContent.replace(/\r?\n/g, '<br />'));
|
84 |
+
|
85 |
editor
|
86 |
.on('SaveContent', function(event){
|
87 |
// Remove <p> in Visual mode
|
97 |
});
|
98 |
}
|
99 |
|
100 |
+
qTagsInit(id, $option, $wrap, $textarea, editor);
|
|
|
|
|
|
|
|
|
|
|
101 |
|
102 |
+
if (!editor.getParam('wpautop') && $wrap.hasClass('tmce-active')) {
|
103 |
+
/**
|
104 |
+
* fixes: when initialContent is with <p>
|
105 |
+
* if no changes are made in editor the <p> are not removed
|
106 |
+
*/
|
107 |
+
{
|
108 |
+
$wrap.find('.switch-html:first').trigger('click');
|
109 |
+
$wrap.find('.switch-tmce:first').trigger('click');
|
|
|
|
|
110 |
}
|
111 |
}
|
112 |
+
|
113 |
+
initialContent = null; // free memory
|
114 |
});
|
115 |
};
|
116 |
|
|
|
|
|
|
|
|
|
117 |
try {
|
118 |
tinymce.init( tinyMCEPreInit.mceInit[ id ] );
|
119 |
|
142 |
}
|
143 |
}
|
144 |
} else {
|
145 |
+
qTagsInit(id, $option, $wrap, $textarea);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
146 |
}
|
147 |
};
|
148 |
|
framework/manifest.php
CHANGED
@@ -4,4 +4,4 @@ $manifest = array();
|
|
4 |
|
5 |
$manifest['name'] = __('Unyson', 'fw');
|
6 |
|
7 |
-
$manifest['version'] = '2.6.
|
4 |
|
5 |
$manifest['name'] = __('Unyson', 'fw');
|
6 |
|
7 |
+
$manifest['version'] = '2.6.15';
|
readme.txt
CHANGED
@@ -3,7 +3,7 @@ Contributors: unyson
|
|
3 |
Tags: page builder, shortcodes, backup, seo, breadcrumbs, portfolio, framework
|
4 |
Requires at least: 4.4
|
5 |
Tested up to: 4.7
|
6 |
-
Stable tag: 2.6.
|
7 |
License: GPLv2 or later
|
8 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
9 |
|
@@ -22,6 +22,7 @@ A simple and easy way to build a powerful website.
|
|
22 |
**Features include:**
|
23 |
|
24 |
* **Drag & Drop Page Builder.** Create countless pages using the content and media shortcodes.
|
|
|
25 |
* **Sliders.** To make you life even easier we have already built in 3 of them that support images and videos.
|
26 |
* **Mega Menu.** User-friendly drop down menu that will let you easily create highly customized menu configurations.
|
27 |
* **Sidebars.** This module will let your users customize WordPress pages with dynamic sidebars.
|
@@ -31,7 +32,6 @@ A simple and easy way to build a powerful website.
|
|
31 |
* **SEO.** SEO settings at finger tips without installing further plugins.
|
32 |
* **Feedback.** We've added a way for users to submit reviews and ratings for events, projects, etc.
|
33 |
* **Events.** It's pretty simple to use and it has Calendar and Map shortcodes.
|
34 |
-
* **Backup & Demo Content.** Create an automated backup schedule, import demo content or even create a demo content archive for migration purposes.
|
35 |
|
36 |
**Get involved**
|
37 |
|
@@ -85,6 +85,9 @@ Yes; Unyson will work with any theme.
|
|
85 |
|
86 |
== Changelog ==
|
87 |
|
|
|
|
|
|
|
88 |
= 2.6.14 =
|
89 |
* Fixed infinite loop when php memory limit is `-1`
|
90 |
* Minor changes
|
3 |
Tags: page builder, shortcodes, backup, seo, breadcrumbs, portfolio, framework
|
4 |
Requires at least: 4.4
|
5 |
Tested up to: 4.7
|
6 |
+
Stable tag: 2.6.15
|
7 |
License: GPLv2 or later
|
8 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
9 |
|
22 |
**Features include:**
|
23 |
|
24 |
* **Drag & Drop Page Builder.** Create countless pages using the content and media shortcodes.
|
25 |
+
* **Backup & Demo Content.** Create an automated backup schedule, import demo content or even create a demo content archive for migration purposes.
|
26 |
* **Sliders.** To make you life even easier we have already built in 3 of them that support images and videos.
|
27 |
* **Mega Menu.** User-friendly drop down menu that will let you easily create highly customized menu configurations.
|
28 |
* **Sidebars.** This module will let your users customize WordPress pages with dynamic sidebars.
|
32 |
* **SEO.** SEO settings at finger tips without installing further plugins.
|
33 |
* **Feedback.** We've added a way for users to submit reviews and ratings for events, projects, etc.
|
34 |
* **Events.** It's pretty simple to use and it has Calendar and Map shortcodes.
|
|
|
35 |
|
36 |
**Get involved**
|
37 |
|
85 |
|
86 |
== Changelog ==
|
87 |
|
88 |
+
= 2.6.15 =
|
89 |
+
* Fixed [#2380](https://github.com/ThemeFuse/Unyson/issues/2380), [#2397](https://github.com/ThemeFuse/Unyson/issues/2397), [#2212](https://github.com/ThemeFuse/Unyson/issues/2212#issuecomment-278954170)
|
90 |
+
|
91 |
= 2.6.14 =
|
92 |
* Fixed infinite loop when php memory limit is `-1`
|
93 |
* Minor changes
|
unyson.php
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
* Plugin Name: Unyson
|
4 |
* Plugin URI: http://unyson.io/
|
5 |
* Description: A free drag & drop framework that comes with a bunch of built in extensions that will help you develop premium themes fast & easy.
|
6 |
-
* Version: 2.6.
|
7 |
* Author: ThemeFuse
|
8 |
* Author URI: http://themefuse.com
|
9 |
* License: GPL2+
|
3 |
* Plugin Name: Unyson
|
4 |
* Plugin URI: http://unyson.io/
|
5 |
* Description: A free drag & drop framework that comes with a bunch of built in extensions that will help you develop premium themes fast & easy.
|
6 |
+
* Version: 2.6.15
|
7 |
* Author: ThemeFuse
|
8 |
* Author URI: http://themefuse.com
|
9 |
* License: GPL2+
|