Initial commit to github
This commit is contained in:
251
lib/activation.php
Normal file
251
lib/activation.php
Normal file
@@ -0,0 +1,251 @@
|
||||
<?php
|
||||
/*
|
||||
* On Theme Activation adds Home and News pages, sets up reading options for front page and posts page, and erases sample page and post
|
||||
*/
|
||||
|
||||
// phpcs:ignore
|
||||
if ( isset( $_GET['activated'] ) && is_admin() ) {
|
||||
// Set Blog Description to nothing
|
||||
update_option( 'blogdescription', '' );
|
||||
|
||||
// List of pages to create with nested structure
|
||||
$arrPages = array(
|
||||
'Home' => array(),
|
||||
'News' => array(),
|
||||
'Page Not Found (Error 404)' => array(),
|
||||
'Contact Us' => array(),
|
||||
/**
|
||||
* Sample nested structure
|
||||
*
|
||||
* 'Parent Page' => array(
|
||||
* 'Subpage 1' => array(
|
||||
* 'Sub-subpage 1',
|
||||
* 'Sub-subpage 2'
|
||||
* ),
|
||||
* 'Subpage 2.2' => array()
|
||||
* ),
|
||||
*/
|
||||
);
|
||||
|
||||
foreach ( $arrPages as $pageTitle => $childPages ) {
|
||||
// phpcs:ignore
|
||||
$pageExists = get_page_by_title($pageTitle);
|
||||
|
||||
if ( ! $pageExists ) {
|
||||
// Create the parent page
|
||||
$pageId = wp_insert_post(
|
||||
array(
|
||||
'post_title' => $pageTitle,
|
||||
'post_content' => '',
|
||||
'post_type' => 'page',
|
||||
'post_status' => 'publish',
|
||||
)
|
||||
);
|
||||
|
||||
// If there are child pages, create them under the parent page
|
||||
foreach ( $childPages as $childPageTitle => $subChildPages ) {
|
||||
if ( is_array( $subChildPages ) ) {
|
||||
$subpageId = wp_insert_post(
|
||||
array(
|
||||
'post_title' => $childPageTitle,
|
||||
'post_content' => '',
|
||||
'post_type' => 'page',
|
||||
'post_status' => 'publish',
|
||||
'post_parent' => $pageId,
|
||||
)
|
||||
);
|
||||
|
||||
foreach ( $subChildPages as $subChildPageTitle ) {
|
||||
wp_insert_post(
|
||||
array(
|
||||
'post_title' => $subChildPageTitle,
|
||||
'post_content' => '',
|
||||
'post_type' => 'page',
|
||||
'post_status' => 'publish',
|
||||
'post_parent' => $subpageId,
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
wp_insert_post(
|
||||
array(
|
||||
'post_title' => $childPageTitle,
|
||||
'post_content' => '',
|
||||
'post_type' => 'page',
|
||||
'post_status' => 'publish',
|
||||
'post_parent' => $pageId,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use a static front page
|
||||
// phpcs:ignore
|
||||
$home = get_page_by_title('Home');
|
||||
update_option( 'page_on_front', $home->ID );
|
||||
update_option( 'show_on_front', 'page' );
|
||||
|
||||
// Set the blog/news page
|
||||
// phpcs:ignore
|
||||
$news = get_page_by_title('News');
|
||||
update_option( 'page_for_posts', $news->ID );
|
||||
|
||||
// Trash the samples
|
||||
wp_delete_post( 1, true );
|
||||
wp_delete_post( 2, true );
|
||||
|
||||
// Flush rewrite rules to ensure new pages are recognized
|
||||
flush_rewrite_rules();
|
||||
|
||||
/**
|
||||
* Install and activate must-use plugins
|
||||
*/
|
||||
|
||||
$muPlugins = array(
|
||||
array(
|
||||
'url' => 'https://docs.vincentdevelopment.ca/files/advanced-custom-fields-pro.zip',
|
||||
'active' => true,
|
||||
),
|
||||
array(
|
||||
'url' => 'https://docs.vincentdevelopment.ca/files/gravity-forms.zip',
|
||||
'active' => true,
|
||||
),
|
||||
array(
|
||||
'url' => 'https://updraftplus.com/wp-content/uploads/updraftplus.zip',
|
||||
'active' => false,
|
||||
),
|
||||
array(
|
||||
'url' => 'https://downloads.wordpress.org/plugin/simple-history.5.11.0.zip',
|
||||
'active' => true,
|
||||
),
|
||||
array(
|
||||
'url' => 'https://downloads.wordpress.org/plugin/autodescription.5.1.2.zip',
|
||||
'active' => true,
|
||||
),
|
||||
array(
|
||||
'url' => 'https://downloads.wordpress.org/plugin/better-search-replace.1.4.10.zip',
|
||||
'active' => true,
|
||||
),
|
||||
array(
|
||||
'url' => 'https://downloads.wordpress.org/plugin/google-site-kit.1.153.0.zip',
|
||||
'active' => false,
|
||||
),
|
||||
);
|
||||
|
||||
// Custom log file
|
||||
$logFile = WP_CONTENT_DIR . '/mu-plugin-install.log';
|
||||
|
||||
/**
|
||||
* Simple logging function.
|
||||
*
|
||||
* @param string $message The message to log.
|
||||
*/
|
||||
function log_message( $message ) {
|
||||
global $logFile;
|
||||
$timestamp = gmdate( 'Y-m-d H:i:s' );
|
||||
// phpcs:ignore
|
||||
file_put_contents( $logFile, "[$timestamp] $message\n", FILE_APPEND );
|
||||
}
|
||||
|
||||
// Include necessary WordPress files
|
||||
require_once ABSPATH . 'wp-admin/includes/file.php';
|
||||
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
|
||||
// Force direct filesystem access
|
||||
add_filter( 'filesystem_method', fn() => 'direct' );
|
||||
|
||||
global $wp_filesystem;
|
||||
if ( ! WP_Filesystem() ) {
|
||||
log_message( 'Filesystem initialization failed.' );
|
||||
return;
|
||||
}
|
||||
|
||||
// Define a silent skin to avoid show_message() errors
|
||||
// phpcs:disable
|
||||
class Silent_Upgrader_Skin extends WP_Upgrader_Skin {
|
||||
public function feedback( $string, ...$args ) {}
|
||||
public function header() {}
|
||||
public function footer() {}
|
||||
public function error( $errors ) {
|
||||
log_message( 'Upgrader error: ' . print_r( $errors, true ) );
|
||||
}
|
||||
public function before() {}
|
||||
public function after() {}
|
||||
}
|
||||
// phpcs:enable
|
||||
|
||||
$skin = new Silent_Upgrader_Skin();
|
||||
$upgrader = new Plugin_Upgrader( $skin );
|
||||
|
||||
// Process each plugin
|
||||
foreach ( $muPlugins as $plug ) {
|
||||
$plugUrl = $plug['url'];
|
||||
$shouldActivate = ! empty( $plug['active'] );
|
||||
|
||||
// Download plugin
|
||||
$response = wp_remote_get( $plugUrl );
|
||||
if ( is_wp_error( $response ) ) {
|
||||
log_message( "Failed to download plugin from {$plugUrl}: " . $response->get_error_message() );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Extract filename from URL
|
||||
$plugFileName = basename( wp_parse_url( $plugUrl, PHP_URL_PATH ) );
|
||||
|
||||
// Save the plugin zip
|
||||
$plugZip = wp_upload_bits( $plugFileName, null, wp_remote_retrieve_body( $response ) );
|
||||
if ( $plugZip['error'] ) {
|
||||
log_message( "Failed to save plugin zip {$plugFileName}: " . $plugZip['error'] );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Install the plugin
|
||||
$installResult = $upgrader->install( $plugZip['file'] );
|
||||
if ( is_wp_error( $installResult ) ) {
|
||||
log_message( "Failed to install plugin {$plugFileName}: " . $installResult->get_error_message() );
|
||||
// Cleanup temp zip
|
||||
if ( file_exists( $plugZip['file'] ) ) {
|
||||
wp_delete_file( $plugZip['file'] );
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get plugin info ( folder/main file )
|
||||
$plugInfo = $upgrader->plugin_info();
|
||||
log_message( "plugin_info for {$plugFileName}: {$plugInfo}" );
|
||||
|
||||
if ( ! $plugInfo || ! file_exists( WP_PLUGIN_DIR . '/' . $plugInfo ) ) {
|
||||
log_message( "Could not determine installed plugin file for {$plugFileName}." );
|
||||
// Cleanup temp zip
|
||||
if ( file_exists( $plugZip['file'] ) ) {
|
||||
wp_delete_file( $plugZip['file'] );
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
log_message( "Successfully installed plugin {$plugInfo}." );
|
||||
|
||||
// Attempt activation if marked active
|
||||
if ( $shouldActivate ) {
|
||||
$activateResult = activate_plugin( $plugInfo );
|
||||
if ( is_wp_error( $activateResult ) ) {
|
||||
log_message( "Failed to activate plugin {$plugInfo}: " . $activateResult->get_error_message() );
|
||||
} else {
|
||||
log_message( "Successfully activated plugin {$plugInfo}." );
|
||||
}
|
||||
} else {
|
||||
log_message( "Plugin {$plugInfo} installed but not activated ( per configuration )." );
|
||||
}
|
||||
|
||||
// Cleanup temp zip
|
||||
if ( file_exists( $plugZip['file'] ) ) {
|
||||
wp_delete_file( $plugZip['file'] );
|
||||
log_message( "Deleted temporary zip file {$plugZip['file']}." );
|
||||
}
|
||||
}
|
||||
|
||||
log_message( '=== Plugin installation and activation process completed ===' );
|
||||
}
|
||||
59
lib/class-acf.php
Normal file
59
lib/class-acf.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* ACF (Advanced Custom Fields) support class & functions
|
||||
*
|
||||
* @package BasicWP
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
namespace BasicWP;
|
||||
|
||||
/**
|
||||
* Class ACF
|
||||
*
|
||||
* This class serves as a wrapper or utility for handling Advanced Custom Fields (ACF) functionality.
|
||||
* It is part of the Basic-WP project and is located in the `lib` directory.
|
||||
*
|
||||
* @package Basic-WP
|
||||
*/
|
||||
class ACF {
|
||||
/**
|
||||
* Variable to hold the file path.
|
||||
*
|
||||
* @var string $path The file path associated with the class.
|
||||
*/
|
||||
public $path;
|
||||
|
||||
/** Constructor.
|
||||
*
|
||||
* This constructor initializes the class by setting the file path and
|
||||
* adding filters for loading and saving JSON files related to ACF.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->path = get_stylesheet_directory() . '/acf';
|
||||
add_filter( 'acf/settings/load_json', array( $this, 'loadJson' ) );
|
||||
add_filter( 'acf/settings/save_json', array( $this, 'saveJson' ) );
|
||||
}
|
||||
|
||||
/** Save JSON.
|
||||
*
|
||||
* @param mixed $path The path to save the JSON file.
|
||||
*/
|
||||
// phpcs:ignore
|
||||
public function saveJson( $path ) {
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/** Load JSON.
|
||||
*
|
||||
* @param mixed $paths The paths to load the JSON file.
|
||||
*/
|
||||
// phpcs:ignore
|
||||
public function loadJson( $paths ) {
|
||||
return array( $this->path );
|
||||
}
|
||||
}
|
||||
|
||||
if ( function_exists( 'get_fields' ) ) {
|
||||
$acfInstance = new ACF();
|
||||
}
|
||||
396
lib/class-breadcrumbs.php
Normal file
396
lib/class-breadcrumbs.php
Normal file
@@ -0,0 +1,396 @@
|
||||
<?php
|
||||
namespace BasicWP;
|
||||
|
||||
/**
|
||||
* Class Breadcrumbs
|
||||
*
|
||||
* This class is responsible for generating and managing breadcrumb navigation
|
||||
* for the WordPress theme. Breadcrumbs provide a navigational aid that helps
|
||||
* users understand their location within the website's hierarchy.
|
||||
*
|
||||
* @package Basic-WP
|
||||
* @subpackage Breadcrumbs
|
||||
*/
|
||||
class Breadcrumbs {
|
||||
/**
|
||||
* Generates the breadcrumb navigation for the website.
|
||||
*
|
||||
* This method is responsible for creating and returning the breadcrumb
|
||||
* trail, which helps users navigate the website by showing their current
|
||||
* location within the site's hierarchy.
|
||||
*
|
||||
* @return array An array of breadcrumb items, where each item is typically an
|
||||
* associative array containing 'title' and 'url' keys.
|
||||
*/
|
||||
public static function generate() {
|
||||
global $post;
|
||||
|
||||
$breadcrumbs = array();
|
||||
|
||||
// Always start with Home
|
||||
$breadcrumbs[] = self::getHomeBreadcrumb();
|
||||
|
||||
if ( is_front_page() ) {
|
||||
// Front Page - do nothing
|
||||
return $breadcrumbs;
|
||||
} elseif ( is_home() ) {
|
||||
// Blog Index - add the Blog Posts Index page title
|
||||
$breadcrumbs[] = self::getBlogPostsIndexBreadcrumb();
|
||||
} elseif ( is_singular( 'post' ) ) {
|
||||
// Single Post - add the category and post title
|
||||
$breadcrumbs = array_merge( $breadcrumbs, self::getSinglePostBreadcrumbs() );
|
||||
} elseif ( is_singular() && ! is_page() ) {
|
||||
// Single Custom Post Type - add the post type archive and post title
|
||||
$breadcrumbs = array_merge( $breadcrumbs, self::getCustomPostTypeBreadcrumbs() );
|
||||
} elseif ( is_page() ) {
|
||||
// Static Page - add the parent pages if any, and the page title
|
||||
$breadcrumbs = array_merge( $breadcrumbs, self::getStaticPageBreadcrumbs( $post ) );
|
||||
} elseif ( is_category() || is_tag() || is_tax() ) {
|
||||
// Taxonomy Archive - add the taxonomy term name
|
||||
$breadcrumbs[] = self::getTaxonomyArchiveBreadcrumb();
|
||||
} elseif ( is_post_type_archive() ) {
|
||||
// Post Type Archive - add the post type name
|
||||
$breadcrumbs[] = self::getPostTypeArchiveBreadcrumb();
|
||||
} elseif ( is_day() ) {
|
||||
// Daily Archive - add the month and day links
|
||||
$breadcrumbs = array_merge( $breadcrumbs, self::getDateArchiveBreadcrumbs() );
|
||||
} elseif ( is_month() ) {
|
||||
// Monthly Archive - add the month link
|
||||
$breadcrumbs[] = self::getMonthArchiveBreadcrumb();
|
||||
} elseif ( is_year() ) {
|
||||
// Yearly Archive - add the year link
|
||||
$breadcrumbs[] = self::getYearArchiveBreadcrumb();
|
||||
} elseif ( is_author() ) {
|
||||
// Author Archive - add the author name
|
||||
$breadcrumbs[] = self::getAuthorArchiveBreadcrumb();
|
||||
} elseif ( is_search() ) {
|
||||
// Search Results - add the search query
|
||||
$breadcrumbs[] = self::getSearchBreadcrumb();
|
||||
} elseif ( is_404() ) {
|
||||
// 404 Not Found - add a 404 message
|
||||
$breadcrumbs[] = self::get404Breadcrumb();
|
||||
}
|
||||
|
||||
return $breadcrumbs;
|
||||
}
|
||||
|
||||
/** Generates the breadcrumb for the home page.
|
||||
*
|
||||
* This method is responsible for creating the breadcrumb
|
||||
* link or label that represents the home page in the breadcrumb
|
||||
* navigation trail.
|
||||
*
|
||||
* @return string The HTML or text representation of the home breadcrumb.
|
||||
*/
|
||||
private static function getHomeBreadcrumb() {
|
||||
return array(
|
||||
'url' => home_url(),
|
||||
'label' => 'Home',
|
||||
);
|
||||
}
|
||||
|
||||
/** Generates the breadcrumb for the blog posts index page.
|
||||
*
|
||||
* This method is responsible for creating a breadcrumb entry
|
||||
* that represents the blog posts index page in the breadcrumb trail.
|
||||
*
|
||||
* @return array An associative array representing the breadcrumb for the blog posts index page.
|
||||
*/
|
||||
private static function getBlogPostsIndexBreadcrumb() {
|
||||
return array( 'label' => get_the_title( get_option( 'page_for_posts' ) ) );
|
||||
}
|
||||
|
||||
/** Generates the breadcrumb trail for a single post.
|
||||
*
|
||||
* This method is responsible for creating the breadcrumb navigation
|
||||
* specific to single post views. It typically includes links to the
|
||||
* homepage, category (or categories) the post belongs to, and the
|
||||
* post title itself.
|
||||
*
|
||||
* @return array An array representing the breadcrumb trail, where each
|
||||
* element is a breadcrumb item (e.g., URL and label).
|
||||
*/
|
||||
private static function getSinglePostBreadcrumbs() {
|
||||
$breadcrumbs = array();
|
||||
$categories = get_the_category();
|
||||
|
||||
$breadcrumbs[] = array(
|
||||
'url' => get_permalink( get_option( 'page_for_posts' ) ),
|
||||
'label' => get_the_title( get_option( 'page_for_posts' ) ),
|
||||
);
|
||||
|
||||
if ( ! empty( $categories ) ) {
|
||||
$cat = $categories[0];
|
||||
$breadcrumbs[] = array(
|
||||
'url' => get_category_link( $cat ),
|
||||
'label' => $cat->name,
|
||||
);
|
||||
}
|
||||
|
||||
$breadcrumbs[] = array( 'label' => get_the_title() );
|
||||
return $breadcrumbs;
|
||||
}
|
||||
|
||||
/** Generates breadcrumbs for custom post types.
|
||||
*
|
||||
* This method is responsible for creating breadcrumb navigation
|
||||
* specific to custom post types. It retrieves and formats the
|
||||
* breadcrumb trail based on the custom post type's hierarchy
|
||||
* and structure.
|
||||
*
|
||||
* @return array An array representing the breadcrumb trail for the custom post type.
|
||||
*/
|
||||
private static function getCustomPostTypeBreadcrumbs() {
|
||||
$breadcrumbs = array();
|
||||
$postType = get_post_type_object( get_post_type() );
|
||||
if ( $postType && ! in_array( get_post_type(), array( 'post', 'page' ), true ) ) {
|
||||
$breadcrumbs[] = array(
|
||||
'url' => get_post_type_archive_link( $postType->name ),
|
||||
'label' => $postType->labels->name,
|
||||
);
|
||||
}
|
||||
$breadcrumbs[] = array( 'label' => get_the_title() );
|
||||
return $breadcrumbs;
|
||||
}
|
||||
|
||||
/** Generates breadcrumbs for a static page.
|
||||
*
|
||||
* This method is responsible for creating breadcrumb navigation
|
||||
* for static pages in a WordPress theme. It utilizes the provided
|
||||
* post object to determine the breadcrumb structure.
|
||||
*
|
||||
* @param WP_Post $post The WordPress post object representing the static page.
|
||||
* @return array An array of breadcrumb items, where each item is typically
|
||||
* an associative array containing 'title' and 'url' keys.
|
||||
*/
|
||||
private static function getStaticPageBreadcrumbs( $post ) {
|
||||
$breadcrumbs = array();
|
||||
if ( $post->post_parent ) {
|
||||
$ancestors = array_reverse( get_post_ancestors( $post->ID ) );
|
||||
foreach ( $ancestors as $ancestor ) {
|
||||
$breadcrumbs[] = array(
|
||||
'url' => get_permalink( $ancestor ),
|
||||
'label' => get_the_title( $ancestor ),
|
||||
);
|
||||
}
|
||||
}
|
||||
$breadcrumbs[] = array( 'label' => get_the_title() );
|
||||
return $breadcrumbs;
|
||||
}
|
||||
|
||||
/** Generates a breadcrumb for taxonomy archive pages.
|
||||
*
|
||||
* This method is responsible for creating breadcrumb navigation
|
||||
* specific to taxonomy archive pages, providing users with a clear
|
||||
* path to navigate back to the taxonomy or related sections.
|
||||
*
|
||||
* @return array An array representing the breadcrumb trail for the taxonomy archive.
|
||||
*/
|
||||
private static function getTaxonomyArchiveBreadcrumb() {
|
||||
$term = get_queried_object();
|
||||
return $term && isset( $term->name ) ? array( 'label' => $term->name ) : array();
|
||||
}
|
||||
|
||||
/** Generates a breadcrumb for the archive page of a custom post type.
|
||||
*
|
||||
* This method is responsible for creating a breadcrumb link that points
|
||||
* to the archive page of a specific custom post type. It is typically used
|
||||
* in breadcrumb navigation to provide users with a way to navigate back
|
||||
* to the archive page of the post type they are currently viewing.
|
||||
*
|
||||
* @return array An array containing the breadcrumb data for the custom post type archive.
|
||||
*/
|
||||
private static function getPostTypeArchiveBreadcrumb() {
|
||||
$postType = get_post_type_object( get_post_type() );
|
||||
return $postType ? array( 'label' => $postType->labels->name ) : array();
|
||||
}
|
||||
|
||||
/** Generates breadcrumbs for date-based archives.
|
||||
*
|
||||
* This method is responsible for creating breadcrumb navigation
|
||||
* for WordPress date archive pages, such as year, month, or day archives.
|
||||
*
|
||||
* @return array An array representing the breadcrumb trail for the date archive.
|
||||
*/
|
||||
private static function getDateArchiveBreadcrumbs() {
|
||||
return array(
|
||||
array(
|
||||
'url' => get_month_link( get_query_var( 'year' ), get_query_var( 'monthnum' ) ),
|
||||
'label' => get_the_date( 'F Y' ),
|
||||
),
|
||||
array( 'label' => get_the_date() ),
|
||||
);
|
||||
}
|
||||
|
||||
/** Generates a breadcrumb for a monthly archive page.
|
||||
*
|
||||
* This method is responsible for creating a breadcrumb
|
||||
* specific to WordPress monthly archive pages. It typically
|
||||
* includes the year and month as part of the breadcrumb trail.
|
||||
*
|
||||
* @return string The HTML markup for the monthly archive breadcrumb.
|
||||
*/
|
||||
private static function getMonthArchiveBreadcrumb() {
|
||||
return array( 'label' => get_the_date( 'F Y' ) );
|
||||
}
|
||||
|
||||
/** Generates a breadcrumb for the year-based archive page.
|
||||
*
|
||||
* This method is responsible for creating a breadcrumb link
|
||||
* specific to the year archive in a WordPress site. It is typically
|
||||
* used to provide navigation for users when they are viewing posts
|
||||
* filtered by a specific year.
|
||||
*
|
||||
* @return string The HTML markup for the year archive breadcrumb.
|
||||
*/
|
||||
private static function getYearArchiveBreadcrumb() {
|
||||
return array( 'label' => get_the_date( 'Y' ) );
|
||||
}
|
||||
|
||||
/** Generates the breadcrumb for the author archive page.
|
||||
*
|
||||
* This method is responsible for creating a breadcrumb trail
|
||||
* specific to the author archive, providing navigation context
|
||||
* for pages that display posts by a particular author.
|
||||
*
|
||||
* @return string The HTML markup for the author archive breadcrumb.
|
||||
*/
|
||||
private static function getAuthorArchiveBreadcrumb() {
|
||||
$author = get_queried_object();
|
||||
return array( 'label' => 'Author: ' . $author->display_name );
|
||||
}
|
||||
|
||||
/** Generates the breadcrumb for search results.
|
||||
*
|
||||
* This method is responsible for creating a breadcrumb trail
|
||||
* specific to search result pages. It helps users navigate back
|
||||
* to the search context or other parts of the site.
|
||||
*
|
||||
* @return string The HTML markup for the search breadcrumb.
|
||||
*/
|
||||
private static function getSearchBreadcrumb() {
|
||||
return array( 'label' => 'Search: ' . get_search_query() );
|
||||
}
|
||||
|
||||
/** Generates the breadcrumb trail for a 404 error page.
|
||||
*
|
||||
* This method is responsible for creating a breadcrumb structure
|
||||
* specifically for 404 error pages, providing users with a navigational
|
||||
* context when a requested page is not found.
|
||||
*
|
||||
* @return array An array representing the breadcrumb trail for the 404 page.
|
||||
*/
|
||||
private static function get404Breadcrumb() {
|
||||
return array( 'label' => '404 Not Found' );
|
||||
}
|
||||
|
||||
/** Renders the breadcrumb navigation.
|
||||
*
|
||||
* This method generates and outputs the breadcrumb trail for the current page.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function render() {
|
||||
$items = self::generate();
|
||||
|
||||
$metadata = array(
|
||||
'@context' => 'https://schema.org',
|
||||
'@type' => 'BreadcrumbList',
|
||||
'itemListElement' => array(),
|
||||
);
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<nav aria-label="Breadcrumbs" class="flex items-center py-2 -mx-2 leading-none" vocab="https://schema.org/" typeof="BreadcrumbList">
|
||||
<?php foreach ( $items as $index => $item ) : ?>
|
||||
<?php
|
||||
$ldItem = array(
|
||||
'@type' => 'ListItem',
|
||||
'position' => $index + 1,
|
||||
'name' => htmlspecialchars( $item['label'], ENT_QUOTES, 'UTF-8' ),
|
||||
);
|
||||
|
||||
$isActive = ( $index === count( $items ) - 1 );
|
||||
$activeClass = $isActive ? ' active' : '';
|
||||
?>
|
||||
<span class="p-2 breadcrumb-item<?php echo esc_attr( $activeClass ); ?>"
|
||||
<?php
|
||||
if ( $isActive ) :
|
||||
?>
|
||||
aria-current="page"<?php endif; ?>>
|
||||
<?php if ( ! empty( $item['url'] ) ) : ?>
|
||||
<a href="<?php echo esc_url( $item['url'] ); ?>">
|
||||
<?php if ( $index === 0 ) : ?>
|
||||
<svg class="flex-shrink-0 w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" aria-label="Home">
|
||||
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z" />
|
||||
</svg>
|
||||
<span class="sr-only"><?php echo esc_attr( $item['label'] ); ?></span>
|
||||
<?php else : ?>
|
||||
<?php echo esc_attr( $item['label'] ); ?>
|
||||
<?php endif; ?>
|
||||
</a>
|
||||
<?php $ldItem['item'] = $item['url']; ?>
|
||||
<?php else : ?>
|
||||
<?php echo esc_attr( $item['label'] ); ?>
|
||||
<?php endif; ?>
|
||||
</span>
|
||||
|
||||
<?php $metadata['itemListElement'][] = $ldItem; ?>
|
||||
|
||||
<?php if ( ! $isActive ) : ?>
|
||||
<svg class="flex-shrink-0 w-5 h-5 text-light" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</nav>
|
||||
|
||||
<script type="application/ld+json"><?php echo esc_js( wp_json_encode( $metadata ) ); ?></script>
|
||||
|
||||
<?php
|
||||
echo wp_kses(
|
||||
ob_get_clean(),
|
||||
array(
|
||||
'a' => array(
|
||||
'class' => array(),
|
||||
'href' => array(),
|
||||
'title' => array(),
|
||||
'rel' => array(),
|
||||
'target' => array(),
|
||||
'aria-label' => array(),
|
||||
'aria-current' => array(),
|
||||
),
|
||||
'nav' => array(
|
||||
'aria-label' => array(),
|
||||
'class' => array(),
|
||||
'vocab' => array(),
|
||||
'typeof' => array(),
|
||||
),
|
||||
'span' => array(
|
||||
'class' => array(),
|
||||
'aria-current' => array(),
|
||||
),
|
||||
'svg' => array(
|
||||
'class' => array(),
|
||||
'xmlns' => array(),
|
||||
'viewBox' => array(),
|
||||
'fill' => array(),
|
||||
'aria-hidden' => array(),
|
||||
'aria-label' => array(),
|
||||
),
|
||||
'path' => array(
|
||||
'd' => array(),
|
||||
'fill-rule' => array(),
|
||||
'clip-rule' => array(),
|
||||
'fill' => array(),
|
||||
'xmlns:xlink' => array(),
|
||||
),
|
||||
'script' => array(
|
||||
'type' => array(),
|
||||
'src' => array(),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
100
lib/class-enqueue.php
Normal file
100
lib/class-enqueue.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
/**
|
||||
* BasicWP Theme Enqueue Class
|
||||
*
|
||||
* @package BasicWP
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
namespace BasicWP;
|
||||
|
||||
/**
|
||||
* Class Enqueue
|
||||
*
|
||||
* Handles the enqueueing of scripts and styles in a WordPress theme or plugin.
|
||||
*
|
||||
* @package Basic-WP
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class Enqueue {
|
||||
/**
|
||||
* Initialize hooks to enqueue scripts and styles.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'enqFEAssets' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqBEAssets' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue frontend CSS and JS files.
|
||||
*/
|
||||
public function enqFEAssets() {
|
||||
$theme_dir = get_stylesheet_directory();
|
||||
$theme_uri = get_stylesheet_directory_uri();
|
||||
|
||||
/**
|
||||
* CSS
|
||||
*/
|
||||
$css_path = '/static/dist/theme.css';
|
||||
if ( file_exists( $theme_dir . $css_path ) ) {
|
||||
$version = filemtime( $theme_dir . $css_path );
|
||||
wp_enqueue_style( 'basicwp-theme', $theme_uri . $css_path, array(), $version );
|
||||
}
|
||||
|
||||
$font_ver = gmdate( 'U' );
|
||||
wp_enqueue_style( 'raleway', 'https://fonts.googleapis.com/css2?family=Raleway:wght@100..900&display=swap', false, $font_ver );
|
||||
|
||||
/**
|
||||
* JS
|
||||
*/
|
||||
$js_path = '/static/js/theme.js';
|
||||
|
||||
if ( file_exists( $theme_dir . $js_path ) ) {
|
||||
$version = filemtime( $theme_dir . $js_path );
|
||||
wp_enqueue_script_module( 'basicwp-theme', $theme_uri . $js_path, array( 'jquery' ), $version, true );
|
||||
wp_enqueue_script_module( 'basicwp-button', $theme_uri . '/static/js/components/button.js', array( 'basicwp-theme' ), $version, true );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue backend (admin/editor) CSS and JS files.
|
||||
*/
|
||||
public function enqBEAssets() {
|
||||
$theme_dir = get_stylesheet_directory();
|
||||
$theme_uri = get_stylesheet_directory_uri();
|
||||
|
||||
/**
|
||||
* Editor CSS
|
||||
*/
|
||||
$editor_css_path = '/styles/backend/editor.css';
|
||||
if ( file_exists( $theme_dir . $editor_css_path ) ) {
|
||||
$version = filemtime( $theme_dir . $editor_css_path );
|
||||
wp_enqueue_style( 'basicwp-editor', $theme_uri . $editor_css_path, array(), $version );
|
||||
}
|
||||
|
||||
$font_ver = gmdate( 'U' );
|
||||
wp_enqueue_style( 'raleway', 'https://fonts.googleapis.com/css2?family=Raleway:wght@100..900&display=swap', false, $font_ver );
|
||||
|
||||
/**
|
||||
* Admin CSS
|
||||
*/
|
||||
$admin_css_path = '/styles/backend/admin.css';
|
||||
if ( file_exists( $theme_dir . $admin_css_path ) ) {
|
||||
$version = filemtime( $theme_dir . $admin_css_path );
|
||||
wp_enqueue_style( 'basicwp-admin', $theme_uri . $admin_css_path, array(), $version );
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin JS
|
||||
*/
|
||||
$admin_js_path = '/static/js/admin.js';
|
||||
if ( file_exists( $theme_dir . $admin_js_path ) ) {
|
||||
$version = filemtime( $theme_dir . $admin_js_path );
|
||||
wp_enqueue_script_module( 'basicwp-admin', $theme_uri . $admin_js_path, array( 'jquery' ), $version, true );
|
||||
wp_enqueue_script_module( 'basicwp-button', $theme_uri . '/static/js/components/button.js', array( 'basicwp-admin' ), $version, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the Enqueue class.
|
||||
$enqueue = new Enqueue();
|
||||
185
lib/class-menuitems.php
Normal file
185
lib/class-menuitems.php
Normal file
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
/**
|
||||
* BasicWP MenuItems Class
|
||||
*
|
||||
* @package BasicWP
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
namespace BasicWP;
|
||||
|
||||
/**
|
||||
* Class MenuItems
|
||||
*
|
||||
* This class is responsible for managing menu items within the Basic-WP theme.
|
||||
*
|
||||
* @package Basic-WP
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class MenuItems {
|
||||
/**
|
||||
* Accepts menu name to populate navigation items.
|
||||
*
|
||||
* @var string $displayLocation
|
||||
*/
|
||||
protected $displayLocation;
|
||||
|
||||
/**
|
||||
* First level menu items (menu_item_parent = 0).
|
||||
*
|
||||
* @var array $topLevelNavItems
|
||||
*/
|
||||
public $topLevelNavItems;
|
||||
|
||||
/**
|
||||
* True if nav item has children items.
|
||||
*
|
||||
* @var bool $hasChildren
|
||||
*/
|
||||
public $hasChildren;
|
||||
|
||||
/**
|
||||
* Children navigation items.
|
||||
*
|
||||
* @var array $nestedNavItems
|
||||
*/
|
||||
public $nestedNavItems;
|
||||
|
||||
/**
|
||||
* True if the current page is the same as the menu item.
|
||||
*
|
||||
* @var bool $currentPage
|
||||
*/
|
||||
public $currentPage;
|
||||
|
||||
/**
|
||||
* Constructor method for initializing the class with a specific menu name.
|
||||
*
|
||||
* @param string $menuName The name of the menu to be used. Defaults to 'main_navigation'.
|
||||
*/
|
||||
public function __construct( $menuName = 'main_navigation' ) {
|
||||
$this->displayLocation = $menuName;
|
||||
|
||||
$this->topLevelNavItems = $this->getTopLevelNavItems();
|
||||
$this->hasChildren = function ( $item ) {
|
||||
return $this->hasChildren( $item );
|
||||
};
|
||||
$this->nestedNavItems = function ( $parentItem ) {
|
||||
return $this->getNestedNavItems( $parentItem );
|
||||
};
|
||||
$this->currentPage = function ( $item ) {
|
||||
return $this->currentPage( $item );
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of menus.
|
||||
*
|
||||
* @return array An array containing menu items.
|
||||
*/
|
||||
public function getMenus() {
|
||||
$menus = get_nav_menu_locations();
|
||||
return empty( $menus ) ? array() : $menus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the navigation items.
|
||||
*
|
||||
* @return array An array of navigation items.
|
||||
*/
|
||||
public function getNavItems() {
|
||||
$locations = $this->getMenus();
|
||||
|
||||
// If the menu location doesn't exist, return empty array
|
||||
if ( ! isset( $locations[ $this->displayLocation ] ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// Get the menu ID for this location
|
||||
$menuId = $locations[ $this->displayLocation ];
|
||||
|
||||
// Direct call to wp_get_nav_menu_items with the menu ID
|
||||
$items = wp_get_nav_menu_items( $menuId );
|
||||
|
||||
// Return empty array if no items
|
||||
return is_array( $items ) ? $items : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given parent item has child items.
|
||||
*
|
||||
* @param mixed $parentItem The parent item to check for children.
|
||||
* @return bool Returns true if the parent item has children, false otherwise.
|
||||
*/
|
||||
public function hasChildren( $parentItem ) {
|
||||
foreach ( $this->getNavItems() as $item ) {
|
||||
if ( $item->menu_item_parent === strval( $parentItem->ID ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the top-level navigation items.
|
||||
*
|
||||
* @return array An array of top-level navigation items.
|
||||
*/
|
||||
public function getTopLevelNavItems() {
|
||||
$items = array();
|
||||
foreach ( $this->getNavItems() as $item ) {
|
||||
if ( $item->menu_item_parent === '0' ) {
|
||||
array_push( $items, $item );
|
||||
}
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the nested navigation items for a given parent item.
|
||||
*
|
||||
* @param mixed $parentItem The parent navigation item for which nested items are to be retrieved.
|
||||
* @return array The list of nested navigation items.
|
||||
*/
|
||||
public function getNestedNavItems( $parentItem ) {
|
||||
$childrenItems = array();
|
||||
foreach ( $this->getNavItems() as $item ) {
|
||||
if ( $item->menu_item_parent === strval( $parentItem->ID ) ) {
|
||||
array_push( $childrenItems, $item );
|
||||
}
|
||||
}
|
||||
return $childrenItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the current page based on the provided item.
|
||||
*
|
||||
* @param mixed $item The item used to determine the current page.
|
||||
* @return mixed The current page information.
|
||||
*/
|
||||
public function currentPage( $item ) {
|
||||
global $wp;
|
||||
return ( $item->url === home_url( $wp->request ) . '/' ) ? 'true' : 'false';
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the output for the current context.
|
||||
*
|
||||
* This method is responsible for generating and returning the
|
||||
* appropriate output for the current context or request.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function render() {
|
||||
global $views;
|
||||
|
||||
// Extract class properties to local variables
|
||||
$location = $this->displayLocation;
|
||||
$topLevelNavItems = $this->topLevelNavItems;
|
||||
$hasChildren = $this->hasChildren;
|
||||
$nestedNavItems = $this->nestedNavItems;
|
||||
$currentPage = $this->currentPage;
|
||||
|
||||
include $views . '/components/menu-items/index.php';
|
||||
}
|
||||
}
|
||||
129
lib/class-resources.php
Normal file
129
lib/class-resources.php
Normal file
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
/**
|
||||
* Resources custom post type & taxonomies
|
||||
*
|
||||
* @package BasicWP
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
namespace BasicWP;
|
||||
|
||||
/**
|
||||
* Class Resources
|
||||
*
|
||||
* This class is responsible for setting up the Resources post type and taxonomies.
|
||||
*
|
||||
* @package Basic-WP
|
||||
*/
|
||||
class Resources {
|
||||
/**
|
||||
* Constructor for the class.
|
||||
*
|
||||
* Initializes the class and sets up any necessary properties or methods.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'init', array( $this, 'registerPostType' ) );
|
||||
add_action( 'init', array( $this, 'registerTaxonomy' ) );
|
||||
add_filter( 'post_type_link', array( $this, 'postTypeLink' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a custom post type.
|
||||
*
|
||||
* This method is responsible for defining and registering a custom post type
|
||||
* with WordPress. It should include all necessary arguments and labels
|
||||
* required for the post type to function correctly.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function registerPostType() {
|
||||
register_post_type(
|
||||
'resources',
|
||||
array(
|
||||
'labels' => array(
|
||||
'name' => 'Resources',
|
||||
'singular_name' => 'Resource',
|
||||
'menu_name' => 'Resources',
|
||||
'name_admin_bar' => 'Resource',
|
||||
'add_new' => 'Add New Resource',
|
||||
'add_new_item' => 'Add New Resource',
|
||||
'edit_item' => 'Edit Resource',
|
||||
'new_item' => 'New Resource',
|
||||
'view_item' => 'View Resource',
|
||||
'search_items' => 'Search Resources',
|
||||
'not_found' => 'No resources found',
|
||||
'not_found_in_trash' => 'No resources found in Trash',
|
||||
),
|
||||
'public' => true,
|
||||
'has_archive' => true,
|
||||
'rewrite' => array( 'slug' => 'resources' ),
|
||||
'supports' => array( 'title', 'editor', 'excerpt', 'thumbnail', 'revisions', 'custom-fields' ),
|
||||
'menu_position' => 20,
|
||||
'menu_icon' => 'dashicons-hammer',
|
||||
'show_in_rest' => true,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a custom taxonomy.
|
||||
*
|
||||
* This method is responsible for defining and registering a custom taxonomy
|
||||
* within the WordPress environment. It should include the necessary arguments
|
||||
* and settings for the taxonomy to function as intended.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function registerTaxonomy() {
|
||||
register_taxonomy(
|
||||
'resource_type',
|
||||
array( 'resources' ),
|
||||
array(
|
||||
'labels' => array(
|
||||
'name' => 'Resource Types',
|
||||
'singular_name' => 'Resource Type',
|
||||
'search_items' => 'Search Resource Types',
|
||||
'all_items' => 'All Resource Types',
|
||||
'parent_item' => 'Parent Resource Type',
|
||||
'parent_item_colon' => 'Parent Resource Type:',
|
||||
'edit_item' => 'Edit Resource Type',
|
||||
'update_item' => 'Update Resource Type',
|
||||
'add_new_item' => 'Add New Resource Type',
|
||||
'new_item_name' => 'New Resource Type Name',
|
||||
'menu_name' => 'Resource Types',
|
||||
),
|
||||
'public' => true,
|
||||
'hierarchical' => true,
|
||||
'show_admin_column' => true,
|
||||
'rewrite' => array(
|
||||
'slug' => 'resources',
|
||||
'with_front' => false,
|
||||
'hierarchical' => true,
|
||||
),
|
||||
'show_in_rest' => true,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the permalink for a post of a specific post type.
|
||||
*
|
||||
* @param string $post_link The post's permalink.
|
||||
* @param WP_Post $post The post object.
|
||||
* @return string The filtered post permalink.
|
||||
*/
|
||||
public function postTypeLink( $post_link, $post ) {
|
||||
if ( 'resources' === $post->post_type ) {
|
||||
$terms = get_the_terms( $post->ID, 'resource_type' );
|
||||
if ( $terms && ! is_wp_error( $terms ) ) {
|
||||
$term_slug = $terms[0]->slug;
|
||||
|
||||
return home_url( "resources/{$term_slug}/{$post->post_name}" );
|
||||
}
|
||||
}
|
||||
|
||||
return $post_link;
|
||||
}
|
||||
}
|
||||
|
||||
new Resources();
|
||||
260
lib/extras.php
Normal file
260
lib/extras.php
Normal file
@@ -0,0 +1,260 @@
|
||||
<?php
|
||||
/**
|
||||
* Filters. etc
|
||||
*
|
||||
* @package BasicWP
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
namespace BasicWP;
|
||||
|
||||
/** Get child pages of the current page, sorted by menu order.
|
||||
*
|
||||
* @return array Array of child page objects, with URL added to each.
|
||||
*/
|
||||
function getChildrenPages() {
|
||||
$children = get_pages(
|
||||
array(
|
||||
'child_of' => get_the_ID(),
|
||||
'sort_order' => 'ASC',
|
||||
'sort_column' => 'menu_order',
|
||||
)
|
||||
);
|
||||
|
||||
foreach ( $children as &$child ) {
|
||||
$child->url = get_page_link( $child->ID );
|
||||
}
|
||||
|
||||
return $children;
|
||||
}
|
||||
|
||||
// Modify which pages should render the sidebar.
|
||||
add_filter(
|
||||
'hasSidebar',
|
||||
function ( $has_sidebar ) {
|
||||
// Add post types that should never have a sidebar.
|
||||
|
||||
if ( is_page() && ! get_field( 'has_sidebar' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $has_sidebar;
|
||||
}
|
||||
);
|
||||
|
||||
/** Helper to check whether or not the sidebar should be rendered
|
||||
* (to add/remove a sidebar from a page, edit the filter instead
|
||||
* of modifying this function).
|
||||
*/
|
||||
function hasSidebar() {
|
||||
return apply_filters( 'hasSidebar', true );
|
||||
}
|
||||
|
||||
// Add extra body classes here.
|
||||
add_filter(
|
||||
'body_class',
|
||||
function ( $classes ) {
|
||||
if ( hasSidebar() ) {
|
||||
$classes = array_merge( $classes, array( 'has-sidebar' ) );
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Checks if the page should render a page header.
|
||||
*
|
||||
* @return bool true if page header should be rendered, false otherwise
|
||||
*/
|
||||
function hasPageHeader() {
|
||||
global $post;
|
||||
|
||||
if ( get_field( 'hero_style' ) !== 'none' ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Create the Owner role.
|
||||
*
|
||||
* This function creates a new role named "Owner" with all the capabilities of the
|
||||
* Administrator role, except for the following:
|
||||
*
|
||||
* - activate_plugins
|
||||
* - delete_plugins
|
||||
* - edit_plugins
|
||||
* - install_plugins
|
||||
* - update_plugins
|
||||
* - switch_themes
|
||||
* - edit_themes
|
||||
* - delete_themes
|
||||
* - install_themes
|
||||
* - update_themes
|
||||
* - update_core
|
||||
* - manage_options
|
||||
*
|
||||
* This role is meant to be used by a person who should have almost all the same
|
||||
* capabilities as an Administrator, but should not have the ability to update
|
||||
* the WordPress core software, manage plugins or themes, or edit other site
|
||||
* options.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function createOwnerRole() {
|
||||
// First, remove the role if it exists.
|
||||
remove_role( 'owner' );
|
||||
|
||||
// Get the administrator role.
|
||||
$admin_role = get_role( 'administrator' );
|
||||
$admin_capabilities = $admin_role->capabilities;
|
||||
|
||||
// Remove specific capabilities.
|
||||
$capabilities_to_remove = array(
|
||||
'activate_plugins',
|
||||
'delete_plugins',
|
||||
'edit_plugins',
|
||||
'install_plugins',
|
||||
'update_plugins',
|
||||
'switch_themes',
|
||||
'edit_themes',
|
||||
'delete_themes',
|
||||
'install_themes',
|
||||
'update_themes',
|
||||
'update_core',
|
||||
'manage_options',
|
||||
);
|
||||
|
||||
foreach ( $capabilities_to_remove as $capability ) {
|
||||
unset( $admin_capabilities[ $capability ] );
|
||||
}
|
||||
|
||||
// Add the Owner role with the modified capabilities.
|
||||
add_role( 'owner', 'Owner', $admin_capabilities );
|
||||
}
|
||||
add_action( 'init', __NAMESPACE__ . '\\createOwnerRole' );
|
||||
|
||||
/** Retrieves the appropriate title for the current page context.
|
||||
*
|
||||
* The function determines the type of page being viewed and returns
|
||||
* the corresponding title. It handles different page types, including
|
||||
* the home page, single posts, archives, search results, and 404 pages.
|
||||
* If none of these conditions apply, it defaults to fetching the page's title.
|
||||
*
|
||||
* @return string The title relevant to the current page context.
|
||||
*/
|
||||
function getTheTitle() {
|
||||
$title = '';
|
||||
|
||||
if ( is_home() || is_single() ) {
|
||||
$title = get_the_title( get_option( 'page_for_posts', true ) );
|
||||
} elseif ( is_archive() ) {
|
||||
$title = get_the_archive_title();
|
||||
} elseif ( is_search() ) {
|
||||
$title = sprintf(
|
||||
/* translators: %s is replaced with the search query */
|
||||
__( 'Search Results for "%s"', 'basicwp' ),
|
||||
get_search_query()
|
||||
);
|
||||
} elseif ( is_404() ) {
|
||||
$title = 'Page Not Found (error 404)';
|
||||
} else {
|
||||
$title = get_the_title();
|
||||
}
|
||||
|
||||
return $title;
|
||||
}
|
||||
|
||||
/** Wraps iframes and embed elements in a div with a specific class.
|
||||
*
|
||||
* This function searches for iframe and embed elements within the provided
|
||||
* content and wraps each found element in a div with the class "embed".
|
||||
* It is useful for applying consistent styling or responsive behavior
|
||||
* to embedded media elements.
|
||||
*
|
||||
* @param string $content The HTML content containing iframes or embeds.
|
||||
* @return string The modified content with wrapped iframes and embeds.
|
||||
*/
|
||||
function divWrapper( $content ) {
|
||||
// match any iframes.
|
||||
$pattern = '~<iframe.*</iframe>|<embed.*</embed>~';
|
||||
preg_match_all( $pattern, $content, $matches );
|
||||
|
||||
foreach ( $matches[0] as $match ) {
|
||||
// wrap matched iframe with div.
|
||||
$wrappedframe = '<div class="embed">' . $match . '</div>';
|
||||
|
||||
// replace original iframe with new in content.
|
||||
$content = str_replace( $match, $wrappedframe, $content );
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
add_filter( 'the_content', __NAMESPACE__ . '\\divWrapper' );
|
||||
|
||||
/** Selectively add sidebar to page.
|
||||
*
|
||||
* This function adds a custom field group to the WordPress editor for
|
||||
* pages, allowing users to specify whether a page should have a sidebar.
|
||||
* Default is no sidebar.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
add_action(
|
||||
'acf/include_fields',
|
||||
function () {
|
||||
if ( ! function_exists( 'acf_add_local_field_group' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
acf_add_local_field_group(
|
||||
array(
|
||||
'key' => 'group_6817d79573087',
|
||||
'title' => 'Page Sidebar',
|
||||
'fields' => array(
|
||||
array(
|
||||
'key' => 'field_6817d7954a168',
|
||||
'label' => '',
|
||||
'name' => 'has_sidebar',
|
||||
'aria-label' => '',
|
||||
'type' => 'true_false',
|
||||
'instructions' => '',
|
||||
'required' => 0,
|
||||
'conditional_logic' => 0,
|
||||
'wrapper' => array(
|
||||
'width' => '',
|
||||
'class' => '',
|
||||
'id' => '',
|
||||
),
|
||||
'message' => 'Should this page have a sidebar?',
|
||||
'default_value' => 0,
|
||||
'allow_in_bindings' => 0,
|
||||
'ui' => 0,
|
||||
'ui_on_text' => '',
|
||||
'ui_off_text' => '',
|
||||
),
|
||||
),
|
||||
'location' => array(
|
||||
array(
|
||||
array(
|
||||
'param' => 'post_type',
|
||||
'operator' => '==',
|
||||
'value' => 'page',
|
||||
),
|
||||
),
|
||||
),
|
||||
'menu_order' => 0,
|
||||
'position' => 'side',
|
||||
'style' => 'default',
|
||||
'label_placement' => 'top',
|
||||
'instruction_placement' => 'label',
|
||||
'hide_on_screen' => '',
|
||||
'active' => true,
|
||||
'description' => '',
|
||||
'show_in_rest' => 0,
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
155
lib/helpers.php
Normal file
155
lib/helpers.php
Normal file
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
/**
|
||||
* BasicWP Theme Helpers
|
||||
*
|
||||
* @package BasicWP
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
namespace BasicWP;
|
||||
|
||||
// Define global variables for theme and views folder paths.
|
||||
global $theme, $views;
|
||||
|
||||
$theme = get_template_directory();
|
||||
$views = $theme . '/views';
|
||||
|
||||
/** Retrieves a nested value from an ACF field.
|
||||
*
|
||||
* @param string $field_path The dot-notated path to the value. For example, 'contact_info.phone'.
|
||||
*
|
||||
* @return mixed The value at the specified path.
|
||||
*/
|
||||
function getFieldValue( $field_path ) {
|
||||
$parts = explode( '.', $field_path );
|
||||
$field = get_field( array_shift( $parts ), 'option' );
|
||||
|
||||
foreach ( $parts as $part ) {
|
||||
$field = $field[ $part ] ?? '';
|
||||
}
|
||||
|
||||
return $field;
|
||||
}
|
||||
|
||||
// Add Global Fields options page.
|
||||
if ( function_exists( 'acf_add_options_page' ) ) {
|
||||
add_action(
|
||||
'init',
|
||||
function () {
|
||||
acf_add_options_page(
|
||||
array(
|
||||
'page_title' => 'Global Fields',
|
||||
'menu_title' => 'Global Fields',
|
||||
'menu_slug' => 'global-fields',
|
||||
'icon_url' => 'dashicons-admin-site',
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/** Customizes the order of the admin menu items in WordPress.
|
||||
*
|
||||
* This function modifies the default menu order in the WordPress admin dashboard
|
||||
* by specifying a custom sequence for menu items, separators, and additional
|
||||
* options. If the menu order is not specified, it returns true to allow the
|
||||
* default order to be used.
|
||||
*
|
||||
* @param bool $menu_ord Indicates whether the menu order has been specified.
|
||||
*
|
||||
* @return array|bool An array specifying the custom menu order, or true if the
|
||||
* menu order is not specified.
|
||||
*/
|
||||
function customMenuOrder( $menu_ord ) {
|
||||
if ( ! $menu_ord ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return array(
|
||||
'index.php', // Dashboard.
|
||||
'acf-options-global-fields', // Global Theme Fields.
|
||||
'edit.php?post_type=acf-field-group', // ACF Field Groups.
|
||||
'separator1', // First separator.
|
||||
'edit.php', // Posts.
|
||||
'edit.php?post_type=page', // Pages.
|
||||
'edit.php?post_type=resources', // Resources.
|
||||
'upload.php', // Media.
|
||||
'separator2', // Second separator.
|
||||
'edit.php?post_type=page-template', // Page Templates.
|
||||
'edit.php?post_type=wp_block', // Reusable Blocks.
|
||||
'edit.php?post_type=block-pattern', // Block Patterns.
|
||||
'edit.php?post_type=element', // Elements.
|
||||
'separator3', // Third separator.
|
||||
'link-manager.php', // Links.
|
||||
'edit-comments.php', // Comments.
|
||||
'gf_edit_forms', // Gravity Forms.
|
||||
'themes.php', // Appearance.
|
||||
'plugins.php', // Plugins.
|
||||
'separator-last', // Last separator.
|
||||
'users.php', // Users.
|
||||
'tools.php', // Tools.
|
||||
'options-general.php', // Settings.
|
||||
);
|
||||
}
|
||||
|
||||
add_filter( 'custom_menu_order', __NAMESPACE__ . '\\customMenuOrder', 10, 1 );
|
||||
add_filter( 'menu_order', __NAMESPACE__ . '\\customMenuOrder', 10, 1 );
|
||||
|
||||
/** Add custom block category for our blocks
|
||||
*
|
||||
* @param array $categories The existing block categories.
|
||||
* @return array
|
||||
*/
|
||||
function blockCategories( $categories ) {
|
||||
$vdi_cat = array(
|
||||
'slug' => 'vdi-blocks',
|
||||
'title' => 'VDI Custom Blocks',
|
||||
'icon' => 'dashicons-admin-customizer',
|
||||
);
|
||||
|
||||
array_unshift( $categories, $vdi_cat );
|
||||
return $categories;
|
||||
}
|
||||
|
||||
add_filter( 'block_categories_all', __NAMESPACE__ . '\\blockCategories', 10, 2 );
|
||||
|
||||
/**
|
||||
* Creates a escaping function to allowed certain HTML for embed content.
|
||||
* Needed for when echoing the innerblock HTML.
|
||||
*
|
||||
* @return array An array of HTML elements allowed.
|
||||
*/
|
||||
function escEmbeds() {
|
||||
/**
|
||||
* Return the allowed html
|
||||
* These are the elements in the rendered embed block for youtube and vimeo videos.
|
||||
* Therefore we need to allow these to keep the same structure.
|
||||
*/
|
||||
return array(
|
||||
'iframe' => array(
|
||||
'role' => true, // Add role="presentation" to iframes.
|
||||
'presentation' => true, // Add role="presentation" to iframes.
|
||||
'src' => true,
|
||||
'height' => true,
|
||||
'width' => true,
|
||||
'frameborder' => true,
|
||||
'allowfullscreen' => true,
|
||||
),
|
||||
'figure' => array(
|
||||
'class' => true,
|
||||
),
|
||||
'div' => array(
|
||||
'class' => true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/** Print a variable to the console for debugging purposes.
|
||||
*
|
||||
* @param mixed $data The data to print to the console.
|
||||
*/
|
||||
function consoleLog( $data ) {
|
||||
echo '<script>';
|
||||
echo 'console.log(' . wp_json_encode( $data ) . ')';
|
||||
echo '</script>';
|
||||
}
|
||||
251
lib/hooks.php
Normal file
251
lib/hooks.php
Normal file
@@ -0,0 +1,251 @@
|
||||
<?php
|
||||
/**
|
||||
* BasicWP Theme Hooks
|
||||
*
|
||||
* @package BasicWP
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
namespace BasicWP;
|
||||
|
||||
/**
|
||||
* Add preconnect for Google fonts to head
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
add_action(
|
||||
'wp_head',
|
||||
function () {
|
||||
?>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<?php
|
||||
},
|
||||
0
|
||||
);
|
||||
|
||||
/**
|
||||
* Register the navigation menus.
|
||||
*
|
||||
* @link https://developer.wordpress.org/reference/functions/register_nav_menus/
|
||||
*/
|
||||
register_nav_menus(
|
||||
array(
|
||||
'main_navigation' => 'Main Navigation',
|
||||
'aux_navigation' => 'Auxiliary Navigation',
|
||||
'footer_navigation' => 'Footer Navigation',
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Widget Areas
|
||||
*
|
||||
* Set up sidebar/widget areas for the theme.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
add_action(
|
||||
'widgets_init',
|
||||
function () {
|
||||
$config = array(
|
||||
'before_widget' => '<div class="widget %1$s %2$s">',
|
||||
'after_widget' => '</div>',
|
||||
'before_title' => '<h2>',
|
||||
'after_title' => '</h2>',
|
||||
);
|
||||
|
||||
$cfg_foot = array(
|
||||
'before_widget' => '<div class="widget %1$s %2$s">',
|
||||
'after_widget' => '</div>',
|
||||
'before_title' => '<h4>',
|
||||
'after_title' => '</h4>',
|
||||
);
|
||||
|
||||
register_sidebar(
|
||||
array(
|
||||
'name' => 'Primary Sidebar',
|
||||
'id' => 'sidebar-primary',
|
||||
) + $config
|
||||
);
|
||||
|
||||
register_sidebar(
|
||||
array(
|
||||
'name' => 'Page Sidebar',
|
||||
'id' => 'sidebar-page',
|
||||
) + $config
|
||||
);
|
||||
|
||||
register_sidebar(
|
||||
array(
|
||||
'name' => 'Footer Area 1',
|
||||
'id' => 'footer-1',
|
||||
) + $cfg_foot
|
||||
);
|
||||
|
||||
register_sidebar(
|
||||
array(
|
||||
'name' => 'Footer Area 2',
|
||||
'id' => 'footer-2',
|
||||
) + $cfg_foot
|
||||
);
|
||||
|
||||
register_sidebar(
|
||||
array(
|
||||
'name' => 'Footer Area 3',
|
||||
'id' => 'footer-3',
|
||||
) + $cfg_foot
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Basic SEO
|
||||
*
|
||||
* {Site URL}: {Title}
|
||||
*/
|
||||
add_filter(
|
||||
'wp_title',
|
||||
function ( $title ) {
|
||||
$site_name = get_bloginfo( 'name' );
|
||||
|
||||
return "{$site_name}: {$title}";
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Excerpt
|
||||
*/
|
||||
add_filter(
|
||||
'excerpt_more',
|
||||
function () {
|
||||
return '…';
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Whether the page hero should hold the main h1 of the page.
|
||||
*/
|
||||
add_filter(
|
||||
'include_page_title_in_hero',
|
||||
function ( $include_title ) {
|
||||
// Add post types that should not use the title in the hero.
|
||||
if ( is_singular( ( 'post' ) ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $include_title;
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* WP Cleanup
|
||||
*/
|
||||
function init() {
|
||||
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
|
||||
remove_action( 'wp_print_styles', 'print_emoji_styles' );
|
||||
remove_action( 'admin_print_styles', 'print_emoji_styles' );
|
||||
remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
|
||||
remove_filter( 'the_content_feed', 'wp_staticize_emoji' );
|
||||
remove_filter( 'comment_text_rss', 'wp_staticize_emoji' );
|
||||
remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' );
|
||||
wp_dequeue_style( 'wp-block-library' ); // Core block styles.
|
||||
wp_dequeue_style( 'wp-block-library-theme' ); // Block theme styles.
|
||||
wp_dequeue_style( 'global-styles' ); // Global styles.
|
||||
wp_dequeue_style( 'core-block-supports' ); // Core block supports.
|
||||
wp_dequeue_style( 'core-block-styles' ); // Core block styles.
|
||||
remove_action( 'wp_enqueue_scripts', 'wp_enqueue_global_styles', 1 );
|
||||
remove_action( 'wp_enqueue_scripts', 'wp_enqueue_classic_theme_styles', 1 );
|
||||
remove_action( 'wp_head', 'wp_print_styles', 8 );
|
||||
remove_action( 'wp_head', 'wp_print_head_scripts', 9 );
|
||||
remove_action( 'wp_head', 'wp_generator' ); // WordPress version.
|
||||
remove_action( 'wp_head', 'rsd_link' ); // RSD link.
|
||||
remove_action( 'wp_head', 'wlwmanifest_link' ); // Windows Live Writer.
|
||||
remove_action( 'wp_head', 'wp_shortlink_wp_head' ); // Shortlink.
|
||||
remove_action( 'wp_head', 'rest_output_link_wp_head' ); // REST API link.
|
||||
remove_action( 'wp_head', 'wp_oembed_add_discovery_links' ); // oEmbed discovery links.
|
||||
remove_action( 'wp_head', 'rel_canonical' ); // Canonical URL.
|
||||
remove_action( 'wp_head', 'wp_resource_hints', 2 ); // DNS Prefetch.
|
||||
add_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' ); // Disable intrinsic image size.
|
||||
add_filter( 'wp_img_tag_add_auto_sizes', '__return_false' ); // Disable auto sizes.
|
||||
add_filter( 'xmlrpc_enabled', '__return_false' );
|
||||
}
|
||||
|
||||
add_action( 'init', __NAMESPACE__ . '\\init', 1 );
|
||||
|
||||
/**
|
||||
* Allow SVG uploads
|
||||
*/
|
||||
add_filter(
|
||||
'wp_check_filetype_and_ext',
|
||||
function ( $data, $file, $filename, $mimes ) {
|
||||
global $wp_version;
|
||||
|
||||
if ( '4.7.1' !== $wp_version ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$filetype = wp_check_filetype( $filename, $mimes );
|
||||
|
||||
return array(
|
||||
'ext' => $filetype['ext'],
|
||||
'type' => $filetype['type'],
|
||||
'proper_filename' => $data['proper_filename'],
|
||||
);
|
||||
},
|
||||
10,
|
||||
4
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'upload_mimes',
|
||||
function ( $mimes ) {
|
||||
$mimes['svg'] = 'image/svg+xml';
|
||||
return $mimes;
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Fix display issues with SVGs in admin
|
||||
*/
|
||||
add_action(
|
||||
'admin_head',
|
||||
function () {
|
||||
echo '<style type="text/css">
|
||||
.attachment-266x266, .thumbnail img {
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
}
|
||||
</style>';
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Filters the email address used as the sender in outgoing emails.
|
||||
*
|
||||
* This function allows you to modify the default "from" email address
|
||||
* used by WordPress when sending emails.
|
||||
*
|
||||
* @param string $old The original email address.
|
||||
* @return string The new email address to use as the sender.
|
||||
*/
|
||||
// phpcs:ignore
|
||||
function new_mail_from( $old ) {
|
||||
return get_option( 'admin_email' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the name used as the sender in outgoing emails.
|
||||
*
|
||||
* This function allows you to modify the default "from" name
|
||||
* used by WordPress when sending emails.
|
||||
*
|
||||
* @param string $old The original name.
|
||||
* @return string The new name to use as the sender.
|
||||
*/
|
||||
// phpcs:ignore
|
||||
function new_mail_from_name( $old ) {
|
||||
return get_option( 'blogname' );
|
||||
}
|
||||
|
||||
add_filter( 'wp_mail_from', __NAMESPACE__ . '\\new_mail_from' );
|
||||
add_filter( 'wp_mail_from_name', __NAMESPACE__ . '\\new_mail_from_name' );
|
||||
122
lib/search-features.php
Normal file
122
lib/search-features.php
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
/**
|
||||
* Search features for BasicWP theme.
|
||||
*
|
||||
* @package BasicWP
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
namespace BasicWP;
|
||||
|
||||
/**
|
||||
* Modifies the WordPress query object for page search functionality.
|
||||
*
|
||||
* @param WP_Query $query The WordPress query object.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function pageSearch( $query ) {
|
||||
if ( ! is_admin() && $query->is_main_query() && $query->is_search ) {
|
||||
$query->set( 'paged', ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1 );
|
||||
$query->set( 'posts_per_page', -1 );
|
||||
}
|
||||
}
|
||||
|
||||
add_action( 'pre_get_posts', __NAMESPACE__ . '\\pageSearch' );
|
||||
|
||||
/**
|
||||
* Sort WP_Post objects in reverse order (most recent first).
|
||||
*
|
||||
* @param string $key WP_Post object property used for sorting. 'post_date' is assumed.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function postSort( $key ) {
|
||||
return function ( $a, $b ) use ( $key ) {
|
||||
if ( $a->$key < $b->$key ) {
|
||||
return 1;
|
||||
} elseif ( $a->$key > $b->$key ) {
|
||||
return -1;
|
||||
} else {
|
||||
// If first comparison is equal, use title as secondary sort key.
|
||||
return strnatcasecmp( $a->post_title, $b->post_title );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove duplicate posts in combined list from default and secondary queries.
|
||||
*
|
||||
* @param array $posts Array of WP_Post objects.
|
||||
* @param string $key Search key used to identify duplicate objects. Post ID is used.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function dedupe( $posts, $key ) {
|
||||
$unique_posts = array();
|
||||
$ids = array();
|
||||
|
||||
foreach ( $posts as $post ) {
|
||||
if ( ! in_array( $post->$key, $ids, true ) ) {
|
||||
$ids[] = $post->$key;
|
||||
$unique_posts[] = $post;
|
||||
}
|
||||
}
|
||||
|
||||
return $unique_posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts_results filter hook callback.
|
||||
*
|
||||
* @param array $posts Array of WP_Post objects.
|
||||
* @param object $query WP_Query object from default search.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function searchResultFilter( $posts, $query ) {
|
||||
if ( \is_search() && $query->is_search() && ! \is_admin() ) {
|
||||
$args = array(
|
||||
'post_type' => array( 'post', 'page' ),
|
||||
'posts_per_page' => -1,
|
||||
'tax_query' => array(
|
||||
'relation' => 'OR', // Include both tags and categories.
|
||||
array(
|
||||
'taxonomy' => 'post_tag',
|
||||
'field' => 'name',
|
||||
'terms' => $query->get( 's' ),
|
||||
),
|
||||
array(
|
||||
'taxonomy' => 'category',
|
||||
'field' => 'name',
|
||||
'terms' => $query->get( 's' ),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Remove callback to avoid infinite loop.
|
||||
remove_filter( 'posts_results', __NAMESPACE__ . '\\searchResultFilter', 10 );
|
||||
|
||||
$secondary_query = new \WP_Query( $args );
|
||||
|
||||
$tagged_posts = $secondary_query->get_posts();
|
||||
|
||||
// Combine default search results with secondary query.
|
||||
$all_posts = array_merge( $posts, $tagged_posts );
|
||||
|
||||
// Remove duplicate posts.
|
||||
$unique_posts = dedupe( $all_posts, 'ID' );
|
||||
|
||||
// Sort by reverse post_date order (most recent first).
|
||||
usort( $unique_posts, postSort( 'post_date' ) );
|
||||
|
||||
// Restore posts_results callback.
|
||||
add_filter( 'posts_results', __NAMESPACE__ . '\\searchResultFilter', 10, 2 );
|
||||
|
||||
return $unique_posts;
|
||||
} else {
|
||||
return $posts;
|
||||
}
|
||||
}
|
||||
|
||||
add_filter( 'posts_results', __NAMESPACE__ . '\\searchResultFilter', 10, 2 );
|
||||
Reference in New Issue
Block a user