Changeset 1975248
- Timestamp:
- 11/16/2018 02:31:06 AM (7 years ago)
- Location:
- allears/trunk
- Files:
-
- 1 added
- 5 edited
-
allears-html-meta.php (modified) (4 diffs)
-
allears-options.php (modified) (12 diffs)
-
allears-shortcode-aetag.php (added)
-
allears-shortcode.php (modified) (9 diffs)
-
allears.php (modified) (3 diffs)
-
readme.txt (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
allears/trunk/allears-html-meta.php
r1964307 r1975248 9 9 // We render the widget only when displaying a single post/page. If this is 10 10 // a multi-post view, then we suppress rendering the widget 11 AllEarsUtils:: add_comment($src_trail . ": rendering suppressed for non-single view");11 AllEarsUtils::emit_comment($src_trail . ": rendering suppressed for non-single view"); 12 12 return false; 13 13 } … … 17 17 18 18 private static function renderOne($name, $content) { 19 return "<meta name='" . $name . "' content='" . esc_attr($content) . "'> ";19 return "<meta name='" . $name . "' content='" . esc_attr($content) . "'>\n"; 20 20 } 21 21 … … 31 31 } 32 32 33 // AllEarsUtils:: add_comment("AllEarsHtmlMeta::render: starting processing for post ID = " . $curr_post_id);33 // AllEarsUtils::emit_comment("AllEarsHtmlMeta::render: starting processing for post ID = " . $curr_post_id); 34 34 35 $bgaudio = AllEarsPostMeta::get_bgaudio_info($curr_post_id); 36 $lang = AllEarsPostMeta::get_lang($curr_post_id); 37 $voice = AllEarsPostMeta::get_voice($curr_post_id); 35 38 $aec = AllEarsPostMeta::get_aec_url($curr_post_id); 36 39 37 40 $output = ""; 41 42 if($bgaudio != null) { 43 $output .= self::renderOne("ae:bgAudioUrl", $bgaudio["url"]); 44 if(isset($bgaudio["gain"])) { 45 $output .= self::renderOne("ae:bgAudioGain", $bgaudio["gain"]); 46 } 47 // These strings are already escaped, but better safe than sorry... 48 if(isset($bgaudio["title"])) { 49 $output .= AllEarsUtils::format_comment("bgAudioTitle: " .$bgaudio["title"]); 50 } 51 if(isset($bgaudio["attribution"])) { 52 $output .= AllEarsUtils::format_comment("bgAudioAttribution: " .$bgaudio["attribution"]); 53 } 54 } 55 56 if($lang !== "") { 57 $output .= self::renderOne("ae:language", $lang); 58 } 59 60 if($voice !== "") { 61 $output .= self::renderOne("ae:voice", $voice); 62 } 38 63 39 64 if($aec !== "") { … … 44 69 if($curr_title !== "") { 45 70 $output .= self::renderOne("ae:title", $curr_title); 71 } 72 73 // "c" is the date format string for ISO-8601 dates (added in PHP5?) 74 // http://php.net/manual/en/function.date.php 75 $date_published = get_the_date("c"); 76 if($date_published !== "") { 77 $output .= self::renderOne("ae:datePublished", $date_published); 78 } 79 80 $date_modified = get_the_modified_date("c"); 81 if($date_modified !== "") { 82 $output .= self::renderOne("ae:dateModified", $date_modified); 83 } 84 85 // See https://developer.wordpress.org/reference/functions/get_feed_link/ 86 // There's also https://developer.wordpress.org/reference/functions/get_default_feed/ 87 $curr_feed = get_feed_link(); 88 if($curr_feed !== "") { 89 // Attribute "data-aec-title" is optional, and it's not used here 90 $output .= self::renderOne("ae:feed", $curr_feed); 46 91 } 47 92 -
allears/trunk/allears-options.php
r1964307 r1975248 39 39 "db_id" => self::DB_ID_WIDGET, 40 40 "db_key_id" => "default_atts", 41 "help" => "Set the default attributes to be automatically added to all <em>[allears-widget]</em> shortcodes on all posts.<br >" .41 "help" => "Set the default attributes to be automatically added to all <em>[allears-widget]</em> shortcodes on all posts.<br />" . 42 42 "This setting applies to shortcodes added explicitly as well as shortcodes added automatically. " . 43 43 "Use the same syntax you use for the shortcode attributes. " . … … 59 59 ); 60 60 61 // OBSOLETE62 // const FIELD_DEF_WIDGET_AUTO_PARAMS = array(63 // "id" => "allEars_field_widget_auto_params",64 // "label" => "Widget parameters",65 // "render_fn" => array("AllEarsOptions", "render_widget_default_atts"),66 // "validation_fn" => array("AllEarsOptions", "validate_widget_default_atts"),67 // "sanitize_fn" => array("AllEarsOptions", "sanitize_widget_default_atts"),68 // "section_id" => self::SECTION_ID_WIDGET,69 // // "id" and all tha follows are added to $args of "render_fn" by AllEarsOptions::add_settings_field()70 // "db_id" => self::DB_ID_WIDGET,71 // "db_key_id" => "auto_params",72 // "help" => "Set the parameters to be used when the widget is added automatically to a post. " .73 // "This setting does not apply to shortcodes added explicitly to a post. ".74 // "Use the syntax of the shortcode parameters."75 // );76 77 61 const WIDGET_FIELDS = array( 78 62 self::FIELD_DEF_WIDGET_KEY, 79 63 self::FIELD_DEF_WIDGET_DEFAULT_ATTS, 80 64 self::FIELD_DEF_WIDGET_AUTO, 81 // self::FIELD_DEF_WIDGET_AUTO_PARAMS82 65 ); 83 66 … … 118 101 return self::get_db_value(self::DB_ID_WIDGET, self::FIELD_DEF_WIDGET_AUTO["db_key_id"]); 119 102 } 120 121 // public static function get_widget_auto_params() {122 // return self::get_db_value(self::DB_ID_WIDGET, self::FIELD_DEF_WIDGET_AUTO_PARAMS["db_key_id"]);123 // }124 103 125 104 public static function section_widget_text() { … … 152 131 </p> 153 132 <?php 154 AllEarsUtils:: add_comment(json_encode(get_option(self::DB_ID_WIDGET)));133 AllEarsUtils::emit_comment(json_encode(get_option(self::DB_ID_WIDGET))); 155 134 } 156 135 … … 189 168 public static function validate_widget_key($value) { 190 169 // $value should already be sanitized 191 if($value === "" || preg_match("/^ [a-z0-9]{20}$/i", $value)) {170 if($value === "" || preg_match("/^([a-z0-9]{5})+$/i", $value)) { 192 171 // Allow the empty string, to allow users to unset the widget key. 193 172 return null; … … 284 263 285 264 private static function render_help($args) { 286 if($args["help"] != "") { 287 echo "<p class='howto'>{$args["help"]}</p>"; 265 if(isset($args["help"])) { 266 # We don't want to escape the help string, it must allow HTML tags... 267 #$escaped_help = esc_attr($args["help"]); 268 $help = $args['help']; 269 echo "<span class='howto' style='margin-left: 0.2rem;'>$help</span>"; 288 270 } 289 271 } … … 450 432 const BOX_ID = "allEars-post-box"; 451 433 452 const INPUT_ID_AEC = "allEars-meta-aec"; 453 const INPUT_ID_DEBUG = "allEars-meta-debug"; 454 const INPUT_ID_DISABLE_AUTO = "allEars-meta-disable-auto"; 455 const META_KEY_AEC = "allEars_aec"; 456 const META_KEY_DEBUG = "allEars_debug"; 457 const META_KEY_DISABLE_AUTO = "allEars_disable_auto"; 434 // This is just a marker to be used in conjunction with any "FIELD_DEF_" 435 // that uses "render_section" 436 const FIELD_DEF_SECTION_END = array(); 437 438 const FIELD_DEF_BGAUDIO_SECTION = array( 439 // "__section" is a special marker. 440 "id" => "__section", 441 "label" => "Background audio", 442 "render_fn" => array("AllEarsPostMeta", "render_section"), 443 ); 444 445 const FIELD_DEF_BGAUDIO_URL = array( 446 "id" => "allEars-meta-bgaudio-url", 447 "label" => "URL", 448 "render_fn" => array("AllEarsPostMeta", "render_text"), 449 "save_fn" => array("AllEarsPostMeta", "save_url"), 450 "meta_key_id" => "allEars_bgaudio_url", 451 ); 452 453 const FIELD_DEF_BGAUDIO_PLAYER = array( 454 "render_fn" => array("AllEarsPostMeta", "render_audio_player"), 455 ); 456 457 const FIELD_DEF_BGAUDIO_GAIN = array( 458 "id" => "allEars-meta-bgaudio-gain", 459 "label" => "Gain", 460 "help" => "A number between 0 and 1", 461 "render_fn" => array("AllEarsPostMeta", "render_text"), 462 "save_fn" => array("AllEarsPostMeta", "save_text"), 463 "meta_key_id" => "allEars_bgaudio_gain", 464 ); 465 466 const FIELD_DEF_BGAUDIO_TITLE = array( 467 "id" => "allEars-meta-bgaudio-title", 468 "label" => "Title", 469 "render_fn" => array("AllEarsPostMeta", "render_text"), 470 "save_fn" => array("AllEarsPostMeta", "save_text"), 471 "meta_key_id" => "allEars_bgaudio_title", 472 ); 473 474 const FIELD_DEF_BGAUDIO_ATTRIBUTION = array( 475 "id" => "allEars-meta-bgaudio-attribution", 476 "label" => "Attribution", 477 "render_fn" => array("AllEarsPostMeta", "render_text"), 478 "save_fn" => array("AllEarsPostMeta", "save_text"), 479 "meta_key_id" => "allEars_bgaudio_attribution", 480 ); 481 482 const FIELD_DEF_LANG = array( 483 "id" => "allEars-meta-lang", 484 "label" => "Language", 485 "help" => "E.g. \"fr\" or \"en-GB\"; use the format of the <html> tag's \"lang\" attribute (BCP47)", 486 "render_fn" => array("AllEarsPostMeta", "render_text"), 487 "save_fn" => array("AllEarsPostMeta", "save_text"), 488 "meta_key_id" => "allEars_lang", 489 ); 490 491 const FIELD_DEF_VOICE = array( 492 "id" => "allEars-meta-voice", 493 "label" => "Voice", 494 "help" => "Note: the \"alt\" voices are not available for all languages", 495 "render_fn" => array("AllEarsPostMeta", "render_voice"), 496 "save_fn" => array("AllEarsPostMeta", "save_text"), 497 "meta_key_id" => "allEars_voice", 498 ); 499 500 const FIELD_DEF_AEC = array( 501 "id" => "allEars-meta-aec", 502 "label" => "AEC URL", 503 "render_fn" => array("AllEarsPostMeta", "render_text"), 504 "save_fn" => array("AllEarsPostMeta", "save_url"), 505 "meta_key_id" => "allEars_aec", 506 ); 507 508 const FIELD_DEF_WIDGET_SECTION = array( 509 "id" => "__section", 510 "label" => "Widget", 511 "render_fn" => array("AllEarsPostMeta", "render_section"), 512 ); 513 514 // This field is just printing a warning, no DB value to manage 515 const FIELD_DEF_WIDGET_KEY_MISSING = array( 516 "render_fn" => array("AllEarsPostMeta", "render_widget_key_missing"), 517 ); 518 519 const FIELD_DEF_DISABLE_AUTO = array( 520 "id" => "allEars-meta-disable-auto", 521 "label" => "No auto-widget on this post", 522 "render_fn" => array("AllEarsPostMeta", "render_widget_disable_auto"), 523 "save_fn" => array("AllEarsPostMeta", "save_widget_disable_auto"), 524 "meta_key_id" => "allEars_disable_auto", 525 ); 526 527 const FIELD_DEF_DEBUG = array( 528 "id" => "allEars-meta-debug", 529 "label" => "Console logging", 530 "render_fn" => array("AllEarsPostMeta", "render_checkbox"), 531 "save_fn" => array("AllEarsPostMeta", "save_checkbox"), 532 "meta_key_id" => "allEars_debug", 533 ); 534 535 const METABOX_FIELDS = array( 536 self::FIELD_DEF_VOICE, 537 self::FIELD_DEF_LANG, 538 self::FIELD_DEF_BGAUDIO_SECTION, 539 self::FIELD_DEF_BGAUDIO_URL, 540 self::FIELD_DEF_BGAUDIO_PLAYER, 541 self::FIELD_DEF_BGAUDIO_GAIN, 542 self::FIELD_DEF_BGAUDIO_TITLE, 543 self::FIELD_DEF_BGAUDIO_ATTRIBUTION, 544 self::FIELD_DEF_SECTION_END, 545 self::FIELD_DEF_AEC, 546 self::FIELD_DEF_WIDGET_SECTION, 547 self::FIELD_DEF_WIDGET_KEY_MISSING, 548 self::FIELD_DEF_DISABLE_AUTO, 549 self::FIELD_DEF_DEBUG, 550 self::FIELD_DEF_SECTION_END, 551 ); 458 552 459 553 private static function is_nonce_valid() { … … 480 574 // Note that get_post_meta() uses the empty string to mean "key not found", so it's 481 575 // safe to do the same here, we use the empty string to mean "no value". 482 return (isset($_POST[$input_id]) ? sanitize_ html_class($_POST[$input_id]) : "" );576 return (isset($_POST[$input_id]) ? sanitize_text_field($_POST[$input_id]) : "" ); 483 577 } 484 578 … … 533 627 534 628 public static function dump_post_meta($post_id) { 535 AllEarsUtils::add_comment("dump_post_meta(post_id = " . $post_id . "): " . json_encode(get_post_meta($post_id))); 629 AllEarsUtils::dbg("dump_post_meta(post_id = " . $post_id . "): " . json_encode(get_post_meta($post_id))); 630 } 631 632 public static function get_bgaudio_info($post_id) { 633 $ret_val = array( 634 "url" => esc_attr(get_post_meta($post_id, self::FIELD_DEF_BGAUDIO_URL["meta_key_id"], true)), 635 "gain" => esc_attr(get_post_meta($post_id, self::FIELD_DEF_BGAUDIO_GAIN["meta_key_id"], true)), 636 "title" => esc_attr(get_post_meta($post_id, self::FIELD_DEF_BGAUDIO_TITLE["meta_key_id"], true)), 637 "attribution" => esc_attr(get_post_meta($post_id, self::FIELD_DEF_BGAUDIO_ATTRIBUTION["meta_key_id"], true)), 638 ); 639 640 // In the loose equality check, "null" and empty string are equivalent to "false" 641 if($ret_val["url"] == false) { 642 return null; 643 } 644 return $ret_val; 645 } 646 647 public static function get_lang($post_id) { 648 return esc_attr(get_post_meta($post_id, self::FIELD_DEF_LANG["meta_key_id"], true)); 649 } 650 651 public static function get_voice($post_id) { 652 return esc_attr(get_post_meta($post_id, self::FIELD_DEF_VOICE["meta_key_id"], true)); 536 653 } 537 654 538 655 public static function get_aec_url($post_id) { 539 return get_post_meta($post_id, self::META_KEY_AEC, true);540 } 541 542 p ublic static function get_debug($post_id) {656 return esc_attr(get_post_meta($post_id, self::FIELD_DEF_AEC["meta_key_id"], true)); 657 } 658 659 private static function get_db_checkbox_value($post_id, $meta_key) { 543 660 // It's stored as a string in the database (see get_box_checkbox_value()). 544 661 // We're converting it to a boolean here... 545 if(get_post_meta($post_id, self::META_KEY_DEBUG, true) === "") {662 if(get_post_meta($post_id, $meta_key, true) === "") { 546 663 return false; 547 664 } … … 549 666 } 550 667 668 public static function get_debug($post_id) { 669 return self::get_db_checkbox_value($post_id, self::FIELD_DEF_DEBUG["meta_key_id"]); 670 } 671 551 672 public static function get_disable_auto($post_id) { 552 // It's stored as a string in the database (see get_box_checkbox_value()). 553 // We're converting it to a boolean here... 554 if(get_post_meta($post_id, self::META_KEY_DISABLE_AUTO, true) === "") { 555 return false; 556 } 557 return true; 673 return self::get_db_checkbox_value($post_id, self::FIELD_DEF_DISABLE_AUTO["meta_key_id"]); 674 } 675 676 private static function save_url($post_id, $args) { 677 $html_id = $args["id"]; 678 $meta_key_id = $args["meta_key_id"]; 679 680 $url = self::get_box_url_value($html_id); 681 682 if($url !== false) { 683 self::update_meta($post_id, $meta_key_id, $url); 684 } // Else silently discard the invalid value 685 } 686 687 private static function save_text($post_id, $args) { 688 $html_id = $args["id"]; 689 $meta_key_id = $args["meta_key_id"]; 690 691 $text = self::get_box_text_value($html_id); 692 693 if($text !== false) { 694 self::update_meta($post_id, $meta_key_id, $text); 695 } // Else silently discard the invalid value 696 } 697 698 private static function save_checkbox($post_id, $args) { 699 $html_id = $args["id"]; 700 $meta_key_id = $args["meta_key_id"]; 701 702 $checkbox_value = self::get_box_checkbox_value($html_id); 703 self::update_meta($post_id, $meta_key_id, $checkbox_value); 704 } 705 706 private static function save_widget_disable_auto($post_id, $args) { 707 if(AllEarsOptions::is_widget_auto()) { 708 // Since we display this option only when the site-wide "auto" flag is on, 709 // we can't try to save the value unless "auto" is on as well. 710 self::save_checkbox($post_id, $args); 711 } 558 712 } 559 713 … … 576 730 // Note that get_post_meta() uses the empty string to mean "key not found", so it's 577 731 // safe to do the same here, we use the empty string to mean "no value". 578 $box_meta_aec = self::get_box_url_value(self::INPUT_ID_AEC); 579 $box_meta_debug = self::get_box_checkbox_value(self::INPUT_ID_DEBUG); 580 581 if($box_meta_aec !== false) { 582 self::update_meta($post_id, self::META_KEY_AEC, $box_meta_aec); 583 } // Else silently discard the invalid value 584 585 self::update_meta($post_id, self::META_KEY_DEBUG, $box_meta_debug); 586 732 foreach(self::METABOX_FIELDS as $def) { 733 if(isset($def["save_fn"])) { 734 call_user_func($def["save_fn"], $post_id, $def); 735 } 736 } 737 } 738 739 // Duplicated from AllEarsOptions, we'll need to consolidate later. 740 private static function render_help($args) { 741 if(isset($args["help"])) { 742 # We don't want to escape the help string, it must allow HTML tags... 743 #$escaped_help = esc_attr($args["help"]); 744 $help = $args['help']; 745 echo "<span class='howto' style='margin-left: 0.2rem;'>$help</span>"; 746 } 747 } 748 749 private static function render_section($post, $args, $end=false) { 750 $label = $args["label"]; 751 if(!$end) { 752 echo "<strong>$label</strong>\n"; 753 echo "<div style='padding: 0 0.5rem; border: 1px solid rgba(0,0,0,.125);'>\n"; 754 } else { 755 echo "</div>\n"; 756 } 757 } 758 759 private static function render_text($post, $args) { 760 $html_id = $args["id"]; 761 $meta_key_id = $args["meta_key_id"]; 762 $label = $args["label"]; 763 764 $db_value = esc_attr(get_post_meta($post->ID, $meta_key_id, true)); 765 766 echo "<p>\n"; 767 echo "<label for='$html_id'>$label</label><br />\n"; 768 echo "<input class='widefat' type='text' name='$html_id' id='$html_id' value='$db_value' size='30' />\n"; 769 self::render_help($args); 770 echo "</p>\n"; 771 } 772 773 private static function render_checkbox($post, $args) { 774 $html_id = $args["id"]; 775 $meta_key_id = $args["meta_key_id"]; 776 $label = $args["label"]; 777 778 $db_value = self::get_db_checkbox_value($post->ID, $meta_key_id); 779 $checked = ($db_value) ? "checked" : ""; 780 781 echo "<p>\n"; 782 echo "<input id='{$html_id}' name='{$html_id}' type='checkbox' {$checked} />\n"; 783 echo "<label for='{$html_id}'>$label</label>\n"; 784 self::render_help($args); 785 echo "</p>\n"; 786 } 787 788 private static function render_dropdown($post, $args, $values) { 789 $html_id = $args["id"]; 790 $meta_key_id = $args["meta_key_id"]; 791 $label = $args["label"]; 792 793 $db_value = esc_attr(get_post_meta($post->ID, $meta_key_id, true)); 794 795 echo "<p>\n"; 796 echo "<label for='$html_id'>$label</label><br />\n"; 797 echo "<select class='widefat' id='$html_id' name='$html_id'>\n"; 798 foreach($values as $key => $value) { 799 if($key == "_none") { 800 $key = ""; 801 } 802 // This should work also for the empty key... 803 if($key == $db_value) { 804 $selected = " selected"; 805 } else { 806 $selected = ""; 807 } 808 echo "<option value='$key'$selected>$value</option>\n"; 809 } 810 echo "</select>\n"; 811 self::render_help($args); 812 echo "</p>\n"; 813 } 814 815 private static function render_voice($post, $args) { 816 self::render_dropdown($post, $args, array( 817 // "_none" is a special marker for the empty option. 818 "_none" => "[ Use player default ]", 819 "m0" => "Male", 820 "m1" => "Male (alt)", 821 "f0" => "Female", 822 "f1" => "Female (alt)", 823 )); 824 } 825 826 private static function render_audio_player($post, $args) { 827 $url = esc_attr(get_post_meta($post->ID, self::FIELD_DEF_BGAUDIO_URL["meta_key_id"], true)); 828 if($url != false) { 829 echo "<audio controls src='$url'></audio>\n"; 830 } 831 } 832 833 private static function render_widget_key_missing($post, $args) { 834 $widget_key = AllEarsOptions::get_widget_key(); 835 if($widget_key === "") { 836 $submenu_url = menu_page_url(AllEarsOptions::PAGE_SLUG , false); 837 echo "<p class='howto'>Warning: the following settings are going to be ignored while no widget key is set. " . 838 "Go to <a href='" . $submenu_url . "'>Settings->allEars</a> to set your widget key.</p>"; 839 } 840 } 841 842 private static function render_widget_disable_auto($post, $args) { 587 843 if(AllEarsOptions::is_widget_auto()) { 588 // Since we display this option only when the site-wide "auto" flag is on, 589 // we can't try to save the value unless "auto" is on as well. 590 $box_meta_disable_auto = self::get_box_checkbox_value(self::INPUT_ID_DISABLE_AUTO); 591 self::update_meta($post_id, self::META_KEY_DISABLE_AUTO, $box_meta_disable_auto); 844 self::render_checkbox($post, $args); 592 845 } 593 846 } … … 596 849 public static function render_box($post) { 597 850 wp_nonce_field(basename( __FILE__ ), self::NONCE); 598 $aec = esc_attr(self::get_aec_url($post->ID)); 599 $debug = self::get_debug($post->ID); 600 $debug_checked = ($debug) ? "checked" : ""; 601 602 $widget_key = AllEarsOptions::get_widget_key(); 603 $widget_warning = "<br />"; 604 if($widget_key === "") { 605 $submenu_url = menu_page_url(AllEarsOptions::PAGE_SLUG , false); 606 $widget_warning = "<p class='howto'>Warning: the following settings are going to be ignored while no widget key is set. ". 607 "Go to <a href='" . $submenu_url . "'>Settings->allEars</a> to set your widget key.</p>"; 608 } 609 610 ?> 611 <div> 612 <label for="<?= self::INPUT_ID_AEC ?>">AEC URL</label> 613 <br /> 614 <input class="widefat" type="text" name="<?= self::INPUT_ID_AEC ?>" id="<?= self::INPUT_ID_AEC ?>" value="<?= $aec ?>" size="30" /> 615 <br /> 616 <?= $widget_warning ?> 617 <?php 618 if(AllEarsOptions::is_widget_auto()) { 619 $disable_auto = self::get_disable_auto($post->ID); 620 $disable_auto_checked = ($disable_auto) ? "checked" : ""; 621 ?> 622 <input type="checkbox" name="<?= self::INPUT_ID_DISABLE_AUTO ?>" id="<?= self::INPUT_ID_DISABLE_AUTO ?>" <?= $disable_auto_checked ?> /> 623 <label for="<?= self::INPUT_ID_DISABLE_AUTO ?>">No auto-widget on this post</label> 624 <br /> 625 <br /> 626 <?php 851 852 $last_section_def = null; 853 foreach(self::METABOX_FIELDS as $def) { 854 if($def === self::FIELD_DEF_SECTION_END) { 855 if($last_section_def != null) { 856 self::render_section($post, $last_section_def, true); 857 $last_section_def = null; 858 } else { 859 // Ignore it if there wasn't anything open, getting here means a bug. 860 } 861 continue; 627 862 } 628 ?> 629 <input type="checkbox" name="<?= self::INPUT_ID_DEBUG ?>" id="<?= self::INPUT_ID_DEBUG ?>" <?= $debug_checked ?> /> 630 <label for="<?= self::INPUT_ID_DEBUG ?>">Console logging</label> 631 </div> 632 <?php 863 864 if(isset($def["render_fn"])) { 865 if($def["id"] == "__section") { 866 // We can "auto close" a previous section even without FIELD_DEF_SECTION_END, 867 // as long as we don't want to support nested sections. 868 if($last_section_def != null) { 869 call_user_func($last_section_def["render_fn"], $post, $last_section_def, true); 870 } 871 $last_section_def = $def; 872 } 873 874 call_user_func($def["render_fn"], $post, $def); 875 } 876 } 633 877 } 634 878 -
allears/trunk/allears-shortcode.php
r1964307 r1975248 34 34 // a multi-post view, then we suppress rendering the widget 35 35 if($src_trail != null) { 36 AllEarsUtils:: add_comment($src_trail . ": rendering suppressed for non-single view");36 AllEarsUtils::emit_comment($src_trail . ": rendering suppressed for non-single view"); 37 37 } 38 38 return false; … … 43 43 if($widget_key === "") { 44 44 if($src_trail != null) { 45 AllEarsUtils:: add_comment($src_trail . ": can't render without a widget key");45 AllEarsUtils::emit_comment($src_trail . ": can't render without a widget key"); 46 46 } 47 47 return false; … … 71 71 } 72 72 if(count($invalid_key_list) > 0 && $emit_invalid) { 73 AllEarsUtils:: add_comment("shortcode_atts_remove_invalid(): invalid attributes discarded: " . json_encode($invalid_key_list));73 AllEarsUtils::emit_comment("shortcode_atts_remove_invalid(): invalid attributes discarded: " . json_encode($invalid_key_list)); 74 74 } 75 75 return $ret_val; … … 138 138 foreach($shortcode_atts as $key => $value) { 139 139 if(isset($map[$key]) && $value !== "") { 140 $ret_val .= " " . $map[$key] . " =\"" . $value . "\"";140 $ret_val .= " " . $map[$key] . "=\"" . $value . "\""; 141 141 } 142 142 } … … 169 169 } 170 170 $debug = AllEarsPostMeta::get_debug($curr_post_id); 171 $log_msg = "debug = " . json_encode($debug) . " orig = " . json_encode(get_post_meta($curr_post_id, AllEarsPostMeta::META_KEY_DEBUG, true)); 172 AllEarsUtils::add_comment($log_msg); 171 $log_msg = "debug = " . json_encode($debug) . ", orig = " . 172 json_encode(get_post_meta($curr_post_id, AllEarsPostMeta::FIELD_DEF_DEBUG["meta_key_id"], true)); 173 AllEarsUtils::emit_comment($log_msg); 173 174 if($debug === true) { 174 175 $data_debug = " data-debug"; … … 180 181 $tag = "<script" . $data_container_id . $data_key . $data_url . $data_debug . $script_atts . 181 182 " type='text/javascript' src='" . $src . "'></script>"; 182 //AllEarsPostMeta::dump_post_meta($curr_post_id);183 AllEarsPostMeta::dump_post_meta($curr_post_id); 183 184 } 184 185 return $tag; … … 206 207 } 207 208 209 // Pass any number of string arguments to get concatenated in output 210 private static function wrap_content() { 211 // Get the argument list as an array 212 $args_array = func_get_args(); 213 214 $start_aetag = AllEarsTag::render("soa"); 215 $end_aetag = AllEarsTag::render("eoa"); 216 217 return $start_aetag . implode($args_array) . $end_aetag; 218 } 219 208 220 public static function prepend_widget_to_content($content) { 209 221 if(!(self::can_render(null) && AllEarsOptions::is_widget_auto())) { … … 212 224 213 225 $curr_post_id = get_the_ID(); 226 214 227 if(AllEarsPostMeta::get_disable_auto($curr_post_id)) { 215 228 // "auto" is on, but this post has been explicitly excluded. 216 return "<!-- allEars: auto-widget explicitly disabled for this post -->" . $content;229 return self::wrap_content("<!-- allEars: auto-widget explicitly disabled for this post -->", $content); 217 230 } 218 231 … … 222 235 if(has_shortcode($post->post_content, self::SHORTCODE_LABEL)) { 223 236 // The post has an explicit shortcode, skip this, we don't want to have two widgets. 224 return $content;225 } 226 return self::w idget_shortcode(AllEarsOptions::get_widget_default_atts()) . $content;237 return self::wrap_content($content); 238 } 239 return self::wrap_content(self::widget_shortcode(AllEarsOptions::get_widget_default_atts()), $content); 227 240 } 228 241 -
allears/trunk/allears.php
r1964307 r1975248 6 6 Author: allEars 7 7 Author URI: https://getallears.com/about 8 Version: 1. 0.08 Version: 1.1.0 9 9 License: Apache License, Version 2.0 10 10 License URI: http://www.apache.org/licenses/LICENSE-2.0 … … 16 16 require_once(ALLEARS_PLUGIN_DIR . "allears-html-meta.php"); 17 17 require_once(ALLEARS_PLUGIN_DIR . "allears-shortcode.php"); 18 require_once(ALLEARS_PLUGIN_DIR . "allears-shortcode-aetag.php"); 18 19 19 20 function allEars_admin_init(){ … … 23 24 const ALLEARS_GLOBALS = array( 24 25 // These variables are filled by the build script 25 "version" => "1. 0.0",26 "version" => "1.1.0", 26 27 "debug" => false, 27 28 "env" => "prod", 28 "buildid" => "P HA-5RI",29 "buildid" => "PI9-LFO", 29 30 "support_email" => "support@getallears.com" 30 31 ); 31 32 32 33 class AllEarsUtils { 34 // Surprisingly WordPress does not have the next function. If you need to emit 35 // HTML comments, the only thing you care about is to avoid anything that looks 36 // like a comment closing "-->". As far as I understand it, WordPress has a lot 37 // of functions to cleanup strings: 38 // https://codex.wordpress.org/Validating_Sanitizing_and_Escaping_User_Data 39 // But you either get functions that only touch "<" ("sanitize*"), or functions 40 // that replace too much (including double quotes, all the "esc*"). So we need 41 // to add this simple function ourselves... 42 public static function sanitize_comment($text) { 43 return str_replace(">", ">", $text); 44 } 45 33 46 public static function dbg($text) { 34 47 if(ALLEARS_GLOBALS["debug"]) { 35 echo "<!-- allEars-debug - " . $text . " -->";48 echo "<!-- allEars-debug - " . self::sanitize_comment($text) . " -->\n"; 36 49 } 37 50 } 38 51 39 public static function add_comment($text) { 40 echo "<!-- allEars - " . $text . " -->"; 52 public static function format_comment($text) { 53 return "<!-- allEars: " . self::sanitize_comment($text) . " -->\n"; 54 } 55 56 public static function emit_comment($text) { 57 echo self::format_comment($text); 41 58 } 42 59 } -
allears/trunk/readme.txt
r1964307 r1975248 1 1 === allEars === 2 2 Contributors: marcodb 3 Tags: allEars, audio, voice, read, tts, text-to-speech4 Stable tag: 1. 0.03 Tags: text-to-speech, tts, speech, audio, RSS feed, podcast, 4 Stable tag: 1.1.0 5 5 Tested up to: 4.9.8 6 6 Requires at least: 4.0 7 7 License: Apache License, Version 2.0 8 8 9 Read aloud your blog posts. 9 Bring your blog to life by adding an audio channel for your readers. 10 10 11 11 12 == Description == 12 13 13 Read aloud the contents of your posts.14 15 This plugin enables your posts to include the allEars widget. For more information, visit https://getallears.com/about.14 The allEars player lets your audience listen to your blog posts, and streams all your content using a playlist fed via RSS. 15 Use this plugin to embed the allEars player into your posts, or use it to add more depth to your allEars audio channel. This plugin allows you to tune your content to sound more like a podcast: add sound effects, a background soundtrack, or use multiple voices. Keep on reading to find out all you can do to enhance the audio version of your blog with this plugin. 16 For more information about allEars, visit https://getallears.com/about. 16 17 17 18 18 = Site configuration =19 = Using the plugin = 19 20 20 Set the widget key on the settings page (Settings->allEars) to start using the widget. 21 When you enable this plugin, you'll see an "allEars" meta box in your post editor. The meta box helps you add some useful properties to your post: 21 22 22 You can choose to automatically show the allEars widget on all posts (after the post title), or to 23 add the [allears-widget] shortcode individually on the posts where you want to use the widget. 23 * **Voice**: choose if the main voice reading the post should be a male or a female voice. You can alternate multiple voices in a post by inserting the *[aetag voice]* shortcode wherever you want the voice to change. 24 24 25 This page also includes the option to specify default values for the [allears-widget] shortcode. 26 Use the same syntax you use for shortcode attributes (see below). If you specify an invalid attribute, 27 it will be automatically purged when you save. You can override any of these attributes in the 28 shortcodes of individual posts. Note however that WordPress will ignore empty attributes, so 29 in order to create an override, the attribute value must not be the empty string (""). 25 * **Language**: tell the allEars player the main language of the post. You can have the post read with different accents by using language codes like "en-US", "en-GB" or "en-AU". If your post includes foreign words or sentences, you can tag them using the *[aetag lang]* shortcode, and the allEars player will do its best to pronounce them as they should. 30 26 31 = Post/page configuration = 27 * **Background audio**: specify the URL of a soundtrack to play while your content is being read, and tune its volume. Your background music can do wonders when you add pauses between sentences, using the *[aetag p]* shortcode in your text. 32 28 33 On each post and page, you'll see an allEars meta box with some per-post/page configuration items. 34 * "AEC URL": (optional) set it to tell the widget and the webapp to use the native AEC URL instead of the post/page URL. 35 Note that this field is validated as a URL, and if validation fails, the update will be ignored (unfortunately 36 with no feedback to the user for now). 37 * "No auto-widget on this post": (only visible if "Widget on all posts" is turned on in the site configuration) 38 allows you to have per-post control, to disable the auto-widget on a specific post. 39 * "Console logging": (optional) enable logging of the widget loader. Only needed to troubleshoot. 29 In addition, you can use the following shortcodes in your text: 40 30 41 If you have "Widget on all posts" enabled, and add the shortcode to your content, only the shortcode in your 42 content will be rendered, you don't need to select "No auto-widget on this post" in this case. 31 * **[aetag fga]**: (for "foreground audio") to insert audio recordings or sound effects 32 * **[aetag sub]**: (for "substitute") to instruct the allEars player to read something differently from what's written on the page (like "Read Colorado when I write CO") 33 * **[aetag ignore]**: useful if there's a section you'd rather want to stay on the written page, but not be read aloud. 43 34 44 = Shortcode examples = 35 If you choose to have the allEars widget on your posts, it can be added automatically to the top of each post, or you can control its exact location on the page using the shortcode *[allears-widget]* 45 36 46 [allears-widget maxwidth="40rem"]47 37 48 Options (all options are case-insensitive): 49 * "width": (optional) the width of the widget container, expressed in any acceptable CSS form. The 50 minimum width should never be below 80px. If it becomes less than 80px, all buttons disappear. 51 * "maxwidth": (optional) the max-width of the widget container, expressed in any acceptable CSS form. 52 * "height": (optional) the height of the widget container, expressed in any acceptable CSS form. 53 * "style": (optional) CSS styles to assign to the widget container. 54 * "class": (optional) CSS classes to assign to the widget container. 55 * "widgetstyle": (optional) use a preset style for the widget. You can choose among: 56 - "docked" (the default), the widget is placed on the page where the shortcode is located, with 57 standard formatting. 58 - "sticky-top", the widget is placed on the page where the shortcode is located, with standard 59 formatting, but stays visible once the reader scrolls below that position. 60 - "none", the widget is placed on the page where the shortcode is located, and no formatting is 61 applied to it. This is useful if you need to use the "style" and/or "class" attributes to 62 define your own widget layout. When you choose "none", attributes "width", "maxwidth" and "height" 63 are also ignored. 38 == Installation == 64 39 65 = Notes = 40 Install the allEars plugin on your blog, then activate it, and you're good to go! Most features don't require a key, but you'll need one in order to use the embedded allEars player on your blog posts. The details to request a widget key can be found in the allEars configuration page, under Settings->allEars of your WordPress installation. 66 41 67 * The widget is rendered only when viewing individual posts.68 42 69 * If the widget is disabled (no widget key), or if no shortcode is set on any 70 post, this plugin will still set some meta tags to help the allEars app better 71 understand the content. 43 == Frequently Asked Questions == 44 45 = Can I control the layout of the allEars widget? = 46 The allEars widget shortcode takes a number of options. These options can be specified with the shortcode, or added as default for the whole site under Settings->allEars. The available options are: 47 48 * **width**: the width of the widget container, expressed in any acceptable CSS form. The minimum width should never be below 80px. 49 * **maxwidth**: the max-width of the widget container, expressed in any acceptable CSS form. 50 * **height**: the height of the widget container, expressed in any acceptable CSS form. 51 * **style**: CSS styles to assign to the widget container. 52 * **class**: CSS classes to assign to the widget container. 53 * **widgetstyle**: use a preset style for the widget. You can choose among: 54 55 - *docked*: (the default) the widget is placed on the page where the shortcode is located, with standard formatting. 56 - *sticky-top*: the widget is placed on the page where the shortcode is located, with standard formatting, but stays visible once the reader scrolls below that position. 57 - *none*: the widget is placed on the page where the shortcode is located, and no formatting is applied to it. This is useful if you need to use the *style* and/or *class* attributes to define your own widget layout. When you choose *none*, attributes *width*, *maxwidth* and *height* are also ignored. 58 59 = How do I add sound effects to my post?= 60 Use the *[aetag fga]* shortcode. The shortcode takes the following options: 61 62 * **href**: (required) the URL of the recorded sound 63 * **pausebefore**: a pause to be added before starting sound playback, in seconds (e.g. "0.5") 64 * **pauseafter**: a pause to be added at the end of sound playback, before resuming the text. In seconds. 65 * **title**: the title of the sound 66 * **attribution**: any additional information about the sound 67 68 *title* and *attribution* are not used by the allEars player, but they're available for compliance with royalty-free audio licenses. 69 70 = Where can I find royalty-free sound effects?= 71 Royalty-free sound effects are readily available for download on websites like [soundbible.com](http://soundbible.com/royalty-free-sounds-1.html) or [audioblocks.com](https://www.audioblocks.com/royalty-free-audio/sound-effects). 72 73 = I've used the *[aetag fga]* but I see an error = 74 The allEars web player and the allEars widget operate under the "getallears.com" domain. When you add an audio file to your site, you need to make sure "getallears.com" is allowed to download the file (by your site's CORS configuration). If your CORS configuration is incomplete, the allEars player might not be allowed to download the audio file. You can confirm this by checking your browser's console. On the Chrome browser, the error will look like this: 75 76 *Access to XMLHttpRequest at '<url>' from origin 'https://getallears.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.* 77 78 = Can I control how text is being read? = 79 You can use the *[aetag sub]*, *[aetag lang]* and *[aetag as]* to control reading pronunciation. These shortcode require one extra parameter, and they enclose the text they need to control. 80 81 *Examples* 82 83 * **[aetag sub "Colorado"]CO[/aetag]**: read the word "Colorado", but show the word "CO" on the webpage and in the allEars player text captions. 84 * **[aetag as "verb"]attribute[/aetag]**: use the "verb" form of the word "attribute" (different stress than the noun). The valid options for the parameter of *[aetag as]* are *verb*, *past* (for words that have different pronunciations in past tense) or *alt* (for words that have multiple pronunciations). 85 * **[aetag lang "it"]Piazza Navona[/aetag]**: pronounce the text in italian, regardless of the language of the rest of the post. 86 72 87 73 88 == Changelog == 74 89 90 = 1.1.0 = 91 *Release Date - 11/15/2018* 92 93 * New per-post configuration 94 - Voice (including alternate male/female voices) 95 - Language 96 - Background audio (URL, gain, title and attribution) 97 * Added new shortcode "[aetag]" to allow embedding of any AEC tag in HTML ("aetags") 98 - Initially supporting the following aetags for end users: 99 * fga, p, voice, sub, ipa, lang, as, ignore 100 101 75 102 = 1.0.0 = 103 *Release Date - 10/27/2018* 104 76 105 * Initial release
Note: See TracChangeset
for help on using the changeset viewer.