Commit cea87de5 authored by Mark Rowatt Anderson's avatar Mark Rowatt Anderson
Browse files

Initial starter plugin setup

parent ff431f7e
*~
.DS_Store
.idea/
node_modules
vendor/*
\ No newline at end of file
=== Plugin Name ===
Contributors: markauk
Requires at least: 4.9
Tested up to: 4.9
License: Global Ecovillage Network
This is a starter plugin to help with customising the GEN Theme.
A common way in WordPress of customising themes is to create a child theme of any given theme,
and customise it. That way, if the original theme is updated, then your customisations will
not be overwritten.
Unfortunately the GEN Theme is already a child theme of the Genesis theme, and WordPress does not
support "Grand Child" themes. This plugin is a simple starter plugin which you can use to
customise the GEN Theme. While the starter plugin may be updated from time to time,
it is not intended that you update your customised version - otherwise your changes would be
overwritten.
The plugin is setup similarly to how the main GEN site is set up. However, there is no requirement
that you follow how the starter plugin is set up. If you are familiar with WordPress coding
and/or already have your own site code, you do not need to use anything in this plugin.
If you are less familiar with WordPress coding, you may, however, find this helpful. See below for
how you can use it to customise the GEN Theme in a similar way to how you would customise a theme.
== functions.php ==
People often supply code snippets to help customise WordPress, and will say "add this code to your
functions.php file". That is a standard file in every WordPress theme. However, you cannot add code to
the GEN Theme functions.php file - if you do, then that code will be overwritten whenever the GEN Theme
is updated.
This plugin, however, has a functions.php file - any code you add here, will work exactly the same as
if you had added it to the Theme functions.php file.
Any code here will be run after the GEN Theme is setup and initialised. If you need to have code run
earlier, you can use the `gen_theme_pre_init` and `gen_theme_post_init` hooks.
`gen_theme_pre_init` runs before any theme initialisation, `gen_theme_post_init` runs after GEN Theme
initialisation, but before Genesis initialisation. Code added to functions.php is run after GEN Theme
and Genesis initialisation.
== styles.css and scripts.js ==
If you want to add custom CSS styles or scripts to the theme, you can do that by adding a styles.css
or scripts.js file to the plugin. See the sample files included in the starter plugin - you will want
to remove those files or remove the sample code in those files.
Any CSS styles you add to styles.css will be loaded after the main GEN Theme stylesheet,
which will allow you to override any of the core theme styles.
== Custom templates ==
If you wish to add custom templates for your theme, add them to the templates directory in the starter
plugin.
== Changelog ==
see file changelog.txt
<?php
namespace GEN_Custom;
/**
* Trait Singleton
*
* @todo refactor Singleton trains/abstract class
*
* @package GEN_Custom;
*/
trait Singleton_Simple {
/**
* Store class instance
*
* @var static
*/
protected static $instance;
public static function get_instance() {
NULL === static::$instance and static::$instance = new static;
return static::$instance;
}
/**
* Get Singleton instance also running init() method if we are instantiating
*
* @return Singleton_Simple
*/
public static function get_instance_init(): self {
if( NULL === static::$instance ) {
static::$instance = new static;
static::$instance->init();
}
return static::$instance;
}
/**
* Setup run when class instantiated with get_instance_init
*/
protected function init(): void {
//ensure that parent class methods are invoked if child class does not have init() method
if( is_callable( [$this, 'init'] ) ) {
parent::init();
}
}
/**
* Don't let $instance be cloned or deserialised
*/
final public function __wakeup() {}
final private function __clone() {}
}
/* EOF */
\ No newline at end of file
## 1.0.0 (2019-08-27)
Initial release.
\ No newline at end of file
<?php
/**
* Any code added to this file will run as if it were added
* to the GEN Theme's functions.php file.
*/
/* EOF */
\ No newline at end of file
<?php
/**
* Plugin Name: GEN Custom Starter Plugin
*
* Author: Global Ecovillage Network
* Author URI: https://www.ecovillage.org
*
* Text Domain: gen-custom
* Domain Path: /languages
*
* Requires PHP: 7.2
* Requires at least: 5.2
* Tested up to: 5.2
*
* Requires theme version: 2.0.8
*
* Version: 1.0.0
*/
const GEN_CUSTOM_VERSION = '1.0.0';
const GEN_CUSTOM_DIR = __DIR__;
const GEN_CUSTOM_TEXT_DOMAIN = 'gen-custom';
// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
die;
}
//setup autoloader
require_once GEN_CUSTOM_DIR . '/lib/Autoloader.php';
new GEN_Custom\Autoloader( [ 'GEN_Custom' => GEN_CUSTOM_DIR . '/lib' ] );
//start the plugin
GEN_Custom\Setup\Init::get_instance();
/* EOF */
\ No newline at end of file
<?php
namespace GEN_Custom;
/*
* Exit if called directly.
*/
if ( ! defined( 'WPINC' ) ) {
die;
}
/**
* Class Autoloader
*
* To use with different plugins be sure to create a new namespace.
*
* Based on Fragen\GitHub_Updater
* @author Andy Fragen <andy@thefragens.com>
* @author Barry Hughes <barry@codingkillsme.com>
* @license GPL-2.0+
* @link http://github.com/afragen/autoloader
* @copyright 2015 Andy Fragen
* @version 2.0.0
*
* @package GEN
*/
class Autoloader {
/**
* Roots to scan when autoloading.
*
* @var array
*/
protected $roots = [];
/**
* List of class names and locations in filesystem, for situations
* where they deviate from convention etc.
*
* @var array
*/
protected $map = [];
/**
* Constructor
*
* @param array $roots
* @param array $static_map
*/
public function __construct( array $roots, array $static_map = null ) {
$this->roots = $roots;
if ( null !== $static_map ) {
$this->map = $static_map;
}
try {
spl_autoload_register( [ $this, 'autoload' ] );
} catch(\Exception $e) {
die( 'Autoloader failure in GEN Custom Plugin.' );
}
}
/**
* Load classes
*
* @param $class
*/
protected function autoload( $class ) {
// Check for a static mapping first of all
if ( isset( $this->map[ $class ] ) && file_exists( $this->map[ $class ] ) ) {
include $this->map[ $class ];
return;
}
// Else scan the namespace roots
foreach ( $this->roots as $namespace => $root_dir ) {
// If the class doesn't belong to this namespace, move on to the next root
if ( 0 !== strpos( $class, $namespace ) ) {
continue;
}
// Determine the possible path to the class
$path = substr( $class, strlen( $namespace ) + 1 );
$path = str_replace( '\\', DIRECTORY_SEPARATOR, $path );
$path = $root_dir . DIRECTORY_SEPARATOR . $path . '.php';
// Test for its existence and load if present
if ( file_exists( $path ) ) {
include $path;
}
}
}
}
/* EOF */
\ No newline at end of file
<?php
namespace GEN_Custom\Setup;
use GEN_Custom\Singleton;
/**
* Class Init
*
* Initialise the plugin
*
* @package GEN_Custom\Setup
*/
class Init {
use Singleton;
protected function init(): void {
add_action( 'gen_theme_functions_php', [ $this, '_functions_php' ] );
add_action( 'wp_enqueue_scripts', [ $this, '_enqueue_scripts' ], 9999 );
Template_Loader::get_instance_init();
}
public function _functions_php(): void {
require_once GEN_CUSTOM_DIR . '/functions.php';
}
/**
* Enqueue custom styles and scripts if the files exist
*/
public function _enqueue_scripts(): void {
if( is_readable( GEN_CUSTOM_DIR . '/style.css' ) ) {
wp_enqueue_script( 'gen-custom-css', GEN_CUSTOM_DIR . '/style.css', [], GEN_CUSTOM_VERSION );
}
if( is_readable( GEN_CUSTOM_DIR . '/scripts.js' ) ) {
wp_enqueue_script( 'gen-custom-js', GEN_CUSTOM_DIR . '/scripts.js', ['jquery'], GEN_CUSTOM_VERSION, 'in_footer' );
}
}
}
/* EOF */
<?php
namespace GEN_Custom\Setup;
use GEN_Custom\Singleton;
use WP_Theme;
/**
* Class Template_Loader
*
* Allows templates in plugin /templates/ directory to override standard templates
* from WordPress hierarchy.
*
* @package GEN_Custom\Setup;
*/
class Template_Loader {
use Singleton;
/**
* @var array Post templates defined in plugin
*/
private $post_templates;
protected function init(): void {
//filter WP template selection
add_filter( '404_template', [ $this, '_template_loader' ], 99, 3 );
add_filter( 'search_template', [ $this, '_template_loader' ], 99, 3 );
add_filter( 'frontpage_template', [ $this, '_template_loader' ], 99, 3 );
add_filter( 'home_template', [ $this, '_template_loader' ], 99, 3 );
add_filter( 'taxonomy_template', [ $this, '_template_loader' ], 99, 3 );
add_filter( 'single_template', [ $this, '_template_loader' ], 99, 3 );
add_filter( 'page_template', [ $this, '_template_loader' ], 99, 3 );
add_filter( 'singular_template', [ $this, '_template_loader' ], 99, 3 );
add_filter( 'author_template', [ $this, '_template_loader' ], 99, 3 );
add_filter( 'archive_template', [ $this, '_template_loader' ], 99, 3 );
//override WC templates
add_filter( 'template_include', [$this, '_wc_template_override'], 99 );
add_filter ('theme_templates', [$this, '_post_templates'], 10, 4 );
}
/**
* Locate template file from our plugin templates if it exists.
* Otherwise will fall back to default WordPress template hierarchy.
*
* @param string $template
* @param string $type
* @param array $template_names
*
* @return string
*/
public function _template_loader( $template, $type, $template_names ): string {
foreach ( (array) $template_names as $template_name ) {
if ( ! $template_name ) {
continue;
}
//if it was a page template stored in theme 'templates' subdirectory
//adjust path name - we store page templates in the post-templates subdirectory
if( 0 === strpos( $template_name, 'templates/' ) ) {
$template_name = str_replace( 'templates/', 'post-templates/', $template_name );
}
$possible_files = [
$template_name,
$type . '/' . $template_name
];
foreach( $possible_files as $possible_file ) {
$possible_file_path = GEN_CUSTOM_TEMPLATE_DIR . '/' . $possible_file;
if ( file_exists( $possible_file_path ) ) {
$template = $possible_file_path;
break 2;
}
}
}
return $template;
}
/**
* Override WC templates
*
* @param $template
*
* @return string
*/
public function _wc_template_override( $template): string {
if( FALSE !== strpos( $template, 'woocommerce/templates' ) ) {
$parts = explode( DIRECTORY_SEPARATOR, $template );
$file = GEN_CUSTOM_TEMPLATE_DIR . '/woocommerce/' . end( $parts );
if( file_exists( $file ) ) {
$template = $file;
}
}
return $template;
}
public function _post_templates( $post_templates, $wp_theme, $post, $post_type ): array {
$plugin_templates = $this->get_post_templates( $wp_theme );
$add_templates = $plugin_templates[ $post_type ] ?? [];
$post_templates = array_merge( $post_templates, $add_templates );
return $post_templates;
}
/**
* Returns the post templates.
*
* Based on @see WP_Theme::get_post_templates()
*
* @param WP_Theme $wp_theme
*
* @return array Array of page templates, keyed by filename and post type,
* with the value of the translated header name.
*/
private function get_post_templates( $wp_theme ): array {
if( null === $this->post_templates ) {
$post_templates = wp_cache_get( 'post_templates-GEN_Custom', 'gen-custom' );
$templates_path = GEN_CUSTOM_DIR . '/templates/post-templates/';
if ( ! is_array( $post_templates ) ) {
$post_templates = [];
$files = glob( $templates_path . '*.php' );
$keys = array_map( static function ( $fullpath ) use ( $templates_path ) {
//prefix all our post-templates with templates/ - compatible with old theme location
return str_replace( $templates_path, 'templates/', $fullpath );
},
$files );
$files = array_combine( $keys, $files );
foreach ( $files as $file => $full_path ) {
if ( ! preg_match( '|Template Name:(.*)$|mi', file_get_contents( $full_path ), $header ) ) {
continue;
}
$types = [ 'page' ];
if ( preg_match( '|Template Post Type:(.*)$|mi', file_get_contents( $full_path ), $type ) ) {
$types = explode( ',', _cleanup_header_comment( $type[ 1 ] ) );
}
foreach ( $types as $type ) {
$type = sanitize_key( $type );
if ( ! isset( $post_templates[ $type ] ) ) {
$post_templates[ $type ] = [];
}
$post_templates[ $type ][ $file ] = _cleanup_header_comment( $header[ 1 ] );
}
}
wp_cache_add( 'post_templates-GEN_Custom', $post_templates, 'gen-custom', DAY_IN_SECONDS );
}
$this->post_templates = $post_templates;
} else {
$post_templates = $this->post_templates;
}
return $post_templates;
}
}
/* EOF */
\ No newline at end of file
<?php
namespace GEN_Custom;
/**
* Trait Singleton
*
* @package GEN_Custom;
*/
trait Singleton {
/**
* Store class instance
*
* @var static
*/
protected static $instance;
public static function get_instance() {
NULL === static::$instance and static::$instance = new static;
return static::$instance;
}
/**
* Get Singleton instance also running init() method if we are instantiating
*
* @return Singleton
*/
public static function get_instance_init(): self {
if( NULL === static::$instance ) {
static::$instance = new static;
static::$instance->init();
}
return static::$instance;
}
/**
* Setup run when class instantiated with get_instance_init
*/
protected function init(): void {
//ensure that parent class methods are invoked if child class does not have init() method
if( is_callable( [$this, 'init'] ) ) {
parent::init();
}
}
/**
* Don't let $instance be cloned or deserialised
*/
final public function __wakeup() {}
final private function __clone() {}
}
/* EOF */
\ No newline at end of file
<?php
namespace GEN_Custom\Template;
class Not_Found_404 {
public function __construct() {
$this->hooks();
genesis();
}
/**
* Replace main loop with 404 loop
*/
private function hooks(): void {
remove_action( 'genesis_loop', 'genesis_do_loop' );
add_action( 'genesis_loop', [ $this, '_do_404' ] );
}
/**
* This function outputs a 404 "Not Found" error message.
*
* Based on Genesis 404 template
* @see genesis_404()
*
* @action genesis_loop
*
*/
public function _do_404(): void {
genesis_markup( array(
'open' => '<article class="entry">',
'context' => 'entry-404',
) );
printf( '<h1 class="entry-title">%s</h1>',
apply_filters( 'genesis_404_entry_title', __( 'Not found, error 404', 'genesis' ) ) );
echo '<div class="entry-content">';
$this->do_noposts();
echo '</div>';
genesis_markup( array(
'close' => '</article>',
'context' => 'entry-404',
) );
}
/**
* Show message when no posts found
*/
private function do_noposts(): void {
$msg = apply_filters(
'genesis_404_entry_content',
__( 'The page you are looking for no longer exists.', 'gen-custom' )
);
echo '<div class="entry">';
printf( '<p>%s</p>', $msg );
printf( '<p>%s</p>',
apply_filters(
'genesis_noposts_search',
__( 'Try searching the site for what you are looking for.', GEN_CUSTOM_TEXT_DOMAIN )
)
);
echo '</div>';
}