{ "path": "themes/asha-wp/functions.php", "size": 13439, "modified": "2026-08-27 08:33:45", "content": " 64,\n\t\t'width' => 64,\n\t\t'flex-height' => true,\n\t\t'flex-width' => true,\n\t) );\n\tadd_theme_support( 'html5', array( 'search-form', 'comment-form', 'comment-list', 'gallery', 'caption', 'style', 'script' ) );\n\tadd_theme_support( 'responsive-embeds' );\n\tadd_theme_support( 'align-wide' );\n\tadd_theme_support( 'wp-block-styles' );\n\tadd_theme_support( 'editor-styles' );\n\tadd_editor_style( 'assets/css/editor-style.css' );\n\n\t// Elementor: let Elementor control the canvas for pages built with it.\n\tadd_theme_support( 'elementor' );\n\n\t// Excerpt length for blog cards.\n\tadd_filter( 'excerpt_length', function () {\n\t\treturn 26;\n\t} );\n\tadd_filter( 'excerpt_more', function () {\n\t\treturn '\u2026';\n\t} );\n\n\tregister_nav_menus( array(\n\t\t'primary' => __( 'Primary navigation (header)', 'asha-wp' ),\n\t\t'footer-product' => __( 'Footer column \u2014 Product', 'asha-wp' ),\n\t\t'footer-devs' => __( 'Footer column \u2014 Developers', 'asha-wp' ),\n\t\t'footer-support' => __( 'Footer column \u2014 Support', 'asha-wp' ),\n\t) );\n}\nadd_action( 'after_setup_theme', 'asha_theme_setup' );\n\n/* ---------------------------------------------------------------\n * Content width (Elementor + block editor need it).\n * --------------------------------------------------------------- */\nfunction asha_content_width() {\n\t$GLOBALS['content_width'] = apply_filters( 'asha_content_width', 1160 );\n}\nadd_action( 'after_setup_theme', 'asha_content_width', 0 );\n\n/* ---------------------------------------------------------------\n * Assets.\n * --------------------------------------------------------------- */\nfunction asha_enqueue_assets() {\n\t// Fonts \u2014 preconnect + the exact families/weights from the static site.\n\twp_enqueue_style( 'asha-google-fonts', 'https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;600;700;800;900&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap', array(), null );\n\n\t// Design stylesheet (verbatim static port) then WP/Elementor layer.\n\twp_enqueue_style( 'asha-base', get_template_directory_uri() . '/assets/css/asha-base.css', array(), ASHA_THEME_VERSION );\n\twp_enqueue_style( 'asha-wp', get_template_directory_uri() . '/assets/css/asha-wp.css', array( 'asha-base' ), ASHA_THEME_VERSION );\n\twp_enqueue_style( 'asha-plans-fix', get_template_directory_uri() . '/assets/css/plans-fix.css', array( 'asha-wp' ), ASHA_THEME_VERSION );\n\twp_enqueue_style( 'asha-theme-style', get_stylesheet_uri(), array( 'asha-plans-fix' ), ASHA_THEME_VERSION ); wp_enqueue_style( 'asha-ashacode-docs', get_template_directory_uri() . '/assets/css/asha-ashacode-docs.css', array( 'asha-theme-style' ), ASHA_THEME_VERSION );\n\n\twp_enqueue_style( 'dashicons' );\n\n\t// Global behaviour: mobile menu, copy buttons, code tabs, docs drawer.\n\twp_enqueue_script( 'asha-main', get_template_directory_uri() . '/assets/js/asha-main.js', array(), ASHA_THEME_VERSION, true );\n\n\twp_localize_script( 'asha-main', 'ashaCfg', array(\n\t\t'copyLabel' => __( 'Copy', 'asha-wp' ),\n\t\t'copiedLabel' => __( 'Copied', 'asha-wp' ),\n\t) );\n\n\tif ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) {\n\t\twp_enqueue_script( 'comment-reply' );\n\t}\n}\nadd_action( 'wp_enqueue_scripts', 'asha_enqueue_assets' );\n\n/* ---------------------------------------------------------------\n * Elementor compatibility: default button / heading styling classes.\n * --------------------------------------------------------------- */\nfunction asha_elementor_classes( $classes ) {\n\t$classes[] = 'asha-theme';\n\treturn $classes;\n}\nadd_filter( 'body_class', 'asha_elementor_classes' );\n\n/* ---------------------------------------------------------------\n * Customizer \u2014 the single edit location for header/footer/app links.\n * --------------------------------------------------------------- */\nfunction asha_customize_register( $wp_customize ) {\n\n\t$wp_customize->add_section( 'asha_site', array(\n\t\t'title' => __( 'Asha \u2014 site & app links', 'asha-wp' ),\n\t\t'priority' => 30,\n\t) );\n\n\t$app_fields = array(\n\t\t'asha_app_url_base' => array( __( 'Application base URL', 'asha-wp' ), ASHA_APP_URL ),\n\t\t'asha_app_url_login' => array( __( 'Login URL', 'asha-wp' ), ASHA_APP_LOGIN_URL ),\n\t\t'asha_app_url_signup' => array( __( 'Signup / Start free URL', 'asha-wp' ), ASHA_APP_SIGNUP_URL ),\n\t);\n\n\tforeach ( $app_fields as $key => $field ) {\n\t\t$wp_customize->add_setting( $key, array(\n\t\t\t'default' => $field[1],\n\t\t\t'sanitize_callback' => 'esc_url_raw',\n\t\t) );\n\t\t$wp_customize->add_control( $key, array(\n\t\t\t'label' => $field[0],\n\t\t\t'section' => 'asha_site',\n\t\t\t'type' => 'url',\n\t\t) );\n\t}\n\n\t$text_fields = array(\n\t\t'asha_header_cta_login' => array( __( 'Header \u2014 \u201cLogin\u201d label', 'asha-wp' ), '\u0648\u0631\u0648\u062f' ),\n\t\t'asha_header_cta_signup' => array( __( 'Header \u2014 \u201cStart free\u201d label', 'asha-wp' ), '\u0634\u0631\u0648\u0639 \u0631\u0627\u06cc\u06af\u0627\u0646' ),\n\t\t'asha_footer_legal' => array( __( 'Footer legal text', 'asha-wp' ), 'Copyright \u00a9 \u06f1\u06f4\u06f0\u06f5 \u0622\u0634\u0627 \u2014 \u062f\u0631\u06af\u0627\u0647 \u0647\u0648\u0634 \u0645\u0635\u0646\u0648\u0639\u06cc. \u0633\u0627\u062e\u062a\u0647\u200c\u0634\u062f\u0647 \u0628\u0627 \u062f\u0642\u062a \u0648 \u0634\u0641\u0627\u0641\u06cc\u062a\u061b \u062a\u0645\u0627\u0645\u06cc \u062d\u0642\u0648\u0642 \u0645\u062d\u0641\u0648\u0638 \u0627\u0633\u062a.' ),\n\t);\n\n\tforeach ( $text_fields as $key => $field ) {\n\t\t$wp_customize->add_setting( $key, array(\n\t\t\t'default' => $field[1],\n\t\t\t'sanitize_callback' => 'sanitize_text_field',\n\t\t) );\n\t\t$wp_customize->add_control( $key, array(\n\t\t\t'label' => $field[0],\n\t\t\t'section' => 'asha_site',\n\t\t\t'type' => 'text',\n\t\t) );\n\t}\n\n\t// Logo override lives in the default custom_logo control; expose a hint.\n\t$wp_customize->selective_refresh->add_partial( 'custom_logo', array(\n\t\t'selector' => '.site-branding',\n\t\t'render_callback' => 'asha_site_branding',\n\t) );\n}\nadd_action( 'customize_register', 'asha_customize_register' );\n\n/**\n * The brand name shown next to the logo.\n *\n * Defaults to \u0622\u0634\u0627 unless the site name in Settings \u2192 General was clearly\n * customized (something other than the bare domain or \"asha-ai\").\n */\nfunction asha_brand_name() {\n\t$name = trim( get_bloginfo( 'name' ) );\n\t$lower = strtolower( $name );\n\tif ( '' === $name || strpos( $lower, 'asha-ai' ) !== false || false !== strpos( $lower, 'wordpress' ) || preg_match( '/\\.(com|ir|net|org|dev)$/i', $lower ) ) {\n\t\treturn apply_filters( 'asha_brand_name', __( '\u0622\u0634\u0627', 'asha-wp' ) );\n\t}\n\treturn apply_filters( 'asha_brand_name', $name );\n}\n\n/**\n * Render the brand block (logo + name + subtitle).\n */\nfunction asha_site_branding() {\n\t$has_logo = has_custom_logo();\n\t$name = asha_brand_name();\n\t?>\n\t
\n\t\t\n\t\t\t\n\t\t\t\t\"\"\n\t\t\t\n\t\t\t\n\t\t\tAI Gateway\n\t\t\n\t
\n\tdocuments->get( get_the_ID() );\n\t\treturn $document && $document->is_built_with_elementor();\n\t}\n\treturn false;\n}\n\n/**\n * Fallback featured image artwork (teal rings) label.\n */\nfunction asha_thumb_fallback_label() {\n\treturn apply_filters( 'asha_thumb_fallback_label', get_bloginfo( 'name' ) );\n}\n\n/**\n * Persian-ish reading minutes from word count.\n *\n * @param int $post_id\n * @return string\n */\nfunction asha_reading_time( $post_id = 0 ) {\n\t$content = get_post_field( 'post_content', $post_id ? $post_id : get_the_ID() );\n\t$words = str_word_count( wp_strip_all_tags( $content ) );\n\t$minutes = max( 1, (int) ceil( $words / 220 ) );\n\n\t// Persian numerals.\n\t$fa = array( '\u06f0', '\u06f1', '\u06f2', '\u06f3', '\u06f4', '\u06f5', '\u06f6', '\u06f7', '\u06f8', '\u06f9' );\n\t$num = '';\n\tforeach ( str_split( (string) $minutes ) as $d ) {\n\t\t$num .= isset( $fa[ (int) $d ] ) ? $fa[ (int) $d ] : $d;\n\t}\n\n\treturn sprintf( __( '\u062e\u0648\u0627\u0646\u062f\u0646 %s \u062f\u0642\u06cc\u0642\u0647', 'asha-wp' ), $num );\n}\n\n/**\n * Display name shown for article authors.\n *\n * The marketing blog is written by the Asha team, so the author label is\n * \u00ab\u062a\u06cc\u0645 \u0622\u0634\u0627\u00bb regardless of the individual WordPress account.\n *\n * @return string\n */\nfunction asha_author_name() {\n\treturn apply_filters( 'asha_author_name', __( '\u062a\u06cc\u0645 \u0622\u0634\u0627', 'asha-wp' ) );\n}\n\n/**\n * Convert an ISO date to a Jalali (Persian) date string for display.\n *\n * @param string $date mysql/iso date\n * @return string e.g. \u06f1\u06f8 \u0645\u0631\u062f\u0627\u062f \u06f1\u06f4\u06f0\u06f5\n */\nfunction asha_jalali_date( $date ) {\n\t$ts = is_numeric( $date ) ? (int) $date : strtotime( $date );\n\tif ( ! $ts ) {\n\t\treturn '';\n\t}\n\n\tlist( $gy, $gm, $gd ) = explode( '-', gmdate( 'Y-n-j', $ts ) );\n\n\t$g_d_m = array( 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 );\n\t$jy = ( $gy <= 1600 ) ? 0 : 979;\n\t$gy -= ( $gy <= 1600 ) ? 621 : 1600;\n\t$gy2 = ( $gm > 2 ) ? ( $gy + 1 ) : $gy;\n\t$days = ( 365 * $gy ) + (int) ( ( $gy2 + 3 ) / 4 ) - (int) ( ( $gy2 + 99 ) / 100 ) + (int) ( ( $gy2 + 399 ) / 400 ) - 80 + $gd + $g_d_m[ $gm - 1 ];\n\t$jy += 33 * (int) ( $days / 12053 );\n\t$days %= 12053;\n\t$jy += 4 * (int) ( $days / 1461 );\n\t$days %= 1461;\n\tif ( $days > 365 ) {\n\t\t$jy += (int) ( ( $days - 1 ) / 365 );\n\t\t$days = ( $days - 1 ) % 365;\n\t}\n\t$jm = ( $days < 186 ) ? 1 + (int) ( $days / 31 ) : 7 + (int) ( ( $days - 186 ) / 30 );\n\t$jd = 1 + ( ( $days < 186 ) ? ( $days % 31 ) : ( ( $days - 186 ) % 30 ) );\n\n\t$months = array( '', '\u0641\u0631\u0648\u0631\u062f\u06cc\u0646', '\u0627\u0631\u062f\u06cc\u0628\u0647\u0634\u062a', '\u062e\u0631\u062f\u0627\u062f', '\u062a\u06cc\u0631', '\u0645\u0631\u062f\u0627\u062f', '\u0634\u0647\u0631\u06cc\u0648\u0631', '\u0645\u0647\u0631', '\u0622\u0628\u0627\u0646', '\u0622\u0630\u0631', '\u062f\u06cc', '\u0628\u0647\u0645\u0646', '\u0627\u0633\u0641\u0646\u062f' );\n\n\t$fa = array( '\u06f0', '\u06f1', '\u06f2', '\u06f3', '\u06f4', '\u06f5', '\u06f6', '\u06f7', '\u06f8', '\u06f9' );\n\t$jd_fa = '';\n\tforeach ( str_split( (string) $jd ) as $c ) {\n\t\t$jd_fa .= isset( $fa[ (int) $c ] ) ? $fa[ (int) $c ] : $c;\n\t}\n\t$jy_fa = '';\n\tforeach ( str_split( (string) $jy ) as $c ) {\n\t\t$jy_fa .= isset( $fa[ (int) $c ] ) ? $fa[ (int) $c ] : $c;\n\t}\n\n\treturn $jd_fa . ' ' . $months[ $jm ] . ' ' . $jy_fa;\n}\n\n/* ---------------------------------------------------------------\n * Blog fix \u2014 remove hard
inside

that causes early breaks\n * like Enter in middle of sentence. Desktop text was 760px +
\n * every ~80 chars leaving empty space. CSS hides
plus this filter\n * cleans content for post type post only.\n * --------------------------------------------------------------- */\nfunction asha_blog_fix_br( $content ) {\n\tif ( ! is_singular( 'post' ) ) {\n\t\treturn $content;\n\t}\n\t// Only fix
that are inside

\u2014 replace with single space\n\t// Use regex to handle
,
,
, case-insensitive\n\t$content = preg_replace( '/\\s*/i', ' ', $content );\n\t// Collapse multiple spaces that may result (but keep single)\n\t$content = preg_replace( '/ +/', ' ', $content );\n\treturn $content;\n}\nadd_filter( 'the_content', 'asha_blog_fix_br', 20 );\n\nrequire get_template_directory() . '/inc/theme-helpers.php';\nrequire get_template_directory() . '/inc/cpt-docs.php';\nrequire get_template_directory() . '/inc/shortcodes.php';\n" }