Welcome to Stoat Games

A Documentation Website for a numerous amount of Projects and Subsidiary Groups.

This Website is under Construction.
Fun fact: You can SCROLL DOWN on all Pages.
EPILEPSY WARNING! If you are epileptic DO NOT hover over the banner.
We offer accessibility options via the Widewalk plugin, the icon is in the bottom left.
This allows you to disable Animations, change the font, and more.

Subsidiaries

Cecil Libraries Organization

We create Libraries for software developers to utilize within their projects and products.

Act 2; The Reimagining

A Inscryption fan game that is planned to be a complete reimagining of Inscryption's Act 2.

Stoat Games

Card Games, Mods, and Software.

Stoat Games Website Functions File

Stoat Games Banner

About this Page

This section will go over everything that you may need to know about this page.

The main thing we ask is Credit for when our Solutions have been used within your Website. With a link back here associated with the Credit.

Not all the code within this file comes from us at Stoat Games. For example, the following snippet of code add_filter('run_wptexturize', '__return_false');, came from John Webber as mentioned in the Attributions. Some small portions of the code were made with AI Assistance primarily in the form of Research, majority of the code was written directly by us, and verified to function properly. You are more than welcome to replace any AI-Assisted sections with your own code, and perhaps submit that code to us directly via Contact&Support@stoatgames.icu as perhaps its a better implementation than what we have!

Without further ado, the Stoat Games Functions File:
Decryption Key: &lt; -> <, &gt; -> >

<?php
/**
 * This is the backend code file for the website Stoat Games
 *
 * @link https://stoatgames.icu
 *
 * @package Stoat Games Backend
 * @since 2.0.0
 */

// ---------------------------------------------------------------------------------------------------------------
define( 'STOAT_GAMES_BACKEND_VERSION', '2.0.0' );
function fix_enqueue_styles() {
    wp_enqueue_style('my-child-theme-css', get_stylesheet_directory_uri() . '/style.css', array('astra-theme-css'), STOAT_GAMES_BACKEND_VERSION, 'all');
}
add_action('wp_enqueue_scripts', 'fix_enqueue_styles', 15);
add_filter('run_wptexturize', '__return_false');
// ---------------------------------------------------------------------------------------------------------------
function add_favicons_to_head() {
    $url = untrailingslashit(site_url());
    ?>
        <link rel="apple-touch-icon" sizes="57x57" href="<?php echo $url; ?>/apple-icon-57x57.png">
        <link rel="apple-touch-icon" sizes="60x60" href="<?php echo $url; ?>/apple-icon-60x60.png">
        <link rel="apple-touch-icon" sizes="72x72" href="<?php echo $url; ?>/apple-icon-72x72.png">
        <link rel="apple-touch-icon" sizes="76x76" href="<?php echo $url; ?>/apple-icon-76x76.png">
        <link rel="apple-touch-icon" sizes="114x114" href="<?php echo $url; ?>/apple-icon-114x114.png">
        <link rel="apple-touch-icon" sizes="120x120" href="<?php echo $url; ?>/apple-icon-120x120.png">
        <link rel="apple-touch-icon" sizes="144x144" href="<?php echo $url; ?>/apple-icon-144x144.png">
        <link rel="apple-touch-icon" sizes="152x152" href="<?php echo $url; ?>/apple-icon-152x152.png">
        <link rel="apple-touch-icon" sizes="180x180" href="<?php echo $url; ?>/apple-icon-180x180.png">
        <link rel="apple-touch-icon" href="<?php echo $url; ?>/apple-touch-icon.png">
        <link rel="icon" type="image/png" sizes="192x192" href="<?php echo $url; ?>/android-icon-192x192.png">
        <link rel="icon" type="image/png" sizes="96x96" href="<?php echo $url; ?>/favicon-96x96.png">
        <link rel="icon" type="image/png" sizes="48x48" href="<?php echo $url; ?>/favicon-48x48.png">
        <link rel="icon" type="image/png" sizes="32x32" href="<?php echo $url; ?>/favicon-32x32.png">
        <link rel="icon" type="image/png" sizes="16x16" href="<?php echo $url; ?>/favicon-16x16.png">
        <link rel="icon" type="image/x-icon" href="<?php echo $url; ?>/favicon.ico">
        <link rel="manifest" href="<?php echo $url; ?>/manifest.json">
        <meta name="msapplication-TileColor" content="#ffffff">
        <meta name="msapplication-TileImage" content="<?php echo $url; ?>/ms-icon-144x144.png">
        <meta name="theme-color" content="#ffffff">
    <?php
}
add_action('wp_head', 'add_favicons_to_head');
// ---------------------------------------------------------------------------------------------------------------
function enableStyle($html, $context) {
    if ('post' == $context) {
        $html['style'] = true;
    }
    return $html;
}

add_filter('wp_kses_allowed_html', 'enableStyle', 10, 2);

function enableHooks () { 
    add_post_type_support( 'astra-advanced-hook', 'post-formats' ); 
}

add_action('init', 'enableHooks');
// ---------------------------------------------------------------------------------------------------------------
function DefineNewQueries($vars) {
    // Subsidiary
    $vars[] = 'subsidiary';
    $vars[] = 'subsidiary_news';

    // Projects
    $vars[] = 'project';
    $vars[] = 'project_version';
    $vars[] = 'project_files';
    
    // Project Download Types
    $vars[] = 'project_download';

    // Documentation
    $vars[] = 'documentation_language';
    $vars[] = 'documentation_sector';
    $vars[] = 'documentation';

    // Slug Subsidiaries
    $vars[] = 'slug_subsidiary';
    $vars[] = 'slug_subsidiary_news';

    // Slug Projects
    $vars[] = 'slug_project';
    $vars[] = 'slug_project_version';
    $vars[] = 'slug_project_file';

    // Slug Project Download Types
    $vars[] = 'slug_project_download';

    // Slug Documentation
    $vars[] = 'slug_documentation_language';
    $vars[] = 'slug_documentation_sector';
    $vars[] = 'slug_documentation';

    // Slug Hierarchy
    $vars[] = 'sg_hierarchy_check';

    return $vars;
}

