'; } function ba_get_svg_clock_icon() { return ' '; } function ba_get_img_masking_shapes($shape) { $masking_shapes = array( 'none' => '', 'shape_1' => '', 'shape_2' => '', 'shape_3' => '', ); return $masking_shapes[$shape]; } function ba_get_post_types() { $post_types = get_post_types( array( 'public' => true, ), 'objects' ); $options = array(); foreach ($post_types as $post_type) { $options[$post_type->name] = $post_type->label; } // Deprecated 'Media' post type. $key = array_search('Media', $options, true); if ('attachment' === $key) { unset($options[$key]); } return apply_filters('uael_loop_post_types', $options); } function ba_get_taxonomies() { $taxonomies = get_taxonomies(array('show_in_nav_menus' => true), 'objects'); $options = array('' => ''); foreach ($taxonomies as $taxonomy) { $options[$taxonomy->name] = $taxonomy->label; } return $options; } function ba_get_date_link($post_id = null) { if (empty($post_id)) { $post_id = get_the_ID(); } $year = get_the_date('Y', $post_id); $month = get_the_time('m', $post_id); $day = get_the_time('d', $post_id); $url = get_day_link($year, $month, $day); return $url; } function ba_get_excerpt($post_id = null, $length = null) { if (empty($length)) { $length = 35; } return wpautop(strip_shortcodes(truncate_post($length, false, '', true))); } function ba_prev_arrow_icon() { return ''; } function ba_next_arrow_icon() { return ''; } if (!function_exists('dt_if_not_migrated')) { function dt_if_not_migrated() { $not_migrated = get_option('ba_version'); if ($not_migrated) { return true; } return false; } } function dt_backend_helpers($helpers) { $helpers['ifMigrated'] = dt_if_not_migrated() ? 'false' : 'true'; return $helpers; } add_filter('et_fb_backend_helpers', 'dt_backend_helpers'); /** * Track which icon-font asset handles this request needs. * * @param string|null $add Handle to mark as needed (et_icons_fa|et_icons_all). * * @return string[] All handles marked needed so far. */ if (!function_exists('dtq_needed_icon_assets')) { function dtq_needed_icon_assets($add = null) { static $needed = array(); if (null !== $add) { $needed[$add] = true; } return array_keys($needed); } } if (!function_exists('dtq_global_assets_list')) { function dtq_global_assets_list($global_list) { $assets_prefix = et_get_dynamic_assets_path(); $map = array( 'et_icons_fa' => "{$assets_prefix}/css/icons_fa_all.css", 'et_icons_all' => "{$assets_prefix}/css/icons_all.css", ); $assets_list = array(); foreach (dtq_needed_icon_assets() as $handle) { if (isset($map[$handle])) { $assets_list[$handle] = array('css' => $map[$handle]); } } return array_merge($global_list, $assets_list); } } /** * Ensure the correct icon font is loaded for a rendered icon. * * FontAwesome icons need the FA font; every other (Divi) icon needs the full * Divi icon font (icons_all) — otherwise only the small base set renders and * extended glyphs (e.g. warning) fall back to a literal letter. * * Pass the full "unicode||type||weight" string: et_pb_maybe_fa_font_icon() * detects FontAwesome by the "||" separator, so a bare unicode is always read * as a Divi icon and the FA font is never requested. * * The two filters below are DIVI 4 ONLY. Divi 4's Dynamic Assets still calls * them, so they must stay. Divi 5 never fires them and has no third-party * equivalent — see dtq_print_full_icon_font(), which is what actually delivers * the font there. * * @param string $icon_data Icon value (unicode, optionally "||type||weight"). */ if (!function_exists('dtq_inject_fa_icons')) { function dtq_inject_fa_icons($icon_data) { $is_fa = function_exists('et_pb_maybe_fa_font_icon') && et_pb_maybe_fa_font_icon($icon_data); dtq_needed_icon_assets($is_fa ? 'et_icons_fa' : 'et_icons_all'); add_filter('et_global_assets_list', 'dtq_global_assets_list'); add_filter('et_late_global_assets_list', 'dtq_global_assets_list'); } } /** * Locate Divi on disk and on the web, whichever way it is installed. * * Divi 5 ships two ways: as the Divi (or Extra) theme, and as the Divi Builder * plugin sitting under any third-party theme. get_template_directory_uri() is * only correct for the first. Under the Builder plugin it returns the ACTIVE * THEME, so a Divi-relative asset URL built from it 404s. * * That is worse than it sounds for @font-face: our rule prints at wp_footer 99, * after Divi's own, and for a given family the last matching rule wins. A rule * pointing at a 404 therefore *overrides* Divi's working font and breaks icons * that would otherwise have rendered. Hence the file_exists() checks in * dtq_icon_font_face() — we would rather emit nothing than emit a broken face. * * Mirrors Divi's own resolver, DynamicAssetsUtils::get_dynamic_assets_path(). * * @return array{dir:string,uri:string} Divi root as a filesystem path and a URL. */ if (!function_exists('dtq_divi_root')) { function dtq_divi_root() { if ( function_exists('et_is_builder_plugin_active') && et_is_builder_plugin_active() && defined('ET_BUILDER_PLUGIN_DIR') && defined('ET_BUILDER_PLUGIN_URI') ) { return array( 'dir' => untrailingslashit(ET_BUILDER_PLUGIN_DIR), 'uri' => untrailingslashit(ET_BUILDER_PLUGIN_URI), ); } return array( 'dir' => untrailingslashit(get_template_directory()), 'uri' => untrailingslashit(get_template_directory_uri()), ); } } /** * Build one @font-face rule for a Divi-bundled icon font. * * Probes woff2 then woff and emits only the formats that actually exist, so the * same call works for FontAwesome (ships woff2 + woff) and for ETmodules (ships * woff, no woff2). Returns an empty string when no file is found, which is what * keeps a wrong Divi root from producing a face that shadows a working one. * * @param string $family Font family name to declare. * @param array $root Divi root from dtq_divi_root(). * @param string $rel Path below the Divi root, without extension. * @param int $weight font-weight for this face. * * @return string One @font-face rule, or '' if the font files are missing. */ if (!function_exists('dtq_icon_font_face')) { function dtq_icon_font_face($family, $root, $rel, $weight) { $sources = array(); foreach (array('woff2', 'woff') as $format) { if (file_exists($root['dir'] . $rel . '.' . $format)) { $sources[] = sprintf( 'url("%1$s") format("%2$s")', esc_url($root['uri'] . $rel . '.' . $format), $format ); } } if (empty($sources)) { return ''; } return sprintf( '@font-face{font-family:"%1$s";font-style:normal;font-weight:%2$d;font-display:swap;src:%3$s;}', $family, (int) $weight, implode(',', $sources) ); } } /** * Ensure the icon fonts our modules just rendered are actually available. * * Two separate problems, one cure. * * ETmodules: Divi's "Dynamic Icons" feature only ships the subset of glyphs it * detects in page content, and its detector does not see icons stored in our D5 * module attributes. Extended Divi icons (warning, star, heart, …) then have no * glyph and fall back to a literal letter. * * FontAwesome: worse. dtq_inject_fa_icons() asks for the FA stylesheet through * et_global_assets_list / et_late_global_assets_list, and under Divi 5 NEITHER * FILTER EVER FIRES — verified by instrumenting both, plus their Divi 5 * replacements (divi_frontend_assets_dynamic_assets_[late_]global_assets_list), * on a real front-end render. Divi 5 gates FA on $feature_state->use_fa_icons, * set from DynamicAssetsUtils::get_font_icon_modules(), a hard-coded list of * divi/* module names with no filter on it. A third-party module can never * enter that list, so Divi will never load FA on our behalf and every FA icon * renders as an empty box. There is no hook to fix this upstream; declare the * faces ourselves. * * @param string[] $handles Handles to declare (et_icons_all|et_icons_fa). * * @return string The @font-face rules, or '' when none could be built. */ if (!function_exists('dtq_icon_font_css')) { function dtq_icon_font_css($handles) { $root = dtq_divi_root(); $css = ''; if (in_array('et_icons_all', (array) $handles, true)) { $css .= dtq_icon_font_face('ETmodules', $root, '/core/admin/fonts/modules/all/modules', 400); } if (in_array('et_icons_fa', (array) $handles, true)) { // The three faces Divi's own icons_fa_all.css declares. Regular and // Brands share weight 400 but cover disjoint codepoints, so both are // needed and neither shadows the other. $fa_faces = array( array('fa-regular-400', 400), array('fa-solid-900', 900), array('fa-brands-400', 400), ); foreach ($fa_faces as $face) { $css .= dtq_icon_font_face('FontAwesome', $root, '/core/admin/fonts/fontawesome/' . $face[0], $face[1]); } } return $css; } } /** * Print the icon fonts this request's modules actually rendered. * * Runs at wp_footer because dtq_needed_icon_assets() is only populated while * modules render, which is long after wp_head. Emits nothing on a page with no * icons, so the cost on the rest of the site is zero. */ if (!function_exists('dtq_print_full_icon_font')) { function dtq_print_full_icon_font() { $css = dtq_icon_font_css(dtq_needed_icon_assets()); if ('' === $css) { return; } // Element id kept from when this only handled ETmodules; changing it // would break any site targeting it from custom CSS. printf('', $css); } } add_action('wp_footer', 'dtq_print_full_icon_font', 99); /** * Normalize an icon unicode value for output. * * Divi 4 stored icons either as a unicode entity ("àXX;", optionally with * "||type||weight") or as the legacy index format "%%NN%%". When a layout using * the legacy format is migrated to Divi 5, that "%%NN%%" string can survive into * the D5 icon object's `unicode` field and would otherwise render as raw glyphs. * Resolve it the same way Divi 4 did, via et_pb_process_font_icon(). * * The result is escaped with esc_html() before it is returned: the stored value * is a module attribute and therefore author-controlled, so printing it raw into * the icon would let crafted markup reach the DOM. esc_html() passes * `$double_encode = false`, so an existing "àXX;" entity survives untouched * and the browser still renders the glyph — the call is idempotent for entities. * * @param string $unicode Raw unicode value from the icon attribute. * * @return string Escaped unicode entity, safe to print inside the icon . */ if (!function_exists('dtq_resolve_icon_unicode')) { function dtq_resolve_icon_unicode($unicode) { $unicode = (string) $unicode; if (false !== strpos($unicode, '%%') && function_exists('et_pb_process_font_icon')) { // et_pb_process_font_icon() returns the entity with the ampersand already // escaped ("&#xe0XX;"); decode once so the raw entity ("àXX;") is // what lands inside the icon and the browser renders the glyph. $unicode = html_entity_decode(et_pb_process_font_icon($unicode)); } return esc_html($unicode); } } /** * mbstring-safe string helpers. * * mbstring is not guaranteed to be present — it is a non-default PHP extension * and some shared hosts ship without it. Calling mb_* unguarded is a fatal on * those hosts, which took the whole page down whenever a module truncated an * excerpt. These wrappers fall back to the single-byte equivalents; the fallback * can split a multi-byte character mid-sequence, which is cosmetically imperfect * but far better than a white screen. */ if (!function_exists('dtq_strlen')) { function dtq_strlen($string) { $string = (string) $string; return function_exists('mb_strlen') ? mb_strlen($string) : strlen($string); } } if (!function_exists('dtq_substr')) { function dtq_substr($string, $start, $length = null) { $string = (string) $string; if (function_exists('mb_substr')) { return null === $length ? mb_substr($string, $start) : mb_substr($string, $start, $length); } return null === $length ? substr($string, $start) : substr($string, $start, $length); } } if (!function_exists('dtq_strimwidth')) { function dtq_strimwidth($string, $start, $width, $trim_marker = '') { $string = (string) $string; if (function_exists('mb_strimwidth')) { return mb_strimwidth($string, $start, $width, $trim_marker); } return dtq_strimwidth_fallback($string, $start, $width, $trim_marker); } } /** * Single-byte implementation of dtq_strimwidth(). * * Split out from the wrapper so it stays reachable — and therefore testable — * on hosts that DO have mbstring, where the wrapper always delegates. Matches * mb_strimwidth(), including its quirk of returning the trim marker itself when * $width is not larger than the marker. */ if (!function_exists('dtq_strimwidth_fallback')) { function dtq_strimwidth_fallback($string, $start, $width, $trim_marker = '') { $slice = substr((string) $string, $start); if (strlen($slice) <= $width) { return $slice; } return substr($slice, 0, max(0, $width - strlen($trim_marker))) . $trim_marker; } } /** * Resolve a Divi `$variable(...)$` token (global colour / variable) to CSS. * * Divi 5 stores a global colour as an encoded `$variable({...})$` string. Style * output that bypasses Divi's own resolver emits that token literally, so the * colour renders as raw text instead of a colour. Run author-supplied values * through this before sanitizing them. * * Returns the value unchanged when it holds no dynamic variable, or when Divi's * resolver is unavailable. * * @param mixed $value Raw attribute value. * * @return mixed */ if (!function_exists('dtq_resolve_css_value')) { function dtq_resolve_css_value($value) { if (is_string($value) && false !== strpos($value, '$variable(') && class_exists('\ET\Builder\Packages\StyleLibrary\Utils\Utils') ) { return \ET\Builder\Packages\StyleLibrary\Utils\Utils::resolve_dynamic_variables_recursive($value); } return $value; } } /** * Sanitize a colour value destined for interpolation into a CSS declaration. * * Module colour attributes are author-controlled. Interpolating them into a * stylesheet unchecked lets a value such as `#fff}