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

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