add_filter('query_vars', 'DefineNewQueries');
// ---------------------------------------------------------------------------------------------------------------
function get_post_root_slug(int $post_id, string $post_type): string {
    $ancestors = get_ancestors($post_id, $post_type, 'post_type');

    if (!empty($ancestors)) {
        $root = get_post(end($ancestors));
        return $root instanceof WP_Post ? $root->post_name : '';
    }

    $post = get_post($post_id);
    return $post instanceof WP_Post ? $post->post_name : '';
}
function get_post_root(int $post_id, string $post_type): ?WP_Post {
    $ancestors = get_ancestors($post_id, $post_type, 'post_type');

    if (!empty($ancestors)) {
        $root = get_post(end($ancestors));
        return $root instanceof WP_Post ? $root : null;
    }

    return get_post($post_id) ?: null;
}
// ---------------------------------------------------------------------------------------------------------------
function handleFilter(string $link, WP_Post $post, string $check, string $parent, string $replacement, string $parentQuery): array {
    if ($post->post_type !== $check) {
        return [$link, $post];
    }
    $type = get_field($parent, $post->ID);
    if (!($type instanceof WP_Post)) {
        return [$link, $post];
    }
    return [str_replace($replacement, get_post_root_slug($type->ID, $parentQuery), $link), $type];
}

function handleParentAndFilter(string $link, WP_Post $post, array $hierarchy): string {
    foreach ($hierarchy as $type => $level) {
        if ($post->post_type !== $type) {
            continue;
        }
        $levels = isset($level['parent']) ? [$level] : $level;
        foreach ($levels as $parentLevel) {
            $parent = get_field($parentLevel['parent'], $post->ID);
            if (!($parent instanceof WP_Post)) {
                continue;
            }
            $link = str_replace($parentLevel['replacement'], get_post_root_slug($parent->ID, $parentLevel['parentType']), $link);
            $post = $parent;
            break;
        }
    }
    return $link;
}

