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

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