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

908 lines 21.0 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 $this->button_css = $css;
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 $name = str_replace('%', '', $name);
368
369 $classes = array("maxbutton-" . $this->id,
370 "maxbutton");
371
372
373 if ($name != '')
374 $classes[] = "maxbutton-" . $name;
375
376 $classes = implode(' ', $classes);
377
378 $domObj = new simple_html_dom();
379 $domObj->load('<a class="' . $classes . '"></a>');
380
381 foreach($this->blocks as $block)
382 {
383 $domObj = $block->parse_button($domObj, $mode);
384 }
385
386
387 $domObj->load($domObj->save());
388
389 $cssParser = $this->getCSSParser();
390 $cssParser->loadDom($domObj);
391
392 return $domObj;
393 }
394
395 /* Display all data and html to allow users to edit button settings */
396 public function admin_fields()
397 {
398 foreach($this->blocks as $block)
399 {
400 $block->admin_fields();
401 $block->display_fields();
402 }
403 //do_action('mb-admin-fields' );
404
405 }
406
407 protected function post_parse_button($domObj, $mode)
408 {
409 foreach($this->blocks as $block)
410 {
411 $domObj = $block->post_parse_button($domObj, $mode);
412 $domObj->load($domObj->save());
413 }
414
415 }
416
417 /* Display the button */
418 public function display($args = array() )
419 {
420 maxUtils::startTime('button-display-'. $this->id);
421 $defaults = array(
422 "mode" => 'normal',
423 "preview_part" => "full",
424 "echo" => true,
425 "load_css" => "footer", // control how css is loaded.
426 "compile" => false, // possibility to force recompile if needed.
427 );
428 $output = ''; // init output;
429
430 $args = wp_parse_args($args, $defaults);
431
432 $cssParser = $this->getCSSParser(); // init parser
433
434 $this->load_css = $args["load_css"];
435
436 if ($this->id == 0) // if button doesn't exists don't display unless in editor
437 {
438 if (! $args["mode"] == 'editor' )
439 return;
440
441 $this->clear();
442
443 $this->data["id"] = 0;
444 //do_action('mb-data-load', $data);
445 }
446
447 $mode = (isset($args["mode"])) ? $args["mode"] : "normal";
448 switch($mode)
449 {
450 case "preview":
451 $preview = true;
452 $compile = false;
453 break;
454 case "editor":
455 $preview = true;
456 $compile = true;
457 // editor is both compile and preview.
458 break;
459 break;
460 case "normal":
461 $preview = false;
462 $compile = false;
463 break;
464 }
465
466
467 // Apply filters for general data override
468 $this->data = apply_filters('mb/button/data_before_display', $this->data, $mode, array('preview' => $preview, 'compile' => $compile) ); // hooks
469
470 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.
471 $compile = true;
472
473 }
474 else
475 $compile = false;
476
477 // reload the data into the blocks, might have been altered by shortcode, filters etc.
478
479 foreach($this->blocks as $block)
480 {
481 $block->set($this->data);
482 }
483
484 // create the button
485 $domObj = $this->parse_button($mode);
486
487 // create CSS
488 maxUtils::startTime('button-parse-css-'. $this->id);
489 $this->parse_css($mode, $compile);
490 maxUtils::endTime('button-parse-css-'. $this->id);
491
492 // operation after creating CSS model. Mainly adding extra classes
493 $this->post_parse_button($domObj, $mode);
494
495
496 if (! $preview) // no js on previews
497 $this->parse_js($mode);
498
499 if ($preview) // mark it preview
500 {
501
502 $domObj->find('a',0)->class .= ' maxbutton-preview';
503 }
504
505 if ($preview && $args["preview_part"] != 'full')
506 {
507
508 if ($args["preview_part"] != 'normal')
509 {
510 $domObj->find('a',0)->class .= ' hover';
511 $domObj = $cssParser->outputInline($domObj,'hover');
512
513 }
514 else
515 {
516 $domObj->find('a',0)->class .= ' normal';
517 $domObj = $cssParser->outputInline($domObj);
518 }
519
520 }
521 elseif ($this->load_css == 'footer')
522 {
523 $css = $this->display_css(false, false);
524
525 do_action('mb-footer',$this->id, $css);
526
527 if (! $preview)
528 {
529 $js = $this->display_js(false, true);
530 do_action('mb-footer', $this->document_id, $js, 'js');
531 }
532 } elseif ($this->load_css == 'inline')
533 {
534 if ($args["echo"])
535 $this->display_css();
536 else
537 $output .= $this->display_css(false);
538 }
539 elseif ($this->load_css == 'element') // not possible to load both normal and hover to an element.
540 {
541 $domObj->find('a',0)->class .= ' normal';
542 $domObj = $this->cssParser->outputInline($domObj);
543 }
544
545
546 $output .= $domObj->save();
547
548 $output = apply_filters('mb-before-button-output', $output);
549 maxButtons::buttonDone(array("button_id" => $this->id, "document_id" => $this->document_id) );
550
551 maxUtils::endTime('button-display-'. $this->id);
552
553 if ($args["echo"])
554 echo $output;
555 else
556 return $output;
557
558 }
559 /* Function used to map field id's to display for Frontend Javascript
560
561 This function bundles all defined fields into a json encoded variable. This is used for the frontend javascript functions in the
562 administrator area like colorpickers and real-time updating of the button preview
563 */
564 public function display_field_map()
565 {
566 $map = array();
567 foreach($this->blocks as $block)
568 {
569 $map = $block->map_fields($map);
570 }
571
572 echo "<script language='javascript'>";
573 echo "var buttonFieldMap = '" . json_encode($map) . "';" ;
574 echo "</script>";
575
576 }
577
578 /* Write parsed CSS to output.
579 @param echo Default true. When true, outputs directly, otherwise returns output string
580 @param style_tag Default true. When true, outputs a html <style> tags around the output.
581 */
582 public function display_css($echo = true, $style_tag = true)
583 {
584 $output = '';
585
586 if ($style_tag)
587 $output .= "<style type='text/css'>";
588
589 $output .= $this->parsed_css;
590
591 if ($style_tag)
592 $output .= "</style>";
593
594
595 if ($echo) echo $output;
596 else return $output;
597
598 }
599
600 /* Output Parsed Javascripting */
601 public function display_js($echo = true, $tag = true)
602 {
603 $output = '';
604
605 if (count($this->button_js) == 0)
606 return; // no output, holiday
607
608 if ($tag)
609 {
610 $output .= "<script type='text/javascript'> ";
611 $output .= " if (typeof maxButton" . $this->document_id . " == 'undefined') { ";
612 $output .= " function maxButton" . $this->document_id . "() { ";
613 }
614
615 foreach($this->button_js as $index => $code)
616 {
617 $output .= $code;
618
619 }
620
621 if ($tag)
622 {
623 $output .= " }
624 }
625 window.onload = maxButton" . $this->document_id . "();
626 </script> ";
627 }
628
629 if ($echo) echo $output;
630 else return $output;
631 }
632
633
634 /* Makes a copy of the current buttons.
635
636 The button to be copied -must- be loaded and set
637 */
638 function copy()
639 {
640 $this->id = 0;
641 $data = $this->data;
642 $data["name"] = $this->name;
643
644 return $this->update($data);
645 }
646 /* Change the publication status of the button.
647
648 */
649 function setStatus($status = "publish")
650 {
651 $data = $this->data;
652 $data["status"] = sanitize_text_field($status);
653
654 return $this->update($data);
655
656 }
657 /* Remove the button from database */
658 public function delete($id)
659 {
660 global $wpdb;
661 $wpdb->query($wpdb->prepare("DELETE FROM " . maxUtils::get_table_name() . " WHERE id = %d", $id));
662 }
663
664 /* Save changes to the button
665
666 Updates or saves the button. Existing buttons must load their data and be set -first- or lose all not-passed data.
667
668 @param post Post data in field - value format (flat $_POST array)
669 @param boolean savedb if false do not save to database
670 */
671 public function save($post, $savedb = true)
672 {
673 $post = stripslashes_deep($post); // don't multiply slashes please.
674 $data = $this->data;
675
676 foreach($this->blocks as $block)
677 {
678 $data = $block->save_fields($data, $post);
679 }
680
681 if (! $savedb ) return $data;
682 return $this->update($data); // save to db.
683
684 }
685
686 /* Updates the button data to the database. Adds a button if it doesn't exist */
687 public function update($data)
688 {
689 global $wpdb;
690 $return = false;
691
692 $fields = array();
693 foreach($this->blocks as $block)
694 {
695 $block_name = $block->get_name();
696
697 if (isset($data[$block_name]))
698 {
699 $blockData = $data[$block_name];
700 $fields[$block_name] = json_encode($blockData);
701 }
702 }
703 if (isset($data["name"])) { // other fields.
704 $fields["name"] = $data["name"];
705 }
706 if (isset($data["status"])) {
707 $fields["status"] = $data["status"];
708 }
709
710
711 $where = array('id' => $this->id );
712 if ($this->id > 0)
713 {
714 $where = array('id' => $this->id);
715 $where_format = array('%d');
716 $result = $wpdb->update(maxUtils::get_table_name(), $fields, $where, null, $where_format);
717 $return = true;
718 }
719 else
720 {
721 $fields['created'] = current_time('mysql',1);
722
723 $result = $wpdb->insert(maxUtils::get_table_name(), $fields);
724 $id = $wpdb->insert_id;
725
726 $this->id = $id;
727 $return = $id;
728
729 }
730
731
732 if ($result === false)
733 {
734 $error = "Database error " . $wpdb->last_error;
735 MB()->add_notice('error', $error);
736 $install = MB()->getClass("install");
737 $install::create_database_table(); // run dbdelta to try and fix.
738
739 }
740
741 // update the cache
742 $this->cache = ''; // empty cache
743 $result = $this->set($this->id); // set the newest values
744
745 if (! $result ) return false;
746
747 $this->display(array("echo" => false, "load_css" => "element")); // do display routing to compile.
748 $css = $this->parsed_css;
749 $this->update_cache($css);
750
751 return $return;
752 }
753
754 /* Updates the CSS cache. */
755 public function update_cache($css)
756 {
757 $return = false;
758 global $wpdb;
759
760 if ($this->id > 0)
761 {
762 $fields = array("cache" => $css, 'updated' => $this->updated); // try not to update timestamp on recache.
763 $where = array('id' => $this->id);
764 $where_format = array('%d');
765 $wpdb->update(maxUtils::get_table_name(), $fields, $where, null, $where_format);
766 $return = true;
767
768 }
769 return $return;
770 }
771
772 // Resets all of the button caches.
773 public function reset_cache()
774 {
775 global $wpdb;
776 $fields = array("cache" => null, 'updated' => 'updated');
777 $where = array(1 => 1);
778 //$where_format = array('%d');
779 $sql = "UPDATE " . maxUtils::get_table_name() . " SET cache = NULL ";
780 $wpdb->query($sql);
781
782 }
783
784
785 /* Display button via shortcode
786
787 Function that accepts WP shortcode arguments and displays or returns a button
788
789 @param $atts array Shortcode Atts
790 @return string HTML presentation of button
791
792 */
793 public function shortcode($button_atts)
794 {
795 $atts = shortcode_atts(array(
796 'id' => null,
797 'name' => null,
798 'text' => null,
799 'url' => null,
800 'linktitle' => null,
801 'window' => null,
802 'nofollow' => null,
803 'nocache' => null,
804 'extraclass' => null,
805 'style' => 'footer',
806
807 ), $button_atts, 'maxbutton');
808
809 $button_id = $atts['id'];
810 $button_name = $atts['name'];
811
812 if (! is_null($button_id) && $button_id > 0)
813 $result = $this->set($button_id);
814 elseif (! is_null($button_name))
815 $result = $this->set(0, $button_name);
816 else return; // no button id or name given
817
818 /* Shortcode cache control
819
820 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!
821
822 @param boolean $nocache True / False
823 */
824 $compile = apply_filters("mb/shortcode/nocache", $atts['nocache'] );
825
826 if (! $result)
827 return; // button doesn't exist
828
829 // If we're not in the admin and the button is in the trash, just return nothing
830 if (!is_admin() && $this->status == 'trash') {
831 return;
832 }
833
834 // Override shortcode options comparing to default button data.
835 //$overrides = false;
836
837 if (! is_null($atts['text']) && $atts['text'] !== '')
838 {
839 $this->data["text"]["text"] = $atts['text'];
840 }
841
842 if (! is_null($atts['url']) && $atts['url'] !== '')
843 {
844 $this->data["basic"]["url"] = $atts['url'];
845 }
846
847 if (! is_null($atts['window']) )
848 {
849 if ($atts['window'] == 'new' )
850 $this->data["basic"]["new_window"] = 1;
851 elseif ($atts['window'] == 'same')
852 $this->data["basic"]["new_window"] = 0;
853 }
854
855 if (! is_null($atts['nofollow']) )
856 {
857 if ($atts['nofollow'] == 'true')
858 $this->data["basic"]["nofollow"] = 1;
859 elseif ($atts['nofollow'] == 'false')
860 $this->data["basic"]["nofollow"] = 0;
861 }
862
863 if (! is_null($atts['linktitle']) )
864 {
865 $this->data['basic']['link_title'] = $atts['linktitle'];
866 }
867
868 if (! is_null($atts['extraclass']))
869 {
870
871 $this->data['advanced']['extra_classes'] .= ' ' . $atts['extraclass'];
872 }
873
874 switch($atts['style'])
875 {
876 case "inline":
877 $load_css = 'inline';
878 break;
879 default:
880 $load_css = 'footer';
881 break;
882 }
883
884 // allow for more flexible changes and data manipulation.
885 $this->data = $this->shortcode_overrides($this->data, $button_atts);
886 $this->data = apply_filters('mb/shortcode/data', $this->data, $atts);
887
888 // if there are no reasons not to display; display
889 $args = array("echo" => false,
890 "load_css" => $load_css,
891 "compile" => $compile,
892 );
893 $args = apply_filters('mb_shortcode_display_args', $args);
894
895 $output = $this->display($args);
896
897 return $output;
898
899 }
900
901
902 public function shortcode_overrides($data, $atts)
903 {
904 return $data;
905 }
906
907 } // class
908