function getStoatGamesHierarchy(): array {
    return [
        'subsidiary-news' => ['parent' => 'subsidiary_parent', 'replacement' => '%subsidiary%', 'query' => 'slug_subsidiary', 'parentType' => 'subsidiaries'],
        'projects' => ['parent' => 'subsidiary_parent', 'replacement' => '%subsidiary%', 'query' => 'slug_subsidiary', 'parentType' => 'subsidiaries'],
        'project-versions' => ['parent' => 'project_parent', 'replacement' => '%project%', 'query' => 'slug_project', 'parentType' => 'projects'],
        'project-files' => ['parent' => 'project_versions_parent', 'replacement' => '%version%', 'query' => 'slug_project_version', 'parentType' => 'project-versions'],
        'project-downloads' => ['parent' => 'project_files_parent', 'replacement' => '%file%', 'query' => 'slug_project_file', 'parentType' => 'project-files'],
        'documentation-lang' => [
            ['parent' => 'project_files_parent', 'replacement' => '%file%', 'query' => 'slug_project_file', 'parentType' => 'project-files'],
            ['parent' => 'subsidiary_parent', 'replacement' => '%subsidiary%', 'query' => 'slug_subsidiary', 'parentType' => 'subsidiaries']
        ],
        'documentation-sector' => ['parent' => 'documentation_languages_parent', 'replacement' => '%lang%', 'query' => 'slug_documentation_language', 'parentType' => 'documentation-lang'],
        'documentation' => ['parent' => 'documentation_sectors_parent', 'replacement' => '%sect%', 'query' => 'slug_documentation_sector', 'parentType' => 'documentation-sector']
    ];
}
// ---------------------------------------------------------------------------------------------------------------
add_filter('post_type_link', function ($link, $post) {

    if ( ! ( $post instanceof WP_Post ) ) {
        return $link;
    }

    if ( false === strpos( $link, '%' ) ) {
        return $link;
    }

    $hierarchy = getStoatGamesHierarchy();

    return handleParentAndFilter($link, $post, $hierarchy);
}, 10, 2);
// ---------------------------------------------------------------------------------------------------------------
add_action('pre_get_posts', function ($query) {

    if (is_admin() || !$query->is_main_query()) {
        return;
    }

    if (!$query->get('sg_hierarchy_check')) {
        return;
    }

    $post_type = $query->get('post_type');
    $post_name = $query->get('name');
    $post = get_page_by_path($post_name, OBJECT, $post_type);

    if (!$post) {
        $query->set_404();
        return;
    }

    $hierarchy = getStoatGamesHierarchy();

    foreach ($hierarchy as $type => $level) {

        if ($post->post_type !== $type) {
            continue;
        }

        $levels = isset($level['parent']) ? [$level] : $level;
        $parentFound = false;

        foreach ($levels as $parentLevel) {

            $parent = get_field($parentLevel['parent'], $post->ID);

            if (!($parent instanceof WP_Post)) {
                continue;
            }

            $expectedSlug = $query->get($parentLevel['query']);

            if ($expectedSlug) {
                $rootSlug = get_post_root_slug($parent->ID, $parentLevel['parentType']);

                if ($rootSlug !== sanitize_title($expectedSlug)) {
                    continue;
                }
            }

            $post = $parent;
            $parentFound = true;
            break;
        }

        if (!$parentFound) {
            $query->set_404();
            return;
        }
    }

}, 1);
// ---------------------------------------------------------------------------------------------------------------
add_action('init', function () {

    /**
     *--------------------------------------------------------------------------
     * News
     *--------------------------------------------------------------------------
     */

	add_rewrite_rule(
    	'^news/([^/]+)/?$',
    	'index.php?name=$matches[1]',
    	'top'
	);

    /**
     *--------------------------------------------------------------------------
     * Subsidiary News
     *--------------------------------------------------------------------------
     */

    add_rewrite_rule(
        '^subsidiaries/([^/]+)/news/([^/]+)/?$',
        'index.php?post_type=subsidiary-news&name=$matches[2]&slug_subsidiary=$matches[1]&sg_hierarchy_check=1',
        'top'
    );

    /**
     *--------------------------------------------------------------------------
     * PROJECTS
     *--------------------------------------------------------------------------
     */

    add_rewrite_rule(
        '^subsidiaries/([^/]+)/projects/([^/]+)/?$',
        'index.php?post_type=projects&name=$matches[2]&slug_subsidiary=$matches[1]&sg_hierarchy_check=1',
        'top'
    );

    /**
     *--------------------------------------------------------------------------
     * PROJECT VERSIONS
     *--------------------------------------------------------------------------
     */

    add_rewrite_rule(
        '^subsidiaries/([^/]+)/projects/([^/]+)/versions/([^/]+)/?$',
        'index.php?post_type=project-versions&name=$matches[3]&slug_subsidiary=$matches[1]&slug_project=$matches[2]&sg_hierarchy_check=1',
        'top'
    );

    /**
     *--------------------------------------------------------------------------
     * PROJECT FILES
     *--------------------------------------------------------------------------
     */

    add_rewrite_rule(
        '^subsidiaries/([^/]+)/projects/([^/]+)/versions/([^/]+)/files/([^/]+)/?$',
        'index.php?post_type=project-files&name=$matches[4]&slug_subsidiary=$matches[1]&slug_project=$matches[2]&slug_project_version=$matches[3]&sg_hierarchy_check=1',
        'top'
    );

    /**
     *--------------------------------------------------------------------------
     * PROJECT DOWNLOADS
     *--------------------------------------------------------------------------
     */

    add_rewrite_rule(
        '^subsidiaries/([^/]+)/projects/([^/]+)/versions/([^/]+)/files/([^/]+)/downloads/([^/]+)/?$',
        'index.php?post_type=project-downloads&name=$matches[5]&slug_subsidiary=$matches[1]&slug_project=$matches[2]&slug_project_version=$matches[3]&slug_project_file=$matches[4]&sg_hierarchy_check=1',
        'top'
    );

    /**
     *--------------------------------------------------------------------------
     * DOCUMENTATION LANGUAGES
     *--------------------------------------------------------------------------
     */

    add_rewrite_rule(
        '^subsidiaries/([^/]+)/projects/([^/]+)/versions/([^/]+)/files/([^/]+)/languages/([^/]+)/?$',
        'index.php?post_type=documentation-lang&name=$matches[5]&slug_subsidiary=$matches[1]&slug_project=$matches[2]&slug_project_version=$matches[3]&slug_project_file=$matches[4]&sg_hierarchy_check=1',
        'top'
    );

    add_rewrite_rule(
        '^subsidiaries/([^/]+)/languages/([^/]+)/?$',
        'index.php?post_type=documentation-lang&name=$matches[2]&slug_subsidiary=$matches[1]&sg_hierarchy_check=1',
        'top'
    );

    /**
     *--------------------------------------------------------------------------
     * DOCUMENTATION SECTORS
     *--------------------------------------------------------------------------
     */

    add_rewrite_rule(
        '^subsidiaries/([^/]+)/projects/([^/]+)/versions/([^/]+)/files/([^/]+)/languages/([^/]+)/sectors/([^/]+)/?$',
        'index.php?post_type=documentation-sector&name=$matches[6]&slug_subsidiary=$matches[1]&slug_project=$matches[2]&slug_project_version=$matches[3]&slug_project_file=$matches[4]&slug_documentation_language=$matches[5]&sg_hierarchy_check=1',
        'top'
    );

    add_rewrite_rule(
        '^subsidiaries/([^/]+)/languages/([^/]+)/sectors/([^/]+)/?$',
        'index.php?post_type=documentation-sector&name=$matches[3]&slug_subsidiary=$matches[1]&slug_documentation_language=$matches[2]&sg_hierarchy_check=1',
        'top'
    );

    /**
     *--------------------------------------------------------------------------
     * DOCUMENTATION
     *--------------------------------------------------------------------------
     */

    add_rewrite_rule(
        '^subsidiaries/([^/]+)/projects/([^/]+)/versions/([^/]+)/files/([^/]+)/languages/([^/]+)/sectors/([^/]+)/documentation/([^/]+)/?$',
        'index.php?post_type=documentation&name=$matches[7]&slug_subsidiary=$matches[1]&slug_project=$matches[2]&slug_project_version=$matches[3]&slug_project_file=$matches[4]&slug_documentation_language=$matches[5]&slug_documentation_sector=$matches[6]&sg_hierarchy_check=1',
        'top'
    );

    add_rewrite_rule(
        '^subsidiaries/([^/]+)/languages/([^/]+)/sectors/([^/]+)/documentation/([^/]+)/?$',
        'index.php?post_type=documentation&name=$matches[4]&slug_subsidiary=$matches[1]&slug_documentation_language=$matches[2]&slug_documentation_sector=$matches[3]&sg_hierarchy_check=1',
        'top'
    );
});
// ---------------------------------------------------------------------------------------------------------------
function getStoatGamesArchiveFilters(): array {
    return [
        'subsidiary-news' => [
            'subsidiary' => [
                'postType' => 'subsidiaries',
                'metaKey'  => 'subsidiary_parent'
            ]
        ],

        'projects' => [
            'subsidiary' => [
                'postType' => 'subsidiaries',
                'metaKey'  => 'subsidiary_parent'
            ]
        ],

        'project-versions' => [
            'project' => [
                'postType' => 'projects',
                'metaKey'  => 'project_parent'
            ],
            'subsidiary' => [
                'postType' => 'subsidiaries',
                'metaKey'  => 'subsidiary_parent'
            ]
        ],

        'project-files' => [
            'project_version' => [
                'postType' => 'project-versions',
                'metaKey'  => 'project_versions_parent'
            ],
            'project' => [
                'postType' => 'projects',
                'metaKey'  => 'project_parent'
            ],
            'subsidiary' => [
                'postType' => 'subsidiaries',
                'metaKey'  => 'subsidiary_parent'
            ]
        ],

        'project-downloads' => [
            'project_file' => [
                'postType' => 'project-files',
                'metaKey'  => 'project_files_parent'
            ],
            'project_version' => [
                'postType' => 'project-versions',
                'metaKey'  => 'project_versions_parent'
            ],
            'project' => [
                'postType' => 'projects',
                'metaKey'  => 'project_parent'
            ],
            'subsidiary' => [
                'postType' => 'subsidiaries',
                'metaKey'  => 'subsidiary_parent'
            ]
        ],

        'documentation-lang' => [
            'project_file' => [
                'postType' => 'project-files',
                'metaKey'  => 'project_files_parent'
            ],
            'project_version' => [
                'postType' => 'project-versions',
                'metaKey'  => 'project_versions_parent'
            ],
            'project' => [
                'postType' => 'projects',
                'metaKey'  => 'project_parent'
            ],
            'subsidiary' => [
                'postType' => 'subsidiaries',
                'metaKey'  => 'subsidiary_parent'
            ]
        ],

        'documentation-sector' => [
            'documentation_language' => [
                'postType' => 'documentation-lang',
                'metaKey'  => 'documentation_languages_parent'
            ],
            'project_file' => [
                'postType' => 'project-files',
                'metaKey'  => 'project_files_parent'
            ],
            'project_version' => [
                'postType' => 'project-versions',
                'metaKey'  => 'project_versions_parent'
            ],
            'project' => [
                'postType' => 'projects',
                'metaKey'  => 'project_parent'
            ],
            'subsidiary' => [
                'postType' => 'subsidiaries',
                'metaKey'  => 'subsidiary_parent'
            ]
        ],

        'documentation' => [
            'documentation_sector' => [
                'postType' => 'documentation-sector',
                'metaKey'  => 'documentation_sectors_parent'
            ],
            'documentation_language' => [
                'postType' => 'documentation-lang',
                'metaKey'  => 'documentation_languages_parent'
            ],
            'project_file' => [
                'postType' => 'project-files',
                'metaKey'  => 'project_files_parent'
            ],
            'project_version' => [
                'postType' => 'project-versions',
                'metaKey'  => 'project_versions_parent'
            ],
            'project' => [
                'postType' => 'projects',
                'metaKey'  => 'project_parent'
            ],
            'subsidiary' => [
                'postType' => 'subsidiaries',
                'metaKey'  => 'subsidiary_parent'
            ]
        ]
    ];
}
// ---------------------------------------------------------------------------------------------------------------
add_action('pre_get_posts', function ($query) {
    if (is_admin() || !$query->is_main_query()) {
        return;
    }

    $post_type = $query->get('post_type');

	if (is_array($post_type)) {
    	$post_type = reset($post_type);
	}

	$archive_filters = getStoatGamesArchiveFilters();

    if (!isset($archive_filters[$post_type]) || !$query->is_post_type_archive($post_type)) {
        return;
    }

    $meta_query = [];

    foreach ($archive_filters[$post_type] as $query_var => $filter) {
        if (empty($_GET[$query_var])) {
            continue;
        }

        $slug = sanitize_title($_GET[$query_var]);
        $parent = get_page_by_path($slug, OBJECT, $filter['postType']);

        if (!$parent) {
            continue;
        }

        $meta_query[] = [
            'key'     => $filter['metaKey'],
            'value'   => $parent->ID,
            'compare' => '='
        ];
    }

    if (!empty($meta_query)) {
        $query->set('meta_query', $meta_query);
    }
});
// ---------------------------------------------------------------------------------------------------------------
function getStoatGamesArchives(): array {
    return [
        'subsidiary-news' => 'subsidiary-news-articles',
        'projects' => 'projects',
        'project-versions' => 'project-versions',
        'project-files' => 'project-files',
        'project-downloads' => 'project-downloads',
        'documentation-lang' => 'documentation-languages',
        'documentation-sector' => 'documentation-sectors',
        'documentation' => 'documentation'
    ];
}

