PluginProbe
MaxButtons – Create buttons / 7.11
MaxButtons – Create buttons v7.11
6.23 6.24 6.25 6.26 6.26.1 6.27 6.28 6.3 6.4 6.5 6.6 6.7 6.8 6.9 7.0 7.1 7.1.1 7.1.2 7.1.3 7.10 7.11 7.13 7.13.1 7.13.2 7.13.3 All 100 releases
maxbuttons / classes / button.php

button.php in MaxButtons – Create buttons 7.11, at classes/button.php

911 lines 21.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace MaxButtons;
3 defined('ABSPATH') or die('No direct access permitted');
4
5 /* Datamodel and base functionality for a button
6
7 */
8
9 use \simple_html_dom as simple_html_dom;
10
11 class maxButton
12 {
13 protected $id = 0;
14 protected $document_id = 0; // an id that's not duplicated when there are multiple buttons on the same page. Preferably reliable as well.
15 protected $name = '';
16 protected $status = '';
17 protected $description = '';
18 protected $cache = '';
19 protected $updated = 0;
20
21 protected $button_loaded = false;
22
23 protected $data = array();
24 protected $blocks; // Block Classes
25 protected $templates = array(); // .tpl files
26
27 protected $button_css = array();
28 protected $button_js = array();
29
30 // output conditions
31 protected $load_css = 'footer'; // [ footer, inline, external, element ]
32 protected $load_js = 'footer';
33
34 protected $cssParser = false;
35 protected $parsed_css = '';
36
37
38 /* Class constructor
39
40 Get als loads the various blocks of which a button is built up. Blocks can be added and removed using the mb-init-blocks filter
41 */
42 function __construct()
43 {
44 maxUtils::addTime("Button construct");
45
46 // the parser
47
48 // get all files from blocks map
49
50 // get all blocks classes, do init on the blocks. Init should not load anything big.
51 $this->loadBlockClasses();
52
53 }
54
55 /* Makes overriding block features possible by subclass
56
57 */
58 private function loadBlockClasses()
59 {
60
61 // set blocks to the 'block name' that survived.
62 maxUtils::addTime("Load Block classes");
63
64 $class_array = maxBlocks::getBlockClasses();
65 $classes = array_map(maxUtils::namespaceit('maxUtils::array_namespace'), $class_array );
66
67 foreach($classes as $block => $class)
68 {
69 $block = new $class();
70
71 $this->blocks[] = $block;
72 if (is_admin())
73 {
74 // used for getValue() operators in the Admin Section
75
76 maxBlocks::add($block); // block collection.
77 }
78 }
79
80
81 $this->clear(); // init
82 }
83
84 /** Simple function to retrieve loaded blocks - * Used by install class */
85 public function getBlocks()
86 {
87 return $this->blocks;
88 }
89
90 /** Get Data from Database and set variables
91 *
92 * You can pass either id or name to this function
93 *
94 * @return Boolean Returns false when no data was found using either ID or name
95 */
96 function set($id = 0, $name = '', $status = 'publish')
97 {
98 maxUtils::addTime("Button set $id");
99
100 $id = intval($id);
101 $name = sanitize_text_field($name);
102 $status = sanitize_text_field($status);
103
104 global $wpdb;
105 $this->clear(); // clear the internals of any previous work
106
107 // check to see if the value passed is NOT numeric. If it is, use title, else assume numeric
108 if($id == 0 && $name != '') {
109 $row = $wpdb->get_row($wpdb->prepare("SELECT * FROM " . maxUtils::get_table_name() . " WHERE name = '%s' and status ='%s'", trim($name), $status ), ARRAY_A);
110
111 } else {
112 $row = $wpdb->get_row($wpdb->prepare("SELECT * FROM " . maxUtils::get_table_name() . " WHERE id = %d and status ='%s'", $id, $status), ARRAY_A);
113 }
114
115 if (! is_array($row)) // no db row results in null / false
116 {
117 return false;
118 }
119 elseif (count($row) == 0)
120 {
121 return false;
122 }
123
124 /* Take the id from the query, otherwise ( when button shortcode called by name ) id might not be present properly' */
125 $button_id = $row["id"];
126 maxButtons::buttonLoad(array("button_id" => $button_id)); // central registration
127
128 return $this->setupData($row);
129
130 }
131 /** Clear button settings
132 *
133 * This function prevent that generated values from previous set actions will still be present.
134 */
135 function clear()
136 {
137 unset($this->data);
138 unset($this->button_css);
139 $this->id = 0; // clear id
140 $this->button_css = array();
141 $this->button_js = array();
142 $this->data = array('id' => 0);
143 $this->data = $this->save(array(),false);
144 $this->updated = 0;
145
146 $this->cache = '';
147 $this->button_loaded = false;
148
149 foreach($this->blocks as $block)
150 {
151 $block->set($this->data); // reset blocks
152 }
153 maxBlocks::setData($this->data);
154 }
155
156 function setupData($data)
157 {
158
159 maxUtils::addTime("Button: Setup data");
160 foreach($this->blocks as $block)
161 {
162 $block_name = $block->get_name();
163
164 if (array_key_exists($block_name, $data)) // strangely isset doesn't work
165 {
166 $this->data[$block_name] = maybe_unserialize($data[$block_name]); // allow to feed unserialized stuff not from dbase
167 if (! is_array($this->data[$block_name]))
168 {
169 $this->data[$block_name] = json_decode($data[$block_name], true);
170 }
171
172 }
173 }
174
175 $this->id = $data["id"];
176
177 // Because PHP 5.3
178 $class = maxUtils::namespaceit('maxButtons');
179 $this->document_id = $class::getDocumentID(array("button_id" => $this->id));
180 $this->cache = isset($data["cache"]) ? trim($data["cache"]) : ''; // not set at button packs / non-dbase buttons!
181 $this->data["id"] = $this->id; // needed for certain blocks, to be button aware.
182 $this->data["document_id"] = $this->document_id; // bound to JS and others.
183 $this->name = $data["name"];
184 $this->status = $data["status"];
185 $this->updated = isset($data['updated']) ? $data['updated'] : 0;
186 $this->description = $this->data["basic"]["description"];
187
188 foreach($this->blocks as $block)
189 {
190 $block->set($this->data);
191 }
192
193
194 maxBlocks::setData($this->data);
195
196
197 return true;
198
199 }
200
201 function get( )
202 {
203 return $this->data;
204
205 }
206
207 public function getID()
208 {
209 return $this->id;
210 }
211
212 public function getDocumentID()
213 {
214 return $this->document_id;
215 }
216 public function getName()
217 {
218 return $this->name;
219 }
220
221 public function getDescription()
222 {
223 return $this->description;
224 }
225
226 public function getStatus()
227 {
228 return $this->status;
229 }
230
231 public function getUpdated($forDisplay = true)
232 {
233 if ($forDisplay)
234 return date_i18n( get_option( 'date_format' ), strtotime($this->updated) );
235 else
236 return strtotime($this->updated);
237 }
238
239 public function getParsedCSS()
240 {
241 return $this->parsed_css;
242 }
243
244 public function getCSSArray()
245 {
246
247 return $this->button_css;
248 }
249
250 function getCSSParser()
251 {
252 if (! $this->cssParser)
253 $this->cssParser = new maxCSSParser();
254
255 return $this->cssParser;
256 }
257 // get the cache
258 function getCache()
259 {
260 return $this->cache;
261 }
262
263 // modify the cache at own risk
264 function setCache($cache)
265 {
266 $this->cache = $cache;
267 }
268
269 /* Used by collections and import. Use sparingly. Button data is reput to blocks on display */
270 function setData($blockname, $data )
271 {
272 foreach($data as $key => $value)
273 {
274 $this->data[$blockname][$key] = $value;
275
276 }
277 }
278
279 /* Parse CSS from the elements
280
281 @param string $mode [normal,preview,editor] - The view needed.
282 @param string $forceCompile Recompile the CSS in any case
283 */
284 function parse_css($mode = "normal", $forceCompile = false )
285 {
286 $css = $this->button_css;
287
288 if (isset($this->cache) && $this->cache != '' && ! $forceCompile)
289 {
290
291 $css = $this->cache;
292 // kill media queries from cache
293 if ($mode != 'normal')
294 {
295 $pattern = "/@media.*}/is";
296 preg_match($pattern, $css, $matches);
297 $css = preg_replace($pattern, '', $css);
298 }
299
300 maxUtils::addTime("Button: Cache loaded");
301 }
302 else
303 {
304 /* Internal filter, please don't use */
305 foreach($this->blocks as $block)
306 {
307 $css = $block->parse_css($css, $mode);
308 }
309
310 /* Filter the raw CSS array before compile
311
312 This filters passes an array with all CSS elements before compile time. This should be CSS elements that can be understood by the CSS parser.
313 @since 4.20
314 @param $css CSS Array - split by element and pseudo (normal/hover)
315 */
316
317 $css = apply_filters('mb/button/rawcss', $css, $mode);
318
319 //echo "<PRE>"; print_r($css); echo "</PRE>";
320 $this->button_css = $css;
321
322 $css = $this->getCSSParser()->parse($this->button_css);
323 $css = apply_filters('mb/button/compiledcss', $css, $mode); // the final result.
324
325 if ($mode == 'normal') // only in general mode, otherwise things go amiss.
326 $this->update_cache($css);
327
328 }
329
330 $this->parsed_css = $css;
331
332
333 return $css;
334 }
335
336 /* Call blocks for javascript
337
338 Function will call for all block element to crunch the required javascript for output ( if any )
339 @param string $mode [normal, preview, editor]
340
341 */
342 function parse_js($mode = "normal")
343 {
344 maxUtils::addTime("Button :: parse JS");
345 $js = $this->button_js;
346 foreach($this->blocks as $block)
347 {
348 $js = $block->parse_js($js,$mode);
349
350 }
351
352 $this->button_js = $js;
353 }
354
355 /* Parse the actual button
356
357 Function adds the basic button components, creates the DOM object for the button and asks all block elements to parse their additions.
358
359 @param string $mode [normal, preview, editor]
360 @return Object DomObj presentation of the button
361 */
362 function parse_button($mode = 'normal')
363 {
364 $name = $this->name;
365 // non-latin breaks CSS / ID's - so move to latin.
366 $name = maxUtils::translit($name);
367 $name = sanitize_title($name);
368 $name = str_replace('%', '', $name);
369
370 $classes = array("maxbutton-" . $this->id,
371 "maxbutton");
372
373
374 if ($name != '')
375 $classes[] = "maxbutton-" . $name;
376
377 $classes = implode(' ', $classes);
378
379 $domObj = new simple_html_dom();
380 $domObj->load('<a class="' . $classes . '"></a>');
381
382 foreach($this->blocks as $block)
383 {
384 $domObj = $block->parse_button($domObj, $mode);
385 }
386
387
388 $domObj->load($domObj->save());
389
390 $cssParser = $this->getCSSParser();
391 $cssParser->loadDom($domObj);
392
393 return $domObj;
394 }
395
396 /* Display all data and html to allow users to edit button settings */
397 public function admin_fields()
398 {
399 foreach($this->blocks as $block)
400 {
401 $block->admin_fields();
402 $block->display_fields();
403 }
404 //do_action('mb-admin-fields' );
405
406 }
407
408 protected function post_parse_button($domObj, $mode)
409 {
410 foreach($this->blocks as $block)
411 {
412 $domObj = $block->post_parse_button($domObj, $mode);
413 $domObj->load($domObj->save());
414 }
415
416 }
417
418 /* Display the button */
419 public function display($args = array() )
420 {
421 maxUtils::startTime('button-display-'. $this->id);
422 $defaults = array(
423 "mode" => 'normal',
424 "preview_part" => "full",
425 "echo" => true,
426 "load_css" => "footer", // control how css is loaded.
427 "compile" => false, // possibility to force recompile if needed.
428 );
429 $output = ''; // init output;
430
431 $args = wp_parse_args($args, $defaults);
432
433 $cssParser = $this->getCSSParser(); // init parser
434
435 $this->load_css = $args["load_css"];
436
437 if ($this->id == 0) // if button doesn't exists don't display unless in editor
438 {
439 if (! $args["mode"] == 'editor' )
440 return;
441
442 $this->clear();
443
444 $this->data["id"] = 0;
445 //do_action('mb-data-load', $data);
446 }
447
448 $mode = (isset($args["mode"])) ? $args["mode"] : "normal";
449 switch($mode)
450 {
451 case "preview":
452 $preview = true;
453 $compile = false;
454 break;
455 case "editor":
456 $preview = true;
457 $compile = true;
458 // editor is both compile and preview.
459 break;
460 break;
461 case "normal":
462 $preview = false;
463 $compile = false;
464 break;
465 }
466
467
468 // Apply filters for general data override
469 $this->data = apply_filters('mb/button/data_before_display', $this->data, $mode, array('preview' => $preview, 'compile' => $compile) ); // hooks
470
471 if ( $this->load_css == "element" || $args["preview_part"] != "full" || $args["compile"] == true) { // if css output is on element, for to compile - otherwise inline styles will not be loaded.
472 $compile = true;
473
474 }
475 else
476 $compile = false;
477
478 // reload the data into the blocks, might have been altered by shortcode, filters etc.
479
480 foreach($this->blocks as $block)
481 {
482 $block->set($this->data);
483 }
484
485 // create the button
486 $domObj = $this->parse_button($mode);
487
488 // create CSS
489 maxUtils::startTime('button-parse-css-'. $this->id);
490 $this->parse_css($mode, $compile);
491 maxUtils::endTime('button-parse-css-'. $this->id);
492
493
494 // operation after creating CSS model. Mainly adding extra classes
495 $this->post_parse_button($domObj, $mode);
496
497
498 if (! $preview) // no js on previews
499 $this->parse_js($mode);
500
501 if ($preview) // mark it preview
502 {
503
504 $domObj->find('a',0)->class .= ' maxbutton-preview';
505 }
506
507 if ($preview && $args["preview_part"] != 'full')
508 {
509
510 if ($args["preview_part"] != 'normal')
511 {
512 $domObj->find('a',0)->class .= ' hover';
513 $domObj = $cssParser->outputInline($domObj,'hover');
514
515 }
516 else
517 {
518 $domObj->find('a',0)->class .= ' normal';
519 $domObj = $cssParser->outputInline($domObj);
520 }
521
522 }
523 elseif ($this->load_css == 'footer')
524 {
525 $css = $this->display_css(false, false);
526
527 do_action('mb-footer',$this->id, $css);
528
529 if (! $preview)
530 {
531 $js = $this->display_js(false, true);
532 do_action('mb-footer', $this->document_id, $js, 'js');
533 }
534 } elseif ($this->load_css == 'inline')
535 {
536 if ($args["echo"])
537 $this->display_css();
538 else
539 $output .= $this->display_css(false);
540 }
541 elseif ($this->load_css == 'element') // not possible to load both normal and hover to an element.
542 {
543 $domObj->find('a',0)->class .= ' normal';
544 $domObj = $this->cssParser->outputInline($domObj);
545 }
546
547
548 $output .= $domObj->save();
549
550 $output = apply_filters('mb-before-button-output', $output);
551 maxButtons::buttonDone(array("button_id" => $this->id, "document_id" => $this->document_id) );
552
553 maxUtils::endTime('button-display-'. $this->id);
554
555 if ($args["echo"])
556 echo $output;
557 else
558 return $output;
559
560 }
561 /* Function used to map field id's to display for Frontend Javascript
562
563 This function bundles all defined fields into a json encoded variable. This is used for the frontend javascript functions in the
564 administrator area like colorpickers and real-time updating of the button preview
565 */
566 public function display_field_map()
567 {
568 $map = array();
569 foreach($this->blocks as $block)
570 {
571 $map = $block->map_fields($map);
572 }
573
574 echo "<script language='javascript'>";
575 echo "var buttonFieldMap = '" . json_encode($map) . "';" ;
576 echo "</script>";
577
578 }
579
580 /* Write parsed CSS to output.
581 @param echo Default true. When true, outputs directly, otherwise returns output string
582 @param style_tag Default true. When true, outputs a html <style> tags around the output.
583 */
584 public function display_css($echo = true, $style_tag = true)
585 {
586 $output = '';
587
588 if ($style_tag)
589 $output .= "<style type='text/css'>";
590
591 $output .= $this->parsed_css;
592
593 if ($style_tag)
594 $output .= "</style>";
595
596
597 if ($echo) echo $output;
598 else return $output;
599
600 }
601
602 /* Output Parsed Javascripting */
603 public function display_js($echo = true, $tag = true)
604 {
605 $output = '';
606
607 if (count($this->button_js) == 0)
608 return; // no output, holiday
609
610 if ($tag)
611 {
612 $output .= "<script type='text/javascript'> ";
613 $output .= " if (typeof maxButton" . $this->document_id . " == 'undefined') { ";
614 $output .= " function maxButton" . $this->document_id . "() { ";
615 }
616
617 foreach($this->button_js as $index => $code)
618 {
619 $output .= $code;
620
621 }
622
623 if ($tag)
624 {
625 $output .= " }
626 }
627 window.onload = maxButton" . $this->document_id . "();
628 </script> ";
629 }
630
631 if ($echo) echo $output;
632 else return $output;
633 }
634
635
636 /* Makes a copy of the current buttons.
637
638 The button to be copied -must- be loaded and set
639 */
640 function copy()
641 {
642 $this->id = 0;
643 $data = $this->data;
644 $data["name"] = $this->name;
645
646 return $this->update($data);
647 }
648 /* Change the publication status of the button.
649
650 */
651 function setStatus($status = "publish")
652 {
653 $data = $this->data;
654 $data["status"] = sanitize_text_field($status);
655
656 return $this->update($data);
657
658 }
659 /* Remove the button from database */
660 public function delete($id)
661 {
662 global $wpdb;
663 $wpdb->query($wpdb->prepare("DELETE FROM " . maxUtils::get_table_name() . " WHERE id = %d", $id));
664 }
665
666 /* Save changes to the button
667
668 Updates or saves the button. Existing buttons must load their data and be set -first- or lose all not-passed data.
669
670 @param post Post data in field - value format (flat $_POST array)
671 @param boolean savedb if false do not save to database
672 */
673 public function save($post, $savedb = true)
674 {
675 $post = stripslashes_deep($post); // don't multiply slashes please.
676 $data = $this->data;
677
678 foreach($this->blocks as $block)
679 {
680 $data = $block->save_fields($data, $post);
681 }
682
683 if (! $savedb ) return $data;
684 return $this->update($data); // save to db.
685
686 }
687
688 /* Updates the button data to the database. Adds a button if it doesn't exist */
689 public function update($data)
690 {
691 global $wpdb;
692 $return = false;
693
694 $fields = array();
695 foreach($this->blocks as $block)
696 {
697 $block_name = $block->get_name();
698
699 if (isset($data[$block_name]))
700 {
701 $blockData = $data[$block_name];
702 $fields[$block_name] = json_encode($blockData);
703 }
704 }
705 if (isset($data["name"])) { // other fields.
706 $fields["name"] = $data["name"];
707 }
708 if (isset($data["status"])) {
709 $fields["status"] = $data["status"];
710 }
711
712
713 $where = array('id' => $this->id );
714 if ($this->id > 0)
715 {
716 $where = array('id' => $this->id);
717 $where_format = array('%d');
718 $result = $wpdb->update(maxUtils::get_table_name(), $fields, $where, null, $where_format);
719 $return = true;
720 }
721 else
722 {
723 $fields['created'] = current_time('mysql',1);
724
725 $result = $wpdb->insert(maxUtils::get_table_name(), $fields);
726 $id = $wpdb->insert_id;
727
728 $this->id = $id;
729 $return = $id;
730
731 }
732
733 do_action('mb/button/save', $id);
734
735 if ($result === false)
736 {
737 $error = "Database error " . $wpdb->last_error;
738 MB()->add_notice('error', $error);
739 $install = MB()->getClass("install");
740 $install::create_database_table(); // run dbdelta to try and fix.
741
742 }
743
744 // update the cache
745 $this->cache = ''; // empty cache
746 $result = $this->set($this->id); // set the newest values
747
748 if (! $result ) return false;
749
750 $this->display(array("echo" => false, "load_css" => "element")); // do display routing to compile.
751 $css = $this->parsed_css;
752 $this->update_cache($css);
753
754 return $return;
755 }
756
757 /* Updates the CSS cache. */
758 public function update_cache($css)
759 {
760 $return = false;
761 global $wpdb;
762
763 if ($this->id > 0)
764 {
765 $fields = array("cache" => $css, 'updated' => $this->updated); // try not to update timestamp on recache.
766 $where = array('id' => $this->id);
767 $where_format = array('%d');
768 $wpdb->update(maxUtils::get_table_name(), $fields, $where, null, $where_format);
769 $return = true;
770
771 }
772 return $return;
773 }
774
775 // Resets all of the button caches.
776 public function reset_cache()
777 {
778 global $wpdb;
779 $fields = array("cache" => null, 'updated' => 'updated');
780 $where = array(1 => 1);
781 //$where_format = array('%d');
782 $sql = "UPDATE " . maxUtils::get_table_name() . " SET cache = NULL ";
783 $wpdb->query($sql);
784
785 }
786
787
788 /* Display button via shortcode
789
790 Function that accepts WP shortcode arguments and displays or returns a button
791
792 @param $atts array Shortcode Atts
793 @return string HTML presentation of button
794
795 */
796 public function shortcode($button_atts)
797 {
798 $atts = shortcode_atts(array(
799 'id' => null,
800 'name' => null,
801 'text' => null,
802 'url' => null,
803 'linktitle' => null,
804 'window' => null,
805 'nofollow' => null,
806 'nocache' => null,
807 'extraclass' => null,
808 'style' => 'footer',
809
810 ), $button_atts, 'maxbutton');
811
812 $button_id = $atts['id'];
813 $button_name = $atts['name'];
814
815 if (! is_null($button_id) && $button_id > 0)
816 $result = $this->set($button_id);
817 elseif (! is_null($button_name))
818 $result = $this->set(0, $button_name);
819 else return; // no button id or name given
820
821 /* Shortcode cache control
822
823 If true the button CSS will be recompiled again. If false the plugin will check the cache for CSS declarations. Set to true if anything is interrupting the caching mechanism. Please note, recompiling causes some load times!
824
825 @param boolean $nocache True / False
826 */
827 $compile = apply_filters("mb/shortcode/nocache", $atts['nocache'] );
828
829 if (! $result)
830 return; // button doesn't exist
831
832 // If we're not in the admin and the button is in the trash, just return nothing
833 if (!is_admin() && $this->status == 'trash') {
834 return;
835 }
836
837 // Override shortcode options comparing to default button data.
838 //$overrides = false;
839
840 if (! is_null($atts['text']) && $atts['text'] !== '')
841 {
842 $this->data["text"]["text"] = $atts['text'];
843 }
844
845 if (! is_null($atts['url']) && $atts['url'] !== '')
846 {
847 $this->data["basic"]["url"] = $atts['url'];
848 }
849
850 if (! is_null($atts['window']) )
851 {
852 if ($atts['window'] == 'new' )
853 $this->data["basic"]["new_window"] = 1;
854 elseif ($atts['window'] == 'same')
855 $this->data["basic"]["new_window"] = 0;
856 }
857
858 if (! is_null($atts['nofollow']) )
859 {
860 if ($atts['nofollow'] == 'true')
861 $this->data["basic"]["nofollow"] = 1;
862 elseif ($atts['nofollow'] == 'false')
863 $this->data["basic"]["nofollow"] = 0;
864 }
865
866 if (! is_null($atts['linktitle']) )
867 {
868 $this->data['basic']['link_title'] = $atts['linktitle'];
869 }
870
871 if (! is_null($atts['extraclass']))
872 {
873
874 $this->data['advanced']['extra_classes'] .= ' ' . $atts['extraclass'];
875 }
876
877 switch($atts['style'])
878 {
879 case "inline":
880 $load_css = 'inline';
881 break;
882 default:
883 $load_css = 'footer';
884 break;
885 }
886
887 // allow for more flexible changes and data manipulation.
888 $this->data = $this->shortcode_overrides($this->data, $button_atts);
889 $this->data = apply_filters('mb/shortcode/data', $this->data, $atts);
890
891 // if there are no reasons not to display; display
892 $args = array("echo" => false,
893 "load_css" => $load_css,
894 "compile" => $compile,
895 );
896 $args = apply_filters('mb_shortcode_display_args', $args);
897
898 $output = $this->display($args);
899
900 return $output;
901
902 }
903
904
905 public function shortcode_overrides($data, $atts)
906 {
907 return $data;
908 }
909
910 } // class
911