Introduction
This section is meant to introduce you to the core of this long-form Article.
You may have noticed this site in maintenance today, this article covers what changed during that period of time.
Updates
This section goes over the updates.
So first and foremost, we abstracted a ton of the backend code to make it something in which we can better work with.
With this update, we now support documentation either under a project file or under the subsidiary itself.
We also fixed up a ton of Breadcrumbs stuff, for example ensuring Archives reflect Archives when your on an Archive page.
We also reweighed the Revelannsi weightings for the CPT, hopefully it aligns better with what you are searching for under this site.
Another fix is that we now direct you to the correct headings when you navigate to pages, for example if you are looking for a project, your taken to the project head rather than the content head.
In the foldout is the full current code as of this news article, note the area specified in it comes from PublishPress
<?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_texturize', '_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');
$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];
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)Also if you haven't noticed we did some spring cleaning over the last month, we're hoping the site will look a bit better and more usable as a result.
As always if you have any feedback leave it in the comments!!!