add_filter('register_post_type_args', function ($args, $post_type) {
    $archives = getStoatGamesArchives();

    if (!isset($archives[$post_type])) {
        return $args;
    }

    $args['has_archive'] = $archives[$post_type];
    $args['rewrite']['with_front'] = false;

    return $args;
}, 20, 2);
// ---------------------------------------------------------------------------------------------------------------
function getStoatGamesArchiveRedirects(): array {
    return [
        [
            'pattern' => '#^subsidiaries/([^/]+)/news/?$#',
            'archive' => 'subsidiary-news',
            'queries' => [
                'subsidiary' => 1
            ]
        ],

        [
            'pattern' => '#^subsidiaries/([^/]+)/projects/?$#',
            'archive' => 'projects',
            'queries' => [
                'subsidiary' => 1
            ]
        ],

        [
            'pattern' => '#^subsidiaries/([^/]+)/projects/([^/]+)/versions/?$#',
            'archive' => 'project-versions',
            'queries' => [
                'subsidiary' => 1,
                'project'    => 2
            ]
        ],

        [
            'pattern' => '#^subsidiaries/([^/]+)/projects/([^/]+)/versions/([^/]+)/files/?$#',
            'archive' => 'project-files',
            'queries' => [
                'subsidiary'      => 1,
                'project'         => 2,
                'project_version' => 3
            ]
        ],

        [
            'pattern' => '#^subsidiaries/([^/]+)/projects/([^/]+)/versions/([^/]+)/files/([^/]+)/downloads/?$#',
            'archive' => 'project-downloads',
            'queries' => [
                'subsidiary'      => 1,
                'project'         => 2,
                'project_version' => 3,
                'project_file'    => 4
            ]
        ],

        [
            'pattern' => '#^subsidiaries/([^/]+)/projects/([^/]+)/versions/([^/]+)/files/([^/]+)/languages/?$#',
            'archive' => 'documentation-lang',
            'queries' => [
                'subsidiary'      => 1,
                'project'         => 2,
                'project_version' => 3,
                'project_file'    => 4
            ]
        ],

        [
            'pattern' => '#^subsidiaries/([^/]+)/projects/([^/]+)/versions/([^/]+)/files/([^/]+)/languages/([^/]+)/sectors/?$#',
            'archive' => 'documentation-sector',
            'queries' => [
                'subsidiary'             => 1,
                'project'                => 2,
                'project_version'        => 3,
                'project_file'           => 4,
                'documentation_language' => 5
            ]
        ],

        [
            'pattern' => '#^subsidiaries/([^/]+)/projects/([^/]+)/versions/([^/]+)/files/([^/]+)/languages/([^/]+)/sectors/([^/]+)/documentation/?$#',
            'archive' => 'documentation',
            'queries' => [
                'subsidiary'             => 1,
                'project'                => 2,
                'project_version'        => 3,
                'project_file'           => 4,
                'documentation_language' => 5,
                'documentation_sector'   => 6
            ]
        ]
    ];
}

