array( self::C_STR_FOOTNOTES_SHORT_CODE_START => '((', self::C_STR_FOOTNOTES_SHORT_CODE_END => '))', self::C_STR_FOOTNOTES_SHORT_CODE_START_USER_DEFINED => '', self::C_STR_FOOTNOTES_SHORT_CODE_END_USER_DEFINED => '', self::C_STR_FOOTNOTE_SHORTCODE_SYNTAX_VALIDATION_ENABLE => 'yes', self::C_STR_FOOTNOTES_COUNTER_STYLE => 'arabic_plain', self::C_STR_COMBINE_IDENTICAL_FOOTNOTES => 'yes', self::C_STR_FOOTNOTES_HARD_LINKS_ENABLE => 'no', self::C_STR_REFERRER_FRAGMENT_ID_SLUG => 'r', self::C_STR_FOOTNOTE_FRAGMENT_ID_SLUG => 'f', self::C_STR_HARD_LINK_IDS_SEPARATOR => '+', self::C_INT_FOOTNOTES_SCROLL_OFFSET => 20, self::C_INT_FOOTNOTES_SCROLL_DURATION => 380, // 2.5.4 fast-tracked. self::C_STR_FOOTNOTES_BACKLINK_TOOLTIP_ENABLE => 'yes', self::C_STR_FOOTNOTES_BACKLINK_TOOLTIP_TEXT => 'Alt+ ←', self::C_STR_REFERENCE_CONTAINER_NAME => 'References', self::C_STR_REFERENCE_CONTAINER_LABEL_ELEMENT => 'p', self::C_STR_REFERENCE_CONTAINER_LABEL_BOTTOM_BORDER => 'yes', self::C_STR_REFERENCE_CONTAINER_COLLAPSE => 'no', self::C_STR_FOOTNOTES_REFERENCE_CONTAINER_SCRIPT_MODE => 'jquery', self::C_STR_REFERENCE_CONTAINER_POSITION => 'post_end', self::C_STR_REFERENCE_CONTAINER_POSITION_SHORTCODE => '[[references]]', self::C_STR_REFERENCE_CONTAINER_START_PAGE_ENABLE => 'yes', // Whether to enqueue additional stylesheet. self::C_STR_FOOTNOTES_PAGE_LAYOUT_SUPPORT => 'none', // Top and bottom margins. self::C_INT_REFERENCE_CONTAINER_TOP_MARGIN => 24, self::C_INT_REFERENCE_CONTAINER_BOTTOM_MARGIN => 0, // Table cell borders. self::C_STR_REFERENCE_CONTAINER_ROW_BORDERS_ENABLE => 'no', // Backlink symbol. self::C_STR_REFERENCE_CONTAINER_3COLUMN_LAYOUT_ENABLE => 'no', self::C_STR_REFERENCE_CONTAINER_BACKLINK_SYMBOL_ENABLE => 'yes', self::C_STR_REFERENCE_CONTAINER_BACKLINK_SYMBOL_SWITCH => 'no', // Backlink separators and terminators are often not preferred.. // But a choice must be provided along with the ability to customize. self::C_STR_BACKLINKS_SEPARATOR_ENABLED => 'yes', self::C_STR_BACKLINKS_SEPARATOR_OPTION => 'comma', self::C_STR_BACKLINKS_SEPARATOR_CUSTOM => '', self::C_STR_BACKLINKS_TERMINATOR_ENABLED => 'no', self::C_STR_BACKLINKS_TERMINATOR_OPTION => 'full_stop', self::C_STR_BACKLINKS_TERMINATOR_CUSTOM => '', // Set backlinks column width. self::C_STR_BACKLINKS_COLUMN_WIDTH_ENABLED => 'no', self::C_INT_BACKLINKS_COLUMN_WIDTH_SCALAR => '50', self::C_STR_BACKLINKS_COLUMN_WIDTH_UNIT => 'px', // Set backlinks column max. width. self::C_STR_BACKLINKS_COLUMN_MAX_WIDTH_ENABLED => 'no', self::C_INT_BACKLINKS_COLUMN_MAX_WIDTH_SCALAR => '140', self::C_STR_BACKLINKS_COLUMN_MAX_WIDTH_UNIT => 'px', // Whether a
tag is inserted. self::C_STR_BACKLINKS_LINE_BREAKS_ENABLED => 'no', // Whether to enable URL line wrapping. self::C_STR_FOOTNOTE_URL_WRAP_ENABLED => 'yes', // Whether to use link elements. self::C_STR_LINK_ELEMENT_ENABLED => 'yes', // Excerpt should be disabled. self::C_STR_FOOTNOTES_IN_EXCERPT => 'no', self::C_STR_FOOTNOTES_EXPERT_MODE => 'yes', self::C_STR_FOOTNOTES_LOVE => 'no', ), 'footnotes_storage_custom' => array( self::C_STR_HYPERLINK_ARROW => '↑', self::C_STR_HYPERLINK_ARROW_USER_DEFINED => '', self::C_STR_FOOTNOTES_TOOLTIP_READON_LABEL => 'Continue reading', self::C_STR_FOOTNOTES_REFERRER_SUPERSCRIPT_TAGS => 'yes', self::C_STR_FOOTNOTES_STYLING_BEFORE => '[', self::C_STR_FOOTNOTES_STYLING_AFTER => ']', self::C_STR_FOOTNOTES_MOUSE_OVER_BOX_ENABLED => 'yes', self::C_STR_FOOTNOTES_MOUSE_OVER_BOX_ALTERNATIVE => 'no', // The mouse over content truncation should be enabled by default. // To raise awareness of the functionality and to prevent the screen. // From being filled at mouse-over, and to allow the Continue reading. self::C_STR_FOOTNOTES_MOUSE_OVER_BOX_EXCERPT_ENABLED => 'yes', // The truncation length is raised from 150 to 200 chars. self::C_INT_FOOTNOTES_MOUSE_OVER_BOX_EXCERPT_LENGTH => 200, // 2.5.4 fast-tracked. self::C_STR_FOOTNOTES_TOOLTIP_EXCERPT_DELIMITER => '[[/tooltip]]', self::C_STR_FOOTNOTES_TOOLTIP_EXCERPT_MIRROR_ENABLE => 'no', self::C_STR_FOOTNOTES_TOOLTIP_EXCERPT_MIRROR_SEPARATOR => ' — ', self::C_STR_FOOTNOTE_REFERRERS_NORMAL_SUPERSCRIPT => 'no', // The default position should not be lateral because of the risk. // The box gets squeezed between note anchor at line end and window edge,. // And top because reading at the bottom of the window is more likely. self::C_STR_FOOTNOTES_MOUSE_OVER_BOX_POSITION => 'top center', self::C_INT_FOOTNOTES_MOUSE_OVER_BOX_OFFSET_X => 0, // The vertical offset must be negative for the box not to cover. // The current line of text (web coordinates origin is top left). self::C_INT_FOOTNOTES_MOUSE_OVER_BOX_OFFSET_Y => -7, // The width should be limited to start with, for the box to have shape. self::C_INT_FOOTNOTES_MOUSE_OVER_BOX_MAX_WIDTH => 450, // Fixed width is for alternative tooltips, cannot reuse max-width nor offsets. self::C_STR_FOOTNOTES_ALTERNATIVE_MOUSE_OVER_BOX_POSITION => 'top right', self::C_INT_FOOTNOTES_ALTERNATIVE_MOUSE_OVER_BOX_OFFSET_X => -50, self::C_INT_FOOTNOTES_ALTERNATIVE_MOUSE_OVER_BOX_OFFSET_Y => 24, self::C_INT_FOOTNOTES_ALTERNATIVE_MOUSE_OVER_BOX_WIDTH => 400, // Tooltip display durations. // Called mouse over box not tooltip for consistency. self::C_INT_MOUSE_OVER_BOX_FADE_IN_DELAY => 0, self::C_INT_MOUSE_OVER_BOX_FADE_IN_DURATION => 200, self::C_INT_MOUSE_OVER_BOX_FADE_OUT_DELAY => 400, self::C_INT_MOUSE_OVER_BOX_FADE_OUT_DURATION => 200, // Tooltip font size reset to legacy by default since 2.1.4;. // Was set to inherit since 2.1.1 as it overrode custom CSS,. // Is moved to settings since 2.1.4 2020-12-04T1023+0100. self::C_STR_MOUSE_OVER_BOX_FONT_SIZE_ENABLED => 'yes', self::C_FLO_MOUSE_OVER_BOX_FONT_SIZE_SCALAR => 13, self::C_STR_MOUSE_OVER_BOX_FONT_SIZE_UNIT => 'px', self::C_STR_FOOTNOTES_MOUSE_OVER_BOX_COLOR => '', // The mouse over box shouldn’t feature a colored background. // By default, due to diverging user preferences. White is neutral. self::C_STR_FOOTNOTES_MOUSE_OVER_BOX_BACKGROUND => '#ffffff', self::C_INT_FOOTNOTES_MOUSE_OVER_BOX_BORDER_WIDTH => 1, self::C_STR_FOOTNOTES_MOUSE_OVER_BOX_BORDER_COLOR => '#cccc99', // The mouse over box corners mustn’t be rounded as that is outdated. self::C_INT_FOOTNOTES_MOUSE_OVER_BOX_BORDER_RADIUS => 0, self::C_STR_FOOTNOTES_MOUSE_OVER_BOX_SHADOW_COLOR => '#666666', // Custom CSS migrates to a dedicated tab. self::C_STR_CUSTOM_CSS => '', ), 'footnotes_storage_expert' => array( // These are checkboxes; keyword 'checked' is converted to Boolean true,. // Empty string to false (default). // Titles should all be enabled by default to prevent users from. // Thinking at first that the feature is broken in post titles.. // See . // Yet in titles, footnotes are still buggy, because WordPress. // Uses the title string in menus and in the title element.. self::C_STR_EXPERT_LOOKUP_THE_TITLE => '', self::C_STR_EXPERT_LOOKUP_THE_CONTENT => 'checked', // And the_excerpt is disabled by default following @nikelaos in. // . // . self::C_STR_EXPERT_LOOKUP_THE_EXCERPT => '', self::C_STR_EXPERT_LOOKUP_WIDGET_TITLE => '', // The widget_text hook must be disabled by default, because it causes. // Multiple reference containers to appear in Elementor accordions, but. // It must be enabled if multiple reference containers are desired, as. // In Elementor toggles.. self::C_STR_EXPERT_LOOKUP_WIDGET_TEXT => '', // Initially hard-coded default. // Shows "9223372036854780000" instead of 9223372036854775807 in the numbox. // Empty should be interpreted as PHP_INT_MAX, but a numbox cannot be set to empty. // . // Interpret -1 as PHP_INT_MAX instead. self::C_INT_EXPERT_LOOKUP_THE_TITLE_PRIORITY_LEVEL => PHP_INT_MAX, // Priority level of the_content and of widget_text as the only relevant. // Hooks must be less than 99 because social buttons may yield scripts. // That contain the strings '((' and '))', i.e. the default footnote. // Start and end short codes, causing issues with fake footnotes.. self::C_INT_EXPERT_LOOKUP_THE_CONTENT_PRIORITY_LEVEL => 98, self::C_INT_EXPERT_LOOKUP_THE_EXCERPT_PRIORITY_LEVEL => PHP_INT_MAX, self::C_INT_EXPERT_LOOKUP_WIDGET_TITLE_PRIORITY_LEVEL => PHP_INT_MAX, self::C_INT_EXPERT_LOOKUP_WIDGET_TEXT_PRIORITY_LEVEL => 98, ), 'footnotes_storage_custom_css' => array( self::C_STR_CUSTOM_CSS_LEGACY_ENABLE => 'yes', self::C_STR_CUSTOM_CSS_NEW => '', ), ); /** * Contains all Settings from each Settings container as soon as this class is initialized. * * @since 1.5.0 * @var array */ private $a_arr_settings = array(); /** * Class Constructor. Loads all Settings from each WordPress Settings container. * * @since 1.5.0 */ private function __construct() { $this->load_all(); } /** * Returns a singleton of this class. * * @since 1.5.0 * @return MCI_Footnotes_Settings */ public static function instance() { // No instance defined yet, load it. if ( ! self::$a_obj_instance ) { self::$a_obj_instance = new self(); } // Return a singleton of this class. return self::$a_obj_instance; } /** * Returns the name of a specified Settings Container. * * @since 1.5.0 * @param int $p_int_index Settings Container Array Key Index. * @return str Settings Container name. */ public function get_container( $p_int_index ) { return $this->a_arr_container[ $p_int_index ]; } /** * Returns the default values of a specific Settings Container. * * @since 1.5.6 * @param int $p_int_index Settings Container Aray Key Index. * @return array */ public function get_defaults( $p_int_index ) { return $this->a_arr_default[ $this->a_arr_container[ $p_int_index ] ]; } /** * Loads all Settings from each Settings container. * * @since 1.5.0 */ private function load_all() { // Clear current settings. $this->a_arr_settings = array(); $num_settings = count( $this->a_arr_container ); for ( $i = 0; $i < $num_settings; $i++ ) { // Load settings. $this->a_arr_settings = array_merge( $this->a_arr_settings, $this->load( $i ) ); } } /** * Loads all Settings from specified Settings Container. * * @since 1.5.0 * @param int $p_int_index Settings Container Array Key Index. * @return array Settings loaded from Container of Default Settings if Settings Container is empty (first usage). * * @since ditched trimming whitespace from text box content in response to user request. * @link https://wordpress.org/support/topic/leading-space-in-footnotes-tag/#post-5347966 */ private function load( $p_int_index ) { // Load all settings from container. $l_arr_options = get_option( $this->get_container( $p_int_index ) ); // Load all default settings. $l_arr_default = $this->a_arr_default[ $this->get_container( $p_int_index ) ]; // No settings found, set them to their default value. if ( empty( $l_arr_options ) ) { return $l_arr_default; } // Iterate through all available settings ( = default values). foreach ( $l_arr_default as $l_str_key => $l_str_value ) { // Available setting not found in the container. if ( ! array_key_exists( $l_str_key, $l_arr_options ) ) { // Define the setting with its default value. $l_arr_options[ $l_str_key ] = $l_str_value; } } // Iterate through each setting in the container. foreach ( $l_arr_options as $l_str_key => $l_str_value ) { // Remove all whitespace at the beginning and end of a setting. // Trimming whitespace is ditched. // $l_str_value = trim($l_str_value);. // Write the sanitized value back to the setting container. $l_arr_options[ $l_str_key ] = $l_str_value; } // Return settings loaded from Container. return $l_arr_options; } /** * Updates a whole Settings container. * * @since 1.5.0 * @param int $p_int_index Index of the Settings container. * @param array $p_arr_new_values new Settings. * @return bool */ public function save_options( $p_int_index, $p_arr_new_values ) { if ( update_option( $this->get_container( $p_int_index ), $p_arr_new_values ) ) { $this->load_all(); return true; } return false; } /** * Returns the value of specified Settings name. * * @since 1.5.0 * @param string $p_str_key Settings Array Key name. * @return mixed Value of the Setting on Success or Null in Settings name is invalid. */ public function get( $p_str_key ) { return array_key_exists( $p_str_key, $this->a_arr_settings ) ? $this->a_arr_settings[ $p_str_key ] : null; } /** * Deletes each Settings Container and loads the default values for each Settings Container. * * @since 1.5.0 * * Edit: This didn’t actually work. * @since 2.2.0 this function is not called any longer when deleting the plugin, * to protect user data against loss, since manually updating a plugin is safer * done by deleting and reinstalling (see the warning about database backup). * 2020-12-13T1353+0100 */ public function clear_all() { // Iterate through each Settings Container. $num_settings = count( $this->a_arr_container ); for ( $i = 0; $i < $num_settings; $i++ ) { // Delete the settings container. delete_option( $this->get_container( $i ) ); } // Set settings back to the default values. $this->a_arr_settings = $this->a_arr_default; } /** * Register all Settings Container for the Plugin Settings Page in the Dashboard. * Settings Container Label will be the same as the Settings Container Name. * * @since 1.5.0 */ public function register_settings() { // Register all settings. $num_settings = count( $this->a_arr_container ); for ( $i = 0; $i < $num_settings; $i++ ) { register_setting( $this->get_container( $i ), $this->get_container( $i ) ); } } }