Plugin Directory

Changeset 1975248


Ignore:
Timestamp:
11/16/2018 02:31:06 AM (7 years ago)
Author:
marcodb
Message:

Adding version 1.1.0 of the plugin

Location:
allears/trunk
Files:
1 added
5 edited

Legend:

Unmodified
Added
Removed
  • allears/trunk/allears-html-meta.php

    r1964307 r1975248  
    99            // We render the widget only when displaying a single post/page. If this is
    1010            // 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");
    1212            return false;
    1313        }
     
    1717
    1818    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";
    2020    }
    2121
     
    3131        }
    3232
    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);
    3434
     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);
    3538        $aec = AllEarsPostMeta::get_aec_url($curr_post_id);
    3639
    3740        $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        }
    3863
    3964        if($aec !== "") {
     
    4469        if($curr_title !== "") {
    4570            $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);
    4691        }
    4792
  • allears/trunk/allears-options.php

    r1964307 r1975248  
    3939        "db_id" => self::DB_ID_WIDGET,
    4040        "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 />" .
    4242                "This setting applies to shortcodes added explicitly as well as shortcodes added automatically. " .
    4343                "Use the same syntax you use for the shortcode attributes. " .
     
    5959    );
    6060
    61     // OBSOLETE
    62 //  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 
    7761    const WIDGET_FIELDS = array(
    7862        self::FIELD_DEF_WIDGET_KEY,
    7963        self::FIELD_DEF_WIDGET_DEFAULT_ATTS,
    8064        self::FIELD_DEF_WIDGET_AUTO,
    81 //      self::FIELD_DEF_WIDGET_AUTO_PARAMS
    8265    );
    8366
     
    118101        return self::get_db_value(self::DB_ID_WIDGET, self::FIELD_DEF_WIDGET_AUTO["db_key_id"]);
    119102    }
    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 //  }
    124103
    125104    public static function section_widget_text() {
     
    152131        </p>
    153132<?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)));
    155134    }
    156135
     
    189168    public static function validate_widget_key($value) {
    190169        // $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)) {
    192171            // Allow the empty string, to allow users to unset the widget key.
    193172            return null;
     
    284263
    285264    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>";
    288270        }
    289271    }
     
    450432    const BOX_ID = "allEars-post-box";
    451433
    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 &lt;html&gt; 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    );
    458552
    459553    private static function is_nonce_valid() {
     
    480574        // Note that get_post_meta() uses the empty string to mean "key not found", so it's
    481575        // 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]) : "" );
    483577    }
    484578
     
    533627
    534628    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));
    536653    }
    537654
    538655    public static function get_aec_url($post_id) {
    539         return get_post_meta($post_id, self::META_KEY_AEC, true);
    540     }
    541 
    542     public 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) {
    543660        // It's stored as a string in the database (see get_box_checkbox_value()).
    544661        // 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) === "") {
    546663            return false;
    547664        }
     
    549666    }
    550667
     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
    551672    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        }
    558712    }
    559713
     
    576730        // Note that get_post_meta() uses the empty string to mean "key not found", so it's
    577731        // 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) {
    587843        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);
    592845        }
    593846    }
     
    596849    public static function render_box($post) {
    597850        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;
    627862            }
    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        }
    633877    }
    634878
  • allears/trunk/allears-shortcode.php

    r1964307 r1975248  
    3434            // a multi-post view, then we suppress rendering the widget
    3535            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");
    3737            }
    3838            return false;
     
    4343        if($widget_key === "") {
    4444            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");
    4646            }
    4747            return false;
     
    7171        }
    7272        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));
    7474        }
    7575        return $ret_val;
     
    138138        foreach($shortcode_atts as $key => $value) {
    139139            if(isset($map[$key]) && $value !== "") {
    140                 $ret_val .= " " . $map[$key] . " = \"" . $value . "\"";
     140                $ret_val .= " " . $map[$key] . "=\"" . $value . "\"";
    141141            }
    142142        }
     
    169169                }
    170170                $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);
    173174                if($debug === true) {
    174175                    $data_debug = " data-debug";
     
    180181            $tag = "<script" . $data_container_id . $data_key . $data_url . $data_debug . $script_atts .
    181182                    " type='text/javascript' src='" . $src . "'></script>";
    182 //          AllEarsPostMeta::dump_post_meta($curr_post_id);
     183            AllEarsPostMeta::dump_post_meta($curr_post_id);
    183184        }
    184185        return $tag;
     
    206207    }
    207208
     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
    208220    public static function prepend_widget_to_content($content) {
    209221        if(!(self::can_render(null) && AllEarsOptions::is_widget_auto())) {
     
    212224
    213225        $curr_post_id = get_the_ID();
     226
    214227        if(AllEarsPostMeta::get_disable_auto($curr_post_id)) {
    215228            // "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);
    217230        }
    218231
     
    222235        if(has_shortcode($post->post_content, self::SHORTCODE_LABEL)) {
    223236            // The post has an explicit shortcode, skip this, we don't want to have two widgets.
    224             return $content;
    225         }
    226         return self::widget_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);
    227240    }
    228241
  • allears/trunk/allears.php

    r1964307 r1975248  
    66Author: allEars
    77Author URI: https://getallears.com/about
    8 Version: 1.0.0
     8Version: 1.1.0
    99License: Apache License, Version 2.0
    1010License URI: http://www.apache.org/licenses/LICENSE-2.0
     
    1616require_once(ALLEARS_PLUGIN_DIR . "allears-html-meta.php");
    1717require_once(ALLEARS_PLUGIN_DIR . "allears-shortcode.php");
     18require_once(ALLEARS_PLUGIN_DIR . "allears-shortcode-aetag.php");
    1819
    1920function allEars_admin_init(){
     
    2324const ALLEARS_GLOBALS = array(
    2425    // These variables are filled by the build script
    25     "version" => "1.0.0",
     26    "version" => "1.1.0",
    2627    "debug" => false,
    2728    "env" => "prod",
    28     "buildid" => "PHA-5RI",
     29    "buildid" => "PI9-LFO",
    2930    "support_email" => "support@getallears.com"
    3031);
    3132
    3233class 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(">", "&gt;", $text);
     44    }
     45
    3346    public static function dbg($text) {
    3447        if(ALLEARS_GLOBALS["debug"]) {
    35             echo "<!-- allEars-debug - " . $text . " -->";
     48            echo "<!-- allEars-debug - " . self::sanitize_comment($text) . " -->\n";
    3649        }
    3750    }
    3851
    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);
    4158    }
    4259}
  • allears/trunk/readme.txt

    r1964307 r1975248  
    11=== allEars ===
    22Contributors: marcodb
    3 Tags: allEars, audio, voice, read, tts, text-to-speech
    4 Stable tag: 1.0.0
     3Tags: text-to-speech, tts, speech, audio, RSS feed, podcast,
     4Stable tag: 1.1.0
    55Tested up to: 4.9.8
    66Requires at least: 4.0
    77License: Apache License, Version 2.0
    88
    9 Read aloud your blog posts.
     9Bring your blog to life by adding an audio channel for your readers.
     10
    1011
    1112== Description ==
    1213
    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.
     14The allEars player lets your audience listen to your blog posts, and streams all your content using a playlist fed via RSS.
     15Use 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.
     16For more information about allEars, visit https://getallears.com/about.
    1617
    1718
    18 = Site configuration =
     19= Using the plugin =
    1920
    20 Set the widget key on the settings page (Settings->allEars) to start using the widget.
     21When 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:
    2122
    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.
    2424
    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.
    3026
    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.
    3228
    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.
     29In addition, you can use the following shortcodes in your text:
    4030
    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.
    4334
    44 = Shortcode examples =
     35If 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]*
    4536
    46 [allears-widget maxwidth="40rem"]
    4737
    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 ==
    6439
    65 = Notes =
     40Install 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.
    6641
    67 * The widget is rendered only when viewing individual posts.
    6842
    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? =
     46The 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?=
     60Use 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?=
     71Royalty-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 =
     74The 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? =
     79You 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
    7287
    7388== Changelog ==
    7489
     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
    75102= 1.0.0 =
     103*Release Date - 10/27/2018*
     104
    76105* Initial release
Note: See TracChangeset for help on using the changeset viewer.