PluginProbe
MaxButtons – Create buttons / 6.7
MaxButtons – Create buttons v6.7
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 6.7, at classes/button.php

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