add_action('init', function () {
    $request = trim(wp_parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/');
    $request = preg_replace('#^[a-z]{2}/#', '', $request);

    foreach (getStoatGamesArchiveRedirects() as $redirect) {
        if (!preg_match($redirect['pattern'], $request, $matches)) {
            continue;
        }

        $query_args = [];

        foreach ($redirect['queries'] as $query => $match) {
            $query_args[$query] = sanitize_title($matches[$match]);
        }

        wp_redirect(
            add_query_arg(
                $query_args,
                get_post_type_archive_link($redirect['archive'])
            ),
            301
        );

        exit;
    }
});
// ---------------------------------------------------------------------------------------------------------------

function getStoatGamesBreadcrumbs(): array {

    return [

        /*
         * Subsidiaries use WordPress' native post_parent hierarchy.
         */
        'subsidiaries' => [
            [
                'type'     => 'subsidiary',
                'postType' => 'subsidiaries',
                'parent'   => 'post_parent'
            ]
        ],

        'subsidiary-news' => [
            [
                'type'     => 'subsidiary',
                'postType' => 'subsidiaries',
                'parent'   => 'subsidiary_parent'
            ],
            [
                'type'    => 'archive',
                'archive' => 'subsidiary-news',
                'label'   => 'Articles',
                'queries' => [
                    'subsidiary' => 'subsidiary'
                ],
                'paths' => [
                    'subsidiaries/%subsidiary%/news/'
                ]
            ]
        ],

        'projects' => [
            [
                'type'     => 'subsidiary',
                'postType' => 'subsidiaries',
                'parent'   => 'subsidiary_parent'
            ],
            [
                'type'    => 'archive',
                'archive' => 'projects',
                'label'   => 'Projects',
                'queries' => [
                    'subsidiary' => 'subsidiary'
                ],
                'paths' => [
                    'subsidiaries/%subsidiary%/projects/'
                ]
            ]
        ],

        'project-versions' => [
            [
                'type'     => 'project',
                'postType' => 'projects',
                'parent'   => 'project_parent'
            ],
            [
                'type'    => 'archive',
                'archive' => 'project-versions',
                'label'   => 'Versions',
                'queries' => [
                    'subsidiary' => 'subsidiary',
                    'project'    => 'project'
                ],
                'paths' => [
                    'subsidiaries/%subsidiary%/projects/%project%/versions/'
                ]
            ]
        ],

        'project-files' => [
            [
                'type'     => 'project_version',
                'postType' => 'project-versions',
                'parent'   => 'project_versions_parent'
            ],
            [
                'type'    => 'archive',
                'archive' => 'project-files',
                'label'   => 'Files',
                'queries' => [
                    'subsidiary'      => 'subsidiary',
                    'project'         => 'project',
                    'project_version' => 'project_version'
                ],
                'paths' => [
                    'subsidiaries/%subsidiary%/projects/%project%/versions/%project_version%/files/'
                ]
            ]
        ],

        'project-downloads' => [
            [
                'type'     => 'project_file',
                'postType' => 'project-files',
                'parent'   => 'project_files_parent'
            ],
            [
                'type'    => 'archive',
                'archive' => 'project-downloads',
                'label'   => 'Downloads',
                'queries' => [
                    'subsidiary'      => 'subsidiary',
                    'project'         => 'project',
                    'project_version' => 'project_version',
                    'project_file'    => 'project_file'
                ],
                'paths' => [
                    'subsidiaries/%subsidiary%/projects/%project%/versions/%project_version%/files/%project_file%/downloads/'
                ]
            ]
        ],

        'documentation-lang' => [
            [
                [
                    'type'     => 'project_file',
                    'postType' => 'project-files',
                    'parent'   => 'project_files_parent'
                ],
                [
                    'type'     => 'subsidiary',
                    'postType' => 'subsidiaries',
                    'parent'   => 'subsidiary_parent'
                ]
            ],
            [
                'type'    => 'archive',
                'archive' => 'documentation-lang',
                'label'   => 'Languages',
                'queries' => [
                    'subsidiary'      => 'subsidiary',
                    'project'         => 'project',
                    'project_version' => 'project_version',
                    'project_file'    => 'project_file'
                ],
                'paths' => [
                    'subsidiaries/%subsidiary%/projects/%project%/versions/%project_version%/files/%project_file%/languages/',
                    'subsidiaries/%subsidiary%/languages/'
                ]
            ]
        ],

        'documentation-sector' => [
            [
                'type'     => 'documentation_language',
                'postType' => 'documentation-lang',
                'parent'   => 'documentation_languages_parent'
            ],
            [
                'type'    => 'archive',
                'archive' => 'documentation-sector',
                'label'   => 'Sectors',
                'queries' => [
                    'subsidiary'          => 'subsidiary',
                    'project'             => 'project',
                    'project_version'     => 'project_version',
                    'project_file'        => 'project_file',
                    'documentation_language' => 'documentation_language'
                ],
                'paths' => [
                    'subsidiaries/%subsidiary%/projects/%project%/versions/%project_version%/files/%project_file%/languages/%documentation_language%/sectors/',
                    'subsidiaries/%subsidiary%/languages/%documentation_language%/sectors/'
                ]
            ]
        ],

        'documentation' => [
            [
                'type'     => 'documentation_sector',
                'postType' => 'documentation-sector',
                'parent'   => 'documentation_sectors_parent'
            ],
            [
                'type'    => 'archive',
                'archive' => 'documentation',
                'label'   => 'Documentation',
                'queries' => [
                    'subsidiary'            => 'subsidiary',
                    'project'               => 'project',
                    'project_version'       => 'project_version',
                    'project_file'          => 'project_file',
                    'documentation_language' => 'documentation_language',
                    'documentation_sector'  => 'documentation_sector'
                ],
                'paths' => [
                    'subsidiaries/%subsidiary%/projects/%project%/versions/%project_version%/files/%project_file%/languages/%documentation_language%/sectors/%documentation_sector%/documentation/',
                    'subsidiaries/%subsidiary%/languages/%documentation_language%/sectors/%documentation_sector%/documentation/'
                ]
            ]
        ]

    ];
}

function getStoatGamesArchiveBreadcrumbUrl(
    string $post_type,
    array $posts,
    array $queries,
    array $paths = []
): string {

    /*
     * Prefer the hierarchical URL defined by getStoatGamesBreadcrumbs().
     */
    foreach ($paths as $path) {

        $resolvedPath = $path;
        $validPath = true;

        foreach ($queries as $query => $postKey) {

            if (strpos($resolvedPath, '%' . $postKey . '%') === false) {
                continue;
            }

            if (
                !isset($posts[$postKey]) ||
                !($posts[$postKey] instanceof WP_Post)
            ) {
                $validPath = false;
                break;
            }

            $resolvedPath = str_replace(
                '%' . $postKey . '%',
                $posts[$postKey]->post_name,
                $resolvedPath
            );
        }

        if (!$validPath) {
            continue;
        }

        /*
         * Do not allow unresolved breadcrumb placeholders into
         * the generated URL.
         */
        if (strpos($resolvedPath, '%') !== false) {
            continue;
        }

        return home_url('/' . trim($resolvedPath, '/') . '/');
    }

    /*
     * Fall back to the archive URL with query arguments if no
     * configured hierarchical path can be resolved.
     */
    $args = [];

    foreach ($queries as $query => $postKey) {

        if (
            isset($posts[$postKey]) &&
            $posts[$postKey] instanceof WP_Post
        ) {
            $args[$query] = $posts[$postKey]->post_name;
        }
    }

    return add_query_arg(
        $args,
        get_post_type_archive_link($post_type)
    );
}

function getStoatGamesBreadcrumbPosts(WP_Post $post): array {

    $posts = [
        'subsidiary'             => null,
        'project'                => null,
        'project_version'        => null,
        'project_file'           => null,
        'documentation_language' => null,
        'documentation_sector'   => null
    ];

    /*
     * Map each post type to the breadcrumb key representing that post.
     *
     * The "type" value in getStoatGamesBreadcrumbs() describes the
     * parent that a breadcrumb entry resolves to. It does NOT describe
     * the current post. Therefore the current post must be mapped
     * separately here.
     */
    $currentTypes = [
        'subsidiaries'         => 'subsidiary',
        'projects'             => 'project',
        'project-versions'     => 'project_version',
        'project-files'        => 'project_file',
        'documentation-lang'   => 'documentation_language',
        'documentation-sector' => 'documentation_sector'
    ];

    $breadcrumbs = getStoatGamesBreadcrumbs();

    if (!isset($breadcrumbs[$post->post_type])) {
        return $posts;
    }

    $current = $post;
    $visited = [];

    while ($current instanceof WP_Post) {

        /*
         * Prevent malformed hierarchy from creating an infinite loop.
         */
        if (isset($visited[$current->ID])) {
            break;
        }

        $visited[$current->ID] = true;

        /*
         * Store the current post under the correct breadcrumb type.
         *
         * This is deliberately based on post_type rather than the
         * "type" value in the breadcrumb configuration.
         */
        if (
            isset($currentTypes[$current->post_type]) &&
            array_key_exists($currentTypes[$current->post_type], $posts)
        ) {
            $posts[$currentTypes[$current->post_type]] = $current;
        }

        /*
         * Find the parent relationship defined for this post type.
         */
        $parentFound = false;

        foreach ($breadcrumbs[$current->post_type] ?? [] as $breadcrumb) {

            /*
             * Archive entries describe archive links, not parents.
             */
            if (
                isset($breadcrumb['type']) &&
                $breadcrumb['type'] === 'archive'
            ) {
                continue;
            }

            /*
             * A breadcrumb entry may contain multiple possible
             * parent relationships.
             */
            if (
                isset($breadcrumb[0]) &&
                is_array($breadcrumb[0])
            ) {
                $levels = $breadcrumb;
            } else {
                $levels = [$breadcrumb];
            }

            foreach ($levels as $level) {

                if (
                    !is_array($level) ||
                    !isset(
                        $level['type'],
                        $level['postType'],
                        $level['parent']
                    )
                ) {
                    continue;
                }

                /*
                 * Make sure this relationship actually points to
                 * the post type declared by the breadcrumb config.
                 */
                if ($current->post_type !== $post->post_type && false) {
                    continue;
                }

                /*
                 * Subsidiaries use WordPress' native post_parent.
                 * Everything else currently uses ACF parent fields.
                 */
                if ($level['parent'] === 'post_parent') {

                    $parent = $current->post_parent
                        ? get_post($current->post_parent)
                        : null;

                } else {

                    $parent = get_field(
                        $level['parent'],
                        $current->ID
                    );
                }

                if (!($parent instanceof WP_Post)) {
                    continue;
                }

                /*
                 * Verify that the resolved parent is actually the
                 * post type declared by this breadcrumb relationship.
                 */
                if ($parent->post_type !== $level['postType']) {
                    continue;
                }

                /*
                 * Store the actual parent.
                 */
                if (array_key_exists($level['type'], $posts)) {
                    $posts[$level['type']] = $parent;
                }

                $current = $parent;
                $parentFound = true;

                break;
            }

            if ($parentFound) {
                break;
            }
        }

        if (!$parentFound) {
            break;
        }
    }

    return $posts;
}
// ---------------------------------------------------------------------------------------------------------------
add_filter('wpseo_breadcrumb_links', function ($crumbs) {

    global $post;

    if (!$post instanceof WP_Post) {
        return $crumbs;
    }

    $post_type = $post->post_type;
    $new_crumbs = [];

    /**
     * Preserve Yoast's Universal Homepage breadcrumb.
     */
    if (!empty($crumbs)) {
        $new_crumbs[] = $crumbs[0];
    }

    /**
     * Standard WordPress News.
     */
    if ($post_type === 'post') {
        $new_crumbs[] = [
            'url'  => home_url('/news/'),
            'text' => 'News'
        ];
    }

    /**
     * Licenses.
     */
    if ($post_type === 'licenses') {
        $new_crumbs[] = [
            'url'  => get_post_type_archive_link('licenses'),
            'text' => 'Licenses'
        ];
    }

    $breadcrumbs = getStoatGamesBreadcrumbs();

    /**
     * Determine whether the current request is a post type archive.
     *
     * On an archive page, the breadcrumb should end at the archive:
     *
     *     ... » Projects
     *     ... » Versions
     *     ... » Files
     *     ... » Downloads
     *
     * It must NOT continue with the individual post represented
     * by the archive.
     */
    $isArchivePage = is_post_type_archive();

    $archivePostType = $isArchivePage
        ? get_query_var('post_type')
        : null;

    /**
     * A post type archive query var may occasionally be returned
     * as an array. Normalize it to the actual post type string.
     */
    if (is_array($archivePostType)) {
        $archivePostType = reset($archivePostType);
    }

    /**
     * Track posts that have already been rendered.
     *
     * This prevents the current post from being appended twice
     * at the end of the breadcrumb chain.
     */
    $renderedPosts = [];

    if (isset($breadcrumbs[$post_type])) {

        $posts = getStoatGamesBreadcrumbPosts($post);

        /**
         * Every configured Stoat Games hierarchy begins under
         * the Subsidiaries archive.
         */
        $new_crumbs[] = [
            'url'  => get_post_type_archive_link('subsidiaries'),
            'text' => 'Subsidiaries'
        ];

        /**
         * Build the structural breadcrumb chain.
         *
         * Each configured object contributes:
         *
         *     Archive
         *     Object
         *
         * For example:
         *
         *     Projects
         *     Cecil.ANSI_Utils
         *     Versions
         *     Cecil.ANSI_Utils 1.0.0
         *
         * Archive pages stop after the Archive element.
         */
        $breadcrumbOrder = [
            [
                'type'  => 'subsidiary',
                'label' => null
            ],
            [
                'type'  => 'project',
                'label' => 'Projects'
            ],
            [
                'type'  => 'project_version',
                'label' => 'Versions'
            ],
            [
                'type'  => 'project_file',
                'label' => 'Files'
            ],
            [
                'type'  => 'project_download',
                'label' => 'Downloads'
            ],
            [
                'type'  => 'documentation_language',
                'label' => 'Languages'
            ],
            [
                'type'  => 'documentation_sector',
                'label' => 'Sectors'
            ],
            [
                'type'  => 'documentation',
                'label' => 'Documentation'
            ]
        ];

        foreach ($breadcrumbOrder as $level) {

            $type = $level['type'];
            $ancestor = $posts[$type] ?? null;

            if (!($ancestor instanceof WP_Post)) {
                continue;
            }

            /**
             * The subsidiary itself comes directly after the
             * Subsidiaries archive.
             */
            if ($type === 'subsidiary') {

                $new_crumbs[] = [
                    'url'  => get_permalink($ancestor->ID),
                    'text' => get_the_title($ancestor->ID)
                ];

                $renderedPosts[$ancestor->ID] = true;

                continue;
            }

            /**
             * Determine the archive represented by this object.
             */
            $archiveType = null;

            switch ($type) {

                case 'project':
                    $archiveType = 'projects';
                    break;

                case 'project_version':
                    $archiveType = 'project-versions';
                    break;

                case 'project_file':
                    $archiveType = 'project-files';
                    break;

                case 'project_download':
                    $archiveType = 'project-downloads';
                    break;

                case 'documentation_language':
                    $archiveType = 'documentation-lang';
                    break;

                case 'documentation_sector':
                    $archiveType = 'documentation-sector';
                    break;

                case 'documentation':
                    $archiveType = 'documentation';
                    break;
            }

            if ($archiveType === null) {
                continue;
            }

            /**
             * Find the archive breadcrumb configuration.
             */
            $archiveBreadcrumb = null;

            foreach ($breadcrumbs[$ancestor->post_type] ?? [] as $breadcrumb) {

                if (
                    isset($breadcrumb['type']) &&
                    $breadcrumb['type'] === 'archive' &&
                    isset($breadcrumb['archive']) &&
                    $breadcrumb['archive'] === $archiveType
                ) {
                    $archiveBreadcrumb = $breadcrumb;
                    break;
                }
            }

            /**
             * Add the archive/category breadcrumb before the object.
             */
            if (is_array($archiveBreadcrumb)) {

                $new_crumbs[] = [
                    'url' => getStoatGamesArchiveBreadcrumbUrl(
                        $archiveBreadcrumb['archive'],
                        $posts,
                        $archiveBreadcrumb['queries'] ?? [],
                        $archiveBreadcrumb['paths'] ?? []
                    ),
                    'text' => $archiveBreadcrumb['label']
                ];
            }

            /**
             * If the current request IS this archive, the archive
             * is the final breadcrumb.
             *
             * For example:
             *
             *     ... » Cecil.ANSI_Utils » Versions
             *
             * NOT:
             *
             *     ... » Cecil.ANSI_Utils » Versions » Cecil.ANSI_Utils 1.0.0
             */
            if (
                $isArchivePage &&
                $archivePostType === $archiveType
            ) {
                break;
            }

            /**
             * Otherwise this is a normal singular page, so add
             * the actual object represented by the archive.
             */
            $new_crumbs[] = [
                'url'  => get_permalink($ancestor->ID),
                'text' => get_the_title($ancestor->ID)
            ];

            $renderedPosts[$ancestor->ID] = true;
        }
    }

    /**
     * Finally add the current page itself, but only if:
     *
     * 1. We are not on the homepage.
     * 2. We are not on a post type archive.
     * 3. The current post was not already rendered as part
     *    of the hierarchy.
     *
     * This prevents:
     *
     *     ... » Versions » Cecil.ANSI_Utils 1.0.0 » Cecil.ANSI_Utils 1.0.0
     *
     * while still allowing terminal post types that are not
     * represented in the hierarchy to appear.
     */
    if (
        !is_home() &&
        !$isArchivePage &&
        !isset($renderedPosts[$post->ID])
    ) {
        $new_crumbs[] = [
            'url'  => get_permalink($post->ID),
            'text' => get_the_title($post->ID)
        ];
    }

    return $new_crumbs;

}, 10, 1);
// ---------------------------------------------------------------------------------------------------------------
add_filter('relevanssi_post_type_weights', function ($weights) {
    $weights['page']                 = 8;
    $weights['subsidiaries']         = 7;
    $weights['projects']             = 6;
    $weights['post']                 = 5;
    $weights['licenses']             = 5;
    $weights['subsidiary-news']      = 5;
    $weights['project-versions']     = 4;
    $weights['project-files']        = 3;
    $weights['project-downloads']    = 2;
    $weights['documentation-lang']   = 2;
    $weights['documentation-sector'] = 2;
    $weights['documentation']        = 1;

    return $weights;
});
// ---------------------------------------------------------------------------------------------------------------
function custom_append_content_anchor_to_loops($permalink, $post = null) {
    if (isset($_SERVER['SCRIPT_FILENAME']) && 'wp-comments-post.php' === basename($_SERVER['SCRIPT_FILENAME'])) {
        return $permalink;
    }

    if (is_admin() || is_feed() || is_singular()) {
        return $permalink;
    }

    if (false !== strpos($permalink, '#')) {
        return $permalink;
    }

    if ($post instanceof WP_Post) {
        switch ($post->post_type) {
            case 'post':
            case 'page':
            case 'licenses':
                break;

            case 'subsidiaries':
                $permalink .= '#header-container-subsite';
                break;

            case 'projects':
                $permalink .= '#header-container-subsite-project';
                break;

            case 'project-versions':
                $permalink .= '#Metadata';
                break;

            default:
                $permalink .= '#content';
                break;
        }
    }

    return $permalink;
}

add_filter('post_link', 'custom_append_content_anchor_to_loops', 99, 2);
add_filter('post_type_link', 'custom_append_content_anchor_to_loops', 99, 2);
add_filter('page_link', 'custom_append_content_anchor_to_loops', 99, 2);
// ---------------------------------------------------------------------------------------------------------------
// PUBLISHPRESS_AUTHORS_ASTRA_CHILD_THEME START

// Created by rizaardiyanto1412 and andergmartins


/*****************************************************/
/**
 * Function to get Author of Post
 *
 * @return html
 * @since 1.0.0
 */
if (!function_exists('astra_post_author')) {
    /**
     * Function to get Author of Post
     *
     * @param string $output_filter Filter string.
     *
     * @return html                Markup.
     */
    function astra_post_author($output_filter = '')
    {
        ob_start();

        echo '<span ';
        echo astra_attr(
            'post-meta-author',
            array(
                'class' => 'posted-by vcard author',
            )
        );
        echo '>';

        $authors = get_multiple_authors();

        foreach ($authors as $index => $author) {
            if ($index > 0) {
                echo ', ';
            }

            echo '<a title="' . sprintf(esc_attr__('View all posts by %1$s', 'astra'), $author->display_name) . '" href="' . esc_url($author->link) . '" rel="author" ' . astra_attr(
                    'author-url',
                    array(
                        'class' => 'url fn n',
                    )
                ) . '>';

            echo '<span ' . astra_attr(
                    'author-name',
                    array(
                        'class' => 'author-name',
                    )
                ) . '>' . $author->display_name . '</span>';

            echo '</a>';
        }
        ?>
        </span>

        <?php

        $output = ob_get_clean();

        return apply_filters('astra_post_author', $output, $output_filter);
    }
}

/**
 * Archive Page Title
 */
if (!function_exists('astra_archive_page_info')) {
    /**
     * Wrapper function for the_title()
     *
     * Displays title only if the page title bar is disabled.
     */
    function astra_archive_page_info()
    {
        if (apply_filters('astra_the_title_enabled', true)) {
            // Author.
            if (is_author()) { ?>

                <section class="ast-author-box ast-archive-description">
                    <div class="ast-author-bio">
                        <?php

						$authors = get_multiple_authors(null, false, true);
						$author = $authors[0] ?? null;

                        do_action('astra_before_archive_title'); ?>
                        <h1 class='page-title ast-archive-title'><?php
                            echo $author->display_name; ?></h1>
                        <?php
                        do_action('astra_after_archive_title'); ?>
                        <p><?php
                            echo wp_kses_post($author->description); ?></p>
                        <?php
                        do_action('astra_after_archive_description'); ?>
                    </div>
                    <div class="ast-author-avatar">
                        <?php
                        echo $author->get_avatar(120); ?>
                    </div>
                </section>

                <?php
                // Category.
            } elseif (is_category()) {
                ?>

                <section class="ast-archive-description">
                    <?php
                    do_action('astra_before_archive_title'); ?>
                    <h1 class="page-title ast-archive-title"><?php
                        echo single_cat_title(); ?></h1>
                    <?php
                    do_action('astra_after_archive_title'); ?>
                    <?php
                    echo wp_kses_post(wpautop(get_the_archive_description())); ?>
                    <?php
                    do_action('astra_after_archive_description'); ?>
                </section>

                <?php
                // Tag.
            } elseif (is_tag()) {
                ?>

                <section class="ast-archive-description">
                    <?php
                    do_action('astra_before_archive_title'); ?>
                    <h1 class="page-title ast-archive-title"><?php
                        echo single_tag_title(); ?></h1>
                    <?php
                    do_action('astra_after_archive_title'); ?>
                    <?php
                    echo wp_kses_post(wpautop(get_the_archive_description())); ?>
                    <?php
                    do_action('astra_after_archive_description'); ?>
                </section>

                <?php
                // Search.
            } elseif (is_search()) {
                ?>

                <section class="ast-archive-description">
                    <?php
                    do_action('astra_before_archive_title'); ?>
                    <?php
                    /* translators: 1: search string */
                    $title = apply_filters(
                        'astra_the_search_page_title',
                        sprintf(
                            __('Search Results for: %s', 'astra'),
                            '<span>' . get_search_query() . '</span>'
                        )
                    );
                    ?>
                    <h1 class="page-title ast-archive-title"> <?php
                        echo $title; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> </h1>
                    <?php
                    do_action('astra_after_archive_title'); ?>
                </section>

                <?php
                // Other.
            } else {
                ?>

                <section class="ast-archive-description">
                    <?php
                    do_action('astra_before_archive_title'); ?>
                    <?php
                    the_archive_title('<h1 class="page-title ast-archive-title">', '</h1>'); ?>
                    <?php
                    do_action('astra_after_archive_title'); ?>
                    <?php
                    echo wp_kses_post(wpautop(get_the_archive_description())); ?>
                    <?php
                    do_action('astra_after_archive_description'); ?>
                </section>

                <?php
            }
        }
    }

    add_action('astra_archive_header', 'astra_archive_page_info');
}

// PUBLISHPRESS_AUTHORS_ASTRA_CHILD_THEME END
Code language: PHP (php)

Author