From bb3b55514829dda0e61f038e8c8844c3ae656d86 Mon Sep 17 00:00:00 2001 From: Rumperuu Date: Sun, 21 Feb 2021 09:50:23 +0000 Subject: [PATCH] Ran [conversion script](https://gist.github.com/Rumperuu/51bccc659e1b7882f5101c1ed008cd9d) --- class/convert.php | 458 +- .../class-mci-footnotes-layout-init.php | 214 + ...p => class-mci-footnotes-layoutengine.php} | 323 +- class/dashboard/init.php | 212 - class/hooks.php | 188 +- class/init.php | 754 +-- class/language.php | 215 +- class/settings.php | 2506 ++++----- class/task.php | 4677 +++++++++-------- class/template.php | 520 +- class/wysiwyg.php | 171 +- footnotes.php | 150 +- includes.php | 74 +- 13 files changed, 5243 insertions(+), 5219 deletions(-) create mode 100644 class/dashboard/class-mci-footnotes-layout-init.php rename class/dashboard/{layout.php => class-mci-footnotes-layoutengine.php} (55%) delete mode 100644 class/dashboard/init.php diff --git a/class/convert.php b/class/convert.php index 767d25c..52e8a4c 100644 --- a/class/convert.php +++ b/class/convert.php @@ -1,229 +1,229 @@ - 26) { - // increase offset and reduce counter - $l_int_Offset++; - $p_int_Value -= 26; - } - // if offset set (more then Z), then add a new letter in front - if ($l_int_Offset > 0) { - $l_str_Return = chr($l_int_Offset + 64); - } - // add the origin letter - $l_str_Return .= chr($p_int_Value + 64); - // return the latin character representing the integer - if ($p_bool_UpperCase) { - return strtoupper($l_str_Return); - } - return strtolower($l_str_Return); - } - - /** - * Converts an integer to a leading-0 integer. - * - * @author Stefan Herndler - * @since 1.0-gamma - * @param int $p_int_Value Value/Index to be converted. - * @return string Value with a leading zero. - */ - private static function toArabicLeading($p_int_Value) { - // add a leading 0 if number lower then 10 - if ($p_int_Value < 10) { - return "0" . $p_int_Value; - } - return $p_int_Value; - } - - /** - * Converts an integer to a romanic letter. - * - * @author Stefan Herndler - * @since 1.0-gamma - * @param int $p_int_Value Value/Index to be converted. - * @return string - * - * Edited: - * @since 2.2.0 optionally lowercase (code from Latin) 2020-12-12T1538+0100 - */ - private static function toRomanic($p_int_Value, $p_bool_UpperCase) { - // table containing all necessary romanic letters - $l_arr_RomanicLetters = array( - 'M' => 1000, - 'CM' => 900, - 'D' => 500, - 'CD' => 400, - 'C' => 100, - 'XC' => 90, - 'L' => 50, - 'XL' => 40, - 'X' => 10, - 'IX' => 9, - 'V' => 5, - 'IV' => 4, - 'I' => 1 - ); - // return value - $l_str_Return = ''; - // iterate through integer value until it is reduced to 0 - while ($p_int_Value > 0) { - foreach ($l_arr_RomanicLetters as $l_str_Romanic => $l_int_Arabic) { - if ($p_int_Value >= $l_int_Arabic) { - $p_int_Value -= $l_int_Arabic; - $l_str_Return .= $l_str_Romanic; - break; - } - } - } - // return romanic letters as string - if ($p_bool_UpperCase) { - return strtoupper($l_str_Return); - } - return strtolower($l_str_Return); - } - - /** - * Converts a string depending on its value to a boolean. - * - * @author Stefan Herndler - * @since 1.0-beta - * @param string $p_str_Value String to be converted to boolean. - * @return bool Boolean representing the string. - */ - public static function toBool($p_str_Value) { - // convert string to lower-case to make it easier - $p_str_Value = strtolower($p_str_Value); - // check if string seems to contain a "true" value - switch ($p_str_Value) { - case "checked": - case "yes": - case "true": - case "on": - case "1": - return true; - } - // nothing found that says "true", so we return false - return false; - } - - /** - * Get a html Array short code depending on Arrow-Array key index. - * - * @author Stefan Herndler - * @since 1.3.2 - * @param int $p_int_Index Index representing the Arrow. If empty all Arrows are specified. - * @return array|string Array of all Arrows if Index is empty otherwise html tag of a specific arrow. - */ - public static function getArrow($p_int_Index = -1) { - // define all possible arrows - $l_arr_Arrows = array("↑", "↥", "↟", "↩", "↲", "↵", "⇑", "⇡", "⇧", "↑"); - // convert index to an integer - if (!is_int($p_int_Index)) { - $p_int_Index = intval($p_int_Index); - } - // return the whole arrow array - if ($p_int_Index < 0 || $p_int_Index > count($l_arr_Arrows)) { - return $l_arr_Arrows; - } - // return a single arrow - return $l_arr_Arrows[$p_int_Index]; - } - - /** - * Displays a Variable. - * - * @author Stefan Herndler - * @since 1.5.0 - * @param mixed $p_mixed_Value - */ - public static function debug($p_mixed_Value) { - if (empty($p_mixed_Value)) { - var_dump($p_mixed_Value); - - } else if (is_array($p_mixed_Value)) { - printf("
");
-			print_r($p_mixed_Value);
-			printf("
"); - - } else if (is_object($p_mixed_Value)) { - printf("
");
-			print_r($p_mixed_Value);
-			printf("
"); - - } else if (is_numeric($p_mixed_Value) || is_int($p_mixed_Value)) { - var_dump($p_mixed_Value); - - } else if (is_date($p_mixed_Value)) { - var_dump($p_mixed_Value); - - } else { - var_dump($p_mixed_Value); - } - echo "
"; - } -} + 26 ) { + // Increase offset and reduce counter. + $l_int_offset++; + $p_int_value -= 26; + } + // If offset set (more then Z), then add a new letter in front. + if ( $l_int_offset > 0 ) { + $l_str_return = chr( $l_int_offset + 64 ); + } + // Add the origin letter. + $l_str_return .= chr( $p_int_value + 64 ); + // Return the latin character representing the integer. + if ( $p_bool_upper_case ) { + return strtoupper( $l_str_return ); + } + return strtolower( $l_str_return ); + } + + /** + * Converts an integer to a leading-0 integer. + * + * @author Stefan Herndler + * @since 1.0-gamma + * @param int $p_int_value Value/Index to be converted. + * @return string Value with a leading zero. + */ + private static function to_arabic_leading( $p_int_value ) { + // Add a leading 0 if number lower then 10. + if ( $p_int_value < 10 ) { + return '0' . $p_int_value; + } + return $p_int_value; + } + + /** + * Converts an integer to a romanic letter. + * + * @author Stefan Herndler + * @since 1.0-gamma + * @param int $p_int_value Value/Index to be converted. + * @return string + * + * Edited: + * @since 2.2.0 optionally lowercase (code from Latin) 2020-12-12T1538+0100 + */ + private static function to_romanic( $p_int_value, $p_bool_upper_case ) { + // Table containing all necessary romanic letters. + $l_arr_romanic_letters = array( + 'M' => 1000, + 'CM' => 900, + 'D' => 500, + 'CD' => 400, + 'C' => 100, + 'XC' => 90, + 'L' => 50, + 'XL' => 40, + 'X' => 10, + 'IX' => 9, + 'V' => 5, + 'IV' => 4, + 'I' => 1, + ); + // Return value. + $l_str_return = ''; + // Iterate through integer value until it is reduced to 0. + while ( $p_int_value > 0 ) { + foreach ( $l_arr_romanic_letters as $l_str_romanic => $l_int_arabic ) { + if ( $p_int_value >= $l_int_arabic ) { + $p_int_value -= $l_int_arabic; + $l_str_return .= $l_str_romanic; + break; + } + } + } + // Return romanic letters as string. + if ( $p_bool_upper_case ) { + return strtoupper( $l_str_return ); + } + return strtolower( $l_str_return ); + } + + /** + * Converts a string depending on its value to a boolean. + * + * @author Stefan Herndler + * @since 1.0-beta + * @param string $p_str_value String to be converted to boolean. + * @return bool Boolean representing the string. + */ + public static function to_bool( $p_str_value ) { + // Convert string to lower-case to make it easier. + $p_str_value = strtolower( $p_str_value ); + // Check if string seems to contain a "true" value. + switch ( $p_str_value ) { + case 'checked': + case 'yes': + case 'true': + case 'on': + case '1': + return true; + } + // Nothing found that says "true", so we return false. + return false; + } + + /** + * Get a html Array short code depending on Arrow-Array key index. + * + * @author Stefan Herndler + * @since 1.3.2 + * @param int $p_int_index Index representing the Arrow. If empty all Arrows are specified. + * @return array|string Array of all Arrows if Index is empty otherwise html tag of a specific arrow. + */ + public static function get_arrow( $p_int_index = -1 ) { + // Define all possible arrows. + $l_arr_arrows = array( '↑', '↥', '↟', '↩', '↲', '↵', '⇑', '⇡', '⇧', '↑' ); + // Convert index to an integer. + if ( ! is_int( $p_int_index ) ) { + $p_int_index = intval( $p_int_index ); + } + // Return the whole arrow array. + if ( $p_int_index < 0 || $p_int_index > count( $l_arr_arrows ) ) { + return $l_arr_arrows; + } + // Return a single arrow. + return $l_arr_arrows[ $p_int_index ]; + } + + /** + * Displays a Variable. + * + * @author Stefan Herndler + * @since 1.5.0 + * @param mixed $p_mixed_Value + */ + public static function debug( $p_mixed_Value ) { + if ( empty( $p_mixed_Value ) ) { + var_dump( $p_mixed_Value ); + + } else if ( is_array( $p_mixed_Value ) ) { + printf( '
' );
+			print_r( $p_mixed_Value );
+			printf( '
' ); + + } else if ( is_object( $p_mixed_Value ) ) { + printf( '
' );
+			print_r( $p_mixed_Value );
+			printf( '
' ); + + } else if ( is_numeric( $p_mixed_Value ) || is_int( $p_mixed_Value ) ) { + var_dump( $p_mixed_Value ); + + } else if ( is_date( $p_mixed_Value ) ) { + var_dump( $p_mixed_Value ); + + } else { + var_dump( $p_mixed_Value ); + } + echo '
'; + } +} diff --git a/class/dashboard/class-mci-footnotes-layout-init.php b/class/dashboard/class-mci-footnotes-layout-init.php new file mode 100644 index 0000000..ccfa988 --- /dev/null +++ b/class/dashboard/class-mci-footnotes-layout-init.php @@ -0,0 +1,214 @@ +a_arr_sub_page_classes[ $l_obj_class->getPriority() ] = $l_obj_class; + } + } + ksort( $this->a_arr_sub_page_classes ); + + // register hooks/actions. + add_action( 'admin_init', array( $this, 'initialize_settings' ) ); + add_action( 'admin_menu', array( $this, 'register_main_menu' ) ); + // register AJAX callbacks for Plugin information. + add_action( 'wp_ajax_nopriv_footnotes_getPluginInfo', array( $this, 'get_plugin_meta_information' ) ); + add_action( 'wp_ajax_footnotes_getPluginInfo', array( $this, 'get_plugin_meta_information' ) ); + } + + /** + * Initializes all sub pages and registers the settings. + * + * @author Stefan Herndler + * @since 1.5.0 + */ + public function initialize_settings() { + MCI_Footnotes_Settings::instance()->RegisterSettings(); + // iterate though each sub class of the layout engine and register their sections. + foreach ( $this->a_arr_sub_page_classes as $l_obj_layout_engine_sub_class ) { + $l_obj_layout_engine_sub_class->registerSections(); + } + } + + /** + * Registers the new main menu for the WordPress dashboard. + * Registers all sub menu pages for the new main menu. + * + * @author Stefan Herndler + * @since 1.5.0 + * @see http://codex.wordpress.org/Function_Reference/add_menu_page + */ + public function register_main_menu() { + global $menu; + // iterate through each main menu. + foreach ( $menu as $l_arr_main_menu ) { + // iterate through each main menu attribute. + foreach ( $l_arr_main_menu as $l_str_attribute ) { + // main menu already added, append sub pages and stop. + if ( self::C_STR_MAIN_MENU_SLUG === $l_str_attribute ) { + $this->register_sub_pages(); + return; + } + } + } + + // add a new main menu page to the WordPress dashboard. + add_menu_page( + self::C_STR_MAIN_MENU_TITLE, // page title. + self::C_STR_MAIN_MENU_TITLE, // menu title. + 'manage_options', // capability. + self::C_STR_MAIN_MENU_SLUG, // menu slug. + array( $this, 'display_other_plugins' ), // function. + plugins_url( 'footnotes/img/main-menu.png' ), // icon url. + null // position. + ); + $this->register_sub_pages(); + } + + /** + * Registers all SubPages for this Plugin. + * + * @author Stefan Herndler + * @since 1.5.0 + */ + private function register_sub_pages() { + // first registered sub menu page MUST NOT contain a unique slug suffix. + // iterate though each sub class of the layout engine and register their sub page. + foreach ( $this->a_arr_sub_page_classes as $l_obj_layout_engine_sub_class ) { + $l_obj_layout_engine_sub_class->register_sub_page(); + } + } + + /** + * Displays other Plugins from the developers. + * + * @author Stefan Herndler + * @since 1.5.0 + */ + public function display_other_plugins() { + printf( '

' ); + // load template file. + $l_obj_template = new MCI_Footnotes_Template( MCI_Footnotes_Template::C_STR_DASHBOARD, 'manfisher' ); + echo wp_kses_post( $l_obj_template->getContent() ); + + printf( 'visit Mark Cheret' ); + printf( '

' ); + + printf( '' ); + } + + /** + * AJAX call. returns a JSON string containing meta information about a specific WordPress Plugin. + * + * @author Stefan Herndler + * @since 1.5.0 + */ + public function get_plugin_meta_information() { + // TODO: add nonce verification. + + // get plugin internal name from POST data. + if ( isset( $_POST['plugin'] ) && 'true' === $_POST['plugin'] ) { + $l_str_plugin_name = wp_kses_post( wp_unslash( $_POST['plugin'] ) ); + } + + if ( empty( $l_str_plugin_name ) ) { + echo json_encode( array( 'error' => 'Plugin name invalid.' ) ); + exit; + } + $l_str_url = 'https://api.wordpress.org/plugins/info/1.0/' . $l_str_plugin_name . '.json'; + // call URL and collect data. + $l_arr_response = wp_remote_get( $l_str_url ); + // check if response is valid. + if ( is_wp_error( $l_arr_response ) ) { + echo json_encode( array( 'error' => 'Error receiving Plugin Information from WordPress.' ) ); + exit; + } + if ( ! array_key_exists( 'body', $l_arr_response ) ) { + echo json_encode( array( 'error' => 'Error reading WordPress API response message.' ) ); + exit; + } + // get the body of the response. + $l_str_response = $l_arr_response['body']; + // get plugin object. + $l_arr_plugin = json_decode( $l_str_response, true ); + if ( empty( $l_arr_plugin ) ) { + echo json_encode( array( 'error' => 'Error reading Plugin meta information.
URL: ' . $l_str_url . '
Response: ' . $l_str_response ) ); + exit; + } + + $l_int_num_ratings = array_key_exists( 'num_ratings', $l_arr_plugin ) ? intval( $l_arr_plugin['num_ratings'] ) : 0; + $l_int_rating = array_key_exists( 'rating', $l_arr_plugin ) ? floatval( $l_arr_plugin['rating'] ) : 0.0; + $l_int_stars = round( 5 * $l_int_rating / 100.0, 1 ); + + // return Plugin information as JSON encoded string. + echo json_encode( + array( + 'error' => '', + 'PluginDescription' => array_key_exists( 'short_description', $l_arr_plugin ) ? html_entity_decode( $l_arr_plugin['short_description'] ) : 'Error reading Plugin information', + 'PluginAuthor' => array_key_exists( 'author', $l_arr_plugin ) ? html_entity_decode( $l_arr_plugin['author'] ) : 'unknown', + 'PluginRatingText' => $l_int_stars . ' ' . __( 'rating based on', 'footnotes' ) . ' ' . $l_int_num_ratings . ' ' . __( 'ratings', 'footnotes' ), + 'PluginRating1' => $l_int_stars >= 0.5 ? 'star-full' : 'star-empty', + 'PluginRating2' => $l_int_stars >= 1.5 ? 'star-full' : 'star-empty', + 'PluginRating3' => $l_int_stars >= 2.5 ? 'star-full' : 'star-empty', + 'PluginRating4' => $l_int_stars >= 3.5 ? 'star-full' : 'star-empty', + 'PluginRating5' => $l_int_stars >= 4.5 ? 'star-full' : 'star-empty', + 'PluginRating' => $l_int_num_ratings, + 'PluginLastUpdated' => array_key_exists( 'last_updated', $l_arr_plugin ) ? $l_arr_plugin['last_updated'] : 'unknown', + 'PluginDownloads' => array_key_exists( 'downloaded', $l_arr_plugin ) ? $l_arr_plugin['downloaded'] : '---', + ) + ); + exit; + } +} diff --git a/class/dashboard/layout.php b/class/dashboard/class-mci-footnotes-layoutengine.php similarity index 55% rename from class/dashboard/layout.php rename to class/dashboard/class-mci-footnotes-layoutengine.php index 2c157c6..73dbc5e 100644 --- a/class/dashboard/layout.php +++ b/class/dashboard/class-mci-footnotes-layoutengine.php @@ -3,6 +3,7 @@ * Includes Layout Engine for the admin dashboard. * * @filesource + * @package footnotes * @author Stefan Herndler * @since 1.5.0 12.09.14 10:56 * @@ -32,7 +33,7 @@ abstract class MCI_Footnotes_LayoutEngine { * @since 1.5.0 * @var null|string */ - protected $a_str_SubPageHook = null; + protected $a_str_sub_page_hook = null; /** * Stores all Sections for the child sub page. @@ -41,7 +42,7 @@ abstract class MCI_Footnotes_LayoutEngine { * @since 1.5.0 * @var array */ - protected $a_arr_Sections = array(); + protected $a_arr_sections = array(); /** * Returns a Priority index. Lower numbers have a higher Priority. @@ -50,7 +51,7 @@ abstract class MCI_Footnotes_LayoutEngine { * @since 1.5.0 * @return int */ - abstract public function getPriority(); + abstract public function get_priority(); /** * Returns the unique slug of the child sub page. @@ -59,7 +60,7 @@ abstract class MCI_Footnotes_LayoutEngine { * @since 1.5.0 * @return string */ - abstract protected function getSubPageSlug(); + abstract protected function get_sub_page_slug(); /** * Returns the title of the child sub page. @@ -68,7 +69,7 @@ abstract class MCI_Footnotes_LayoutEngine { * @since 1.5.0 * @return string */ - abstract protected function getSubPageTitle(); + abstract protected function get_sub_page_title(); /** * Returns an array of all registered sections for a sub page. @@ -77,7 +78,7 @@ abstract class MCI_Footnotes_LayoutEngine { * @since 1.5.0 * @return array */ - abstract protected function getSections(); + abstract protected function get_sections(); /** * Returns an array of all registered meta boxes. @@ -86,25 +87,25 @@ abstract class MCI_Footnotes_LayoutEngine { * @since 1.5.0 * @return array */ - abstract protected function getMetaBoxes(); + abstract protected function get_meta_boxes(); /** * Returns an array describing a sub page section. * * @author Stefan Herndler * @since 1.5.0 - * @param string $p_str_ID Unique ID suffix. - * @param string $p_str_Title Title of the section. - * @param int $p_int_SettingsContainerIndex Settings Container Index. - * @param bool $p_bool_hasSubmitButton Should a Submit Button be displayed for this section, default: true. + * @param string $p_str_id Unique ID suffix. + * @param string $p_str_title Title of the section. + * @param int $p_int_settings_container_index Settings Container Index. + * @param bool $p_bool_has_submit_button Should a Submit Button be displayed for this section, default: true. * @return array Array describing the section. */ - protected function addSection( $p_str_ID, $p_str_Title, $p_int_SettingsContainerIndex, $p_bool_hasSubmitButton = true ) { + protected function add_section( $p_str_id, $p_str_title, $p_int_settings_container_index, $p_bool_has_submit_button = true ) { return array( - 'id' => MCI_Footnotes_Config::C_STR_PLUGIN_NAME . '-' . $p_str_ID, - 'title' => $p_str_Title, - 'submit' => $p_bool_hasSubmitButton, - 'container' => $p_int_SettingsContainerIndex, + 'id' => MCI_Footnotes_Config::C_STR_PLUGIN_NAME . '-' . $p_str_id, + 'title' => $p_str_title, + 'submit' => $p_bool_has_submit_button, + 'container' => $p_int_settings_container_index, ); } @@ -113,18 +114,18 @@ abstract class MCI_Footnotes_LayoutEngine { * * @author Stefan Herndler * @since 1.5.0 - * @param string $p_str_SectionID Parent Section ID. - * @param string $p_str_ID Unique ID suffix. - * @param string $p_str_Title Title for the meta box. - * @param string $p_str_CallbackFunctionName Class method name for callback. + * @param string $p_str_section_id Parent Section ID. + * @param string $p_str_id Unique ID suffix. + * @param string $p_str_title Title for the meta box. + * @param string $p_str_callback_function_name Class method name for callback. * @return array meta box description to be able to append a meta box to the output. */ - protected function addMetaBox( $p_str_SectionID, $p_str_ID, $p_str_Title, $p_str_CallbackFunctionName ) { + protected function add_meta_box( $p_str_section_id, $p_str_id, $p_str_title, $p_str_callback_function_name ) { return array( - 'parent' => MCI_Footnotes_Config::C_STR_PLUGIN_NAME . '-' . $p_str_SectionID, - 'id' => $p_str_ID, - 'title' => $p_str_Title, - 'callback' => $p_str_CallbackFunctionName, + 'parent' => MCI_Footnotes_Config::C_STR_PLUGIN_NAME . '-' . $p_str_section_id, + 'id' => $p_str_id, + 'title' => $p_str_title, + 'callback' => $p_str_callback_function_name, ); } @@ -134,26 +135,26 @@ abstract class MCI_Footnotes_LayoutEngine { * @author Stefan Herndler * @since 1.5.0 */ - public function registerSubPage() { + public function register_sub_page() { global $submenu; // any sub menu for our main menu exists if ( array_key_exists( plugin_basename( MCI_Footnotes_Layout_Init::C_STR_MAIN_MENU_SLUG ), $submenu ) ) { // iterate through all sub menu entries of the ManFisher main menu - foreach ( $submenu[ plugin_basename( MCI_Footnotes_Layout_Init::C_STR_MAIN_MENU_SLUG ) ] as $l_arr_SubMenu ) { - if ( $l_arr_SubMenu[2] == plugin_basename( MCI_Footnotes_Layout_Init::C_STR_MAIN_MENU_SLUG . $this->getSubPageSlug() ) ) { + foreach ( $submenu[ plugin_basename( MCI_Footnotes_Layout_Init::C_STR_MAIN_MENU_SLUG ) ] as $l_arr_sub_menu ) { + if ( $l_arr_sub_menu[2] == plugin_basename( MCI_Footnotes_Layout_Init::C_STR_MAIN_MENU_SLUG . $this->get_sub_page_slug() ) ) { // remove that sub menu and add it again to move it to the bottom - remove_submenu_page( MCI_Footnotes_Layout_Init::C_STR_MAIN_MENU_SLUG, MCI_Footnotes_Layout_Init::C_STR_MAIN_MENU_SLUG . $this->getSubPageSlug() ); + remove_submenu_page( MCI_Footnotes_Layout_Init::C_STR_MAIN_MENU_SLUG, MCI_Footnotes_Layout_Init::C_STR_MAIN_MENU_SLUG . $this->get_sub_page_slug() ); } } } - $this->a_str_SubPageHook = add_submenu_page( + $this->a_str_sub_page_hook = add_submenu_page( MCI_Footnotes_Layout_Init::C_STR_MAIN_MENU_SLUG, // parent slug - $this->getSubPageTitle(), // page title - $this->getSubPageTitle(), // menu title + $this->get_sub_page_title(), // page title + $this->get_sub_page_title(), // menu title 'manage_options', // capability - MCI_Footnotes_Layout_Init::C_STR_MAIN_MENU_SLUG . $this->getSubPageSlug(), // menu slug - array( $this, 'displayContent' ) // function + MCI_Footnotes_Layout_Init::C_STR_MAIN_MENU_SLUG . $this->get_sub_page_slug(), // menu slug + array( $this, 'display_content' ) // function ); } @@ -163,18 +164,18 @@ abstract class MCI_Footnotes_LayoutEngine { * @author Stefan Herndler * @since 1.5.0 */ - public function registerSections() { + public function register_sections() { // iterate through each section - foreach ( $this->getSections() as $l_arr_Section ) { + foreach ( $this->get_sections() as $l_arr_section ) { // append tab to the tab-array - $this->a_arr_Sections[ $l_arr_Section['id'] ] = $l_arr_Section; + $this->a_arr_sections[ $l_arr_section['id'] ] = $l_arr_section; add_settings_section( - $l_arr_Section['id'], // unique id - '', // $l_arr_Section["title"], // title + $l_arr_section['id'], // unique id + '', // $l_arr_section["title"], // title array( $this, 'Description' ), // callback function for the description - $l_arr_Section['id'] // parent sub page slug + $l_arr_section['id'] // parent sub page slug ); - $this->registerMetaBoxes( $l_arr_Section['id'] ); + $this->register_meta_boxes( $l_arr_section['id'] ); } } @@ -183,19 +184,19 @@ abstract class MCI_Footnotes_LayoutEngine { * * @author Stefan Herndler * @since 1.5.0 - * @param string $p_str_ParentID Parent section unique id. + * @param string $p_str_parent_iD Parent section unique id. */ - private function registerMetaBoxes( $p_str_ParentID ) { + private function register_meta_boxes( $p_str_parent_iD ) { // iterate through each meta box - foreach ( $this->getMetaBoxes() as $l_arr_MetaBox ) { - if ( $l_arr_MetaBox['parent'] != $p_str_ParentID ) { + foreach ( $this->get_meta_boxes() as $l_arr_meta_box ) { + if ( $l_arr_meta_box['parent'] != $p_str_parent_iD ) { continue; } add_meta_box( - $p_str_ParentID . '-' . $l_arr_MetaBox['id'], // unique id - $l_arr_MetaBox['title'], // meta box title - array( $this, $l_arr_MetaBox['callback'] ), // callback function to display (echo) the content - $p_str_ParentID, // post type = parent section id + $p_str_parent_iD . '-' . $l_arr_meta_box['id'], // unique id + $l_arr_meta_box['title'], // meta box title + array( $this, $l_arr_meta_box['callback'] ), // callback function to display (echo) the content + $p_str_parent_iD, // post type = parent section id 'main' // context ); } @@ -207,7 +208,7 @@ abstract class MCI_Footnotes_LayoutEngine { * @author Stefan Herndler * @since 1.5.0 */ - private function appendScripts() { + private function append_scripts() { // enable meta boxes layout and close functionality wp_enqueue_script( 'postbox' ); // add WordPress color picker layout @@ -252,20 +253,20 @@ abstract class MCI_Footnotes_LayoutEngine { * @author Stefan Herndler * @since 1.5.0 */ - public function displayContent() { + public function display_content() { // register and enqueue scripts and styling - $this->appendScripts(); + $this->append_scripts(); // get current section - reset( $this->a_arr_Sections ); - $l_str_ActiveSectionID = isset( $_GET['t'] ) ? $_GET['t'] : key( $this->a_arr_Sections ); - $l_arr_ActiveSection = $this->a_arr_Sections[ $l_str_ActiveSectionID ]; + reset( $this->a_arr_sections ); + $l_str_active_section_iD = isset( $_GET['t'] ) ? $_GET['t'] : key( $this->a_arr_sections ); + $l_arr_active_section = $this->a_arr_sections[ $l_str_active_section_iD ]; // store settings - $l_bool_SettingsUpdated = false; + $l_bool_settings_updated = false; if ( array_key_exists( 'save-settings', $_POST ) ) { if ( $_POST['save-settings'] == 'save' ) { unset( $_POST['save-settings'] ); unset( $_POST['submit'] ); - $l_bool_SettingsUpdated = $this->saveSettings(); + $l_bool_settings_updated = $this->save_settings(); } } @@ -273,31 +274,31 @@ abstract class MCI_Footnotes_LayoutEngine { echo '
'; echo '
'; - if ( $l_bool_SettingsUpdated ) { + if ( $l_bool_settings_updated ) { echo sprintf( '
%s
', __( 'Settings saved', MCI_Footnotes_Config::C_STR_PLUGIN_NAME ) ); } // form to submit the active section - echo '
'; - // settings_fields($l_arr_ActiveSection["container"]); + echo ''; + // settings_fields($l_arr_active_section["container"]); echo ''; // outputs the settings field of the active section - do_settings_sections( $l_arr_ActiveSection['id'] ); - do_meta_boxes( $l_arr_ActiveSection['id'], 'main', null ); + do_settings_sections( $l_arr_active_section['id'] ); + do_meta_boxes( $l_arr_active_section['id'], 'main', null ); // add submit button to active section if defined - if ( $l_arr_ActiveSection['submit'] ) { + if ( $l_arr_active_section['submit'] ) { submit_button(); } // close the form to submit data @@ -306,10 +307,10 @@ abstract class MCI_Footnotes_LayoutEngine { echo '
'; // output special javascript for the expand/collapse function of the meta boxes echo ''; } @@ -321,25 +322,25 @@ abstract class MCI_Footnotes_LayoutEngine { * @since 1.5.0 * @return bool */ - private function saveSettings() { - $l_arr_newSettings = array(); + private function save_settings() { + $l_arr_new_settings = array(); // get current section - reset( $this->a_arr_Sections ); - $l_str_ActiveSectionID = isset( $_GET['t'] ) ? $_GET['t'] : key( $this->a_arr_Sections ); - $l_arr_ActiveSection = $this->a_arr_Sections[ $l_str_ActiveSectionID ]; + reset( $this->a_arr_sections ); + $l_str_active_section_iD = isset( $_GET['t'] ) ? $_GET['t'] : key( $this->a_arr_sections ); + $l_arr_active_section = $this->a_arr_sections[ $l_str_active_section_iD ]; // iterate through each value that has to be in the specific container - foreach ( MCI_Footnotes_Settings::instance()->getDefaults( $l_arr_ActiveSection['container'] ) as $l_str_Key => $l_mixed_Value ) { + foreach ( MCI_Footnotes_Settings::instance()->get_defaults( $l_arr_active_section['container'] ) as $l_str_key => $l_mixed_Value ) { // setting is available in the POST array, use it - if ( array_key_exists( $l_str_Key, $_POST ) ) { - $l_arr_newSettings[ $l_str_Key ] = $_POST[ $l_str_Key ]; + if ( array_key_exists( $l_str_key, $_POST ) ) { + $l_arr_new_settings[ $l_str_key ] = $_POST[ $l_str_key ]; } else { // setting is not defined in the POST array, define it to avoid the Default value - $l_arr_newSettings[ $l_str_Key ] = ''; + $l_arr_new_settings[ $l_str_key ] = ''; } } // update settings - return MCI_Footnotes_Settings::instance()->saveOptions( $l_arr_ActiveSection['container'], $l_arr_newSettings ); + return MCI_Footnotes_Settings::instance()->save_options( $l_arr_active_section['container'], $l_arr_new_settings ); } /** @@ -357,17 +358,17 @@ abstract class MCI_Footnotes_LayoutEngine { * * @author Stefan Herndler * @since 1.5.0 - * @param string $p_str_SettingKeyName Settings Array key name. + * @param string $p_str_setting_key_name Settings Array key name. * @return array Contains Settings ID, Settings Name and Settings Value. */ - protected function LoadSetting( $p_str_SettingKeyName ) { + protected function Load_setting( $p_str_setting_key_name ) { // get current section - reset( $this->a_arr_Sections ); - $p_arr_Return = array(); - $p_arr_Return['id'] = sprintf( '%s', $p_str_SettingKeyName ); - $p_arr_Return['name'] = sprintf( '%s', $p_str_SettingKeyName ); - $p_arr_Return['value'] = esc_attr( MCI_Footnotes_Settings::instance()->get( $p_str_SettingKeyName ) ); - return $p_arr_Return; + reset( $this->a_arr_sections ); + $p_arr_return = array(); + $p_arr_return['id'] = sprintf( '%s', $p_str_setting_key_name ); + $p_arr_return['name'] = sprintf( '%s', $p_str_setting_key_name ); + $p_arr_return['value'] = esc_attr( MCI_Footnotes_Settings::instance()->get( $p_str_setting_key_name ) ); + return $p_arr_return; } /** @@ -377,7 +378,7 @@ abstract class MCI_Footnotes_LayoutEngine { * @since 1.5.0 * @return string */ - protected function addNewline() { + protected function add_newline() { return '
'; } @@ -388,7 +389,7 @@ abstract class MCI_Footnotes_LayoutEngine { * @since 1.5.0 * @return string */ - protected function addLineSpace() { + protected function add_line_space() { return '

'; } @@ -397,11 +398,11 @@ abstract class MCI_Footnotes_LayoutEngine { * * @author Stefan Herndler * @since 1.5.0 - * @param string $p_str_Text Message to be surrounded with simple html tag (span). + * @param string $p_str_text Message to be surrounded with simple html tag (span). * @return string */ - protected function addText( $p_str_Text ) { - return sprintf( '%s', $p_str_Text ); + protected function add_text( $p_str_text ) { + return sprintf( '%s', $p_str_text ); } /** @@ -409,15 +410,15 @@ abstract class MCI_Footnotes_LayoutEngine { * * @author Stefan Herndler * @since 1.5.0 - * @param string $p_str_SettingName Name of the Settings key to connect the Label with the input/select field. - * @param string $p_str_Caption Label caption. + * @param string $p_str_setting_name Name of the Settings key to connect the Label with the input/select field. + * @param string $p_str_caption Label caption. * @return string * * Edited 2020-12-01T0159+0100.. * @since 2.1.6 no colon */ - protected function addLabel( $p_str_SettingName, $p_str_Caption ) { - if ( empty( $p_str_Caption ) ) { + protected function add_label( $p_str_setting_name, $p_str_caption ) { + if ( empty( $p_str_caption ) ) { return ''; } // remove the colon causing localization issues with French, @@ -429,7 +430,7 @@ abstract class MCI_Footnotes_LayoutEngine { // Add colon to label strings for inclusion in localization. // Colon after label is widely preferred best practice, mandatory per style guides. // - return sprintf( '', $p_str_SettingName, $p_str_Caption ); + return sprintf( '', $p_str_setting_name, $p_str_caption ); // ^ here deleted colon 2020-12-08T1546+0100 } @@ -438,27 +439,27 @@ abstract class MCI_Footnotes_LayoutEngine { * * @author Stefan Herndler * @since 1.5.0 - * @param string $p_str_SettingName Name of the Settings key to pre load the input field. - * @param int $p_str_MaxLength Maximum length of the input, default 999 characters. - * @param bool $p_bool_Readonly Set the input to be read only, default false. - * @param bool $p_bool_Hidden Set the input to be hidden, default false. + * @param string $p_str_setting_name Name of the Settings key to pre load the input field. + * @param int $p_str_maxLength Maximum length of the input, default 999 characters. + * @param bool $p_bool_readonly Set the input to be read only, default false. + * @param bool $p_bool_hidden Set the input to be hidden, default false. * @return string */ - protected function addTextBox( $p_str_SettingName, $p_str_MaxLength = 999, $p_bool_Readonly = false, $p_bool_Hidden = false ) { - $l_str_Style = ''; + protected function add_text_box( $p_str_setting_name, $p_str_maxLength = 999, $p_bool_readonly = false, $p_bool_hidden = false ) { + $l_str_style = ''; // collect data for given settings field - $l_arr_Data = $this->LoadSetting( $p_str_SettingName ); - if ( $p_bool_Hidden ) { - $l_str_Style .= 'display:none;'; + $l_arr_data = $this->Load_setting( $p_str_setting_name ); + if ( $p_bool_hidden ) { + $l_str_style .= 'display:none;'; } return sprintf( '', - $l_arr_Data['name'], - $l_arr_Data['id'], - $p_str_MaxLength, - $l_str_Style, - $l_arr_Data['value'], - $p_bool_Readonly ? 'readonly="readonly"' : '' + $l_arr_data['name'], + $l_arr_data['id'], + $p_str_maxLength, + $l_str_style, + $l_arr_data['value'], + $p_bool_readonly ? 'readonly="readonly"' : '' ); } @@ -467,17 +468,17 @@ abstract class MCI_Footnotes_LayoutEngine { * * @author Stefan Herndler * @since 1.5.0 - * @param string $p_str_SettingName Name of the Settings key to pre load the input field. + * @param string $p_str_setting_name Name of the Settings key to pre load the input field. * @return string */ - protected function addCheckbox( $p_str_SettingName ) { + protected function add_checkbox( $p_str_setting_name ) { // collect data for given settings field - $l_arr_Data = $this->LoadSetting( $p_str_SettingName ); + $l_arr_data = $this->Load_setting( $p_str_setting_name ); return sprintf( '', - $l_arr_Data['name'], - $l_arr_Data['id'], - MCI_Footnotes_Convert::toBool( $l_arr_Data['value'] ) ? 'checked="checked"' : '' + $l_arr_data['name'], + $l_arr_data['id'], + MCI_Footnotes_Convert::to_bool( $l_arr_data['value'] ) ? 'checked="checked"' : '' ); } @@ -486,29 +487,29 @@ abstract class MCI_Footnotes_LayoutEngine { * * @author Stefan Herndler * @since 1.5.0 - * @param string $p_str_SettingName Name of the Settings key to pre select the current value. - * @param array $p_arr_Options Possible options to be selected. + * @param string $p_str_setting_name Name of the Settings key to pre select the current value. + * @param array $p_arr_options Possible options to be selected. * @return string */ - protected function addSelectBox( $p_str_SettingName, $p_arr_Options ) { + protected function add_select_box( $p_str_setting_name, $p_arr_options ) { // collect data for given settings field - $l_arr_Data = $this->LoadSetting( $p_str_SettingName ); - $l_str_Options = ''; + $l_arr_data = $this->Load_setting( $p_str_setting_name ); + $l_str_options = ''; /* loop through all array keys */ - foreach ( $p_arr_Options as $l_str_Value => $l_str_Caption ) { - $l_str_Options .= sprintf( + foreach ( $p_arr_options as $l_str_value => $l_str_caption ) { + $l_str_options .= sprintf( '', - $l_str_Value, - $l_arr_Data['value'] == $l_str_Value ? 'selected' : '', - $l_str_Caption + $l_str_value, + $l_arr_data['value'] == $l_str_value ? 'selected' : '', + $l_str_caption ); } return sprintf( '', - $l_arr_Data['name'], - $l_arr_Data['id'], - $l_str_Options + $l_arr_data['name'], + $l_arr_data['id'], + $l_str_options ); } @@ -517,17 +518,17 @@ abstract class MCI_Footnotes_LayoutEngine { * * @author Stefan Herndler * @since 1.5.0 - * @param string $p_str_SettingName Name of the Settings key to pre fill the text area. + * @param string $p_str_setting_name Name of the Settings key to pre fill the text area. * @return string */ - protected function addTextArea( $p_str_SettingName ) { + protected function add_text_area( $p_str_setting_name ) { // collect data for given settings field - $l_arr_Data = $this->LoadSetting( $p_str_SettingName ); + $l_arr_data = $this->Load_setting( $p_str_setting_name ); return sprintf( '', - $l_arr_Data['name'], - $l_arr_Data['id'], - $l_arr_Data['value'] + $l_arr_data['name'], + $l_arr_data['id'], + $l_arr_data['value'] ); } @@ -536,17 +537,17 @@ abstract class MCI_Footnotes_LayoutEngine { * * @author Stefan Herndler * @since 1.5.6 - * @param string $p_str_SettingName Name of the Settings key to pre load the input field. + * @param string $p_str_setting_name Name of the Settings key to pre load the input field. * @return string */ - protected function addColorSelection( $p_str_SettingName ) { + protected function add_color_selection( $p_str_setting_name ) { // collect data for given settings field - $l_arr_Data = $this->LoadSetting( $p_str_SettingName ); + $l_arr_data = $this->Load_setting( $p_str_setting_name ); return sprintf( '', - $l_arr_Data['name'], - $l_arr_Data['id'], - $l_arr_Data['value'] + $l_arr_data['name'], + $l_arr_data['id'], + $l_arr_data['value'] ); } @@ -555,37 +556,37 @@ abstract class MCI_Footnotes_LayoutEngine { * * @author Stefan Herndler * @since 1.5.0 - * @param string $p_str_SettingName Name of the Settings key to pre load the input field. + * @param string $p_str_setting_name Name of the Settings key to pre load the input field. * @param int $p_in_Min Minimum value. - * @param int $p_int_Max Maximum value. - * @param bool $p_bool_Deci true if 0.1 steps and floating to string, false if integer (default) + * @param int $p_int_max Maximum value. + * @param bool $p_bool_deci true if 0.1 steps and floating to string, false if integer (default) * @return string * * Edited: * @since 2.1.4 step argument and number_format() to allow decimals 2020-12-03T0631+0100..2020-12-12T1110+0100 */ - protected function addNumBox( $p_str_SettingName, $p_in_Min, $p_int_Max, $p_bool_Deci = false ) { + protected function add_num_box( $p_str_setting_name, $p_in_Min, $p_int_max, $p_bool_deci = false ) { // collect data for given settings field - $l_arr_Data = $this->LoadSetting( $p_str_SettingName ); + $l_arr_data = $this->Load_setting( $p_str_setting_name ); - if ( $p_bool_Deci ) { - $l_str_Value = number_format( floatval( $l_arr_Data['value'] ), 1 ); + if ( $p_bool_deci ) { + $l_str_value = number_format( floatval( $l_arr_data['value'] ), 1 ); return sprintf( '', - $l_arr_Data['name'], - $l_arr_Data['id'], - $l_str_Value, + $l_arr_data['name'], + $l_arr_data['id'], + $l_str_value, $p_in_Min, - $p_int_Max + $p_int_max ); } else { return sprintf( '', - $l_arr_Data['name'], - $l_arr_Data['id'], - $l_arr_Data['value'], + $l_arr_data['name'], + $l_arr_data['id'], + $l_arr_data['value'], $p_in_Min, - $p_int_Max + $p_int_max ); } } diff --git a/class/dashboard/init.php b/class/dashboard/init.php deleted file mode 100644 index fdbfd12..0000000 --- a/class/dashboard/init.php +++ /dev/null @@ -1,212 +0,0 @@ -a_arr_SubPageClasses[ $l_obj_Class->getPriority() ] = $l_obj_Class; - } - } - ksort( $this->a_arr_SubPageClasses ); - - // register hooks/actions - add_action( 'admin_init', array( $this, 'initializeSettings' ) ); - add_action( 'admin_menu', array( $this, 'registerMainMenu' ) ); - // register AJAX callbacks for Plugin information - add_action( 'wp_ajax_nopriv_footnotes_getPluginInfo', array( $this, 'getPluginMetaInformation' ) ); - add_action( 'wp_ajax_footnotes_getPluginInfo', array( $this, 'getPluginMetaInformation' ) ); - } - - /** - * Initializes all sub pages and registers the settings. - * - * @author Stefan Herndler - * @since 1.5.0 - */ - public function initializeSettings() { - MCI_Footnotes_Settings::instance()->RegisterSettings(); - // iterate though each sub class of the layout engine and register their sections - /** @var MCI_Footnotes_LayoutEngine $l_obj_LayoutEngineSubClass */ - foreach ( $this->a_arr_SubPageClasses as $l_obj_LayoutEngineSubClass ) { - $l_obj_LayoutEngineSubClass->registerSections(); - } - } - - /** - * Registers the new main menu for the WordPress dashboard. - * Registers all sub menu pages for the new main menu. - * - * @author Stefan Herndler - * @since 1.5.0 - * @see http://codex.wordpress.org/Function_Reference/add_menu_page - */ - public function registerMainMenu() { - global $menu; - // iterate through each main menu - foreach ( $menu as $l_arr_MainMenu ) { - // iterate through each main menu attribute - foreach ( $l_arr_MainMenu as $l_str_Attribute ) { - // main menu already added, append sub pages and stop - if ( $l_str_Attribute == self::C_STR_MAIN_MENU_SLUG ) { - $this->registerSubPages(); - return; - } - } - } - - // add a new main menu page to the WordPress dashboard - add_menu_page( - self::C_STR_MAIN_MENU_TITLE, // page title - self::C_STR_MAIN_MENU_TITLE, // menu title - 'manage_options', // capability - self::C_STR_MAIN_MENU_SLUG, // menu slug - array( $this, 'displayOtherPlugins' ), // function - plugins_url( 'footnotes/img/main-menu.png' ), // icon url - null // position - ); - $this->registerSubPages(); - } - - /** - * Registers all SubPages for this Plugin. - * - * @author Stefan Herndler - * @since 1.5.0 - */ - private function registerSubPages() { - // first registered sub menu page MUST NOT contain a unique slug suffix - // iterate though each sub class of the layout engine and register their sub page - /** @var MCI_Footnotes_LayoutEngine $l_obj_LayoutEngineSubClass */ - foreach ( $this->a_arr_SubPageClasses as $l_obj_LayoutEngineSubClass ) { - $l_obj_LayoutEngineSubClass->registerSubPage(); - } - } - - /** - * Displays other Plugins from the developers. - * - * @author Stefan Herndler - * @since 1.5.0 - */ - public function displayOtherPlugins() { - printf( '

' ); - // load template file - $l_obj_Template = new MCI_Footnotes_Template( MCI_Footnotes_Template::C_STR_DASHBOARD, 'manfisher' ); - echo $l_obj_Template->getContent(); - - printf( 'visit Mark Cheret' ); - printf( '

' ); - - printf( '' ); - } - - /** - * AJAX call. returns a JSON string containing meta information about a specific WordPress Plugin. - * - * @author Stefan Herndler - * @since 1.5.0 - */ - public function getPluginMetaInformation() { - // get plugin internal name from POST data - $l_str_PluginName = array_key_exists( 'plugin', $_POST ) ? $_POST['plugin'] : null; - if ( empty( $l_str_PluginName ) ) { - echo json_encode( array( 'error' => 'Plugin name invalid.' ) ); - exit; - } - $l_str_Url = 'https://api.wordpress.org/plugins/info/1.0/' . $l_str_PluginName . '.json'; - // call URL and collect data - $l_arr_Response = wp_remote_get( $l_str_Url ); - // check if response is valid - if ( is_wp_error( $l_arr_Response ) ) { - echo json_encode( array( 'error' => 'Error receiving Plugin Information from WordPress.' ) ); - exit; - } - if ( ! array_key_exists( 'body', $l_arr_Response ) ) { - echo json_encode( array( 'error' => 'Error reading WordPress API response message.' ) ); - exit; - } - // get the body of the response - $l_str_Response = $l_arr_Response['body']; - // get plugin object - $l_arr_Plugin = json_decode( $l_str_Response, true ); - if ( empty( $l_arr_Plugin ) ) { - echo json_encode( array( 'error' => 'Error reading Plugin meta information.
URL: ' . $l_str_Url . '
Response: ' . $l_str_Response ) ); - exit; - } - - $l_int_NumRatings = array_key_exists( 'num_ratings', $l_arr_Plugin ) ? intval( $l_arr_Plugin['num_ratings'] ) : 0; - $l_int_Rating = array_key_exists( 'rating', $l_arr_Plugin ) ? floatval( $l_arr_Plugin['rating'] ) : 0.0; - $l_int_Stars = round( 5 * $l_int_Rating / 100.0, 1 ); - - // return Plugin information as JSON encoded string - echo json_encode( - array( - 'error' => '', - 'PluginDescription' => array_key_exists( 'short_description', $l_arr_Plugin ) ? html_entity_decode( $l_arr_Plugin['short_description'] ) : 'Error reading Plugin information', - 'PluginAuthor' => array_key_exists( 'author', $l_arr_Plugin ) ? html_entity_decode( $l_arr_Plugin['author'] ) : 'unknown', - 'PluginRatingText' => $l_int_Stars . ' ' . __( 'rating based on', MCI_Footnotes_Config::C_STR_PLUGIN_NAME ) . ' ' . $l_int_NumRatings . ' ' . __( 'ratings', MCI_Footnotes_Config::C_STR_PLUGIN_NAME ), - 'PluginRating1' => $l_int_Stars >= 0.5 ? 'star-full' : 'star-empty', - 'PluginRating2' => $l_int_Stars >= 1.5 ? 'star-full' : 'star-empty', - 'PluginRating3' => $l_int_Stars >= 2.5 ? 'star-full' : 'star-empty', - 'PluginRating4' => $l_int_Stars >= 3.5 ? 'star-full' : 'star-empty', - 'PluginRating5' => $l_int_Stars >= 4.5 ? 'star-full' : 'star-empty', - 'PluginRating' => $l_int_NumRatings, - 'PluginLastUpdated' => array_key_exists( 'last_updated', $l_arr_Plugin ) ? $l_arr_Plugin['last_updated'] : 'unknown', - 'PluginDownloads' => array_key_exists( 'downloaded', $l_arr_Plugin ) ? $l_arr_Plugin['downloaded'] : '---', - ) - ); - exit; - } -} diff --git a/class/hooks.php b/class/hooks.php index ceb7147..7a9d253 100644 --- a/class/hooks.php +++ b/class/hooks.php @@ -1,94 +1,94 @@ -ClearAll(); - } - - /** - * Add Links to the Plugin in the "installed Plugins" page. - * - * @author Stefan Herndler - * @since 1.5.0 - * @param array $p_arr_Links Current Links. - * @param string $p_str_PluginFileName Plugins init file name. - * @return array - */ - public static function PluginLinks($p_arr_Links, $p_str_PluginFileName) { - // append link to the WordPress Plugin page - $p_arr_Links[] = sprintf('%s', __('Support', MCI_Footnotes_Config::C_STR_PLUGIN_NAME)); - // append link to the Settings page - $p_arr_Links[] = sprintf('%s', admin_url('admin.php?page=mfmmf-footnotes'), __('Settings', MCI_Footnotes_Config::C_STR_PLUGIN_NAME)); - // append link to the PlayPal Donate function - $p_arr_Links[] = sprintf('%s', __('Donate', MCI_Footnotes_Config::C_STR_PLUGIN_NAME)); - // return new links - return $p_arr_Links; - } -} +Clear_all();. + } + + /** + * Add Links to the Plugin in the "installed Plugins" page. + * + * @author Stefan Herndler + * @since 1.5.0 + * @param array $p_arr_links Current Links. + * @param string $p_str_plugin_file_name Plugins init file name. + * @return array + */ + public static function Plugin_links( $p_arr_links, $p_str_plugin_file_name ) { + // Append link to the WordPress Plugin page. + $p_arr_links[] = sprintf( '%s', __( 'Support', MCI_Footnotes_Config::C_STR_PLUGIN_NAME ) ); + // Append link to the Settings page. + $p_arr_links[] = sprintf( '%s', admin_url( 'admin.php?page=mfmmf-footnotes' ), __( 'Settings', MCI_Footnotes_Config::C_STR_PLUGIN_NAME ) ); + // Append link to the Play_pal Donate function. + $p_arr_links[] = sprintf( '%s', __( 'Donate', MCI_Footnotes_Config::C_STR_PLUGIN_NAME ) ); + // Return new links. + return $p_arr_links; + } +} diff --git a/class/init.php b/class/init.php index 2eb6ff4..32ba613 100644 --- a/class/init.php +++ b/class/init.php @@ -1,373 +1,381 @@ -initializeDashboard(); - // initialize the Plugin Task - $this->initializeTask(); - - // Register all Public Stylesheets and Scripts - add_action('init', array($this, 'registerPublic')); - // Enqueue all Public Stylesheets and Scripts - add_action('wp_enqueue_scripts', array($this, 'registerPublic')); - // Register all Widgets of the Plugin. - add_action('widgets_init', array($this, 'initializeWidgets')); - } - - /** - * Initializes all Widgets of the Plugin. - * - * @author Stefan Herndler - * @since 1.5.0 - * - * - * - Update: Fix for deprecated PHP function create_function(), thanks to @psykonevro @daliasued bug reports, thanks to @felipelavinz code contribution - * - * @since 1.6.5 - * - * @contributor @felipelavinz - * @link https://github.com/media-competence-institute/footnotes/commit/87173d2980c7ff90e12ffee94ca7153e11163793 - * - * @reporter @psykonevro - * @link https://wordpress.org/support/topic/bug-function-create_function-is-deprecated/ - * @link https://wordpress.org/support/topic/deprecated-function-create_function-14/ - * - * @reporter @daliasued - * @link https://wordpress.org/support/topic/deprecated-function-create_function-14/#post-13312853 - * - * create_function() was deprecated in PHP 7.2.0 and removed in PHP 8.0.0. - * @link https://www.php.net/manual/en/function.create-function.php - * - * The fix is to move add_action() above into run(), - * and use the bare register_widget() here. - * @see self::run() - * - * Also, the visibility of initializeWidgets() is not private any longer. - */ - public function initializeWidgets() { - register_widget( "MCI_Footnotes_Widget_ReferenceContainer" ); - } - - /** - * Initializes the Dashboard of the Plugin and loads them. - * - * @author Stefan Herndler - * @since 1.5.0 - */ - private function initializeDashboard() { - new MCI_Footnotes_Layout_Init(); - } - - /** - * Initializes the Plugin Task and registers the Task hooks. - * - * @author Stefan Herndler - * @since 1.5.0 - */ - private function initializeTask() { - $this->a_obj_Task = new MCI_Footnotes_Task(); - $this->a_obj_Task->registerHooks(); - } - - /** - * Registers and enqueues scripts and stylesheets to the public pages. - * - * @author Stefan Herndler - * @since 1.5.0 - * - * @since 2.0.0 Update: Tooltips: fix disabling bug by loading jQuery UI library, thanks to @rajinderverma @ericcorbett2 @honlapdavid @mmallett bug reports, thanks to @vonpiernik code contribution. - * @since 2.0.3 add versioning of public.css for cache busting 2020-10-29T1413+0100 - * @since 2.0.4 add jQuery UI from WordPress 2020-11-01T1902+0100 - * @since 2.1.4 automate passing version number for cache busting 2020-11-30T0646+0100 - * @since 2.1.4 optionally enqueue an extra stylesheet 2020-12-04T2231+0100 - */ - public function registerPublic() { - - /** - * Enqueues external scripts. - * - * - Bugfix: Libraries: optimize processes by loading external and internal scripts only if needed, thanks to @docteurfitness issue report. - * - * @since 2.5.5 - * @reporter @docteurfitness - * @link https://wordpress.org/support/topic/simply-speed-optimisation/ - * - * The condition about tooltips was missing, only the not-alternative-tooltips part was present. - */ - // set conditions re-used for stylesheet enqueuing: - self::$a_bool_TooltipsEnabled = MCI_Footnotes_Convert::toBool(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_BOOL_FOOTNOTES_MOUSE_OVER_BOX_ENABLED ) ); - self::$a_bool_AlternativeTooltipsEnabled = MCI_Footnotes_Convert::toBool(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_BOOL_FOOTNOTES_MOUSE_OVER_BOX_ALTERNATIVE ) ); - $l_str_ScriptMode = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_REFERENCE_CONTAINER_SCRIPT_MODE); - - /** - * Enqueues the jQuery library registered by WordPress. - * - * - Bugfix: Reference container: optional alternative expanding and collapsing without jQuery for use with hard links, thanks to @hopper87it @pkverma99 issue reports. - * - * @since 2.5.6 - * - * @reporter @hopper87it - * @link https://wordpress.org/support/topic/footnotes-wp-rocket/ - * - * jQuery is also used for animated scrolling, so it was loaded by default. - * The function wp_enqueue_script() avoids loading the same library multiple times. - * After adding the alternative reference container, jQuery has become optional, - * but still enabled by default. - */ - if ( $l_str_ScriptMode == 'jquery' || ( self::$a_bool_TooltipsEnabled && ! self::$a_bool_AlternativeTooltipsEnabled ) ) { - - wp_enqueue_script( 'jquery' ); - - } - - if ( self::$a_bool_TooltipsEnabled && ! self::$a_bool_AlternativeTooltipsEnabled ) { - - /** - * Enqueues the jQuery Tools library shipped with the plugin. - * - * redacted jQuery.browser, completed minification; - * see full header in js/jquery.tools.js - * added versioning 2020-11-18T2150+0100 - * not use '-js' in the handle, is appended automatically - */ - wp_enqueue_script( - 'mci-footnotes-jquery-tools', - plugins_url('footnotes/js/jquery.tools.min.js'), - array(), - '1.2.7.redacted.2' - ); - - /** - * Registers jQuery UI from the JavaScript Content Delivery Network. - * - * - Update: Tooltips: fix disabling bug by loading jQuery UI library, thanks to @rajinderverma @ericcorbett2 @honlapdavid @mmallett bug reports, thanks to @vonpiernik code contribution. - * - * @since 2.0.0 - * Alternatively, fetch jQuery UI from cdnjs.cloudflare.com: - * @since 2.0.0 add jQueryUI from Cloudflare 2020-10-26T1907+0100 - * Used to add jQuery UI following @vonpiernik: - * : - * - * - * jQueryUI re-enables the tooltip infobox disabled when WPv5.5 was released. - * - * Updated for v2.0.4 by adding jQuery UI from WordPress following @check2020de: - * - * See - * - * This was enabled in Footnotes v2.0.0 through v2.0.3. - * Re-added for 2.0.9d1 / 2.1.1d0 to look whether it can fix a broken tooltip display. 2020-11-07T1601+0100/2020-11-08T2246+0100 - */ - //wp_register_script( 'jQueryUI', 'https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js', null, null, false ); // in header 2020-11-09T2003+0100 - //wp_enqueue_script( 'jQueryUI' ); - /** - * This is then needed instead of the above first instance: - * Add jQuery Tools and finish adding jQueryUI: 2020-11-08T1638+0100/2020-11-08T2246+0100 - */ - //wp_enqueue_script('mci-footnotes-js-jquery-tools', plugins_url('../js/jquery.tools.min.js', __FILE__), ['jQueryUI']); - - /** - * Enqueues some jQuery UI libraries registered by WordPress. - * - * @since 2.0.4 add jQuery UI from WordPress 2020-11-01T1902+0100 - * If alternative tooltips are enabled, these libraries are not needed. - */ - wp_enqueue_script( 'jquery-ui-core' ); - wp_enqueue_script( 'jquery-ui-widget' ); - wp_enqueue_script( 'jquery-ui-position' ); - wp_enqueue_script( 'jquery-ui-tooltip' ); - - } - - /** - * Enables enqueuing a new-scheme stylesheet. - * - * @since 2.5.5 - * @date 2021-02-14T1512+0100 - * - * Enables enqueuing the formatted individual stylesheets if false. - * WARNING: This facility is designed for development and must NOT be used in production. - * - * The Boolean may be set at the bottom of the plugin’s main PHP file. - * @see footnotes.php - */ - if ( C_BOOL_CSS_PRODUCTION_MODE === true ) { - - /** - * Enqueues a minified united external stylesheet in production. - * - * - Update: Stylesheets: increase speed and energy efficiency by tailoring stylesheets to the needs of the instance, thanks to @docteurfitness design contribution. - * - Bugfix: Stylesheets: minify to shrink the carbon footprint, increase speed and implement best practice, thanks to @docteurfitness issue report. - * - * @since 2.5.5 - * @date 2021-02-14T1543+0100 - * - * @contributor @docteurfitness - * @link https://wordpress.org/support/topic/simply-speed-optimisation/ - * - * @reporter @docteurfitness - * @link https://wordpress.org/support/topic/simply-speed-optimisation/ - * - * The dashboard stylesheet is minified as-is. - * @see class/dashboard/layout.php - * - * @since 2.0.3 add versioning of public.css for cache busting. - * @date 2020-10-29T1413+0100 - * Plugin version number is needed for busting browser caches after each plugin update. - * @since 2.1.4 automate passing version number for cache busting. - * @date 2020-11-30T0646+0100 - * The constant C_STR_FOOTNOTES_VERSION is defined at start of footnotes.php. - * - * The media scope argument 'all' is the default. - * No need to use '-css' in the handle, as this is appended automatically. - */ - // set tooltip mode for use in stylesheet name: - if ( self::$a_bool_TooltipsEnabled ) { - if ( self::$a_bool_AlternativeTooltipsEnabled ) { - $l_str_TooltipMode = 'al'; - $l_str_TComplement = 'ternative-tooltips'; - } else { - $l_str_TooltipMode = 'jq'; - $l_str_TComplement = 'uery-tooltips'; - } - } else { - $l_str_TooltipMode = 'no'; - $l_str_TComplement = '-tooltips'; - } - - // set basic responsive page layout mode for use in stylesheet name: - $l_str_PageLayoutOption = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_PAGE_LAYOUT_SUPPORT); - switch ( $l_str_PageLayoutOption ) { - case "reference-container": $l_str_LayoutMode = '1'; break; - case "entry-content" : $l_str_LayoutMode = '2'; break; - case "main-content" : $l_str_LayoutMode = '3'; break; - case "none": default: $l_str_LayoutMode = '0'; break; - } - - // enqueue the tailored united minified stylesheet: - wp_enqueue_style( - 'mci-footnotes-' . $l_str_TooltipMode . $l_str_TComplement . '-pagelayout-' . $l_str_PageLayoutOption, - plugins_url( - MCI_Footnotes_Config::C_STR_PLUGIN_NAME . '/css/footnotes-' . $l_str_TooltipMode . 'ttbrpl' . $l_str_LayoutMode . '.min.css' - ), - array(), - C_STR_FOOTNOTES_VERSION, - 'all' - ); - - } else { - - /** - * Enqueues external stylesheets, ONLY in development now. - * - * @since 2.1.4 optionally enqueue an extra stylesheet. - * @date 2020-12-04T2231+0100 - * - * This optional layout fix is useful by lack of layout support. - */ - wp_enqueue_style( 'mci-footnotes-common', plugins_url( MCI_Footnotes_Config::C_STR_PLUGIN_NAME . '/css/dev-common.css' ), array(), C_STR_FOOTNOTES_VERSION ); - wp_enqueue_style( 'mci-footnotes-tooltips', plugins_url( MCI_Footnotes_Config::C_STR_PLUGIN_NAME . '/css/dev-tooltips.css' ), array(), C_STR_FOOTNOTES_VERSION ); - wp_enqueue_style( 'mci-footnotes-alternative', plugins_url( MCI_Footnotes_Config::C_STR_PLUGIN_NAME . '/css/dev-tooltips-alternative.css' ), array(), C_STR_FOOTNOTES_VERSION ); - - $l_str_PageLayoutOption = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_PAGE_LAYOUT_SUPPORT); - if ($l_str_PageLayoutOption != 'none') { - wp_enqueue_style( - 'mci-footnotes-layout-' . $l_str_PageLayoutOption, - plugins_url( - MCI_Footnotes_Config::C_STR_PLUGIN_NAME . '/css/dev-layout-' . $l_str_PageLayoutOption . '.css' - ), - array(), - C_STR_FOOTNOTES_VERSION, - 'all' - ); - } - } - } -} +initialize_dashboard(); + // Initialize the Plugin Task. + $this->initialize_task(); + + // Register all Public Stylesheets and Scripts. + add_action( 'init', array( $this, 'register_public' ) ); + // Enqueue all Public Stylesheets and Scripts. + add_action( 'wp_enqueue_scripts', array( $this, 'register_public' ) ); + // Register all Widgets of the Plugin.. + add_action( 'widgets_init', array( $this, 'initialize_widgets' ) ); + } + + /** + * Initializes all Widgets of the Plugin. + * + * @author Stefan Herndler + * @since 1.5.0 + * + * + * - Update: Fix for deprecated PHP function create_function(), thanks to @psykonevro @daliasued bug reports, thanks to @felipelavinz code contribution + * + * @since 1.6.5 + * + * @contributor @felipelavinz + * @link https://github.com/media-competence-institute/footnotes/commit/87173d2980c7ff90e12ffee94ca7153e11163793 + * + * @reporter @psykonevro + * @link https://wordpress.org/support/topic/bug-function-create_function-is-deprecated/ + * @link https://wordpress.org/support/topic/deprecated-function-create_function-14/ + * + * @reporter @daliasued + * @link https://wordpress.org/support/topic/deprecated-function-create_function-14/#post-13312853 + * + * create_function() was deprecated in PHP 7.2.0 and removed in PHP 8.0.0. + * @link https://www.php.net/manual/en/function.create-function.php + * + * The fix is to move add_action() above into run(), + * and use the bare register_widget() here. + * @see self::run() + * + * Also, the visibility of initialize_widgets() is not private any longer. + */ + public function initialize_widgets() { + register_widget( 'MCI_Footnotes_Widget_Reference_container' ); + } + + /** + * Initializes the Dashboard of the Plugin and loads them. + * + * @author Stefan Herndler + * @since 1.5.0 + */ + private function initialize_dashboard() { + new MCI_Footnotes_Layout_Init(); + } + + /** + * Initializes the Plugin Task and registers the Task hooks. + * + * @author Stefan Herndler + * @since 1.5.0 + */ + private function initialize_task() { + $this->a_obj_task = new MCI_Footnotes_Task(); + $this->a_obj_task->register_hooks(); + } + + /** + * Registers and enqueues scripts and stylesheets to the public pages. + * + * @author Stefan Herndler + * @since 1.5.0 + * + * @since 2.0.0 Update: Tooltips: fix disabling bug by loading j_query UI library, thanks to @rajinderverma @ericcorbett2 @honlapdavid @mmallett bug reports, thanks to @vonpiernik code contribution. + * @since 2.0.3 add versioning of public.css for cache busting 2020-10-29T1413+0100 + * @since 2.0.4 add j_query UI from WordPress 2020-11-01T1902+0100 + * @since 2.1.4 automate passing version number for cache busting 2020-11-30T0646+0100 + * @since 2.1.4 optionally enqueue an extra stylesheet 2020-12-04T2231+0100 + */ + public function register_public() { + + /** + * Enqueues external scripts. + * + * - Bugfix: Libraries: optimize processes by loading external and internal scripts only if needed, thanks to @docteurfitness issue report. + * + * @since 2.5.5 + * @reporter @docteurfitness + * @link https://wordpress.org/support/topic/simply-speed-optimisation/ + * + * The condition about tooltips was missing, only the not-alternative-tooltips part was present. + */ + // Set conditions re-used for stylesheet enqueuing:. + self::$a_bool_tooltips_enabled = MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_FOOTNOTES_MOUSE_OVER_BOX_ENABLED ) ); + self::$a_bool_alternative_tooltips_enabled = MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_FOOTNOTES_MOUSE_OVER_BOX_ALTERNATIVE ) ); + $l_str_script_mode = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_REFERENCE_CONTAINER_SCRIPT_MODE ); + + /** + * Enqueues the j_query library registered by WordPress. + * + * - Bugfix: Reference container: optional alternative expanding and collapsing without j_query for use with hard links, thanks to @hopper87it @pkverma99 issue reports. + * + * @since 2.5.6 + * + * @reporter @hopper87it + * @link https://wordpress.org/support/topic/footnotes-wp-rocket/ + * + * j_query is also used for animated scrolling, so it was loaded by default. + * The function wp_enqueue_script() avoids loading the same library multiple times. + * After adding the alternative reference container, j_query has become optional, + * but still enabled by default. + */ + if ( $l_str_script_mode == 'jquery' || ( self::$a_bool_tooltips_enabled && ! self::$a_bool_alternative_tooltips_enabled ) ) { + + wp_enqueue_script( 'jquery' ); + + } + + if ( self::$a_bool_tooltips_enabled && ! self::$a_bool_alternative_tooltips_enabled ) { + + /** + * Enqueues the j_query Tools library shipped with the plugin. + * + * redacted j_query.browser, completed minification; + * see full header in js/jquery.tools.js + * added versioning 2020-11-18T2150+0100 + * not use '-js' in the handle, is appended automatically + */ + wp_enqueue_script( + 'mci-footnotes-jquery-tools', + plugins_url( 'footnotes/js/jquery.tools.min.js' ), + array(), + '1.2.7.redacted.2' + ); + + /** + * Registers j_query UI from the Java_script Content Delivery Network. + * + * - Update: Tooltips: fix disabling bug by loading j_query UI library, thanks to @rajinderverma @ericcorbett2 @honlapdavid @mmallett bug reports, thanks to @vonpiernik code contribution. + * + * @since 2.0.0 + * Alternatively, fetch j_query UI from cdnjs.cloudflare.com: + * @since 2.0.0 add j_query_uI from Cloudflare 2020-10-26T1907+0100 + * Used to add j_query UI following @vonpiernik: + * : + * + * + * j_query_uI re-enables the tooltip infobox disabled when WPv5.5 was released. + * + * Updated for v2.0.4 by adding j_query UI from WordPress following @check2020de: + * + * See + * + * This was enabled in Footnotes v2.0.0 through v2.0.3. + * Re-added for 2.0.9d1 / 2.1.1d0 to look whether it can fix a broken tooltip display. 2020-11-07T1601+0100/2020-11-08T2246+0100 + */ + // Wp_register_script( 'j_query_uI', 'https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js', null, null, false ); // In header 2020-11-09T2003+0100. + // Wp_enqueue_script( 'j_query_uI' );. + /** + * This is then needed instead of the above first instance: + * Add j_query Tools and finish adding j_query_uI: 2020-11-08T1638+0100/2020-11-08T2246+0100 + */ + // Wp_enqueue_script('mci-footnotes-js-jquery-tools', plugins_url('../js/jquery.tools.min.js', __FILE__), ['j_query_uI']);. + + /** + * Enqueues some j_query UI libraries registered by WordPress. + * + * @since 2.0.4 add j_query UI from WordPress 2020-11-01T1902+0100 + * If alternative tooltips are enabled, these libraries are not needed. + */ + wp_enqueue_script( 'jquery-ui-core' ); + wp_enqueue_script( 'jquery-ui-widget' ); + wp_enqueue_script( 'jquery-ui-position' ); + wp_enqueue_script( 'jquery-ui-tooltip' ); + + } + + /** + * Enables enqueuing a new-scheme stylesheet. + * + * @since 2.5.5 + * @date 2021-02-14T1512+0100 + * + * Enables enqueuing the formatted individual stylesheets if false. + * WARNING: This facility is designed for development and must NOT be used in production. + * + * The Boolean may be set at the bottom of the plugin’s main PHP file. + * @see footnotes.php + */ + if ( C_BOOL_CSS_PRODUCTION_MODE === true ) { + + /** + * Enqueues a minified united external stylesheet in production. + * + * - Update: Stylesheets: increase speed and energy efficiency by tailoring stylesheets to the needs of the instance, thanks to @docteurfitness design contribution. + * - Bugfix: Stylesheets: minify to shrink the carbon footprint, increase speed and implement best practice, thanks to @docteurfitness issue report. + * + * @since 2.5.5 + * @date 2021-02-14T1543+0100 + * + * @contributor @docteurfitness + * @link https://wordpress.org/support/topic/simply-speed-optimisation/ + * + * @reporter @docteurfitness + * @link https://wordpress.org/support/topic/simply-speed-optimisation/ + * + * The dashboard stylesheet is minified as-is. + * @see class/dashboard/layout.php + * + * @since 2.0.3 add versioning of public.css for cache busting. + * @date 2020-10-29T1413+0100 + * Plugin version number is needed for busting browser caches after each plugin update. + * @since 2.1.4 automate passing version number for cache busting. + * @date 2020-11-30T0646+0100 + * The constant C_STR_FOOTNOTES_VERSION is defined at start of footnotes.php. + * + * The media scope argument 'all' is the default. + * No need to use '-css' in the handle, as this is appended automatically. + */ + // Set tooltip mode for use in stylesheet name:. + if ( self::$a_bool_tooltips_enabled ) { + if ( self::$a_bool_alternative_tooltips_enabled ) { + $l_str_tooltip_mode = 'al'; + $l_str_tcomplement = 'ternative-tooltips'; + } else { + $l_str_tooltip_mode = 'jq'; + $l_str_tcomplement = 'uery-tooltips'; + } + } else { + $l_str_tooltip_mode = 'no'; + $l_str_tcomplement = '-tooltips'; + } + + // Set basic responsive page layout mode for use in stylesheet name:. + $l_str_page_layout_option = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_PAGE_LAYOUT_SUPPORT ); + switch ( $l_str_page_layout_option ) { + case 'reference-container': + $l_str_layout_mode = '1'; + break; + case 'entry-content': + $l_str_layout_mode = '2'; + break; + case 'main-content': + $l_str_layout_mode = '3'; + break; + case 'none': + default: + $l_str_layout_mode = '0'; + break; + } + + // Enqueue the tailored united minified stylesheet:. + wp_enqueue_style( + 'mci-footnotes-' . $l_str_tooltip_mode . $l_str_tcomplement . '-pagelayout-' . $l_str_page_layout_option, + plugins_url( + MCI_Footnotes_Config::C_STR_PLUGIN_NAME . '/css/footnotes-' . $l_str_tooltip_mode . 'ttbrpl' . $l_str_layout_mode . '.min.css' + ), + array(), + C_STR_FOOTNOTES_VERSION, + 'all' + ); + + } else { + + /** + * Enqueues external stylesheets, ONLY in development now. + * + * @since 2.1.4 optionally enqueue an extra stylesheet. + * @date 2020-12-04T2231+0100 + * + * This optional layout fix is useful by lack of layout support. + */ + wp_enqueue_style( 'mci-footnotes-common', plugins_url( MCI_Footnotes_Config::C_STR_PLUGIN_NAME . '/css/dev-common.css' ), array(), C_STR_FOOTNOTES_VERSION ); + wp_enqueue_style( 'mci-footnotes-tooltips', plugins_url( MCI_Footnotes_Config::C_STR_PLUGIN_NAME . '/css/dev-tooltips.css' ), array(), C_STR_FOOTNOTES_VERSION ); + wp_enqueue_style( 'mci-footnotes-alternative', plugins_url( MCI_Footnotes_Config::C_STR_PLUGIN_NAME . '/css/dev-tooltips-alternative.css' ), array(), C_STR_FOOTNOTES_VERSION ); + + $l_str_page_layout_option = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_PAGE_LAYOUT_SUPPORT ); + if ( $l_str_page_layout_option != 'none' ) { + wp_enqueue_style( + 'mci-footnotes-layout-' . $l_str_page_layout_option, + plugins_url( + MCI_Footnotes_Config::C_STR_PLUGIN_NAME . '/css/dev-layout-' . $l_str_page_layout_option . '.css' + ), + array(), + C_STR_FOOTNOTES_VERSION, + 'all' + ); + } + } + } +} diff --git a/class/language.php b/class/language.php index 6172cdf..461e2a4 100644 --- a/class/language.php +++ b/class/language.php @@ -1,108 +1,107 @@ - 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_BOOL_FOOTNOTE_SHORTCODE_SYNTAX_VALIDATION_ENABLE => 'yes', - - self::C_STR_FOOTNOTES_COUNTER_STYLE => 'arabic_plain', - self::C_BOOL_COMBINE_IDENTICAL_FOOTNOTES => 'yes', - - self::C_BOOL_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_BOOL_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_BOOL_REFERENCE_CONTAINER_LABEL_BOTTOM_BORDER => 'yes', - self::C_BOOL_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_BOOL_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_BOOL_REFERENCE_CONTAINER_ROW_BORDERS_ENABLE => 'no', - - // backlink symbol: - self::C_BOOL_REFERENCE_CONTAINER_3COLUMN_LAYOUT_ENABLE => 'no', - self::C_BOOL_REFERENCE_CONTAINER_BACKLINK_SYMBOL_ENABLE => 'yes', - self::C_BOOL_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_BOOL_BACKLINKS_SEPARATOR_ENABLED => 'yes', - self::C_STR_BACKLINKS_SEPARATOR_OPTION => 'comma', - self::C_STR_BACKLINKS_SEPARATOR_CUSTOM => '', - self::C_BOOL_BACKLINKS_TERMINATOR_ENABLED => 'no', - self::C_STR_BACKLINKS_TERMINATOR_OPTION => 'full_stop', - self::C_STR_BACKLINKS_TERMINATOR_CUSTOM => '', - - // set backlinks column width: - self::C_BOOL_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_BOOL_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_BOOL_BACKLINKS_LINE_BREAKS_ENABLED => 'no', - - // whether to enable URL line wrapping: - self::C_BOOL_FOOTNOTE_URL_WRAP_ENABLED => 'yes', - - // whether to use link elements: - self::C_BOOL_LINK_ELEMENT_ENABLED => 'yes', - - // excerpt should be disabled: - self::C_BOOL_FOOTNOTES_IN_EXCERPT => 'no', - - self::C_BOOL_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_BOOL_FOOTNOTES_REFERRER_SUPERSCRIPT_TAGS => 'yes', - - self::C_STR_FOOTNOTES_STYLING_BEFORE => '[', - self::C_STR_FOOTNOTES_STYLING_AFTER => ']', - - self::C_BOOL_FOOTNOTES_MOUSE_OVER_BOX_ENABLED => 'yes', - - self::C_BOOL_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_BOOL_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_BOOL_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_BOOL_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_BOOL_EXPERT_LOOKUP_THE_TITLE => '', - - self::C_BOOL_EXPERT_LOOKUP_THE_CONTENT => 'checked', - - // And the_excerpt is disabled by default following @nikelaos in - // - // - self::C_BOOL_EXPERT_LOOKUP_THE_EXCERPT => '', - - self::C_BOOL_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_BOOL_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_BOOL_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. - * - * @author Stefan Herndler - * @since 1.5.0 - * @var array - */ - private $a_arr_Settings = array(); - - /** - * Class Constructor. Loads all Settings from each WordPress Settings container. - * - * @author Stefan Herndler - * @since 1.5.0 - */ - private function __construct() { - $this->loadAll(); - } - - /** - * Returns a singleton of this class. - * - * @author Stefan Herndler - * @since 1.5.0 - * @return MCI_Footnotes_Settings - */ - public static function instance() { - // no instance defined yet, load it - if (self::$a_obj_Instance === null) { - 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. - * - * @author Stefan Herndler - * @since 1.5.0 - * @param int $p_int_Index Settings Container Array Key Index. - * @return str Settings Container name. - */ - public function getContainer($p_int_Index) { - return $this->a_arr_Container[$p_int_Index]; - } - - /** - * Returns the default values of a specific Settings Container. - * - * @author Stefan Herndler - * @since 1.5.6 - * @param int $p_int_Index Settings Container Aray Key Index. - * @return array - */ - public function getDefaults($p_int_Index) { - return $this->a_arr_Default[$this->a_arr_Container[$p_int_Index]]; - } - - /** - * Loads all Settings from each Settings container. - * - * @author Stefan Herndler - * @since 1.5.0 - */ - private function loadAll() { - // clear current settings - $this->a_arr_Settings = array(); - for ($i = 0; $i < count($this->a_arr_Container); $i++) { - // load settings - $this->a_arr_Settings = array_merge($this->a_arr_Settings, $this->Load($i)); - } - } - - /** - * Loads all Settings from specified Settings Container. - * - * @author Stefan Herndler - * @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->getContainer($p_int_Index)); - // load all default settings - $l_arr_Default = $this->a_arr_Default[$this->getContainer($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. - * - * @author Stefan Herndler - * @since 1.5.0 - * @param int $p_int_Index Index of the Settings container. - * @param array $p_arr_newValues new Settings. - * @return bool - */ - public function saveOptions($p_int_Index, $p_arr_newValues) { - if (update_option($this->getContainer($p_int_Index), $p_arr_newValues)) { - $this->loadAll(); - return true; - } - return false; - } - - /** - * Returns the value of specified Settings name. - * - * @author Stefan Herndler - * @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. - * - * @author Stefan Herndler - * @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 ClearAll() { - // iterate through each Settings Container - for ($i = 0; $i < count($this->a_arr_Container); $i++) { - // delete the settings container - delete_option($this->getContainer($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. - * - * @author Stefan Herndler - * @since 1.5.0 - */ - public function RegisterSettings() { - // register all settings - for ($i = 0; $i < count($this->a_arr_Container); $i++) { - register_setting($this->getContainer($i), $this->getContainer($i)); - } - } -} + 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_BOOL_FOOTNOTE_SHORTCODE_SYNTAX_VALIDATION_ENABLE => 'yes', + + self::C_STR_FOOTNOTES_COUNTER_STYLE => 'arabic_plain', + self::C_BOOL_COMBINE_IDENTICAL_FOOTNOTES => 'yes', + + self::C_BOOL_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_BOOL_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_BOOL_REFERENCE_CONTAINER_LABEL_BOTTOM_BORDER => 'yes', + self::C_BOOL_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_BOOL_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_BOOL_REFERENCE_CONTAINER_ROW_BORDERS_ENABLE => 'no', + + // Backlink symbol:. + self::C_BOOL_REFERENCE_CONTAINER_3COLUMN_LAYOUT_ENABLE => 'no', + self::C_BOOL_REFERENCE_CONTAINER_BACKLINK_SYMBOL_ENABLE => 'yes', + self::C_BOOL_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_BOOL_BACKLINKS_SEPARATOR_ENABLED => 'yes', + self::C_STR_BACKLINKS_SEPARATOR_OPTION => 'comma', + self::C_STR_BACKLINKS_SEPARATOR_CUSTOM => '', + self::C_BOOL_BACKLINKS_TERMINATOR_ENABLED => 'no', + self::C_STR_BACKLINKS_TERMINATOR_OPTION => 'full_stop', + self::C_STR_BACKLINKS_TERMINATOR_CUSTOM => '', + + // Set backlinks column width:. + self::C_BOOL_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_BOOL_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_BOOL_BACKLINKS_LINE_BREAKS_ENABLED => 'no', + + // Whether to enable URL line wrapping:. + self::C_BOOL_FOOTNOTE_URL_WRAP_ENABLED => 'yes', + + // Whether to use link elements:. + self::C_BOOL_LINK_ELEMENT_ENABLED => 'yes', + + // Excerpt should be disabled:. + self::C_BOOL_FOOTNOTES_IN_EXCERPT => 'no', + + self::C_BOOL_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_BOOL_FOOTNOTES_REFERRER_SUPERSCRIPT_TAGS => 'yes', + + self::C_STR_FOOTNOTES_STYLING_BEFORE => '[', + self::C_STR_FOOTNOTES_STYLING_AFTER => ']', + + self::C_BOOL_FOOTNOTES_MOUSE_OVER_BOX_ENABLED => 'yes', + + self::C_BOOL_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_BOOL_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_BOOL_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_BOOL_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_BOOL_EXPERT_LOOKUP_THE_TITLE => '', + + self::C_BOOL_EXPERT_LOOKUP_THE_CONTENT => 'checked', + + // And the_excerpt is disabled by default following @nikelaos in. + // . + // . + self::C_BOOL_EXPERT_LOOKUP_THE_EXCERPT => '', + + self::C_BOOL_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_BOOL_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_BOOL_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. + * + * @author Stefan Herndler + * @since 1.5.0 + * @var array + */ + private $a_arr_settings = array(); + + /** + * Class Constructor. Loads all Settings from each WordPress Settings container. + * + * @author Stefan Herndler + * @since 1.5.0 + */ + private function __construct() { + $this->load_all(); + } + + /** + * Returns a singleton of this class. + * + * @author Stefan Herndler + * @since 1.5.0 + * @return MCI_Footnotes_Settings + */ + public static function instance() { + // No instance defined yet, load it. + if ( self::$a_obj_instance === null ) { + 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. + * + * @author Stefan Herndler + * @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. + * + * @author Stefan Herndler + * @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. + * + * @author Stefan Herndler + * @since 1.5.0 + */ + private function load_all() { + // Clear current settings. + $this->a_arr_settings = array(); + for ( $i = 0; $i < count( $this->a_arr_container ); $i++ ) { + // Load settings. + $this->a_arr_settings = array_merge( $this->a_arr_settings, $this->Load( $i ) ); + } + } + + /** + * Loads all Settings from specified Settings Container. + * + * @author Stefan Herndler + * @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. + * + * @author Stefan Herndler + * @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. + * + * @author Stefan Herndler + * @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. + * + * @author Stefan Herndler + * @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. + for ( $i = 0; $i < count( $this->a_arr_container ); $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. + * + * @author Stefan Herndler + * @since 1.5.0 + */ + public function Register_settings() { + // Register all settings. + for ( $i = 0; $i < count( $this->a_arr_container ); $i++ ) { + register_setting( $this->get_container( $i ), $this->get_container( $i ) ); + } + } +} diff --git a/class/task.php b/class/task.php index 4fae34f..589734f 100644 --- a/class/task.php +++ b/class/task.php @@ -1,2333 +1,2344 @@ -get(MCI_Footnotes_Settings::C_INT_EXPERT_LOOKUP_THE_TITLE_PRIORITY_LEVEL)); - $l_int_TheContentPriority = intval(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_INT_EXPERT_LOOKUP_THE_CONTENT_PRIORITY_LEVEL)); - $l_int_TheExcerptPriority = intval(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_INT_EXPERT_LOOKUP_THE_EXCERPT_PRIORITY_LEVEL)); - $l_int_WidgetTitlePriority = intval(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_INT_EXPERT_LOOKUP_WIDGET_TITLE_PRIORITY_LEVEL)); - $l_int_WidgetTextPriority = intval(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_INT_EXPERT_LOOKUP_WIDGET_TEXT_PRIORITY_LEVEL)); - - // PHP_INT_MAX can be set by -1: - $l_int_TheTitlePriority = ($l_int_TheTitlePriority == -1) ? PHP_INT_MAX : $l_int_TheTitlePriority ; - $l_int_TheContentPriority = ($l_int_TheContentPriority == -1) ? PHP_INT_MAX : $l_int_TheContentPriority ; - $l_int_TheExcerptPriority = ($l_int_TheExcerptPriority == -1) ? PHP_INT_MAX : $l_int_TheExcerptPriority ; - $l_int_WidgetTitlePriority = ($l_int_WidgetTitlePriority == -1) ? PHP_INT_MAX : $l_int_WidgetTitlePriority; - $l_int_WidgetTextPriority = ($l_int_WidgetTextPriority == -1) ? PHP_INT_MAX : $l_int_WidgetTextPriority ; - - - // append custom css to the header - add_filter('wp_head', array($this, "wp_head"), PHP_INT_MAX); - - // append the love and share me slug to the footer - add_filter('wp_footer', array($this, "wp_footer"), PHP_INT_MAX); - - if (MCI_Footnotes_Convert::toBool(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_BOOL_EXPERT_LOOKUP_THE_TITLE))) { - add_filter('the_title', array($this, "the_title"), $l_int_TheTitlePriority); - } - - // configurable priority level for reference container relative positioning; default 98: - if (MCI_Footnotes_Convert::toBool(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_BOOL_EXPERT_LOOKUP_THE_CONTENT))) { - add_filter('the_content', array($this, "the_content"), $l_int_TheContentPriority); - - /** - * Hook for category pages - * - * - Bugfix: Hooks: support footnotes on category pages, thanks to @vitaefit bug report, thanks to @misfist code contribution. - * - * @since 2.5.0 - * @date 2021-01-05T1402+0100 - * - * @contributor @misfist - * @link https://wordpress.org/support/topic/footnote-doesntwork-on-category-page/#post-13864859 - * - * @reporter @vitaefit - * @link https://wordpress.org/support/topic/footnote-doesntwork-on-category-page/ - * - * Category pages can have rich HTML content in a term description with article status. - * For this to happen, WordPress’ built-in partial HTML blocker needs to be disabled. - * @link https://docs.woocommerce.com/document/allow-html-in-term-category-tag-descriptions/ - */ - add_filter('term_description', array($this, "the_content"), $l_int_TheContentPriority); - - /** - * Hook for popup maker popups - * - * - Bugfix: Hooks: support footnotes in Popup Maker popups, thanks to @squatcher bug report. - * - * @since 2.5.1 - * @date 2021-01-18T2038+0100 - * - * @reporter @squatcher - * @link https://wordpress.org/support/topic/footnotes-use-in-popup-maker/ - */ - add_filter('pum_popup_content', array($this, "the_content"), $l_int_TheContentPriority); - } - - if (MCI_Footnotes_Convert::toBool(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_BOOL_EXPERT_LOOKUP_THE_EXCERPT))) { - add_filter('the_excerpt', array($this, "the_excerpt"), $l_int_TheExcerptPriority); - } - if (MCI_Footnotes_Convert::toBool(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_BOOL_EXPERT_LOOKUP_WIDGET_TITLE))) { - add_filter('widget_title', array($this, "widget_title"), $l_int_WidgetTitlePriority); - } - if (MCI_Footnotes_Convert::toBool(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_BOOL_EXPERT_LOOKUP_WIDGET_TEXT))) { - add_filter('widget_text', array($this, "widget_text"), $l_int_WidgetTextPriority); - } - - - /** - * The the_post hook - * - * - Adding: Hooks: support 'the_post' in response to user request for custom post types. - * - * @since 1.5.4 - * @accountable @aricura - * @link https://wordpress.org/support/topic/doesnt-work-in-custon-post-types/#post-5339110 - * - * - * - Update: Hooks: Default-enable all hooks to prevent footnotes from seeming broken in some parts. - * - * @since 2.0.5 - * @accountable @pewgeuges - * - * - * - BUGFIX: Hooks: Default-disable 'the_post', thanks to @spaceling @markcheret @nyamachi @whichgodsaves @spiralofhope2 @mmallett @andreasra @widecast @ymorin007 @tashi1es bug reports. - * - * @since 2.0.7 - * @accountable @pewgeuges - * @link https://wordpress.org/support/topic/change-the-position-5/page/2/#post-13630114 - * @link https://wordpress.org/support/topic/footnotes-appearing-in-header/#post-13630303 - * @link https://wordpress.org/support/topic/footnotes-appearing-in-header/page/2/#post-13630799 - * @link https://wordpress.org/support/topic/no-footnotes-anymore/#post-13813233 - * - * @reporter @spaceling - * @link https://wordpress.org/support/topic/change-the-position-5/#post-13612697 - * - * @reporter @markcheret on behalf of W. Beinert - * @link https://wordpress.org/support/topic/footnotes-now-appear-in-summaries-even-though-this-is-marked-no/ - * - * @reporter @nyamachi - * @link https://wordpress.org/support/topic/footnotes-appearing-in-header/ - * - * @reporter @whichgodsaves - * @link https://wordpress.org/support/topic/footnotes-appearing-in-header/#post-13622694 - * - * @reporter @spiralofhope2 - * @link https://wordpress.org/support/topic/2-0-5-broken/ - * - * @reporter @mmallett - * @link https://wordpress.org/support/topic/2-0-5-broken/#post-13623208 - * - * @reporter @andreasra - * @link https://wordpress.org/support/topic/footnotes-appearing-in-header/#post-13624091 - * - * @reporter @widecast - * @link https://wordpress.org/support/topic/2-0-5-broken/#post-13626222 - * - * @reporter @ymorin007 - * @link https://wordpress.org/support/topic/footnotes-appearing-in-header/#post-13627050 - * - * @reporter @markcheret on behalf of L. Smith - * @link https://wordpress.org/support/topic/footnotes-appear-in-random-places-on-academic-website/ - * - * @reporter @tashi1es - * @link https://wordpress.org/support/topic/footnotes-appear-in-random-places-on-academic-website/#post-13630495 - * - * - * - UPDATE: Hooks: remove 'the_post', the plugin stops supporting this hook. - * - * @since 2.1.0 - * @date 2020-11-08T1839+0100 - * @accountable @pewgeuges - */ - - // reset stored footnotes when displaying the header - self::$a_arr_Footnotes = array(); - self::$a_bool_AllowLoveMe = true; - } - - /** - * Outputs the custom css to the header of the public page. - * - * @author Stefan Herndler - * @since 1.5.0 - * - * - * @since 2.1.1 Bugfix: Reference container: fix start pages by making its display optional, thanks to @dragon013 bug report. - * @since 2.1.1 Bugfix: Tooltips: optional alternative JS implementation with CSS transitions to fix configuration-related outage, thanks to @andreasra feedback. - * @since 2.1.3 raise settings priority to override theme stylesheets - * @since 2.1.4 Bugfix: Tooltips: Styling: fix font size issue by adding font size to settings with legacy as default. - * @since 2.1.4 Bugfix: Reference container: fix layout issues by moving backlink column width to settings. - * @since 2.2.5 Bugfix: Reference container: Label: make bottom border an option, thanks to @markhillyer issue report. - * @since 2.2.5 Bugfix: Reference container: Label: option to select paragraph or heading element, thanks to @markhillyer issue report. - * @since 2.3.0 Bugfix: Reference container: convert top padding to margin and make it a setting, thanks to @hamshe bug report. - * @since 2.5.4 Bugfix: Referrers: optional fixes to vertical alignment, font size and position (static) for in-theme consistency and cross-theme stability, thanks to @tomturowski bug report. - */ - public function wp_head() { - - // insert start tag without switching out of PHP: - echo "\r\n\r\n"; - - /** - * Alternative tooltip implementation relying on plain JS and CSS transitions. - * - * - Bugfix: Tooltips: optional alternative JS implementation with CSS transitions to fix configuration-related outage, thanks to @andreasra feedback. - * - * @since 2.1.1 - * - * @reporter @andreasra - * @link https://wordpress.org/support/topic/footnotes-appearing-in-header/page/2/#post-13632566 - * - * The script for alternative tooltips is printed formatted, not minified: - */ - if ( self::$a_bool_AlternativeTooltipsEnabled ) { - ?> - -get(MCI_Footnotes_Settings::C_STR_REFERENCE_CONTAINER_POSITION) == "footer") { - echo $this->ReferenceContainer(); - } - // get setting for love and share this plugin - $l_str_LoveMeIndex = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_LOVE); - // check if the admin allows to add a link to the footer - if (empty($l_str_LoveMeIndex) || strtolower($l_str_LoveMeIndex) == "no" || !self::$a_bool_AllowLoveMe) { - return; - } - // set a hyperlink to the word "footnotes" in the Love slug - $l_str_LinkedName = sprintf('%s', MCI_Footnotes_Config::C_STR_PLUGIN_PUBLIC_NAME); - // get random love me text - if (strtolower($l_str_LoveMeIndex) == "random") { - $l_str_LoveMeIndex = "text-" . rand(1,7); - } - switch ($l_str_LoveMeIndex) { - // options named wrt backcompat, simplest is default: - case "text-1": $l_str_LoveMeText = sprintf(__('I %2$s %1$s', MCI_Footnotes_Config::C_STR_PLUGIN_NAME), $l_str_LinkedName, MCI_Footnotes_Config::C_STR_LOVE_SYMBOL); break; - case "text-2": $l_str_LoveMeText = sprintf(__('This website uses the awesome %s plugin.', MCI_Footnotes_Config::C_STR_PLUGIN_NAME), $l_str_LinkedName); break; - case "text-4": $l_str_LoveMeText = sprintf('%s %s', $l_str_LinkedName, MCI_Footnotes_Config::C_STR_LOVE_SYMBOL); break; - case "text-5": $l_str_LoveMeText = sprintf('%s %s', MCI_Footnotes_Config::C_STR_LOVE_SYMBOL, $l_str_LinkedName); break; - case "text-6": $l_str_LoveMeText = sprintf(__('This website uses %s.', MCI_Footnotes_Config::C_STR_PLUGIN_NAME), $l_str_LinkedName); break; - case "text-7": $l_str_LoveMeText = sprintf(__('This website uses the %s plugin.', MCI_Footnotes_Config::C_STR_PLUGIN_NAME), $l_str_LinkedName); break; - case "text-3": default: $l_str_LoveMeText = sprintf('%s', $l_str_LinkedName); break; - } - echo sprintf('
%s
', $l_str_LoveMeText); - } - - /** - * Replaces footnotes in the post/page title. - * - * @author Stefan Herndler - * @since 1.5.0 - * @param string $p_str_Content Widget content. - * @return string Content with replaced footnotes. - */ - public function the_title($p_str_Content) { - // appends the reference container if set to "post_end" - return $this->exec($p_str_Content, false); - } - - /** - * Replaces footnotes in the content of the current page/post. - * - * @author Stefan Herndler - * @since 1.5.0 - * @param string $p_str_Content Page/Post content. - * @return string Content with replaced footnotes. - */ - public function the_content($p_str_Content) { - // appends the reference container if set to "post_end" - return $this->exec($p_str_Content, MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_REFERENCE_CONTAINER_POSITION) == "post_end" ? true : false); - } - - /** - * Replaces footnotes in the excerpt of the current page/post. - * - * @author Stefan Herndler - * @since 1.5.0 - * @param string $p_str_Content Page/Post content. - * @return string Content with replaced footnotes. - */ - public function the_excerpt($p_str_Content) { - return $this->exec($p_str_Content, false, !MCI_Footnotes_Convert::toBool(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_BOOL_FOOTNOTES_IN_EXCERPT))); - } - - /** - * Replaces footnotes in the widget title. - * - * @author Stefan Herndler - * @since 1.5.0 - * @param string $p_str_Content Widget content. - * @return string Content with replaced footnotes. - */ - public function widget_title($p_str_Content) { - // appends the reference container if set to "post_end" - return $this->exec($p_str_Content, false); - } - - /** - * Replaces footnotes in the content of the current widget. - * - * @author Stefan Herndler - * @since 1.5.0 - * @param string $p_str_Content Widget content. - * @return string Content with replaced footnotes. - */ - public function widget_text($p_str_Content) { - // appends the reference container if set to "post_end" - return $this->exec($p_str_Content, MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_REFERENCE_CONTAINER_POSITION) == "post_end" ? true : false); - } - - /** - * Replaces footnotes in each Content var of the current Post object. - * - * @author Stefan Herndler - * @since 1.5.4 - * @param array|WP_Post $p_mixed_Posts - */ - public function the_post(&$p_mixed_Posts) { - // single WP_Post object received - if (!is_array($p_mixed_Posts)) { - $p_mixed_Posts = $this->replacePostObject($p_mixed_Posts); - return; - } - // array of WP_Post objects received - for($l_int_Index = 0; $l_int_Index < count($p_mixed_Posts); $l_int_Index++) { - $p_mixed_Posts[$l_int_Index] = $this->replacePostObject($p_mixed_Posts[$l_int_Index]); - } - } - - /** - * Replace all Footnotes in a WP_Post object. - * - * @author Stefan Herndler - * @since 1.5.6 - * @param WP_Post $p_obj_Post - * @return WP_Post - */ - private function replacePostObject($p_obj_Post) { - //MCI_Footnotes_Convert::debug($p_obj_Post); - $p_obj_Post->post_content = $this->exec($p_obj_Post->post_content); - $p_obj_Post->post_content_filtered = $this->exec($p_obj_Post->post_content_filtered); - $p_obj_Post->post_excerpt = $this->exec($p_obj_Post->post_excerpt); - return $p_obj_Post; - } - - /** - * Replaces all footnotes that occur in the given content. - * - * @author Stefan Herndler - * @since 1.5.0 - * @param string $p_str_Content Any string that may contain footnotes to be replaced. - * @param bool $p_bool_OutputReferences Appends the Reference Container to the output if set to true, default true. - * @param bool $p_bool_HideFootnotesText Hide footnotes found in the string. - * @return string - * - * - * @since 2.2.0 Adding: Reference container: support for custom position shortcode, thanks to @hamshe issue report. - * @since 2.2.5 Bugfix: Reference container: delete position shortcode if unused because position may be widget or footer, thanks to @hamshe bug report. - */ - public function exec($p_str_Content, $p_bool_OutputReferences = false, $p_bool_HideFootnotesText = false) { - - // replace all footnotes in the content, settings are converted to html characters - $p_str_Content = $this->search($p_str_Content, true, $p_bool_HideFootnotesText); - // replace all footnotes in the content, settings are NOT converted to html characters - $p_str_Content = $this->search($p_str_Content, false, $p_bool_HideFootnotesText); - - /** - * Reference container customized positioning through shortcode - * - * - Adding: Reference container: support for custom position shortcode, thanks to @hamshe issue report. - * - * @since 2.2.0 - * @date 2020-12-13T2057+0100 - * - * @reporter @hamshe - * @link https://wordpress.org/support/topic/reference-container-in-elementor/ - * - * - * - Bugfix: Reference container: delete position shortcode if unused because position may be widget or footer, thanks to @hamshe bug report. - * - * @since 2.2.5 - * @date 2020-12-18T1434+0100 - * - * @reporter @hamshe - * @link https://wordpress.org/support/topic/reference-container-in-elementor/#post-13784126 - * - */ - // append the reference container or insert at shortcode: - $l_str_ReferenceContainerPositionShortcode = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_REFERENCE_CONTAINER_POSITION_SHORTCODE); - if ( empty( $l_str_ReferenceContainerPositionShortcode ) ) { - $l_str_ReferenceContainerPositionShortcode = '[[references]]'; - } - - if ( $p_bool_OutputReferences ) { - - if ( strpos( $p_str_Content, $l_str_ReferenceContainerPositionShortcode ) !== false ) { - - $p_str_Content = str_replace( $l_str_ReferenceContainerPositionShortcode, $this->ReferenceContainer(), $p_str_Content ); - - } else { - - $p_str_Content .= $this->ReferenceContainer(); - - } - - // increment the container ID: - self::$a_int_ReferenceContainerId++; - } - - // delete position shortcode should any remain: - $p_str_Content = str_replace( $l_str_ReferenceContainerPositionShortcode, '', $p_str_Content ); - - // take a look if the LOVE ME slug should NOT be displayed on this page/post, remove the short code if found - if (strpos($p_str_Content, MCI_Footnotes_Config::C_STR_NO_LOVE_SLUG) !== false) { - self::$a_bool_AllowLoveMe = false; - $p_str_Content = str_replace(MCI_Footnotes_Config::C_STR_NO_LOVE_SLUG, "", $p_str_Content); - } - // return the content with replaced footnotes and optional reference container appended: - return $p_str_Content; - } - - /** - * Replaces all footnotes in the given content and appends them to the static property. - * - * @author Stefan Herndler - * @since 1.5.0 - * @param string $p_str_Content Content to be searched for footnotes. - * @param bool $p_bool_ConvertHtmlChars html encode settings, default true. - * @param bool $p_bool_HideFootnotesText Hide footnotes found in the string. - * @return string - * - * @since 2.0.0 various. - * @since 2.4.0 Adding: Footnote delimiters: syntax validation for balanced footnote start and end tag short codes. - * @since 2.5.0 Bugfix: Footnote delimiters: Syntax validation: exclude certain cases involving scripts, thanks to @andreasra bug report. - * @since 2.5.0 Bugfix: Footnote delimiters: Syntax validation: complete message with hint about setting, thanks to @andreasra bug report. - * @since 2.5.0 Bugfix: Footnote delimiters: Syntax validation: limit length of quoted string to 300 characters, thanks to @andreasra bug report. - */ - public function search($p_str_Content, $p_bool_ConvertHtmlChars, $p_bool_HideFootnotesText) { - - // post ID to make everything unique wrt infinite scroll and archive view - self::$a_int_PostId = get_the_id(); - - // contains the index for the next footnote on this page - $l_int_FootnoteIndex = count(self::$a_arr_Footnotes) + 1; - - // contains the starting position for the lookup of a footnote - $l_int_PosStart = 0; - - // get start and end tag for the footnotes short code - $l_str_StartingTag = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_SHORT_CODE_START); - $l_str_EndingTag = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_SHORT_CODE_END); - if ($l_str_StartingTag == "userdefined" || $l_str_EndingTag == "userdefined") { - $l_str_StartingTag = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_SHORT_CODE_START_USER_DEFINED); - $l_str_EndingTag = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_SHORT_CODE_END_USER_DEFINED); - } - // decode html special chars - if ($p_bool_ConvertHtmlChars) { - $l_str_StartingTag = htmlspecialchars($l_str_StartingTag); - $l_str_EndingTag = htmlspecialchars($l_str_EndingTag); - } - - // if footnotes short code is empty, return the content without changes - if (empty($l_str_StartingTag) || empty($l_str_EndingTag)) { - return $p_str_Content; - } - - /** - * Footnote delimiter syntax validation - * - * - Adding: Footnote delimiters: syntax validation for balanced footnote start and end tag short codes. - * - * @since 2.4.0 - * - * - * - Bugfix: Footnote delimiters: Syntax validation: exclude certain cases involving scripts, thanks to @andreasra bug report. - * - Bugfix: Footnote delimiters: Syntax validation: complete message with hint about setting, thanks to @andreasra bug report. - * - Bugfix: Footnote delimiters: Syntax validation: limit length of quoted string to 300 characters, thanks to @andreasra bug report. - * - * @since 2.5.0 - * @date 2021-01-07T0824+0100 - * - * @reporter @andreasra - * @link https://wordpress.org/support/topic/warning-unbalanced-footnote-start-tag-short-code-before/ - * - * - * If footnotes short codes are unbalanced, and syntax validation is not disabled, - * prepend a warning to the content; displays de facto beneath the post title. - */ - if (MCI_Footnotes_Convert::toBool(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_BOOL_FOOTNOTE_SHORTCODE_SYNTAX_VALIDATION_ENABLE))) { - - // make shortcodes conform to regex syntax: - $l_str_StartTagRegex = preg_replace( '#([\(\)\{\}\[\]\*\.\?\!])#', '\\\\$1', $l_str_StartingTag ); - $l_str_EndTagRegex = preg_replace( '#([\(\)\{\}\[\]\*\.\?\!])#', '\\\\$1', $l_str_EndingTag ); - - // apply different regex depending on whether start shortcode is double/triple opening parenthesis: - if ( $l_str_StartingTag == '((' || $l_str_StartingTag == '(((' ) { - - // this prevents from catching a script containing e.g. a double opening parenthesis: - $l_str_ValidationRegex = '#' . $l_str_StartTagRegex . '(((?!' . $l_str_EndTagRegex . ')[^\{\}])*?)(' . $l_str_StartTagRegex . '|$)#s'; - - } else { - - // catch all only if the start shortcode is not double/triple opening parenthesis, i.e. is unlikely to occur in scripts: - $l_str_ValidationRegex = '#' . $l_str_StartTagRegex . '(((?!' . $l_str_EndTagRegex . ').)*?)(' . $l_str_StartTagRegex . '|$)#s'; - } - - // check syntax and get error locations: - preg_match( $l_str_ValidationRegex, $p_str_Content, $p_arr_ErrorLocation ); - if ( empty( $p_arr_ErrorLocation ) ) { - self::$a_bool_SyntaxErrorFlag = false; - } - - // prevent generating and inserting the warning multiple times: - if ( self::$a_bool_SyntaxErrorFlag ) { - - // get plain text string for error location: - $l_str_ErrorSpotString = strip_tags( $p_arr_ErrorLocation[1] ); - - // limit string length to 300 characters: - if ( strlen( $l_str_ErrorSpotString ) > 300 ) { - $l_str_ErrorSpotString = substr( $l_str_ErrorSpotString, 0, 299 ) . '…'; - } - - // compose warning box: - $l_str_SyntaxErrorWarning = '

'; - $l_str_SyntaxErrorWarning .= __("WARNING: unbalanced footnote start tag short code found.", MCI_Footnotes_Config::C_STR_PLUGIN_NAME); - $l_str_SyntaxErrorWarning .= '

'; - - // syntax validation setting in the dashboard under the General settings tab: - $l_str_SyntaxErrorWarning .= sprintf( __("If this warning is irrelevant, please disable the syntax validation feature in the dashboard under %s > %s > %s.", MCI_Footnotes_Config::C_STR_PLUGIN_NAME), __("General settings", MCI_Footnotes_Config::C_STR_PLUGIN_NAME), __("Footnote start and end short codes", MCI_Footnotes_Config::C_STR_PLUGIN_NAME), __("Check for balanced shortcodes", MCI_Footnotes_Config::C_STR_PLUGIN_NAME) ); - - $l_str_SyntaxErrorWarning .= '

'; - $l_str_SyntaxErrorWarning .= __("Unbalanced start tag short code found before:", MCI_Footnotes_Config::C_STR_PLUGIN_NAME); - $l_str_SyntaxErrorWarning .= '

“'; - $l_str_SyntaxErrorWarning .= $l_str_ErrorSpotString; - $l_str_SyntaxErrorWarning .= '”

'; - - // prepend the warning box to the content: - $p_str_Content = $l_str_SyntaxErrorWarning . $p_str_Content; - - // checked, set flag to false to prevent duplicate warning: - self::$a_bool_SyntaxErrorFlag = false; - - return $p_str_Content; - } - } - - - // load referrer templates if footnotes text not hidden: - if (!$p_bool_HideFootnotesText) { - - // load footnote referrer template file: - if (self::$a_bool_AlternativeTooltipsEnabled) { - $l_obj_Template = new MCI_Footnotes_Template(MCI_Footnotes_Template::C_STR_PUBLIC, "footnote-alternative"); - } else { - $l_obj_Template = new MCI_Footnotes_Template(MCI_Footnotes_Template::C_STR_PUBLIC, "footnote"); - } - - /** - * Call Boolean again for robustness when priority levels don’t match any longer. - * - * - Bugfix: Tooltips: fix display in Popup Maker popups by correcting a coding error. - * - * @since 2.5.4 - * @see self::add_filter('pum_popup_content', array($this, "the_content"), $l_int_TheContentPriority) - */ - self::$a_bool_TooltipsEnabled = MCI_Footnotes_Convert::toBool(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_BOOL_FOOTNOTES_MOUSE_OVER_BOX_ENABLED)); - self::$a_bool_AlternativeTooltipsEnabled = MCI_Footnotes_Convert::toBool(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_BOOL_FOOTNOTES_MOUSE_OVER_BOX_ALTERNATIVE)); - - // load tooltip inline script if jQuery tooltips are enabled: - if (self::$a_bool_TooltipsEnabled && ! self::$a_bool_AlternativeTooltipsEnabled) { - $l_obj_TemplateTooltip = new MCI_Footnotes_Template(MCI_Footnotes_Template::C_STR_PUBLIC, "tooltip"); - } - - } else { - $l_obj_Template = null; - $l_obj_TemplateTooltip = null; - } - - // search footnotes short codes in the content - do { - // get first occurrence of the footnote start tag short code: - $i_int_LenContent = strlen($p_str_Content); - if ($l_int_PosStart > $i_int_LenContent) $l_int_PosStart = $i_int_LenContent; - $l_int_PosStart = strpos($p_str_Content, $l_str_StartingTag, $l_int_PosStart); - // no short code found, stop here - if ($l_int_PosStart === false) { - break; - } - // get first occurrence of the footnote end tag short code: - $l_int_PosEnd = strpos($p_str_Content, $l_str_EndingTag, $l_int_PosStart); - // no short code found, stop here - if ($l_int_PosEnd === false) { - break; - } - // calculate the length of the footnote - $l_int_Length = $l_int_PosEnd - $l_int_PosStart; - - // get footnote text - $l_str_FootnoteText = substr($p_str_Content, $l_int_PosStart + strlen($l_str_StartingTag), $l_int_Length - strlen($l_str_StartingTag)); - - // get tooltip text if present: - self::$a_str_TooltipShortcode = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_TOOLTIP_EXCERPT_DELIMITER); - self::$a_int_TooltipShortcodeLength = strlen( self::$a_str_TooltipShortcode ); - $l_int_TooltipTextLength = strpos( $l_str_FootnoteText, self::$a_str_TooltipShortcode ); - $l_bool_HasTooltipText = $l_int_TooltipTextLength === false ? false : true; - if ( $l_bool_HasTooltipText ) { - $l_str_TooltipText = substr( $l_str_FootnoteText, 0, $l_int_TooltipTextLength ); - } else { - $l_str_TooltipText = ''; - } - - /** - * URL line wrapping for Unicode non conformant browsers - * - * @since 2.1.1 (CSS) - * @since 2.1.4 (PHP) - * - * Despite Unicode recommends to line-wrap URLs at slashes, and Firefox follows - * the Unicode standard, Chrome does not, making long URLs hang out of tooltips - * or extend reference containers, so that the end is hidden outside the window - * and may eventually be viewed after we scroll horizontally or zoom out. It is - * up to the web page to make URLs breaking anywhere by wrapping them in a span - * that is assigned appropriate CSS properties and values. - * @see css/public.css - * - * - Bugfix: Tooltips: fix line breaking for hyperlinked URLs in Unicode-non-compliant user agents, thanks to @andreasra bug report. - * - * @since 2.1.1 - * - * @reporter @andreasra - * @link https://wordpress.org/support/topic/footnotes-appearing-in-header/page/3/#post-13657398 - * - * - * - Bugfix: Reference container: fix width in mobile view by URL wrapping for Unicode-non-conformant browsers, thanks to @karolszakiel bug report. - * - * @since 2.1.3 - * @date 2020-11-23 - * - * @reporter @karolszakiel - * @link https://wordpress.org/support/topic/footnotes-on-mobile-phones/ - * - * - * - Bugfix: Reference container, tooltips: fix line wrapping of URLs (hyperlinked or not) based on pattern, not link element. - * - * @since 2.1.4 - * @date 2020-11-25T0837+0100 - * @link https://wordpress.org/support/topic/footnotes-on-mobile-phones/#post-13710682 - * - * - * - Bugfix: Reference container, tooltips: URL wrap: exclude image source too, thanks to @bjrnet21 bug report. - * - * @since 2.1.5 - * - * @reporter @bjrnet21 - * @link https://wordpress.org/support/topic/2-1-4-breaks-on-my-site-images-dont-show/ - * - * - * - Bugfix: Reference container, tooltips: URL wrap: fix regex, thanks to @a223123131 bug report. - * - * @since 2.1.6 - * @date 2020-12-09T1921+0100 - * - * @reporter @a223123131 - * @link https://wordpress.org/support/topic/broken-layout-starting-version-2-1-4/ - * - * Even ARIA labels may take a URL as value, so use \w=[\'"] as a catch-all 2020-12-10T1005+0100 - * - * - Bugfix: Dashboard: URL wrap: add option to properly enable/disable URL wrap. - * - * @since 2.1.6 - * @date 2020-12-09T1606+0100 - * - * - * - Bugfix: Reference container, tooltips: URL wrap: make the quotation mark optional wrt query parameters, thanks to @spiralofhope2 bug report. - * - * @since 2.2.6 - * @date 2020-12-23T0409+0100 - * - * @reporter @spiralofhope2 - * @link https://wordpress.org/support/topic/two-links-now-breaks-footnotes-with-blogtext/ - * - * - * - Bugfix: Reference container, tooltips: URL wrap: remove a bug introduced in the regex, thanks to @rjl20 @spaceling @lukashuggenberg @klusik @friedrichnorth @bernardzit bug reports. - * - * @since 2.2.7 - * @date 2020-12-23T1046+0100 - * - * @reporter @rjl20 - * @link https://wordpress.org/support/topic/two-links-now-breaks-footnotes-with-blogtext/#post-13825479 - * - * @reporter @spaceling - * @link https://wordpress.org/support/topic/two-links-now-breaks-footnotes-with-blogtext/#post-13825532 - * - * @reporter @lukashuggenberg - * @link https://wordpress.org/support/topic/2-2-6-breaks-all-footnotes/ - * - * @reporter @klusik - * @link https://wordpress.org/support/topic/2-2-6-breaks-all-footnotes/#post-13825885 - * - * @reporter @friedrichnorth - * @link https://wordpress.org/support/topic/footnotes-dont-show-after-update-to-2-2-6/ - * - * @reporter @bernardzit - * @link https://wordpress.org/support/topic/footnotes-dont-show-after-update-to-2-2-6/#post-13826029 - * - * - * @since 2.2.8 Bugfix: Reference container, tooltips: URL wrap: correctly make the quotation mark optional wrt query parameters, thanks to @spiralofhope2 bug report. - * @date 2020-12-23T1107+0100 - * - * Correct is duplicating the negative lookbehind w/o quotes: '(?get( MCI_Footnotes_Settings::C_BOOL_FOOTNOTE_URL_WRAP_ENABLED ) ) ) { - - $l_str_FootnoteText = preg_replace( - '#(?$1', - $l_str_FootnoteText - ); - } - - // Text to be displayed instead of the footnote - $l_str_FootnoteReplaceText = ""; - - // whether hard links are enabled: - if (self::$a_bool_HardLinksEnable) { - - // get the configurable parts: - self::$a_str_ReferrerLinkSlug = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_REFERRER_FRAGMENT_ID_SLUG); - self::$a_str_FootnoteLinkSlug = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTE_FRAGMENT_ID_SLUG); - self::$a_str_LinkIdsSeparator = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_HARD_LINK_IDS_SEPARATOR); - - // streamline ID concatenation: - self::$a_str_PostContainerIdCompound = self::$a_str_LinkIdsSeparator; - self::$a_str_PostContainerIdCompound .= self::$a_int_PostId; - self::$a_str_PostContainerIdCompound .= self::$a_str_LinkIdsSeparator; - self::$a_str_PostContainerIdCompound .= self::$a_int_ReferenceContainerId; - self::$a_str_PostContainerIdCompound .= self::$a_str_LinkIdsSeparator; - - } - - // display the footnote referrers and the tooltips: - if (!$p_bool_HideFootnotesText) { - $l_int_Index = MCI_Footnotes_Convert::Index($l_int_FootnoteIndex, MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_COUNTER_STYLE)); - - // display only a truncated footnote text if option enabled: - $l_bool_EnableExcerpt = MCI_Footnotes_Convert::toBool(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_BOOL_FOOTNOTES_MOUSE_OVER_BOX_EXCERPT_ENABLED)); - $l_int_MaxLength = intval(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_INT_FOOTNOTES_MOUSE_OVER_BOX_EXCERPT_LENGTH)); - - // define excerpt text as footnote text by default: - $l_str_ExcerptText = $l_str_FootnoteText; - - /** - * Tooltip truncation - * - * - Adding: Tooltips: Read-on button: Label: configurable instead of localizable, thanks to @rovanov example provision. - * - * @since 2.1.0 - * @date 2020-11-08T2146+0100 - * - * @reporter @rovanov - * @link https://wordpress.org/support/topic/offset-x-axis-and-offset-y-axis-does-not-working/ - * - * If the tooltip truncation option is enabled, it’s done based on character count, - * and a trailing incomplete word is cropped. - * This is equivalent to the WordPress default excerpt generation, i.e. without a - * custom excerpt and without a delimiter. But WordPress does word count, usually 55. - */ - if (self::$a_bool_TooltipsEnabled && $l_bool_EnableExcerpt) { - $l_str_DummyText = strip_tags($l_str_FootnoteText); - if (is_int($l_int_MaxLength) && strlen($l_str_DummyText) > $l_int_MaxLength) { - $l_str_ExcerptText = substr($l_str_DummyText, 0, $l_int_MaxLength); - $l_str_ExcerptText = substr($l_str_ExcerptText, 0, strrpos($l_str_ExcerptText, ' ')); - $l_str_ExcerptText .= ' … <'; - $l_str_ExcerptText .= self::$a_bool_HardLinksEnable ? 'a' : 'span'; - $l_str_ExcerptText .= ' class="footnote_tooltip_continue" '; - $l_str_ExcerptText .= 'onclick="footnote_moveToAnchor_' . self::$a_int_PostId; - $l_str_ExcerptText .= '_' . self::$a_int_ReferenceContainerId; - $l_str_ExcerptText .= '(\'footnote_plugin_reference_' . self::$a_int_PostId; - $l_str_ExcerptText .= '_' . self::$a_int_ReferenceContainerId; - $l_str_ExcerptText .= "_$l_int_Index');\""; - - // if enabled, add the hard link fragment ID: - if (self::$a_bool_HardLinksEnable) { - - $l_str_ExcerptText .= ' href="#'; - $l_str_ExcerptText .= self::$a_str_FootnoteLinkSlug; - $l_str_ExcerptText .= self::$a_str_PostContainerIdCompound; - $l_str_ExcerptText .= $l_int_Index; - $l_str_ExcerptText .= '"'; - } - - $l_str_ExcerptText .= '>'; - - /** - * Configurable read-on button label - * - * - Adding: Tooltips: Read-on button: Label: configurable instead of localizable, thanks to @rovanov example provision. - * - * @since 2.1.0 - * @date 2020-11-08T2146+0100 - * - * @reporter @rovanov - * @link https://wordpress.org/support/topic/offset-x-axis-and-offset-y-axis-does-not-working/ - */ - $l_str_ExcerptText .= MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_TOOLTIP_READON_LABEL); - - $l_str_ExcerptText .= self::$a_bool_HardLinksEnable ? '' : ''; - } - } - - /** - * Referrers element superscript or baseline - * - * Referrers: new setting for vertical align: superscript (default) or baseline (optional), thanks to @cwbayer bug report - * @since 2.1.1 - * - * @reporter @cwbayer - * @link https://wordpress.org/support/topic/footnote-number-in-text-superscript-disrupts-leading/ - * - * define the HTML element to use for the referrers: - */ - if (MCI_Footnotes_Convert::toBool(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_BOOL_FOOTNOTES_REFERRER_SUPERSCRIPT_TAGS))) { - - $l_str_SupSpan = 'sup'; - - } else { - - $l_str_SupSpan = 'span'; - } - - // whether hard links are enabled: - if (self::$a_bool_HardLinksEnable) { - - self::$a_str_LinkSpan = 'a'; - self::$a_str_LinkCloseTag = ''; - // self::$a_str_LinkOpenTag will be defined as needed - - // compose hyperlink address (leading space is in template): - $l_str_FootnoteLinkArgument = 'href="#'; - $l_str_FootnoteLinkArgument .= self::$a_str_FootnoteLinkSlug; - $l_str_FootnoteLinkArgument .= self::$a_str_PostContainerIdCompound; - $l_str_FootnoteLinkArgument .= $l_int_Index; - $l_str_FootnoteLinkArgument .= '" class="footnote_hard_link"'; - - /** - * Compose fragment ID anchor with offset, for use in reference container. - * Empty span, child of empty span, to avoid tall dotted rectangles in browser. - */ - $l_str_ReferrerAnchorElement = ''; - - } else { - - /** - * Initialize hard link variables when hard links are disabled. - * - * - Bugfix: Process: initialize hard link address variables to empty string to fix 'undefined variable' bug, thanks to @a223123131 bug report. - * - * @since 2.4.0 - * @date 2021-01-04T1622+0100 - * - * @reporter @a223123131 - * @link https://wordpress.org/support/topic/wp_debug-php-notice/ - * - * If no hyperlink nor offset anchor is needed, initialize as empty. - */ - $l_str_FootnoteLinkArgument = ''; - $l_str_ReferrerAnchorElement = ''; - - // The link element is set independently as it may be needed for styling: - if ( MCI_Footnotes_Convert::toBool(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_BOOL_LINK_ELEMENT_ENABLED)) ) { - - self::$a_str_LinkSpan = 'a'; - self::$a_str_LinkOpenTag = ''; - self::$a_str_LinkCloseTag = ''; - - } - } - - // determine tooltip content: - if ( self::$a_bool_TooltipsEnabled ) { - $l_str_TooltipContent = $l_bool_HasTooltipText ? $l_str_TooltipText : $l_str_ExcerptText; - } else { - $l_str_TooltipContent = ''; - } - - /** - * Determine shrink width if alternative tooltips are enabled. - * - * @since 2.5.6 - */ - $l_str_TooltipStyle = ''; - if ( self::$a_bool_AlternativeTooltipsEnabled && self::$a_bool_TooltipsEnabled ) { - $l_int_TooltipLength = strlen( strip_tags( $l_str_TooltipContent ) ); - if ( $l_int_TooltipLength < 70 ) { - $l_str_TooltipStyle = ' style="width: '; - $l_str_TooltipStyle .= ( $l_int_TooltipLength * .7 ); - $l_str_TooltipStyle .= 'em;"'; - } - } - - // fill in 'templates/public/footnote.html': - $l_obj_Template->replace( - array( - "link-span" => self::$a_str_LinkSpan, - "post_id" => self::$a_int_PostId, - "container_id" => self::$a_int_ReferenceContainerId, - "note_id" => $l_int_Index, - "hard-link" => $l_str_FootnoteLinkArgument, - "sup-span" => $l_str_SupSpan, - "before" => MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_STYLING_BEFORE), - "index" => $l_int_Index, - "after" => MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_STYLING_AFTER), - "anchor-element" => $l_str_ReferrerAnchorElement, - "style" => $l_str_TooltipStyle, - "text" => $l_str_TooltipContent, - ) - ); - $l_str_FootnoteReplaceText = $l_obj_Template->getContent(); - - // reset the template - $l_obj_Template->reload(); - - // if standard tooltips are enabled but alternative are not: - if (self::$a_bool_TooltipsEnabled && ! self::$a_bool_AlternativeTooltipsEnabled) { - - $l_int_OffsetY = intval(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_INT_FOOTNOTES_MOUSE_OVER_BOX_OFFSET_Y)); - $l_int_OffsetX = intval(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_INT_FOOTNOTES_MOUSE_OVER_BOX_OFFSET_X)); - $l_int_FadeInDelay = intval(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_INT_MOUSE_OVER_BOX_FADE_IN_DELAY )); - $l_int_FadeInDuration = intval(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_INT_MOUSE_OVER_BOX_FADE_IN_DURATION )); - $l_int_FadeOutDelay = intval(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_INT_MOUSE_OVER_BOX_FADE_OUT_DELAY )); - $l_int_FadeOutDuration = intval(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_INT_MOUSE_OVER_BOX_FADE_OUT_DURATION)); - - // fill in 'templates/public/tooltip.html': - $l_obj_TemplateTooltip->replace( - array( - "post_id" => self::$a_int_PostId, - "container_id" => self::$a_int_ReferenceContainerId, - "note_id" => $l_int_Index, - "position" => MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_MOUSE_OVER_BOX_POSITION), - "offset-y" => !empty($l_int_OffsetY) ? $l_int_OffsetY : 0, - "offset-x" => !empty($l_int_OffsetX) ? $l_int_OffsetX : 0, - "fade-in-delay" => !empty($l_int_FadeInDelay ) ? $l_int_FadeInDelay : 0, - "fade-in-duration" => !empty($l_int_FadeInDuration ) ? $l_int_FadeInDuration : 0, - "fade-out-delay" => !empty($l_int_FadeOutDelay ) ? $l_int_FadeOutDelay : 0, - "fade-out-duration" => !empty($l_int_FadeOutDuration) ? $l_int_FadeOutDuration : 0, - ) - ); - $l_str_FootnoteReplaceText .= $l_obj_TemplateTooltip->getContent(); - $l_obj_TemplateTooltip->reload(); - } - } - // replace the footnote with the template - $p_str_Content = substr_replace($p_str_Content, $l_str_FootnoteReplaceText, $l_int_PosStart, $l_int_Length + strlen($l_str_EndingTag)); - - // add footnote only if not empty - if (!empty($l_str_FootnoteText)) { - // set footnote to the output box at the end - self::$a_arr_Footnotes[] = $l_str_FootnoteText; - // increase footnote index - $l_int_FootnoteIndex++; - } - - /** - * Fixes a footnotes numbering bug (happening under de facto rare circumstances). - * - * - Bugfix: Fixed occasional bug where footnote ordering could be out of sequence - * - * @since 1.6.4 - * @date 2016-06-29T0054+0000 - * @committer @dartiss - * @link https://plugins.trac.wordpress.org/browser/footnotes/trunk/class/task.php?rev=1445718 @dartiss’ class/task.php - * @link https://plugins.trac.wordpress.org/log/footnotes/trunk/class/task.php?rev=1445718 @dartiss re-added class/task.php - * @link https://plugins.trac.wordpress.org/browser/footnotes/trunk/class?rev=1445711 class/ w/o task.php - * @link https://plugins.trac.wordpress.org/changeset/1445711/footnotes/trunk/class @dartiss deleted class/task.php - * @link https://plugins.trac.wordpress.org/browser/footnotes/trunk/class/task.php?rev=1026210 @aricura’s latest class/task.php - * - * - * - Bugfix: Process: fix numbering bug impacting footnote #2 with footnote #1 close to start, thanks to @rumperuu bug report, thanks to @lolzim code contribution. - * - * @since 2.5.5 - * - * @contributor @lolzim - * @link https://wordpress.org/support/topic/footnotes-numbered-incorrectly/#post-14062032 - * - * @reporter @rumperuu - * @link https://wordpress.org/support/topic/footnotes-numbered-incorrectly/ - * - * This assignment was overridden by another one, causing the algorithm to jump back - * near the post start to a position calculated as the sum of the length of the last - * footnote and the length of the last footnote replace text. - * A bug disturbing the order of the footnotes depending on the text before the first - * footnote, the length of the first footnote and the length of the templates for the - * footnote and the tooltip. Moreover, it was causing non-trivial process garbage. - */ - // add offset to the new starting position - $l_int_PosStart += $l_int_Length + strlen($l_str_EndingTag); - - } while (true); - - // return content - return $p_str_Content; - } - - /** - * Generates the reference container. - * - * @author Stefan Herndler - * @since 1.5.0 - * @return string - * - * @since 2.0.0 Update: remove backlink symbol along with column 2 of the reference container - * @since 2.0.3 Bugfix: prepend an arrow on user request - * @since 2.0.6 Bugfix: Reference container: fix line breaking behavior in footnote number clusters. - * @since 2.0.4 Bugfix: restore the arrow select and backlink symbol input settings - * @since 2.1.1 Bugfix: Referrers, reference container: Combining identical footnotes: fix dead links and ensure referrer-backlink bijectivity, thanks to @happyches bug report. - * @since 2.1.1 Bugfix: Reference container: Backlink symbol: make optional, not suggest configuring it to invisible, thanks to @spaceling feedback. - */ - public function ReferenceContainer() { - - // no footnotes have been replaced on this page: - if (empty(self::$a_arr_Footnotes)) { - return ""; - } - - - /** - * Footnote index backlink symbol - * - * - Bugfix: Reference container: Backlink symbol: make optional, not suggest configuring it to invisible, thanks to @spaceling feedback. - * - * @since 2.1.1 - * - * @reporter @spaceling - * @link https://wordpress.org/support/topic/change-the-position-5/page/2/#post-13671138 - * - * If the backlink symbol is enabled: - */ - if (MCI_Footnotes_Convert::toBool(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_BOOL_REFERENCE_CONTAINER_BACKLINK_SYMBOL_ENABLE))) { - - // get html arrow - $l_str_Arrow = MCI_Footnotes_Convert::getArrow(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_HYPERLINK_ARROW)); - // set html arrow to the first one if invalid index defined - if (is_array($l_str_Arrow)) { - $l_str_Arrow = MCI_Footnotes_Convert::getArrow(0); - } - // get user defined arrow - $l_str_ArrowUserDefined = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_HYPERLINK_ARROW_USER_DEFINED); - if (!empty($l_str_ArrowUserDefined)) { - $l_str_Arrow = $l_str_ArrowUserDefined; - } - - // wrap the arrow in a @media print { display:hidden } span: - $l_str_FootnoteArrow = ''; - $l_str_FootnoteArrow .= $l_str_Arrow . ''; - - } else { - - // If the backlink symbol isn’t enabled, set it to empty: - $l_str_Arrow = ''; - $l_str_FootnoteArrow = ''; - - } - - - /** - * Backlink separator - * - * - Bugfix: Reference container: make separating and terminating punctuation optional and configurable, thanks to @docteurfitness issue report and code contribution. - * - * @since 2.1.4 - * @date 2020-11-28T1048+0100 - * - * @contributor @docteurfitness - * @link https://wordpress.org/support/topic/update-2-1-3/#post-13704194 - * - * @reporter @docteurfitness - * @link https://wordpress.org/support/topic/update-2-1-3/ - * - * Initially a comma was appended in this algorithm for enumerations. - * The comma in enumerations is not generally preferred. - */ - if (MCI_Footnotes_Convert::toBool(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_BOOL_BACKLINKS_SEPARATOR_ENABLED))) { - - // check if it is input-configured: - $l_str_Separator = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_BACKLINKS_SEPARATOR_CUSTOM); - - if (empty($l_str_Separator)) { - - // if it is not, check which option is on: - $l_str_SeparatorOption = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_BACKLINKS_SEPARATOR_OPTION); - switch ($l_str_SeparatorOption) { - case 'comma' : $l_str_Separator = ','; break; - case 'semicolon': $l_str_Separator = ';'; break; - case 'en_dash' : $l_str_Separator = ' –'; break; - } - } - - } else { - - $l_str_Separator = ''; - } - - /** - * Backlink terminator - * - * Initially a dot was appended in the table row template. - * @since 2.0.6 a dot after footnote numbers is discarded as not localizable; - * making it optional was envisaged. - * @since 2.1.4 the terminator is optional, has options, and is configurable: - */ - if (MCI_Footnotes_Convert::toBool(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_BOOL_BACKLINKS_TERMINATOR_ENABLED))) { - - // check if it is input-configured: - $l_str_Terminator = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_BACKLINKS_TERMINATOR_CUSTOM); - - if (empty($l_str_Terminator)) { - - // if it is not, check which option is on: - $l_str_TerminatorOption = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_BACKLINKS_TERMINATOR_OPTION); - switch ($l_str_TerminatorOption) { - case 'period' : $l_str_Terminator = '.'; break; - case 'parenthesis': $l_str_Terminator = ')'; break; - case 'colon' : $l_str_Terminator = ':'; break; - } - } - - } else { - - $l_str_Terminator = ''; - } - - - /** - * Line breaks - * - * - Bugfix: Reference container: Backlinks: fix stacked enumerations by adding optional line breaks. - * - * @since 2.1.4 - * @date 2020-11-28T1049+0100 - * - * The backlinks of combined footnotes are generally preferred in an enumeration. - * But when few footnotes are identical, stacking the items in list form is better. - * Variable number length and proportional character width require explicit line breaks. - * Otherwise, an ordinary space character offering a line break opportunity is inserted. - */ - $l_str_LineBreak = MCI_Footnotes_Convert::toBool(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_BOOL_BACKLINKS_LINE_BREAKS_ENABLED)) ? '
' : ' '; - - /** - * For maintenance and support, table rows in the reference container should be - * separated by an empty line. So we add these line breaks for source readability. - * Before the first table row (breaks between rows are ~200 lines below): - */ - $l_str_Body = "\r\n\r\n"; - - - /** - * Reference container table row template load - * - * - Bugfix: Reference container: option to restore pre-2.0.0 layout with the backlink symbol in an extra column. - * - * @since 2.1.1 - * @date 2020-11-16T2024+0100 - */ - - // when combining identical footnotes is turned on, another template is needed: - if (MCI_Footnotes_Convert::toBool(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_BOOL_COMBINE_IDENTICAL_FOOTNOTES))) { - // the combining template allows for backlink clusters and supports cell clicking for single notes: - $l_obj_Template = new MCI_Footnotes_Template(MCI_Footnotes_Template::C_STR_PUBLIC, "reference-container-body-combi"); - - } else { - - // when 3-column layout is turned on (only available if combining is turned off): - if (MCI_Footnotes_Convert::toBool(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_BOOL_REFERENCE_CONTAINER_3COLUMN_LAYOUT_ENABLE))) { - $l_obj_Template = new MCI_Footnotes_Template(MCI_Footnotes_Template::C_STR_PUBLIC, "reference-container-body-3column"); - - } else { - - // when switch symbol and index is turned on, and combining and 3-columns are off: - if (MCI_Footnotes_Convert::toBool(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_BOOL_REFERENCE_CONTAINER_BACKLINK_SYMBOL_SWITCH))) { - $l_obj_Template = new MCI_Footnotes_Template(MCI_Footnotes_Template::C_STR_PUBLIC, "reference-container-body-switch"); - - } else { - - // default is the standard template: - $l_obj_Template = new MCI_Footnotes_Template(MCI_Footnotes_Template::C_STR_PUBLIC, "reference-container-body"); - - } - } - } - - /** - * Switch backlink symbol and footnote number - * - * - Bugfix: Reference container: option to append symbol (prepended by default), thanks to @spaceling code contribution. - * - * @since 2.1.1 - * @date 2020-11-16T2024+0100 - * - * @contributor @spaceling - * @link https://wordpress.org/support/topic/change-the-position-5/#post-13615994 - * - * - * - Bugfix: Reference container: Backlink symbol: support for appending when combining identicals is on. - * - * @since 2.1.4 - * @date 2020-11-26T1633+0100 - */ - $l_bool_SymbolSwitch = MCI_Footnotes_Convert::toBool(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_BOOL_REFERENCE_CONTAINER_BACKLINK_SYMBOL_SWITCH)); - - // loop through all footnotes found in the page - for ($l_int_Index = 0; $l_int_Index < count(self::$a_arr_Footnotes); $l_int_Index++) { - - // get footnote text - $l_str_FootnoteText = self::$a_arr_Footnotes[$l_int_Index]; - - // if footnote is empty, go to the next one; - // With combine identicals turned on, identicals will be deleted and are skipped: - if (empty($l_str_FootnoteText)) { - continue; - } - - // generate content of footnote index cell - $l_int_FirstFootnoteIndex = ($l_int_Index + 1); - - // get the footnote index string and - // keep supporting legacy index placeholder: - $l_str_FootnoteId = MCI_Footnotes_Convert::Index(($l_int_Index + 1), MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_COUNTER_STYLE)); - - /** - * Case of only one backlink per table row - * - * If enabled, and for the case the footnote is single, compose hard link: - */ - // define anyway: - $l_str_HardLinkAddress = ''; - - if (self::$a_bool_HardLinksEnable) { - - /** - * Use-Backbutton-Hint tooltip, optional and configurable. - * - * - Update: Reference container: Hard backlinks (optional): optional configurable tooltip hinting to use the backbutton instead, thanks to @theroninjedi47 bug report. - * - * @since 2.5.4 - * - * @reporter @theroninjedi47 - * @link https://wordpress.org/support/topic/hyperlinked-footnotes-creating-excessive-back-history/ - * - * When hard links are enabled, clicks on the backlinks are logged in the browsing history. - * This tooltip hints to use the backbutton instead, so the history gets streamlined again. - * @link https://wordpress.org/support/topic/making-it-amp-compatible/#post-13837359 - */ - if ( MCI_Footnotes_Convert::toBool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_FOOTNOTES_BACKLINK_TOOLTIP_ENABLE ) ) ) { - $l_str_UseBackbuttonHint = ' title="'; - $l_str_UseBackbuttonHint .= MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_BACKLINK_TOOLTIP_TEXT); - $l_str_UseBackbuttonHint .= '"'; - } else { - $l_str_UseBackbuttonHint = ''; - } - - /** - * Compose fragment ID anchor with offset, for use in reference container. - * Empty span, child of empty span, to avoid tall dotted rectangles in browser. - */ - $l_str_FootnoteAnchorElement = ''; - - // compose optional hard link address: - $l_str_HardLinkAddress = ' href="#'; - $l_str_HardLinkAddress .= self::$a_str_ReferrerLinkSlug; - $l_str_HardLinkAddress .= self::$a_str_PostContainerIdCompound; - $l_str_HardLinkAddress .= $l_str_FootnoteId . '"'; - $l_str_HardLinkAddress .= $l_str_UseBackbuttonHint; - - // compose optional opening link tag with optional hard link, mandatory for instance: - self::$a_str_LinkOpenTag = 'get(MCI_Footnotes_Settings::C_BOOL_COMBINE_IDENTICAL_FOOTNOTES))) { - - // ID, optional hard link address, and class: - $l_str_FootnoteReference = '<' . self::$a_str_LinkSpan; - $l_str_FootnoteReference .= ' id="footnote_plugin_reference_'; - $l_str_FootnoteReference .= self::$a_int_PostId; - $l_str_FootnoteReference .= '_' . self::$a_int_ReferenceContainerId; - $l_str_FootnoteReference .= "_$l_str_FootnoteId\""; - if (self::$a_bool_HardLinksEnable) { - $l_str_FootnoteReference .= ' href="#'; - $l_str_FootnoteReference .= self::$a_str_ReferrerLinkSlug; - $l_str_FootnoteReference .= self::$a_str_PostContainerIdCompound; - $l_str_FootnoteReference .= $l_str_FootnoteId . '"'; - $l_str_FootnoteReference .= $l_str_UseBackbuttonHint; - } - $l_str_FootnoteReference .= ' class="footnote_backlink"'; - - // the click event goes in the table cell if footnote remains single: - $l_str_BacklinkEvent = ' onclick="footnote_moveToAnchor_'; - $l_str_BacklinkEvent .= self::$a_int_PostId; - $l_str_BacklinkEvent .= '_' . self::$a_int_ReferenceContainerId; - $l_str_BacklinkEvent .= "('footnote_plugin_tooltip_"; - $l_str_BacklinkEvent .= self::$a_int_PostId; - $l_str_BacklinkEvent .= '_' . self::$a_int_ReferenceContainerId; - $l_str_BacklinkEvent .= "_$l_str_FootnoteId');\""; - - - // the dedicated template enumerating backlinks uses another variable: - $l_str_FootnoteBacklinks = $l_str_FootnoteReference; - - // append the click event right to the backlink item for enumerations; - // else it goes in the table cell: - $l_str_FootnoteBacklinks .= $l_str_BacklinkEvent . '>'; - $l_str_FootnoteReference .= '>'; - - // append the optional offset anchor for hard links: - if (self::$a_bool_HardLinksEnable) { - $l_str_FootnoteReference .= $l_str_FootnoteAnchorElement; - $l_str_FootnoteBacklinks .= $l_str_FootnoteAnchorElement; - } - - // continue both single note and notes cluster, depending on switch option status: - if ($l_bool_SymbolSwitch) { - - $l_str_FootnoteReference .= "$l_str_FootnoteId$l_str_FootnoteArrow"; - $l_str_FootnoteBacklinks .= "$l_str_FootnoteId$l_str_FootnoteArrow"; - - } else { - - $l_str_FootnoteReference .= "$l_str_FootnoteArrow$l_str_FootnoteId"; - $l_str_FootnoteBacklinks .= "$l_str_FootnoteArrow$l_str_FootnoteId"; - - } - - // If that is the only footnote with this text, we’re almost done. - - // check if it isn't the last footnote in the array: - if ($l_int_FirstFootnoteIndex < count(self::$a_arr_Footnotes)) { - - // get all footnotes that haven't passed yet: - for ($l_int_CheckIndex = $l_int_FirstFootnoteIndex; $l_int_CheckIndex < count(self::$a_arr_Footnotes); $l_int_CheckIndex++) { - - // check if a further footnote is the same as the actual one: - if ($l_str_FootnoteText == self::$a_arr_Footnotes[$l_int_CheckIndex]) { - - // if so, set the further footnote as empty so it won't be displayed later: - self::$a_arr_Footnotes[$l_int_CheckIndex] = ""; - - // set the flag to true for the combined status: - $l_bool_FlagCombined = true; - - // update the footnote ID: - $l_str_FootnoteId = MCI_Footnotes_Convert::Index(($l_int_CheckIndex + 1), MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_COUNTER_STYLE)); - - // resume composing the backlinks enumeration: - $l_str_FootnoteBacklinks .= "$l_str_Separator'; - $l_str_FootnoteBacklinks .= $l_str_LineBreak; - $l_str_FootnoteBacklinks .= '<' . self::$a_str_LinkSpan; - $l_str_FootnoteBacklinks .= ' id="footnote_plugin_reference_'; - $l_str_FootnoteBacklinks .= self::$a_int_PostId; - $l_str_FootnoteBacklinks .= '_' . self::$a_int_ReferenceContainerId; - $l_str_FootnoteBacklinks .= "_$l_str_FootnoteId\""; - - // insert the optional hard link address: - if (self::$a_bool_HardLinksEnable) { - $l_str_FootnoteBacklinks .= ' href="#'; - $l_str_FootnoteBacklinks .= self::$a_str_ReferrerLinkSlug; - $l_str_FootnoteBacklinks .= self::$a_str_PostContainerIdCompound; - $l_str_FootnoteBacklinks .= $l_str_FootnoteId . '"'; - $l_str_FootnoteBacklinks .= $l_str_UseBackbuttonHint; - } - - $l_str_FootnoteBacklinks .= ' class="footnote_backlink"'; - $l_str_FootnoteBacklinks .= ' onclick="footnote_moveToAnchor_'; - $l_str_FootnoteBacklinks .= self::$a_int_PostId; - $l_str_FootnoteBacklinks .= '_' . self::$a_int_ReferenceContainerId; - $l_str_FootnoteBacklinks .= "('footnote_plugin_tooltip_"; - $l_str_FootnoteBacklinks .= self::$a_int_PostId; - $l_str_FootnoteBacklinks .= '_' . self::$a_int_ReferenceContainerId; - $l_str_FootnoteBacklinks .= "_$l_str_FootnoteId');\">"; - - // append the offset anchor for optional hard links: - if (self::$a_bool_HardLinksEnable) { - $l_str_FootnoteBacklinks .= ''; - } - - $l_str_FootnoteBacklinks .= $l_bool_SymbolSwitch ? '' : $l_str_FootnoteArrow; - $l_str_FootnoteBacklinks .= $l_str_FootnoteId; - $l_str_FootnoteBacklinks .= $l_bool_SymbolSwitch ? $l_str_FootnoteArrow : ''; - - } - } - } - - // append terminator and end tag: - $l_str_FootnoteReference .= $l_str_Terminator . ''; - $l_str_FootnoteBacklinks .= $l_str_Terminator . ''; - - } - - // line wrapping of URLs already fixed, see above - - // get reference container item text if tooltip text goes separate: - $l_int_TooltipTextLength = strpos( $l_str_FootnoteText, self::$a_str_TooltipShortcode ); - $l_bool_HasTooltipText = $l_int_TooltipTextLength === false ? false : true; - if ( $l_bool_HasTooltipText ) { - $l_str_NotTooltipText = substr( $l_str_FootnoteText, ( $l_int_TooltipTextLength + self::$a_int_TooltipShortcodeLength ) ); - self::$a_bool_MirrorTooltipText = MCI_Footnotes_Convert::toBool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_FOOTNOTES_TOOLTIP_EXCERPT_MIRROR_ENABLE ) ); - if ( self::$a_bool_MirrorTooltipText ) { - $l_str_TooltipText = substr( $l_str_FootnoteText, 0, $l_int_TooltipTextLength ); - $l_str_ReferenceTextIntroducer = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_TOOLTIP_EXCERPT_MIRROR_SEPARATOR); - $l_str_ReferenceText = $l_str_TooltipText . $l_str_ReferenceTextIntroducer . $l_str_NotTooltipText; - } else { - $l_str_ReferenceText = $l_str_NotTooltipText; - } - } else { - $l_str_ReferenceText = $l_str_FootnoteText; - } - - // replace all placeholders in table row template: - $l_obj_Template->replace( - array( - - // placeholder used in all templates: - "text" => $l_str_ReferenceText, - - // used in standard layout W/O COMBINED FOOTNOTES: - "post_id" => self::$a_int_PostId, - "container_id" => self::$a_int_ReferenceContainerId, - "note_id" => MCI_Footnotes_Convert::Index($l_int_FirstFootnoteIndex, MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_COUNTER_STYLE)), - "link-start" => self::$a_str_LinkOpenTag, - "link-end" => self::$a_str_LinkCloseTag, - "link-span" => self::$a_str_LinkSpan, - "terminator" => $l_str_Terminator, - "anchor-element" => $l_str_FootnoteAnchorElement, - "hard-link" => $l_str_HardLinkAddress, - - // used in standard layout WITH COMBINED IDENTICALS TURNED ON: - "pointer" => $l_bool_FlagCombined ? '' : ' pointer', - "event" => $l_bool_FlagCombined ? '' : $l_str_BacklinkEvent, - "backlinks" => $l_bool_FlagCombined ? $l_str_FootnoteBacklinks : $l_str_FootnoteReference, - - // Legacy placeholders for use in legacy layout templates: - "arrow" => $l_str_FootnoteArrow, - "index" => $l_str_FootnoteId, - ) - ); - - $l_str_Body .= $l_obj_Template->getContent(); - - // extra line breaks for page source readability: - $l_str_Body .= "\r\n\r\n"; - - $l_obj_Template->reload(); - - } - - // call again for robustness when priority levels don’t match any longer: - self::$a_int_ScrollOffset = intval(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_INT_FOOTNOTES_SCROLL_OFFSET)); - - // streamline: - $l_bool_CollapseDefault = MCI_Footnotes_Convert::toBool(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_BOOL_REFERENCE_CONTAINER_COLLAPSE)); - - /** - * Reference container label - * - * - Bugfix: Reference container: Label: set empty label to U+202F NNBSP for more robustness, thanks to @lukashuggenberg feedback. - * - * @since 2.4.0 - * @date 2021-01-04T0504+0100 - * - * @reporter @lukashuggenberg - * - * Themes may drop-cap a first letter of initial paragraphs, like this label. - * In case of empty label that would apply to the left half button character. - * Hence the point in setting an empty label to U+202F NARROW NO-BREAK SPACE. - */ - $l_str_ReferenceContainerLabel = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_REFERENCE_CONTAINER_NAME); - - /** - * Select the reference container template according to the script mode. - * - * - Bugfix: Reference container: optional alternative expanding and collapsing without jQuery for use with hard links, thanks to @hopper87it @pkverma99 issue reports. - * - * @since 2.5.6 - * - * @reporter @hopper87it - * @link https://wordpress.org/support/topic/footnotes-wp-rocket/ - * - * @reporter @pkverma99 - * @link https://wordpress.org/support/topic/footnotes-wp-rocket/#post-14076188 - */ - $l_str_ScriptMode = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_REFERENCE_CONTAINER_SCRIPT_MODE); - - if ( $l_str_ScriptMode == 'jquery' ) { - - // load 'templates/public/reference-container.html': - $l_obj_TemplateContainer = new MCI_Footnotes_Template(MCI_Footnotes_Template::C_STR_PUBLIC, "reference-container"); - - } else { - - // load 'templates/public/js-reference-container.html': - $l_obj_TemplateContainer = new MCI_Footnotes_Template(MCI_Footnotes_Template::C_STR_PUBLIC, "js-reference-container"); - } - - $l_obj_TemplateContainer->replace( - array( - "post_id" => self::$a_int_PostId, - "container_id" => self::$a_int_ReferenceContainerId, - "element" => MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_REFERENCE_CONTAINER_LABEL_ELEMENT), - "name" => empty($l_str_ReferenceContainerLabel) ? ' ' : $l_str_ReferenceContainerLabel, - "button-style" => !$l_bool_CollapseDefault ? 'display: none;' : '', - "style" => $l_bool_CollapseDefault ? 'display: none;' : '', - "content" => $l_str_Body, - "scroll-offset" => (self::$a_int_ScrollOffset / 100), - "scroll-duration" => intval(MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_INT_FOOTNOTES_SCROLL_DURATION)), - ) - ); - - // free all found footnotes if reference container will be displayed - self::$a_arr_Footnotes = array(); - - return $l_obj_TemplateContainer->getContent(); - } -} +get( MCI_Footnotes_Settings::C_INT_EXPERT_LOOKUP_THE_TITLE_PRIORITY_LEVEL ) ); + $l_int_the_content_priority = intval( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_INT_EXPERT_LOOKUP_THE_CONTENT_PRIORITY_LEVEL ) ); + $l_int_the_excerpt_priority = intval( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_INT_EXPERT_LOOKUP_THE_EXCERPT_PRIORITY_LEVEL ) ); + $l_int_widget_title_priority = intval( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_INT_EXPERT_LOOKUP_WIDGET_TITLE_PRIORITY_LEVEL ) ); + $l_int_widget_text_priority = intval( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_INT_EXPERT_LOOKUP_WIDGET_TEXT_PRIORITY_LEVEL ) ); + + // PHP_INT_MAX can be set by -1:. + $l_int_the_title_priority = ( $l_int_the_title_priority == -1 ) ? PHP_INT_MAX : $l_int_the_title_priority; + $l_int_the_content_priority = ( $l_int_the_content_priority == -1 ) ? PHP_INT_MAX : $l_int_the_content_priority; + $l_int_the_excerpt_priority = ( $l_int_the_excerpt_priority == -1 ) ? PHP_INT_MAX : $l_int_the_excerpt_priority; + $l_int_widget_title_priority = ( $l_int_widget_title_priority == -1 ) ? PHP_INT_MAX : $l_int_widget_title_priority; + $l_int_widget_text_priority = ( $l_int_widget_text_priority == -1 ) ? PHP_INT_MAX : $l_int_widget_text_priority; + + // Append custom css to the header. + add_filter( 'wp_head', array( $this, 'wp_head' ), PHP_INT_MAX ); + + // Append the love and share me slug to the footer. + add_filter( 'wp_footer', array( $this, 'wp_footer' ), PHP_INT_MAX ); + + if ( MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_EXPERT_LOOKUP_THE_TITLE ) ) ) { + add_filter( 'the_title', array( $this, 'the_title' ), $l_int_the_title_priority ); + } + + // Configurable priority level for reference container relative positioning; default 98:. + if ( MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_EXPERT_LOOKUP_THE_CONTENT ) ) ) { + add_filter( 'the_content', array( $this, 'the_content' ), $l_int_the_content_priority ); + + /** + * Hook for category pages + * + * - Bugfix: Hooks: support footnotes on category pages, thanks to @vitaefit bug report, thanks to @misfist code contribution. + * + * @since 2.5.0 + * @date 2021-01-05T1402+0100 + * + * @contributor @misfist + * @link https://wordpress.org/support/topic/footnote-doesntwork-on-category-page/#post-13864859 + * + * @reporter @vitaefit + * @link https://wordpress.org/support/topic/footnote-doesntwork-on-category-page/ + * + * Category pages can have rich HTML content in a term description with article status. + * For this to happen, WordPress’ built-in partial HTML blocker needs to be disabled. + * @link https://docs.woocommerce.com/document/allow-html-in-term-category-tag-descriptions/ + */ + add_filter( 'term_description', array( $this, 'the_content' ), $l_int_the_content_priority ); + + /** + * Hook for popup maker popups + * + * - Bugfix: Hooks: support footnotes in Popup Maker popups, thanks to @squatcher bug report. + * + * @since 2.5.1 + * @date 2021-01-18T2038+0100 + * + * @reporter @squatcher + * @link https://wordpress.org/support/topic/footnotes-use-in-popup-maker/ + */ + add_filter( 'pum_popup_content', array( $this, 'the_content' ), $l_int_the_content_priority ); + } + + if ( MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_EXPERT_LOOKUP_THE_EXCERPT ) ) ) { + add_filter( 'the_excerpt', array( $this, 'the_excerpt' ), $l_int_the_excerpt_priority ); + } + if ( MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_EXPERT_LOOKUP_WIDGET_TITLE ) ) ) { + add_filter( 'widget_title', array( $this, 'widget_title' ), $l_int_widget_title_priority ); + } + if ( MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_EXPERT_LOOKUP_WIDGET_TEXT ) ) ) { + add_filter( 'widget_text', array( $this, 'widget_text' ), $l_int_widget_text_priority ); + } + + /** + * The the_post hook + * + * - Adding: Hooks: support 'the_post' in response to user request for custom post types. + * + * @since 1.5.4 + * @accountable @aricura + * @link https://wordpress.org/support/topic/doesnt-work-in-custon-post-types/#post-5339110 + * + * + * - Update: Hooks: Default-enable all hooks to prevent footnotes from seeming broken in some parts. + * + * @since 2.0.5 + * @accountable @pewgeuges + * + * + * - BUGFIX: Hooks: Default-disable 'the_post', thanks to @spaceling @markcheret @nyamachi @whichgodsaves @spiralofhope2 @mmallett @andreasra @widecast @ymorin007 @tashi1es bug reports. + * + * @since 2.0.7 + * @accountable @pewgeuges + * @link https://wordpress.org/support/topic/change-the-position-5/page/2/#post-13630114 + * @link https://wordpress.org/support/topic/footnotes-appearing-in-header/#post-13630303 + * @link https://wordpress.org/support/topic/footnotes-appearing-in-header/page/2/#post-13630799 + * @link https://wordpress.org/support/topic/no-footnotes-anymore/#post-13813233 + * + * @reporter @spaceling + * @link https://wordpress.org/support/topic/change-the-position-5/#post-13612697 + * + * @reporter @markcheret on behalf of W. Beinert + * @link https://wordpress.org/support/topic/footnotes-now-appear-in-summaries-even-though-this-is-marked-no/ + * + * @reporter @nyamachi + * @link https://wordpress.org/support/topic/footnotes-appearing-in-header/ + * + * @reporter @whichgodsaves + * @link https://wordpress.org/support/topic/footnotes-appearing-in-header/#post-13622694 + * + * @reporter @spiralofhope2 + * @link https://wordpress.org/support/topic/2-0-5-broken/ + * + * @reporter @mmallett + * @link https://wordpress.org/support/topic/2-0-5-broken/#post-13623208 + * + * @reporter @andreasra + * @link https://wordpress.org/support/topic/footnotes-appearing-in-header/#post-13624091 + * + * @reporter @widecast + * @link https://wordpress.org/support/topic/2-0-5-broken/#post-13626222 + * + * @reporter @ymorin007 + * @link https://wordpress.org/support/topic/footnotes-appearing-in-header/#post-13627050 + * + * @reporter @markcheret on behalf of L. Smith + * @link https://wordpress.org/support/topic/footnotes-appear-in-random-places-on-academic-website/ + * + * @reporter @tashi1es + * @link https://wordpress.org/support/topic/footnotes-appear-in-random-places-on-academic-website/#post-13630495 + * + * + * - UPDATE: Hooks: remove 'the_post', the plugin stops supporting this hook. + * + * @since 2.1.0 + * @date 2020-11-08T1839+0100 + * @accountable @pewgeuges + */ + + // Reset stored footnotes when displaying the header. + self::$a_arr_footnotes = array(); + self::$a_bool_allow_love_me = true; + } + + /** + * Outputs the custom css to the header of the public page. + * + * @author Stefan Herndler + * @since 1.5.0 + * + * @since 2.1.1 Bugfix: Reference container: fix start pages by making its display optional, thanks to @dragon013 bug report. + * @since 2.1.1 Bugfix: Tooltips: optional alternative JS implementation with CSS transitions to fix configuration-related outage, thanks to @andreasra feedback. + * @since 2.1.3 raise settings priority to override theme stylesheets + * @since 2.1.4 Bugfix: Tooltips: Styling: fix font size issue by adding font size to settings with legacy as default. + * @since 2.1.4 Bugfix: Reference container: fix layout issues by moving backlink column width to settings. + * @since 2.2.5 Bugfix: Reference container: Label: make bottom border an option, thanks to @markhillyer issue report. + * @since 2.2.5 Bugfix: Reference container: Label: option to select paragraph or heading element, thanks to @markhillyer issue report. + * @since 2.3.0 Bugfix: Reference container: convert top padding to margin and make it a setting, thanks to @hamshe bug report. + * @since 2.5.4 Bugfix: Referrers: optional fixes to vertical alignment, font size and position (static) for in-theme consistency and cross-theme stability, thanks to @tomturowski bug report. + */ + public function wp_head() { + + // Insert start tag without switching out of PHP:. + echo "\r\n\r\n"; + + /** + * Alternative tooltip implementation relying on plain JS and CSS transitions. + * + * - Bugfix: Tooltips: optional alternative JS implementation with CSS transitions to fix configuration-related outage, thanks to @andreasra feedback. + * + * @since 2.1.1 + * + * @reporter @andreasra + * @link https://wordpress.org/support/topic/footnotes-appearing-in-header/page/2/#post-13632566 + * + * The script for alternative tooltips is printed formatted, not minified: + */ + if ( self::$a_bool_alternative_tooltips_enabled ) { + ?> + + get( MCI_Footnotes_Settings::C_STR_REFERENCE_CONTAINER_POSITION ) == 'footer' ) { + echo $this->Reference_container(); + } + // Get setting for love and share this plugin. + $l_str_love_me_index = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_LOVE ); + // Check if the admin allows to add a link to the footer. + if ( empty( $l_str_love_me_index ) || strtolower( $l_str_love_me_index ) == 'no' || ! self::$a_bool_allow_love_me ) { + return; + } + // Set a hyperlink to the word "footnotes" in the Love slug. + $l_str_linked_name = sprintf( '%s', MCI_Footnotes_Config::C_STR_PLUGIN_PUBLIC_NAME ); + // Get random love me text. + if ( strtolower( $l_str_love_me_index ) == 'random' ) { + $l_str_love_me_index = 'text-' . rand( 1, 7 ); + } + switch ( $l_str_love_me_index ) { + // Options named wrt backcompat, simplest is default:. + case 'text-1': + $l_str_love_me_text = sprintf( __( 'I %2$s %1$s', MCI_Footnotes_Config::C_STR_PLUGIN_NAME ), $l_str_linked_name, MCI_Footnotes_Config::C_STR_LOVE_SYMBOL ); + break; + case 'text-2': + $l_str_love_me_text = sprintf( __( 'This website uses the awesome %s plugin.', MCI_Footnotes_Config::C_STR_PLUGIN_NAME ), $l_str_linked_name ); + break; + case 'text-4': + $l_str_love_me_text = sprintf( '%s %s', $l_str_linked_name, MCI_Footnotes_Config::C_STR_LOVE_SYMBOL ); + break; + case 'text-5': + $l_str_love_me_text = sprintf( '%s %s', MCI_Footnotes_Config::C_STR_LOVE_SYMBOL, $l_str_linked_name ); + break; + case 'text-6': + $l_str_love_me_text = sprintf( __( 'This website uses %s.', MCI_Footnotes_Config::C_STR_PLUGIN_NAME ), $l_str_linked_name ); + break; + case 'text-7': + $l_str_love_me_text = sprintf( __( 'This website uses the %s plugin.', MCI_Footnotes_Config::C_STR_PLUGIN_NAME ), $l_str_linked_name ); + break; + case 'text-3': + default: + $l_str_love_me_text = sprintf( '%s', $l_str_linked_name ); + break; + } + echo sprintf( '
%s
', $l_str_love_me_text ); + } + + /** + * Replaces footnotes in the post/page title. + * + * @author Stefan Herndler + * @since 1.5.0 + * @param string $p_str_content Widget content. + * @return string Content with replaced footnotes. + */ + public function the_title( $p_str_content ) { + // Appends the reference container if set to "post_end". + return $this->exec( $p_str_content, false ); + } + + /** + * Replaces footnotes in the content of the current page/post. + * + * @author Stefan Herndler + * @since 1.5.0 + * @param string $p_str_content Page/Post content. + * @return string Content with replaced footnotes. + */ + public function the_content( $p_str_content ) { + // Appends the reference container if set to "post_end". + return $this->exec( $p_str_content, MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_REFERENCE_CONTAINER_POSITION ) == 'post_end' ? true : false ); + } + + /** + * Replaces footnotes in the excerpt of the current page/post. + * + * @author Stefan Herndler + * @since 1.5.0 + * @param string $p_str_content Page/Post content. + * @return string Content with replaced footnotes. + */ + public function the_excerpt( $p_str_content ) { + return $this->exec( $p_str_content, false, ! MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_FOOTNOTES_IN_EXCERPT ) ) ); + } + + /** + * Replaces footnotes in the widget title. + * + * @author Stefan Herndler + * @since 1.5.0 + * @param string $p_str_content Widget content. + * @return string Content with replaced footnotes. + */ + public function widget_title( $p_str_content ) { + // Appends the reference container if set to "post_end". + return $this->exec( $p_str_content, false ); + } + + /** + * Replaces footnotes in the content of the current widget. + * + * @author Stefan Herndler + * @since 1.5.0 + * @param string $p_str_content Widget content. + * @return string Content with replaced footnotes. + */ + public function widget_text( $p_str_content ) { + // Appends the reference container if set to "post_end". + return $this->exec( $p_str_content, MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_REFERENCE_CONTAINER_POSITION ) == 'post_end' ? true : false ); + } + + /** + * Replaces footnotes in each Content var of the current Post object. + * + * @author Stefan Herndler + * @since 1.5.4 + * @param array|WP_Post $p_mixed_Posts + */ + public function the_post( &$p_mixed_Posts ) { + // Single WP_Post object received. + if ( ! is_array( $p_mixed_Posts ) ) { + $p_mixed_Posts = $this->replace_post_object( $p_mixed_Posts ); + return; + } + // Array of WP_Post objects received. + for ( $l_int_index = 0; $l_int_index < count( $p_mixed_Posts ); $l_int_index++ ) { + $p_mixed_Posts[ $l_int_index ] = $this->replace_post_object( $p_mixed_Posts[ $l_int_index ] ); + } + } + + /** + * Replace all Footnotes in a WP_Post object. + * + * @author Stefan Herndler + * @since 1.5.6 + * @param WP_Post $p_obj_post + * @return WP_Post + */ + private function replace_post_object( $p_obj_post ) { + // MCI_Footnotes_Convert::debug($p_obj_post);. + $p_obj_post->post_content = $this->exec( $p_obj_post->post_content ); + $p_obj_post->post_content_filtered = $this->exec( $p_obj_post->post_content_filtered ); + $p_obj_post->post_excerpt = $this->exec( $p_obj_post->post_excerpt ); + return $p_obj_post; + } + + /** + * Replaces all footnotes that occur in the given content. + * + * @author Stefan Herndler + * @since 1.5.0 + * @param string $p_str_content Any string that may contain footnotes to be replaced. + * @param bool $p_bool_output_references Appends the Reference Container to the output if set to true, default true. + * @param bool $p_bool_hide_footnotes_text Hide footnotes found in the string. + * @return string + * + * @since 2.2.0 Adding: Reference container: support for custom position shortcode, thanks to @hamshe issue report. + * @since 2.2.5 Bugfix: Reference container: delete position shortcode if unused because position may be widget or footer, thanks to @hamshe bug report. + */ + public function exec( $p_str_content, $p_bool_output_references = false, $p_bool_hide_footnotes_text = false ) { + + // Replace all footnotes in the content, settings are converted to html characters. + $p_str_content = $this->search( $p_str_content, true, $p_bool_hide_footnotes_text ); + // Replace all footnotes in the content, settings are NOT converted to html characters. + $p_str_content = $this->search( $p_str_content, false, $p_bool_hide_footnotes_text ); + + /** + * Reference container customized positioning through shortcode + * + * - Adding: Reference container: support for custom position shortcode, thanks to @hamshe issue report. + * + * @since 2.2.0 + * @date 2020-12-13T2057+0100 + * + * @reporter @hamshe + * @link https://wordpress.org/support/topic/reference-container-in-elementor/ + * + * + * - Bugfix: Reference container: delete position shortcode if unused because position may be widget or footer, thanks to @hamshe bug report. + * + * @since 2.2.5 + * @date 2020-12-18T1434+0100 + * + * @reporter @hamshe + * @link https://wordpress.org/support/topic/reference-container-in-elementor/#post-13784126 + */ + // Append the reference container or insert at shortcode:. + $l_str_reference_container_position_shortcode = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_REFERENCE_CONTAINER_POSITION_SHORTCODE ); + if ( empty( $l_str_reference_container_position_shortcode ) ) { + $l_str_reference_container_position_shortcode = '[[references]]'; + } + + if ( $p_bool_output_references ) { + + if ( strpos( $p_str_content, $l_str_reference_container_position_shortcode ) !== false ) { + + $p_str_content = str_replace( $l_str_reference_container_position_shortcode, $this->Reference_container(), $p_str_content ); + + } else { + + $p_str_content .= $this->Reference_container(); + + } + + // Increment the container ID:. + self::$a_int_reference_container_id++; + } + + // Delete position shortcode should any remain:. + $p_str_content = str_replace( $l_str_reference_container_position_shortcode, '', $p_str_content ); + + // Take a look if the LOVE ME slug should NOT be displayed on this page/post, remove the short code if found. + if ( strpos( $p_str_content, MCI_Footnotes_Config::C_STR_NO_LOVE_SLUG ) !== false ) { + self::$a_bool_allow_love_me = false; + $p_str_content = str_replace( MCI_Footnotes_Config::C_STR_NO_LOVE_SLUG, '', $p_str_content ); + } + // Return the content with replaced footnotes and optional reference container appended:. + return $p_str_content; + } + + /** + * Replaces all footnotes in the given content and appends them to the static property. + * + * @author Stefan Herndler + * @since 1.5.0 + * @param string $p_str_content Content to be searched for footnotes. + * @param bool $p_bool_convert_html_chars html encode settings, default true. + * @param bool $p_bool_hide_footnotes_text Hide footnotes found in the string. + * @return string + * + * @since 2.0.0 various. + * @since 2.4.0 Adding: Footnote delimiters: syntax validation for balanced footnote start and end tag short codes. + * @since 2.5.0 Bugfix: Footnote delimiters: Syntax validation: exclude certain cases involving scripts, thanks to @andreasra bug report. + * @since 2.5.0 Bugfix: Footnote delimiters: Syntax validation: complete message with hint about setting, thanks to @andreasra bug report. + * @since 2.5.0 Bugfix: Footnote delimiters: Syntax validation: limit length of quoted string to 300 characters, thanks to @andreasra bug report. + */ + public function search( $p_str_content, $p_bool_convert_html_chars, $p_bool_hide_footnotes_text ) { + + // Post ID to make everything unique wrt infinite scroll and archive view. + self::$a_int_post_id = get_the_id(); + + // Contains the index for the next footnote on this page. + $l_int_footnote_index = count( self::$a_arr_footnotes ) + 1; + + // Contains the starting position for the lookup of a footnote. + $l_int_pos_start = 0; + + // Get start and end tag for the footnotes short code. + $l_str_starting_tag = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_SHORT_CODE_START ); + $l_str_ending_tag = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_SHORT_CODE_END ); + if ( $l_str_starting_tag == 'userdefined' || $l_str_ending_tag == 'userdefined' ) { + $l_str_starting_tag = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_SHORT_CODE_START_USER_DEFINED ); + $l_str_ending_tag = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_SHORT_CODE_END_USER_DEFINED ); + } + // Decode html special chars. + if ( $p_bool_convert_html_chars ) { + $l_str_starting_tag = htmlspecialchars( $l_str_starting_tag ); + $l_str_ending_tag = htmlspecialchars( $l_str_ending_tag ); + } + + // If footnotes short code is empty, return the content without changes. + if ( empty( $l_str_starting_tag ) || empty( $l_str_ending_tag ) ) { + return $p_str_content; + } + + /** + * Footnote delimiter syntax validation + * + * - Adding: Footnote delimiters: syntax validation for balanced footnote start and end tag short codes. + * + * @since 2.4.0 + * + * + * - Bugfix: Footnote delimiters: Syntax validation: exclude certain cases involving scripts, thanks to @andreasra bug report. + * - Bugfix: Footnote delimiters: Syntax validation: complete message with hint about setting, thanks to @andreasra bug report. + * - Bugfix: Footnote delimiters: Syntax validation: limit length of quoted string to 300 characters, thanks to @andreasra bug report. + * + * @since 2.5.0 + * @date 2021-01-07T0824+0100 + * + * @reporter @andreasra + * @link https://wordpress.org/support/topic/warning-unbalanced-footnote-start-tag-short-code-before/ + * + * + * If footnotes short codes are unbalanced, and syntax validation is not disabled, + * prepend a warning to the content; displays de facto beneath the post title. + */ + if ( MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_FOOTNOTE_SHORTCODE_SYNTAX_VALIDATION_ENABLE ) ) ) { + + // Make shortcodes conform to regex syntax:. + $l_str_start_tag_regex = preg_replace( '#([\(\)\{\}\[\]\*\.\?\!])#', '\\\\$1', $l_str_starting_tag ); + $l_str_end_tag_regex = preg_replace( '#([\(\)\{\}\[\]\*\.\?\!])#', '\\\\$1', $l_str_ending_tag ); + + // Apply different regex depending on whether start shortcode is double/triple opening parenthesis:. + if ( $l_str_starting_tag == '((' || $l_str_starting_tag == '(((' ) { + + // This prevents from catching a script containing e.g. a double opening parenthesis:. + $l_str_validation_regex = '#' . $l_str_start_tag_regex . '(((?!' . $l_str_end_tag_regex . ')[^\{\}])*?)(' . $l_str_start_tag_regex . '|$)#s'; + + } else { + + // Catch all only if the start shortcode is not double/triple opening parenthesis, i.e. is unlikely to occur in scripts:. + $l_str_validation_regex = '#' . $l_str_start_tag_regex . '(((?!' . $l_str_end_tag_regex . ').)*?)(' . $l_str_start_tag_regex . '|$)#s'; + } + + // Check syntax and get error locations:. + preg_match( $l_str_validation_regex, $p_str_content, $p_arr_error_location ); + if ( empty( $p_arr_error_location ) ) { + self::$a_bool_syntax_error_flag = false; + } + + // Prevent generating and inserting the warning multiple times:. + if ( self::$a_bool_syntax_error_flag ) { + + // Get plain text string for error location:. + $l_str_error_spot_string = strip_tags( $p_arr_error_location[1] ); + + // Limit string length to 300 characters:. + if ( strlen( $l_str_error_spot_string ) > 300 ) { + $l_str_error_spot_string = substr( $l_str_error_spot_string, 0, 299 ) . '…'; + } + + // Compose warning box:. + $l_str_syntax_error_warning = '

'; + $l_str_syntax_error_warning .= __( 'WARNING: unbalanced footnote start tag short code found.', MCI_Footnotes_Config::C_STR_PLUGIN_NAME ); + $l_str_syntax_error_warning .= '

'; + + // Syntax validation setting in the dashboard under the General settings tab:. + $l_str_syntax_error_warning .= sprintf( __( 'If this warning is irrelevant, please disable the syntax validation feature in the dashboard under %1$s > %2$s > %3$s.', MCI_Footnotes_Config::C_STR_PLUGIN_NAME ), __( 'General settings', MCI_Footnotes_Config::C_STR_PLUGIN_NAME ), __( 'Footnote start and end short codes', MCI_Footnotes_Config::C_STR_PLUGIN_NAME ), __( 'Check for balanced shortcodes', MCI_Footnotes_Config::C_STR_PLUGIN_NAME ) ); + + $l_str_syntax_error_warning .= '

'; + $l_str_syntax_error_warning .= __( 'Unbalanced start tag short code found before:', MCI_Footnotes_Config::C_STR_PLUGIN_NAME ); + $l_str_syntax_error_warning .= '

“'; + $l_str_syntax_error_warning .= $l_str_error_spot_string; + $l_str_syntax_error_warning .= '”

'; + + // Prepend the warning box to the content:. + $p_str_content = $l_str_syntax_error_warning . $p_str_content; + + // Checked, set flag to false to prevent duplicate warning:. + self::$a_bool_syntax_error_flag = false; + + return $p_str_content; + } + } + + // Load referrer templates if footnotes text not hidden:. + if ( ! $p_bool_hide_footnotes_text ) { + + // Load footnote referrer template file:. + if ( self::$a_bool_alternative_tooltips_enabled ) { + $l_obj_template = new MCI_Footnotes_Template( MCI_Footnotes_Template::C_STR_PUBLIC, 'footnote-alternative' ); + } else { + $l_obj_template = new MCI_Footnotes_Template( MCI_Footnotes_Template::C_STR_PUBLIC, 'footnote' ); + } + + /** + * Call Boolean again for robustness when priority levels don’t match any longer. + * + * - Bugfix: Tooltips: fix display in Popup Maker popups by correcting a coding error. + * + * @since 2.5.4 + * @see self::add_filter('pum_popup_content', array($this, "the_content"), $l_int_the_content_priority) + */ + self::$a_bool_tooltips_enabled = MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_FOOTNOTES_MOUSE_OVER_BOX_ENABLED ) ); + self::$a_bool_alternative_tooltips_enabled = MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_FOOTNOTES_MOUSE_OVER_BOX_ALTERNATIVE ) ); + + // Load tooltip inline script if j_query tooltips are enabled:. + if ( self::$a_bool_tooltips_enabled && ! self::$a_bool_alternative_tooltips_enabled ) { + $l_obj_template_tooltip = new MCI_Footnotes_Template( MCI_Footnotes_Template::C_STR_PUBLIC, 'tooltip' ); + } + } else { + $l_obj_template = null; + $l_obj_template_tooltip = null; + } + + // Search footnotes short codes in the content. + do { + // Get first occurrence of the footnote start tag short code:. + $i_int_len_content = strlen( $p_str_content ); + if ( $l_int_pos_start > $i_int_len_content ) { + $l_int_pos_start = $i_int_len_content; + } + $l_int_pos_start = strpos( $p_str_content, $l_str_starting_tag, $l_int_pos_start ); + // No short code found, stop here. + if ( $l_int_pos_start === false ) { + break; + } + // Get first occurrence of the footnote end tag short code:. + $l_int_pos_end = strpos( $p_str_content, $l_str_ending_tag, $l_int_pos_start ); + // No short code found, stop here. + if ( $l_int_pos_end === false ) { + break; + } + // Calculate the length of the footnote. + $l_int_length = $l_int_pos_end - $l_int_pos_start; + + // Get footnote text. + $l_str_footnote_text = substr( $p_str_content, $l_int_pos_start + strlen( $l_str_starting_tag ), $l_int_length - strlen( $l_str_starting_tag ) ); + + // Get tooltip text if present:. + self::$a_str_tooltip_shortcode = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_TOOLTIP_EXCERPT_DELIMITER ); + self::$a_int_tooltip_shortcode_length = strlen( self::$a_str_tooltip_shortcode ); + $l_int_tooltip_text_length = strpos( $l_str_footnote_text, self::$a_str_tooltip_shortcode ); + $l_bool_has_tooltip_text = $l_int_tooltip_text_length === false ? false : true; + if ( $l_bool_has_tooltip_text ) { + $l_str_tooltip_text = substr( $l_str_footnote_text, 0, $l_int_tooltip_text_length ); + } else { + $l_str_tooltip_text = ''; + } + + /** + * URL line wrapping for Unicode non conformant browsers + * + * @since 2.1.1 (CSS) + * @since 2.1.4 (PHP) + * + * Despite Unicode recommends to line-wrap URLs at slashes, and Firefox follows + * the Unicode standard, Chrome does not, making long URLs hang out of tooltips + * or extend reference containers, so that the end is hidden outside the window + * and may eventually be viewed after we scroll horizontally or zoom out. It is + * up to the web page to make URLs breaking anywhere by wrapping them in a span + * that is assigned appropriate CSS properties and values. + * @see css/public.css + * + * - Bugfix: Tooltips: fix line breaking for hyperlinked URLs in Unicode-non-compliant user agents, thanks to @andreasra bug report. + * + * @since 2.1.1 + * + * @reporter @andreasra + * @link https://wordpress.org/support/topic/footnotes-appearing-in-header/page/3/#post-13657398 + * + * + * - Bugfix: Reference container: fix width in mobile view by URL wrapping for Unicode-non-conformant browsers, thanks to @karolszakiel bug report. + * + * @since 2.1.3 + * @date 2020-11-23 + * + * @reporter @karolszakiel + * @link https://wordpress.org/support/topic/footnotes-on-mobile-phones/ + * + * + * - Bugfix: Reference container, tooltips: fix line wrapping of URLs (hyperlinked or not) based on pattern, not link element. + * + * @since 2.1.4 + * @date 2020-11-25T0837+0100 + * @link https://wordpress.org/support/topic/footnotes-on-mobile-phones/#post-13710682 + * + * + * - Bugfix: Reference container, tooltips: URL wrap: exclude image source too, thanks to @bjrnet21 bug report. + * + * @since 2.1.5 + * + * @reporter @bjrnet21 + * @link https://wordpress.org/support/topic/2-1-4-breaks-on-my-site-images-dont-show/ + * + * + * - Bugfix: Reference container, tooltips: URL wrap: fix regex, thanks to @a223123131 bug report. + * + * @since 2.1.6 + * @date 2020-12-09T1921+0100 + * + * @reporter @a223123131 + * @link https://wordpress.org/support/topic/broken-layout-starting-version-2-1-4/ + * + * Even ARIA labels may take a URL as value, so use \w=[\'"] as a catch-all 2020-12-10T1005+0100 + * + * - Bugfix: Dashboard: URL wrap: add option to properly enable/disable URL wrap. + * + * @since 2.1.6 + * @date 2020-12-09T1606+0100 + * + * + * - Bugfix: Reference container, tooltips: URL wrap: make the quotation mark optional wrt query parameters, thanks to @spiralofhope2 bug report. + * + * @since 2.2.6 + * @date 2020-12-23T0409+0100 + * + * @reporter @spiralofhope2 + * @link https://wordpress.org/support/topic/two-links-now-breaks-footnotes-with-blogtext/ + * + * + * - Bugfix: Reference container, tooltips: URL wrap: remove a bug introduced in the regex, thanks to @rjl20 @spaceling @lukashuggenberg @klusik @friedrichnorth @bernardzit bug reports. + * + * @since 2.2.7 + * @date 2020-12-23T1046+0100 + * + * @reporter @rjl20 + * @link https://wordpress.org/support/topic/two-links-now-breaks-footnotes-with-blogtext/#post-13825479 + * + * @reporter @spaceling + * @link https://wordpress.org/support/topic/two-links-now-breaks-footnotes-with-blogtext/#post-13825532 + * + * @reporter @lukashuggenberg + * @link https://wordpress.org/support/topic/2-2-6-breaks-all-footnotes/ + * + * @reporter @klusik + * @link https://wordpress.org/support/topic/2-2-6-breaks-all-footnotes/#post-13825885 + * + * @reporter @friedrichnorth + * @link https://wordpress.org/support/topic/footnotes-dont-show-after-update-to-2-2-6/ + * + * @reporter @bernardzit + * @link https://wordpress.org/support/topic/footnotes-dont-show-after-update-to-2-2-6/#post-13826029 + * + * @since 2.2.8 Bugfix: Reference container, tooltips: URL wrap: correctly make the quotation mark optional wrt query parameters, thanks to @spiralofhope2 bug report. + * @date 2020-12-23T1107+0100 + * + * Correct is duplicating the negative lookbehind w/o quotes: '(?get( MCI_Footnotes_Settings::C_BOOL_FOOTNOTE_URL_WRAP_ENABLED ) ) ) { + + $l_str_footnote_text = preg_replace( + '#(?$1', + $l_str_footnote_text + ); + } + + // Text to be displayed instead of the footnote. + $l_str_footnote_replace_text = ''; + + // Whether hard links are enabled:. + if ( self::$a_bool_hard_links_enable ) { + + // Get the configurable parts:. + self::$a_str_referrer_link_slug = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_REFERRER_FRAGMENT_ID_SLUG ); + self::$a_str_footnote_link_slug = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTE_FRAGMENT_ID_SLUG ); + self::$a_str_link_ids_separator = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_HARD_LINK_IDS_SEPARATOR ); + + // Streamline ID concatenation:. + self::$a_str_post_container_id_compound = self::$a_str_link_ids_separator; + self::$a_str_post_container_id_compound .= self::$a_int_post_id; + self::$a_str_post_container_id_compound .= self::$a_str_link_ids_separator; + self::$a_str_post_container_id_compound .= self::$a_int_reference_container_id; + self::$a_str_post_container_id_compound .= self::$a_str_link_ids_separator; + + } + + // Display the footnote referrers and the tooltips:. + if ( ! $p_bool_hide_footnotes_text ) { + $l_int_index = MCI_Footnotes_Convert::Index( $l_int_footnote_index, MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_COUNTER_STYLE ) ); + + // Display only a truncated footnote text if option enabled:. + $l_bool_enable_excerpt = MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_FOOTNOTES_MOUSE_OVER_BOX_EXCERPT_ENABLED ) ); + $l_int_maxLength = intval( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_INT_FOOTNOTES_MOUSE_OVER_BOX_EXCERPT_LENGTH ) ); + + // Define excerpt text as footnote text by default:. + $l_str_excerpt_text = $l_str_footnote_text; + + /** + * Tooltip truncation + * + * - Adding: Tooltips: Read-on button: Label: configurable instead of localizable, thanks to @rovanov example provision. + * + * @since 2.1.0 + * @date 2020-11-08T2146+0100 + * + * @reporter @rovanov + * @link https://wordpress.org/support/topic/offset-x-axis-and-offset-y-axis-does-not-working/ + * + * If the tooltip truncation option is enabled, it’s done based on character count, + * and a trailing incomplete word is cropped. + * This is equivalent to the WordPress default excerpt generation, i.e. without a + * custom excerpt and without a delimiter. But WordPress does word count, usually 55. + */ + if ( self::$a_bool_tooltips_enabled && $l_bool_enable_excerpt ) { + $l_str_dummy_text = strip_tags( $l_str_footnote_text ); + if ( is_int( $l_int_maxLength ) && strlen( $l_str_dummy_text ) > $l_int_maxLength ) { + $l_str_excerpt_text = substr( $l_str_dummy_text, 0, $l_int_maxLength ); + $l_str_excerpt_text = substr( $l_str_excerpt_text, 0, strrpos( $l_str_excerpt_text, ' ' ) ); + $l_str_excerpt_text .= ' … <'; + $l_str_excerpt_text .= self::$a_bool_hard_links_enable ? 'a' : 'span'; + $l_str_excerpt_text .= ' class="footnote_tooltip_continue" '; + $l_str_excerpt_text .= 'onclick="footnote_move_to_anchor_' . self::$a_int_post_id; + $l_str_excerpt_text .= '_' . self::$a_int_reference_container_id; + $l_str_excerpt_text .= '(\'footnote_plugin_reference_' . self::$a_int_post_id; + $l_str_excerpt_text .= '_' . self::$a_int_reference_container_id; + $l_str_excerpt_text .= "_$l_int_index');\""; + + // If enabled, add the hard link fragment ID:. + if ( self::$a_bool_hard_links_enable ) { + + $l_str_excerpt_text .= ' href="#'; + $l_str_excerpt_text .= self::$a_str_footnote_link_slug; + $l_str_excerpt_text .= self::$a_str_post_container_id_compound; + $l_str_excerpt_text .= $l_int_index; + $l_str_excerpt_text .= '"'; + } + + $l_str_excerpt_text .= '>'; + + /** + * Configurable read-on button label + * + * - Adding: Tooltips: Read-on button: Label: configurable instead of localizable, thanks to @rovanov example provision. + * + * @since 2.1.0 + * @date 2020-11-08T2146+0100 + * + * @reporter @rovanov + * @link https://wordpress.org/support/topic/offset-x-axis-and-offset-y-axis-does-not-working/ + */ + $l_str_excerpt_text .= MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_TOOLTIP_READON_LABEL ); + + $l_str_excerpt_text .= self::$a_bool_hard_links_enable ? '' : ''; + } + } + + /** + * Referrers element superscript or baseline + * + * Referrers: new setting for vertical align: superscript (default) or baseline (optional), thanks to @cwbayer bug report + * + * @since 2.1.1 + * + * @reporter @cwbayer + * @link https://wordpress.org/support/topic/footnote-number-in-text-superscript-disrupts-leading/ + * + * define the HTML element to use for the referrers: + */ + if ( MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_FOOTNOTES_REFERRER_SUPERSCRIPT_TAGS ) ) ) { + + $l_str_sup_span = 'sup'; + + } else { + + $l_str_sup_span = 'span'; + } + + // Whether hard links are enabled:. + if ( self::$a_bool_hard_links_enable ) { + + self::$a_str_link_span = 'a'; + self::$a_str_link_close_tag = ''; + // Self::$a_str_link_open_tag will be defined as needed. + + // Compose hyperlink address (leading space is in template):. + $l_str_footnote_link_argument = 'href="#'; + $l_str_footnote_link_argument .= self::$a_str_footnote_link_slug; + $l_str_footnote_link_argument .= self::$a_str_post_container_id_compound; + $l_str_footnote_link_argument .= $l_int_index; + $l_str_footnote_link_argument .= '" class="footnote_hard_link"'; + + /** + * Compose fragment ID anchor with offset, for use in reference container. + * Empty span, child of empty span, to avoid tall dotted rectangles in browser. + */ + $l_str_referrer_anchor_element = ''; + + } else { + + /** + * Initialize hard link variables when hard links are disabled. + * + * - Bugfix: Process: initialize hard link address variables to empty string to fix 'undefined variable' bug, thanks to @a223123131 bug report. + * + * @since 2.4.0 + * @date 2021-01-04T1622+0100 + * + * @reporter @a223123131 + * @link https://wordpress.org/support/topic/wp_debug-php-notice/ + * + * If no hyperlink nor offset anchor is needed, initialize as empty. + */ + $l_str_footnote_link_argument = ''; + $l_str_referrer_anchor_element = ''; + + // The link element is set independently as it may be needed for styling:. + if ( MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_LINK_ELEMENT_ENABLED ) ) ) { + + self::$a_str_link_span = 'a'; + self::$a_str_link_open_tag = ''; + self::$a_str_link_close_tag = ''; + + } + } + + // Determine tooltip content:. + if ( self::$a_bool_tooltips_enabled ) { + $l_str_tooltip_content = $l_bool_has_tooltip_text ? $l_str_tooltip_text : $l_str_excerpt_text; + } else { + $l_str_tooltip_content = ''; + } + + /** + * Determine shrink width if alternative tooltips are enabled. + * + * @since 2.5.6 + */ + $l_str_tooltip_style = ''; + if ( self::$a_bool_alternative_tooltips_enabled && self::$a_bool_tooltips_enabled ) { + $l_int_tooltip_length = strlen( strip_tags( $l_str_tooltip_content ) ); + if ( $l_int_tooltip_length < 70 ) { + $l_str_tooltip_style = ' style="width: '; + $l_str_tooltip_style .= ( $l_int_tooltip_length * .7 ); + $l_str_tooltip_style .= 'em;"'; + } + } + + // Fill in 'templates/public/footnote.html':. + $l_obj_template->replace( + array( + 'link-span' => self::$a_str_link_span, + 'post_id' => self::$a_int_post_id, + 'container_id' => self::$a_int_reference_container_id, + 'note_id' => $l_int_index, + 'hard-link' => $l_str_footnote_link_argument, + 'sup-span' => $l_str_sup_span, + 'before' => MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_STYLING_BEFORE ), + 'index' => $l_int_index, + 'after' => MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_STYLING_AFTER ), + 'anchor-element' => $l_str_referrer_anchor_element, + 'style' => $l_str_tooltip_style, + 'text' => $l_str_tooltip_content, + ) + ); + $l_str_footnote_replace_text = $l_obj_template->get_content(); + + // Reset the template. + $l_obj_template->reload(); + + // If standard tooltips are enabled but alternative are not:. + if ( self::$a_bool_tooltips_enabled && ! self::$a_bool_alternative_tooltips_enabled ) { + + $l_int_offset_y = intval( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_INT_FOOTNOTES_MOUSE_OVER_BOX_OFFSET_Y ) ); + $l_int_offset_x = intval( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_INT_FOOTNOTES_MOUSE_OVER_BOX_OFFSET_X ) ); + $l_int_fade_in_delay = intval( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_INT_MOUSE_OVER_BOX_FADE_IN_DELAY ) ); + $l_int_fade_in_duration = intval( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_INT_MOUSE_OVER_BOX_FADE_IN_DURATION ) ); + $l_int_fade_out_delay = intval( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_INT_MOUSE_OVER_BOX_FADE_OUT_DELAY ) ); + $l_int_fade_out_duration = intval( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_INT_MOUSE_OVER_BOX_FADE_OUT_DURATION ) ); + + // Fill in 'templates/public/tooltip.html':. + $l_obj_template_tooltip->replace( + array( + 'post_id' => self::$a_int_post_id, + 'container_id' => self::$a_int_reference_container_id, + 'note_id' => $l_int_index, + 'position' => MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_MOUSE_OVER_BOX_POSITION ), + 'offset-y' => ! empty( $l_int_offset_y ) ? $l_int_offset_y : 0, + 'offset-x' => ! empty( $l_int_offset_x ) ? $l_int_offset_x : 0, + 'fade-in-delay' => ! empty( $l_int_fade_in_delay ) ? $l_int_fade_in_delay : 0, + 'fade-in-duration' => ! empty( $l_int_fade_in_duration ) ? $l_int_fade_in_duration : 0, + 'fade-out-delay' => ! empty( $l_int_fade_out_delay ) ? $l_int_fade_out_delay : 0, + 'fade-out-duration' => ! empty( $l_int_fade_out_duration ) ? $l_int_fade_out_duration : 0, + ) + ); + $l_str_footnote_replace_text .= $l_obj_template_tooltip->get_content(); + $l_obj_template_tooltip->reload(); + } + } + // Replace the footnote with the template. + $p_str_content = substr_replace( $p_str_content, $l_str_footnote_replace_text, $l_int_pos_start, $l_int_length + strlen( $l_str_ending_tag ) ); + + // Add footnote only if not empty. + if ( ! empty( $l_str_footnote_text ) ) { + // Set footnote to the output box at the end. + self::$a_arr_footnotes[] = $l_str_footnote_text; + // Increase footnote index. + $l_int_footnote_index++; + } + + /** + * Fixes a footnotes numbering bug (happening under de facto rare circumstances). + * + * - Bugfix: Fixed occasional bug where footnote ordering could be out of sequence + * + * @since 1.6.4 + * @date 2016-06-29T0054+0000 + * @committer @dartiss + * @link https://plugins.trac.wordpress.org/browser/footnotes/trunk/class/task.php?rev=1445718 @dartiss’ class/task.php + * @link https://plugins.trac.wordpress.org/log/footnotes/trunk/class/task.php?rev=1445718 @dartiss re-added class/task.php + * @link https://plugins.trac.wordpress.org/browser/footnotes/trunk/class?rev=1445711 class/ w/o task.php + * @link https://plugins.trac.wordpress.org/changeset/1445711/footnotes/trunk/class @dartiss deleted class/task.php + * @link https://plugins.trac.wordpress.org/browser/footnotes/trunk/class/task.php?rev=1026210 @aricura’s latest class/task.php + * + * + * - Bugfix: Process: fix numbering bug impacting footnote #2 with footnote #1 close to start, thanks to @rumperuu bug report, thanks to @lolzim code contribution. + * + * @since 2.5.5 + * + * @contributor @lolzim + * @link https://wordpress.org/support/topic/footnotes-numbered-incorrectly/#post-14062032 + * + * @reporter @rumperuu + * @link https://wordpress.org/support/topic/footnotes-numbered-incorrectly/ + * + * This assignment was overridden by another one, causing the algorithm to jump back + * near the post start to a position calculated as the sum of the length of the last + * footnote and the length of the last footnote replace text. + * A bug disturbing the order of the footnotes depending on the text before the first + * footnote, the length of the first footnote and the length of the templates for the + * footnote and the tooltip. Moreover, it was causing non-trivial process garbage. + */ + // Add offset to the new starting position. + $l_int_pos_start += $l_int_length + strlen( $l_str_ending_tag ); + + } while ( true ); + + // Return content. + return $p_str_content; + } + + /** + * Generates the reference container. + * + * @author Stefan Herndler + * @since 1.5.0 + * @return string + * + * @since 2.0.0 Update: remove backlink symbol along with column 2 of the reference container + * @since 2.0.3 Bugfix: prepend an arrow on user request + * @since 2.0.6 Bugfix: Reference container: fix line breaking behavior in footnote number clusters. + * @since 2.0.4 Bugfix: restore the arrow select and backlink symbol input settings + * @since 2.1.1 Bugfix: Referrers, reference container: Combining identical footnotes: fix dead links and ensure referrer-backlink bijectivity, thanks to @happyches bug report. + * @since 2.1.1 Bugfix: Reference container: Backlink symbol: make optional, not suggest configuring it to invisible, thanks to @spaceling feedback. + */ + public function Reference_container() { + + // No footnotes have been replaced on this page:. + if ( empty( self::$a_arr_footnotes ) ) { + return ''; + } + + /** + * Footnote index backlink symbol + * + * - Bugfix: Reference container: Backlink symbol: make optional, not suggest configuring it to invisible, thanks to @spaceling feedback. + * + * @since 2.1.1 + * + * @reporter @spaceling + * @link https://wordpress.org/support/topic/change-the-position-5/page/2/#post-13671138 + * + * If the backlink symbol is enabled: + */ + if ( MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_REFERENCE_CONTAINER_BACKLINK_SYMBOL_ENABLE ) ) ) { + + // Get html arrow. + $l_str_arrow = MCI_Footnotes_Convert::get_arrow( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_HYPERLINK_ARROW ) ); + // Set html arrow to the first one if invalid index defined. + if ( is_array( $l_str_arrow ) ) { + $l_str_arrow = MCI_Footnotes_Convert::get_arrow( 0 ); + } + // Get user defined arrow. + $l_str_arrow_user_defined = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_HYPERLINK_ARROW_USER_DEFINED ); + if ( ! empty( $l_str_arrow_user_defined ) ) { + $l_str_arrow = $l_str_arrow_user_defined; + } + + // Wrap the arrow in a @media print { display:hidden } span:. + $l_str_footnote_arrow = ''; + $l_str_footnote_arrow .= $l_str_arrow . ''; + + } else { + + // If the backlink symbol isn’t enabled, set it to empty:. + $l_str_arrow = ''; + $l_str_footnote_arrow = ''; + + } + + /** + * Backlink separator + * + * - Bugfix: Reference container: make separating and terminating punctuation optional and configurable, thanks to @docteurfitness issue report and code contribution. + * + * @since 2.1.4 + * @date 2020-11-28T1048+0100 + * + * @contributor @docteurfitness + * @link https://wordpress.org/support/topic/update-2-1-3/#post-13704194 + * + * @reporter @docteurfitness + * @link https://wordpress.org/support/topic/update-2-1-3/ + * + * Initially a comma was appended in this algorithm for enumerations. + * The comma in enumerations is not generally preferred. + */ + if ( MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_BACKLINKS_SEPARATOR_ENABLED ) ) ) { + + // Check if it is input-configured:. + $l_str_separator = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_BACKLINKS_SEPARATOR_CUSTOM ); + + if ( empty( $l_str_separator ) ) { + + // If it is not, check which option is on:. + $l_str_separator_option = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_BACKLINKS_SEPARATOR_OPTION ); + switch ( $l_str_separator_option ) { + case 'comma': + $l_str_separator = ','; + break; + case 'semicolon': + $l_str_separator = ';'; + break; + case 'en_dash': + $l_str_separator = ' –'; + break; + } + } + } else { + + $l_str_separator = ''; + } + + /** + * Backlink terminator + * + * Initially a dot was appended in the table row template. + * + * @since 2.0.6 a dot after footnote numbers is discarded as not localizable; + * making it optional was envisaged. + * @since 2.1.4 the terminator is optional, has options, and is configurable: + */ + if ( MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_BACKLINKS_TERMINATOR_ENABLED ) ) ) { + + // Check if it is input-configured:. + $l_str_terminator = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_BACKLINKS_TERMINATOR_CUSTOM ); + + if ( empty( $l_str_terminator ) ) { + + // If it is not, check which option is on:. + $l_str_terminator_option = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_BACKLINKS_TERMINATOR_OPTION ); + switch ( $l_str_terminator_option ) { + case 'period': + $l_str_terminator = '.'; + break; + case 'parenthesis': + $l_str_terminator = ')'; + break; + case 'colon': + $l_str_terminator = ':'; + break; + } + } + } else { + + $l_str_terminator = ''; + } + + /** + * Line breaks + * + * - Bugfix: Reference container: Backlinks: fix stacked enumerations by adding optional line breaks. + * + * @since 2.1.4 + * @date 2020-11-28T1049+0100 + * + * The backlinks of combined footnotes are generally preferred in an enumeration. + * But when few footnotes are identical, stacking the items in list form is better. + * Variable number length and proportional character width require explicit line breaks. + * Otherwise, an ordinary space character offering a line break opportunity is inserted. + */ + $l_str_line_break = MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_BACKLINKS_LINE_BREAKS_ENABLED ) ) ? '
' : ' '; + + /** + * For maintenance and support, table rows in the reference container should be + * separated by an empty line. So we add these line breaks for source readability. + * Before the first table row (breaks between rows are ~200 lines below): + */ + $l_str_body = "\r\n\r\n"; + + /** + * Reference container table row template load + * + * - Bugfix: Reference container: option to restore pre-2.0.0 layout with the backlink symbol in an extra column. + * + * @since 2.1.1 + * @date 2020-11-16T2024+0100 + */ + + // When combining identical footnotes is turned on, another template is needed:. + if ( MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_COMBINE_IDENTICAL_FOOTNOTES ) ) ) { + // The combining template allows for backlink clusters and supports cell clicking for single notes:. + $l_obj_template = new MCI_Footnotes_Template( MCI_Footnotes_Template::C_STR_PUBLIC, 'reference-container-body-combi' ); + + } else { + + // When 3-column layout is turned on (only available if combining is turned off):. + if ( MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_REFERENCE_CONTAINER_3COLUMN_LAYOUT_ENABLE ) ) ) { + $l_obj_template = new MCI_Footnotes_Template( MCI_Footnotes_Template::C_STR_PUBLIC, 'reference-container-body-3column' ); + + } else { + + // When switch symbol and index is turned on, and combining and 3-columns are off:. + if ( MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_REFERENCE_CONTAINER_BACKLINK_SYMBOL_SWITCH ) ) ) { + $l_obj_template = new MCI_Footnotes_Template( MCI_Footnotes_Template::C_STR_PUBLIC, 'reference-container-body-switch' ); + + } else { + + // Default is the standard template:. + $l_obj_template = new MCI_Footnotes_Template( MCI_Footnotes_Template::C_STR_PUBLIC, 'reference-container-body' ); + + } + } + } + + /** + * Switch backlink symbol and footnote number + * + * - Bugfix: Reference container: option to append symbol (prepended by default), thanks to @spaceling code contribution. + * + * @since 2.1.1 + * @date 2020-11-16T2024+0100 + * + * @contributor @spaceling + * @link https://wordpress.org/support/topic/change-the-position-5/#post-13615994 + * + * + * - Bugfix: Reference container: Backlink symbol: support for appending when combining identicals is on. + * + * @since 2.1.4 + * @date 2020-11-26T1633+0100 + */ + $l_bool_symbol_switch = MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_REFERENCE_CONTAINER_BACKLINK_SYMBOL_SWITCH ) ); + + // Loop through all footnotes found in the page. + for ( $l_int_index = 0; $l_int_index < count( self::$a_arr_footnotes ); $l_int_index++ ) { + + // Get footnote text. + $l_str_footnote_text = self::$a_arr_footnotes[ $l_int_index ]; + + // If footnote is empty, go to the next one;. + // With combine identicals turned on, identicals will be deleted and are skipped:. + if ( empty( $l_str_footnote_text ) ) { + continue; + } + + // Generate content of footnote index cell. + $l_int_first_footnote_index = ( $l_int_index + 1 ); + + // Get the footnote index string and. + // Keep supporting legacy index placeholder:. + $l_str_footnote_id = MCI_Footnotes_Convert::Index( ( $l_int_index + 1 ), MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_COUNTER_STYLE ) ); + + /** + * Case of only one backlink per table row + * + * If enabled, and for the case the footnote is single, compose hard link: + */ + // Define anyway:. + $l_str_hard_link_address = ''; + + if ( self::$a_bool_hard_links_enable ) { + + /** + * Use-Backbutton-Hint tooltip, optional and configurable. + * + * - Update: Reference container: Hard backlinks (optional): optional configurable tooltip hinting to use the backbutton instead, thanks to @theroninjedi47 bug report. + * + * @since 2.5.4 + * + * @reporter @theroninjedi47 + * @link https://wordpress.org/support/topic/hyperlinked-footnotes-creating-excessive-back-history/ + * + * When hard links are enabled, clicks on the backlinks are logged in the browsing history. + * This tooltip hints to use the backbutton instead, so the history gets streamlined again. + * @link https://wordpress.org/support/topic/making-it-amp-compatible/#post-13837359 + */ + if ( MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_FOOTNOTES_BACKLINK_TOOLTIP_ENABLE ) ) ) { + $l_str_use_backbutton_hint = ' title="'; + $l_str_use_backbutton_hint .= MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_BACKLINK_TOOLTIP_TEXT ); + $l_str_use_backbutton_hint .= '"'; + } else { + $l_str_use_backbutton_hint = ''; + } + + /** + * Compose fragment ID anchor with offset, for use in reference container. + * Empty span, child of empty span, to avoid tall dotted rectangles in browser. + */ + $l_str_footnote_anchor_element = ''; + + // Compose optional hard link address:. + $l_str_hard_link_address = ' href="#'; + $l_str_hard_link_address .= self::$a_str_referrer_link_slug; + $l_str_hard_link_address .= self::$a_str_post_container_id_compound; + $l_str_hard_link_address .= $l_str_footnote_id . '"'; + $l_str_hard_link_address .= $l_str_use_backbutton_hint; + + // Compose optional opening link tag with optional hard link, mandatory for instance:. + self::$a_str_link_open_tag = 'get( MCI_Footnotes_Settings::C_BOOL_COMBINE_IDENTICAL_FOOTNOTES ) ) ) { + + // ID, optional hard link address, and class:. + $l_str_footnote_reference = '<' . self::$a_str_link_span; + $l_str_footnote_reference .= ' id="footnote_plugin_reference_'; + $l_str_footnote_reference .= self::$a_int_post_id; + $l_str_footnote_reference .= '_' . self::$a_int_reference_container_id; + $l_str_footnote_reference .= "_$l_str_footnote_id\""; + if ( self::$a_bool_hard_links_enable ) { + $l_str_footnote_reference .= ' href="#'; + $l_str_footnote_reference .= self::$a_str_referrer_link_slug; + $l_str_footnote_reference .= self::$a_str_post_container_id_compound; + $l_str_footnote_reference .= $l_str_footnote_id . '"'; + $l_str_footnote_reference .= $l_str_use_backbutton_hint; + } + $l_str_footnote_reference .= ' class="footnote_backlink"'; + + // The click event goes in the table cell if footnote remains single:. + $l_str_backlink_event = ' onclick="footnote_move_to_anchor_'; + $l_str_backlink_event .= self::$a_int_post_id; + $l_str_backlink_event .= '_' . self::$a_int_reference_container_id; + $l_str_backlink_event .= "('footnote_plugin_tooltip_"; + $l_str_backlink_event .= self::$a_int_post_id; + $l_str_backlink_event .= '_' . self::$a_int_reference_container_id; + $l_str_backlink_event .= "_$l_str_footnote_id');\""; + + // The dedicated template enumerating backlinks uses another variable:. + $l_str_footnote_backlinks = $l_str_footnote_reference; + + // Append the click event right to the backlink item for enumerations;. + // Else it goes in the table cell:. + $l_str_footnote_backlinks .= $l_str_backlink_event . '>'; + $l_str_footnote_reference .= '>'; + + // Append the optional offset anchor for hard links:. + if ( self::$a_bool_hard_links_enable ) { + $l_str_footnote_reference .= $l_str_footnote_anchor_element; + $l_str_footnote_backlinks .= $l_str_footnote_anchor_element; + } + + // Continue both single note and notes cluster, depending on switch option status:. + if ( $l_bool_symbol_switch ) { + + $l_str_footnote_reference .= "$l_str_footnote_id$l_str_footnote_arrow"; + $l_str_footnote_backlinks .= "$l_str_footnote_id$l_str_footnote_arrow"; + + } else { + + $l_str_footnote_reference .= "$l_str_footnote_arrow$l_str_footnote_id"; + $l_str_footnote_backlinks .= "$l_str_footnote_arrow$l_str_footnote_id"; + + } + + // If that is the only footnote with this text, we’re almost done.. + + // Check if it isn't the last footnote in the array:. + if ( $l_int_first_footnote_index < count( self::$a_arr_footnotes ) ) { + + // Get all footnotes that haven't passed yet:. + for ( $l_int_check_index = $l_int_first_footnote_index; $l_int_check_index < count( self::$a_arr_footnotes ); $l_int_check_index++ ) { + + // Check if a further footnote is the same as the actual one:. + if ( $l_str_footnote_text == self::$a_arr_footnotes[ $l_int_check_index ] ) { + + // If so, set the further footnote as empty so it won't be displayed later:. + self::$a_arr_footnotes[ $l_int_check_index ] = ''; + + // Set the flag to true for the combined status:. + $l_bool_flag_combined = true; + + // Update the footnote ID:. + $l_str_footnote_id = MCI_Footnotes_Convert::Index( ( $l_int_check_index + 1 ), MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_COUNTER_STYLE ) ); + + // Resume composing the backlinks enumeration:. + $l_str_footnote_backlinks .= "$l_str_separator'; + $l_str_footnote_backlinks .= $l_str_line_break; + $l_str_footnote_backlinks .= '<' . self::$a_str_link_span; + $l_str_footnote_backlinks .= ' id="footnote_plugin_reference_'; + $l_str_footnote_backlinks .= self::$a_int_post_id; + $l_str_footnote_backlinks .= '_' . self::$a_int_reference_container_id; + $l_str_footnote_backlinks .= "_$l_str_footnote_id\""; + + // Insert the optional hard link address:. + if ( self::$a_bool_hard_links_enable ) { + $l_str_footnote_backlinks .= ' href="#'; + $l_str_footnote_backlinks .= self::$a_str_referrer_link_slug; + $l_str_footnote_backlinks .= self::$a_str_post_container_id_compound; + $l_str_footnote_backlinks .= $l_str_footnote_id . '"'; + $l_str_footnote_backlinks .= $l_str_use_backbutton_hint; + } + + $l_str_footnote_backlinks .= ' class="footnote_backlink"'; + $l_str_footnote_backlinks .= ' onclick="footnote_move_to_anchor_'; + $l_str_footnote_backlinks .= self::$a_int_post_id; + $l_str_footnote_backlinks .= '_' . self::$a_int_reference_container_id; + $l_str_footnote_backlinks .= "('footnote_plugin_tooltip_"; + $l_str_footnote_backlinks .= self::$a_int_post_id; + $l_str_footnote_backlinks .= '_' . self::$a_int_reference_container_id; + $l_str_footnote_backlinks .= "_$l_str_footnote_id');\">"; + + // Append the offset anchor for optional hard links:. + if ( self::$a_bool_hard_links_enable ) { + $l_str_footnote_backlinks .= ''; + } + + $l_str_footnote_backlinks .= $l_bool_symbol_switch ? '' : $l_str_footnote_arrow; + $l_str_footnote_backlinks .= $l_str_footnote_id; + $l_str_footnote_backlinks .= $l_bool_symbol_switch ? $l_str_footnote_arrow : ''; + + } + } + } + + // Append terminator and end tag:. + $l_str_footnote_reference .= $l_str_terminator . ''; + $l_str_footnote_backlinks .= $l_str_terminator . ''; + + } + + // Line wrapping of URLs already fixed, see above. + + // Get reference container item text if tooltip text goes separate:. + $l_int_tooltip_text_length = strpos( $l_str_footnote_text, self::$a_str_tooltip_shortcode ); + $l_bool_has_tooltip_text = $l_int_tooltip_text_length === false ? false : true; + if ( $l_bool_has_tooltip_text ) { + $l_str_not_tooltip_text = substr( $l_str_footnote_text, ( $l_int_tooltip_text_length + self::$a_int_tooltip_shortcode_length ) ); + self::$a_bool_mirror_tooltip_text = MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_FOOTNOTES_TOOLTIP_EXCERPT_MIRROR_ENABLE ) ); + if ( self::$a_bool_mirror_tooltip_text ) { + $l_str_tooltip_text = substr( $l_str_footnote_text, 0, $l_int_tooltip_text_length ); + $l_str_reference_text_introducer = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_TOOLTIP_EXCERPT_MIRROR_SEPARATOR ); + $l_str_reference_text = $l_str_tooltip_text . $l_str_reference_text_introducer . $l_str_not_tooltip_text; + } else { + $l_str_reference_text = $l_str_not_tooltip_text; + } + } else { + $l_str_reference_text = $l_str_footnote_text; + } + + // Replace all placeholders in table row template:. + $l_obj_template->replace( + array( + + // Placeholder used in all templates:. + 'text' => $l_str_reference_text, + + // Used in standard layout W/O COMBINED FOOTNOTES:. + 'post_id' => self::$a_int_post_id, + 'container_id' => self::$a_int_reference_container_id, + 'note_id' => MCI_Footnotes_Convert::Index( $l_int_first_footnote_index, MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_COUNTER_STYLE ) ), + 'link-start' => self::$a_str_link_open_tag, + 'link-end' => self::$a_str_link_close_tag, + 'link-span' => self::$a_str_link_span, + 'terminator' => $l_str_terminator, + 'anchor-element' => $l_str_footnote_anchor_element, + 'hard-link' => $l_str_hard_link_address, + + // Used in standard layout WITH COMBINED IDENTICALS TURNED ON:. + 'pointer' => $l_bool_flag_combined ? '' : ' pointer', + 'event' => $l_bool_flag_combined ? '' : $l_str_backlink_event, + 'backlinks' => $l_bool_flag_combined ? $l_str_footnote_backlinks : $l_str_footnote_reference, + + // Legacy placeholders for use in legacy layout templates:. + 'arrow' => $l_str_footnote_arrow, + 'index' => $l_str_footnote_id, + ) + ); + + $l_str_body .= $l_obj_template->get_content(); + + // Extra line breaks for page source readability:. + $l_str_body .= "\r\n\r\n"; + + $l_obj_template->reload(); + + } + + // Call again for robustness when priority levels don’t match any longer:. + self::$a_int_scroll_offset = intval( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_INT_FOOTNOTES_SCROLL_OFFSET ) ); + + // Streamline:. + $l_bool_collapse_default = MCI_Footnotes_Convert::to_bool( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_BOOL_REFERENCE_CONTAINER_COLLAPSE ) ); + + /** + * Reference container label + * + * - Bugfix: Reference container: Label: set empty label to U+202F NNBSP for more robustness, thanks to @lukashuggenberg feedback. + * + * @since 2.4.0 + * @date 2021-01-04T0504+0100 + * + * @reporter @lukashuggenberg + * + * Themes may drop-cap a first letter of initial paragraphs, like this label. + * In case of empty label that would apply to the left half button character. + * Hence the point in setting an empty label to U+202F NARROW NO-BREAK SPACE. + */ + $l_str_reference_container_label = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_REFERENCE_CONTAINER_NAME ); + + /** + * Select the reference container template according to the script mode. + * + * - Bugfix: Reference container: optional alternative expanding and collapsing without j_query for use with hard links, thanks to @hopper87it @pkverma99 issue reports. + * + * @since 2.5.6 + * + * @reporter @hopper87it + * @link https://wordpress.org/support/topic/footnotes-wp-rocket/ + * + * @reporter @pkverma99 + * @link https://wordpress.org/support/topic/footnotes-wp-rocket/#post-14076188 + */ + $l_str_script_mode = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_REFERENCE_CONTAINER_SCRIPT_MODE ); + + if ( $l_str_script_mode == 'jquery' ) { + + // Load 'templates/public/reference-container.html':. + $l_obj_template_container = new MCI_Footnotes_Template( MCI_Footnotes_Template::C_STR_PUBLIC, 'reference-container' ); + + } else { + + // Load 'templates/public/js-reference-container.html':. + $l_obj_template_container = new MCI_Footnotes_Template( MCI_Footnotes_Template::C_STR_PUBLIC, 'js-reference-container' ); + } + + $l_obj_template_container->replace( + array( + 'post_id' => self::$a_int_post_id, + 'container_id' => self::$a_int_reference_container_id, + 'element' => MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_REFERENCE_CONTAINER_LABEL_ELEMENT ), + 'name' => empty( $l_str_reference_container_label ) ? ' ' : $l_str_reference_container_label, + 'button-style' => ! $l_bool_collapse_default ? 'display: none;' : '', + 'style' => $l_bool_collapse_default ? 'display: none;' : '', + 'content' => $l_str_body, + 'scroll-offset' => ( self::$a_int_scroll_offset / 100 ), + 'scroll-duration' => intval( MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_INT_FOOTNOTES_SCROLL_DURATION ) ), + ) + ); + + // Free all found footnotes if reference container will be displayed. + self::$a_arr_footnotes = array(); + + return $l_obj_template_container->get_content(); + } +} diff --git a/class/template.php b/class/template.php index d8c19b9..8dff773 100644 --- a/class/template.php +++ b/class/template.php @@ -1,261 +1,259 @@ -plugin_directory = plugin_dir_path( dirname( __FILE__ ) ); - - /** - * Modularize functions - * - * @since 2.4.0d3 - * - * @author Patrizia Lutz @misfist - */ - if( $template = $this->get_template( $p_str_FileType, $p_str_FileName, $p_str_Extension ) ) { - $this->process_template( $template ); - } else { - return; - } - - } - - /** - * Replace all placeholders specified in array. - * - * @author Stefan Herndler - * @since 1.5.0 - * @param array $p_arr_Placeholders Placeholders (key = placeholder, value = value). - * @return bool True on Success, False if Placeholders invalid. - */ - public function replace($p_arr_Placeholders) { - // no placeholders set - if (empty($p_arr_Placeholders)) { - return false; - } - // template content is empty - if (empty($this->a_str_ReplacedContent)) { - return false; - } - // iterate through each placeholder and replace it with its value - foreach($p_arr_Placeholders as $l_str_Placeholder => $l_str_Value) { - $this->a_str_ReplacedContent = str_replace("[[" . $l_str_Placeholder . "]]", $l_str_Value, $this->a_str_ReplacedContent); - } - // success - return true; - } - - /** - * Reloads the original content of the template file. - * - * @author Stefan Herndler - * @since 1.5.0 - */ - public function reload() { - $this->a_str_ReplacedContent = $this->a_str_OriginalContent; - } - - /** - * Returns the content of the template file with replaced placeholders. - * - * @author Stefan Herndler - * @since 1.5.0 - * @return string Template content with replaced placeholders. - */ - public function getContent() { - return $this->a_str_ReplacedContent; - } - - /** - * Process template file - * - * @author Patrizia Lutz @misfist - * - * @since 2.4.0d3 - * - * @param string $template - * @return void - * - * - * @since 2.0.3 replace tab with a space - * @since 2.0.3 replace 2 spaces with 1 - * @since 2.0.4 collapse multiple spaces - * @since 2.2.6 delete a space before a closing pointy bracket - * @since 2.5.4 collapse HTML comments and PHP/JS docblocks (only) - */ - public function process_template( $template ) { - $this->a_str_OriginalContent = preg_replace( '##s', "", file_get_contents( $template ) ); - $this->a_str_OriginalContent = preg_replace( '#/\*\*.+?\*/#s', "", $this->a_str_OriginalContent ); - $this->a_str_OriginalContent = str_replace( "\n", "", $this->a_str_OriginalContent ); - $this->a_str_OriginalContent = str_replace( "\r", "", $this->a_str_OriginalContent ); - $this->a_str_OriginalContent = str_replace( "\t", " ", $this->a_str_OriginalContent ); - $this->a_str_OriginalContent = preg_replace( '# +#', " ", $this->a_str_OriginalContent ); - $this->a_str_OriginalContent = str_replace( " >", ">", $this->a_str_OriginalContent ); - $this->reload(); - } - - /** - * Get the template - * - * @author Patrizia Lutz @misfist - * - * @since 2.4.0d3 - * - * @param string $p_str_FileType - * @param string $p_str_FileName - * @param string $p_str_Extension - * @return mixed false | template path - */ - public function get_template( $p_str_FileType, $p_str_FileName, $p_str_Extension = "html" ) { - $located = false; - - /** - * The directory change be modified - * @usage to change location of templates to `template_parts/footnotes/': - * add_filter( 'mci_footnotes_template_directory', function( $directory ) { - * return 'template_parts/footnotes/; - * } ); - */ - $template_directory = apply_filters( 'mci_footnotes_template_directory', 'footnotes/templates/' ); - $custom_directory = apply_filters( 'mci_footnotes_custom_template_directory', 'footnotes-custom/' ); - $template_name = $p_str_FileType . '/' . $p_str_FileName . '.' . $p_str_Extension; - - /** - * Look in active (child) theme - */ - if ( file_exists( trailingslashit( get_stylesheet_directory() ) . $template_directory . $template_name ) ) { - $located = trailingslashit( get_stylesheet_directory() ) . $template_directory . $template_name; - - /** - * Look in parent theme - */ - } elseif ( file_exists( trailingslashit( get_template_directory() ) . $template_directory . $template_name ) ) { - $located = trailingslashit( get_template_directory() ) . $template_directory . $template_name; - - /** - * Look in custom directory - */ - } elseif ( file_exists( trailingslashit( WP_PLUGIN_DIR ) . $custom_directory . 'templates/' . $template_name ) ) { - $located = trailingslashit( WP_PLUGIN_DIR ) . $custom_directory . 'templates/' . $template_name; - - /** - * Look in plugin - */ - } elseif ( file_exists( $this->plugin_directory . 'templates/' . $template_name ) ) { - $located = $this->plugin_directory . 'templates/' . $template_name; - } - - return $located; - } - -} // end of class +plugin_directory = plugin_dir_path( dirname( __FILE__ ) ); + + /** + * Modularize functions + * + * @since 2.4.0d3 + * + * @author Patrizia Lutz @misfist + */ + if ( $template = $this->get_template( $p_str_file_type, $p_str_file_name, $p_str_extension ) ) { + $this->process_template( $template ); + } else { + return; + } + + } + + /** + * Replace all placeholders specified in array. + * + * @author Stefan Herndler + * @since 1.5.0 + * @param array $p_arr_placeholders Placeholders (key = placeholder, value = value). + * @return bool True on Success, False if Placeholders invalid. + */ + public function replace( $p_arr_placeholders ) { + // No placeholders set. + if ( empty( $p_arr_placeholders ) ) { + return false; + } + // Template content is empty. + if ( empty( $this->a_str_replaced_content ) ) { + return false; + } + // Iterate through each placeholder and replace it with its value. + foreach ( $p_arr_placeholders as $l_str_placeholder => $l_str_value ) { + $this->a_str_replaced_content = str_replace( '[[' . $l_str_placeholder . ']]', $l_str_value, $this->a_str_replaced_content ); + } + // Success. + return true; + } + + /** + * Reloads the original content of the template file. + * + * @author Stefan Herndler + * @since 1.5.0 + */ + public function reload() { + $this->a_str_replaced_content = $this->a_str_original_content; + } + + /** + * Returns the content of the template file with replaced placeholders. + * + * @author Stefan Herndler + * @since 1.5.0 + * @return string Template content with replaced placeholders. + */ + public function get_content() { + return $this->a_str_replaced_content; + } + + /** + * Process template file + * + * @author Patrizia Lutz @misfist + * + * @since 2.4.0d3 + * + * @param string $template + * @return void + * + * @since 2.0.3 replace tab with a space + * @since 2.0.3 replace 2 spaces with 1 + * @since 2.0.4 collapse multiple spaces + * @since 2.2.6 delete a space before a closing pointy bracket + * @since 2.5.4 collapse HTML comments and PHP/JS docblocks (only) + */ + public function process_template( $template ) { + $this->a_str_original_content = preg_replace( '##s', '', file_get_contents( $template ) ); + $this->a_str_original_content = preg_replace( '#/\*\*.+?\*/#s', '', $this->a_str_original_content ); + $this->a_str_original_content = str_replace( "\n", '', $this->a_str_original_content ); + $this->a_str_original_content = str_replace( "\r", '', $this->a_str_original_content ); + $this->a_str_original_content = str_replace( "\t", ' ', $this->a_str_original_content ); + $this->a_str_original_content = preg_replace( '# +#', ' ', $this->a_str_original_content ); + $this->a_str_original_content = str_replace( ' >', '>', $this->a_str_original_content ); + $this->reload(); + } + + /** + * Get the template + * + * @author Patrizia Lutz @misfist + * + * @since 2.4.0d3 + * + * @param string $p_str_file_type + * @param string $p_str_file_name + * @param string $p_str_extension + * @return mixed false | template path + */ + public function get_template( $p_str_file_type, $p_str_file_name, $p_str_extension = 'html' ) { + $located = false; + + /** + * The directory change be modified + * + * @usage to change location of templates to `template_parts/footnotes/': + * add_filter( 'mci_footnotes_template_directory', function( $directory ) { + * return 'template_parts/footnotes/; + * } ); + */ + $template_directory = apply_filters( 'mci_footnotes_template_directory', 'footnotes/templates/' ); + $custom_directory = apply_filters( 'mci_footnotes_custom_template_directory', 'footnotes-custom/' ); + $template_name = $p_str_file_type . '/' . $p_str_file_name . '.' . $p_str_extension; + + /** + * Look in active (child) theme + */ + if ( file_exists( trailingslashit( get_stylesheet_directory() ) . $template_directory . $template_name ) ) { + $located = trailingslashit( get_stylesheet_directory() ) . $template_directory . $template_name; + + /** + * Look in parent theme + */ + } elseif ( file_exists( trailingslashit( get_template_directory() ) . $template_directory . $template_name ) ) { + $located = trailingslashit( get_template_directory() ) . $template_directory . $template_name; + + /** + * Look in custom directory + */ + } elseif ( file_exists( trailingslashit( WP_PLUGIN_DIR ) . $custom_directory . 'templates/' . $template_name ) ) { + $located = trailingslashit( WP_PLUGIN_DIR ) . $custom_directory . 'templates/' . $template_name; + + /** + * Look in plugin + */ + } elseif ( file_exists( $this->plugin_directory . 'templates/' . $template_name ) ) { + $located = $this->plugin_directory . 'templates/' . $template_name; + } + + return $located; + } + +} // End of class. diff --git a/class/wysiwyg.php b/class/wysiwyg.php index d8b558b..c58c7fb 100644 --- a/class/wysiwyg.php +++ b/class/wysiwyg.php @@ -1,83 +1,88 @@ -getContent(); - } - - /** - * Includes the Plugins WYSIWYG editor script. - * - * @author Stefan Herndler - * @since 1.5.0 - * @param array $p_arr_Plugins Scripts to be included to the editor. - * @return array - */ - public static function includeScripts($p_arr_Plugins) { - $p_arr_Plugins[MCI_Footnotes_Config::C_STR_PLUGIN_NAME] = plugins_url('/../js/wysiwyg-editor.js', __FILE__); - return $p_arr_Plugins; - } - - /** - * AJAX Callback function when the Footnotes Button is clicked. Either in the Plain text or Visual editor. - * Returns an JSON encoded array with the Footnotes start and end short code. - * - * @author Stefan Herndler - * @since 1.5.0 - */ - public static function ajaxCallback() { - // get start and end tag for the footnotes short code - $l_str_StartingTag = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_SHORT_CODE_START); - $l_str_EndingTag = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_SHORT_CODE_END); - if ($l_str_StartingTag == "userdefined" || $l_str_EndingTag == "userdefined") { - $l_str_StartingTag = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_SHORT_CODE_START_USER_DEFINED); - $l_str_EndingTag = MCI_Footnotes_Settings::instance()->get(MCI_Footnotes_Settings::C_STR_FOOTNOTES_SHORT_CODE_END_USER_DEFINED); - } - echo json_encode(array("start" => htmlspecialchars($l_str_StartingTag), "end" => htmlspecialchars($l_str_EndingTag))); - exit; - } -} \ No newline at end of file +get_content(); + } + + /** + * Includes the Plugins WYSIWYG editor script. + * + * @author Stefan Herndler + * @since 1.5.0 + * @param array $p_arr_plugins Scripts to be included to the editor. + * @return array + */ + public static function include_scripts( $p_arr_plugins ) { + $p_arr_plugins[ MCI_Footnotes_Config::C_STR_PLUGIN_NAME ] = plugins_url( '/../js/wysiwyg-editor.js', __FILE__ ); + return $p_arr_plugins; + } + + /** + * AJAX Callback function when the Footnotes Button is clicked. Either in the Plain text or Visual editor. + * Returns an JSON encoded array with the Footnotes start and end short code. + * + * @author Stefan Herndler + * @since 1.5.0 + */ + public static function ajax_callback() { + // Get start and end tag for the footnotes short code. + $l_str_starting_tag = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_SHORT_CODE_START ); + $l_str_ending_tag = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_SHORT_CODE_END ); + if ( $l_str_starting_tag == 'userdefined' || $l_str_ending_tag == 'userdefined' ) { + $l_str_starting_tag = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_SHORT_CODE_START_USER_DEFINED ); + $l_str_ending_tag = MCI_Footnotes_Settings::instance()->get( MCI_Footnotes_Settings::C_STR_FOOTNOTES_SHORT_CODE_END_USER_DEFINED ); + } + echo json_encode( + array( + 'start' => htmlspecialchars( $l_str_starting_tag ), + 'end' => htmlspecialchars( $l_str_ending_tag ), + ) + ); + exit; + } +} diff --git a/footnotes.php b/footnotes.php index ef4848f..306275b 100755 --- a/footnotes.php +++ b/footnotes.php @@ -1,75 +1,75 @@ -run(); - -/** - * Sets the stylesheet enqueuing mode for production. - * - * @since 2.5.5 - * @var bool - * @see class/init.php - * - * In production, a minified CSS file tailored to the settings is enqueued. - * - * Developing stylesheets is meant to be easier when this is set to false. - * WARNING: This facility designed for development must NOT be used in production. - */ -define( 'C_BOOL_CSS_PRODUCTION_MODE', true ); +run(); + +/** + * Sets the stylesheet enqueuing mode for production. + * + * @since 2.5.5 + * @var bool + * @see class/init.php + * + * In production, a minified CSS file tailored to the settings is enqueued. + * + * Developing stylesheets is meant to be easier when this is set to false. + * WARNING: This facility designed for development must NOT be used in production. + */ +define( 'C_BOOL_CSS_PRODUCTION_MODE', true ); diff --git a/includes.php b/includes.php index aa35d1c..15a2de6 100644 --- a/includes.php +++ b/includes.php @@ -1,37 +1,37 @@ -