1349 lines
44 KiB
PHP
1349 lines
44 KiB
PHP
<?php
|
|
|
|
namespace ofc;
|
|
|
|
use PostTypes\PostType;
|
|
use Handlebars\Handlebars;
|
|
use Handlebars\Loader\FilesystemLoader;
|
|
use PostTypes\Taxonomy;
|
|
use WP_Post;
|
|
|
|
class Site
|
|
{
|
|
private $config;
|
|
private $hb;
|
|
private $partialsDir;
|
|
private $fileExtension;
|
|
private $templateDirectory;
|
|
public $cptSlugs = [];
|
|
|
|
// singleton instance goes here
|
|
private static ?Site $instance = null;
|
|
|
|
public static function getInstance($config = []): Site
|
|
{
|
|
if (self::$instance === null) {
|
|
self::$instance = new self($config);
|
|
self::$instance->bootstrap();
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
private function __construct(array $config)
|
|
{
|
|
if ($config == [] && file_exists(TEMPLATEPATH . "/config.php")) {
|
|
$config = include(TEMPLATEPATH . "/config.php");
|
|
}
|
|
|
|
$this->config = $config;
|
|
}
|
|
|
|
private function bootstrap()
|
|
{
|
|
$this->checkfavicons();
|
|
|
|
// wordpress added this style for us, but we don't want it
|
|
add_action('wp_enqueue_scripts', function () {
|
|
wp_dequeue_style('classic-theme-styles');
|
|
}, 20);
|
|
|
|
if (isset($this->config["excerpt-more-text"])) {
|
|
add_filter('excerpt_more', fn ($more) => $this->config["excerpt-more-text"]);
|
|
}
|
|
|
|
// disable various things
|
|
if (isset($this->config["disable"])) {
|
|
$this->disable($this->config["disable"]);
|
|
}
|
|
|
|
// enable various things, starting with TinyMCE
|
|
if (isset($this->config["tinyMCEAdditions"]) || isset($this->config["tiny-mce-additions"])) {
|
|
$this->addEditorStyles();
|
|
}
|
|
if (isset($this->config["enable"])) {
|
|
$this->enable($this->config["enable"]);
|
|
}
|
|
|
|
// adjust wordpress filters
|
|
$this->processFilters();
|
|
|
|
// create wordpress menu locations
|
|
$this->registerMenuLocations();
|
|
|
|
// initialize handlebars
|
|
$this->setUpHandlebars();
|
|
|
|
// register custom post types
|
|
$this->registerCPTs();
|
|
|
|
// add custom shortcodes
|
|
$this->registerShortcodes();
|
|
|
|
// add mix static assets
|
|
$this->includeManifestFiles();
|
|
|
|
// register ACF options pages
|
|
$this->addOptionsPages();
|
|
|
|
// register custom fields that aren't through ACF
|
|
$this->processCustomFields();
|
|
|
|
// register any ajax callbacks
|
|
$this->processAdminAJAX();
|
|
$this->processGuestAJAX();
|
|
}
|
|
|
|
public function getFlexFilePrefix(): string
|
|
{
|
|
if (!isset($this->config["flex-file-prefix"])) {
|
|
return "";
|
|
}
|
|
return $this->config["flex-file-prefix"]."_";
|
|
}
|
|
|
|
private function processGuestAJAX()
|
|
{
|
|
if (!isset($this->config["guest-ajax"])) {
|
|
return;
|
|
}
|
|
if (!is_array($this->config["guest-ajax"])) {
|
|
return;
|
|
}
|
|
foreach ($this->config["guest-ajax"] as $hook => $callback) {
|
|
// guest ajax methos work both for guests and authed users
|
|
add_action("wp_ajax_nopriv_$hook", function () use ($callback) {
|
|
echo json_encode($callback());
|
|
wp_die();
|
|
});
|
|
add_action("wp_ajax_$hook", function () use ($callback) {
|
|
echo json_encode($callback());
|
|
wp_die();
|
|
});
|
|
}
|
|
}
|
|
|
|
private function processAdminAJAX()
|
|
{
|
|
if (!isset($this->config["admin-ajax"])) {
|
|
return;
|
|
}
|
|
if (!is_array($this->config["admin-ajax"])) {
|
|
return;
|
|
}
|
|
foreach ($this->config["admin-ajax"] as $hook => $callback) {
|
|
add_action("wp_ajax_$hook", function () use ($callback) {
|
|
echo json_encode($callback());
|
|
wp_die();
|
|
});
|
|
}
|
|
}
|
|
|
|
private function addEditorStyles()
|
|
{
|
|
$formats = [];
|
|
|
|
if (isset($this->config["tinyMCEAdditions"])) {
|
|
$formats = $this->config["tinyMCEAdditions"];
|
|
}
|
|
|
|
if (isset($this->config["tiny-mce-additions"])) {
|
|
$formats = $this->config["tiny-mce-additions"];
|
|
}
|
|
|
|
$defaults = [
|
|
[
|
|
'title' => 'Headings',
|
|
'items' => [
|
|
[
|
|
'title' => 'Heading 1',
|
|
'format' => 'h1',
|
|
],
|
|
[
|
|
'title' => 'Heading 2',
|
|
'format' => 'h2',
|
|
],
|
|
[
|
|
'title' => 'Heading 3',
|
|
'format' => 'h3',
|
|
],
|
|
[
|
|
'title' => 'Heading 4',
|
|
'format' => 'h4',
|
|
],
|
|
[
|
|
'title' => 'Heading 5',
|
|
'format' => 'h5',
|
|
],
|
|
[
|
|
'title' => 'Heading 6',
|
|
'format' => 'h6',
|
|
],
|
|
],
|
|
],
|
|
[
|
|
'title' => 'Inline',
|
|
'items' => [
|
|
[
|
|
'title' => 'Bold',
|
|
'format' => 'bold',
|
|
'icon' => 'bold',
|
|
],
|
|
[
|
|
'title' => 'Italic',
|
|
'format' => 'italic',
|
|
'icon' => 'italic',
|
|
],
|
|
[
|
|
'title' => 'Underline',
|
|
'format' => 'underline',
|
|
'icon' => 'underline',
|
|
],
|
|
[
|
|
'title' => 'Strikethrough',
|
|
'format' => 'strikethrough',
|
|
'icon' => 'strikethrough',
|
|
],
|
|
[
|
|
'title' => 'Superscript',
|
|
'format' => 'superscript',
|
|
'icon' => 'superscript',
|
|
],
|
|
[
|
|
'title' => 'Subscript',
|
|
'format' => 'subscript',
|
|
'icon' => 'subscript',
|
|
],
|
|
[
|
|
'title' => 'Code',
|
|
'format' => 'code',
|
|
'icon' => 'code',
|
|
],
|
|
],
|
|
],
|
|
[
|
|
'title' => 'Blocks',
|
|
'items' => [
|
|
[
|
|
'title' => 'Paragraph',
|
|
'format' => 'p',
|
|
],
|
|
[
|
|
'title' => 'Blockquote',
|
|
'format' => 'blockquote',
|
|
],
|
|
[
|
|
'title' => 'Div',
|
|
'format' => 'div',
|
|
],
|
|
[
|
|
'title' => 'Pre',
|
|
'format' => 'pre',
|
|
],
|
|
],
|
|
],
|
|
[
|
|
'title' => 'Alignment',
|
|
'items' => [
|
|
[
|
|
'title' => 'Left',
|
|
'format' => 'alignleft',
|
|
'icon' => 'alignleft',
|
|
],
|
|
[
|
|
'title' => 'Center',
|
|
'format' => 'aligncenter',
|
|
'icon' => 'aligncenter',
|
|
],
|
|
[
|
|
'title' => 'Right',
|
|
'format' => 'alignright',
|
|
'icon' => 'alignright',
|
|
],
|
|
[
|
|
'title' => 'Justify',
|
|
'format' => 'alignjustify',
|
|
'icon' => 'alignjustify',
|
|
],
|
|
],
|
|
],
|
|
];
|
|
|
|
add_filter('tiny_mce_before_init', function ($settings) use ($formats, $defaults) {
|
|
$settings['style_formats'] = json_encode(array_merge($defaults, [
|
|
[
|
|
"title" => "Custom",
|
|
"items" => $formats
|
|
]
|
|
]));
|
|
return $settings;
|
|
});
|
|
}
|
|
|
|
private function includeManifestFiles()
|
|
{
|
|
$manifestFile = get_template_directory() . "/dist/mix-manifest.json";
|
|
if (!file_exists($manifestFile)) {
|
|
return;
|
|
}
|
|
|
|
$items = json_decode(file_get_contents($manifestFile), true);
|
|
foreach ($items as $file => $version) {
|
|
$extension = end(explode(".", $file));
|
|
$tag = $this->stringify($file);
|
|
if ($extension === "js") {
|
|
add_action('wp_enqueue_scripts', function () use ($version, $tag) {
|
|
wp_register_script($tag, get_template_directory_uri() . '/dist' . $version, [], '', true);
|
|
wp_enqueue_script($tag);
|
|
});
|
|
continue;
|
|
}
|
|
|
|
if ($extension === "css" && $file != "/inline.css") {
|
|
add_action('wp_enqueue_scripts', function () use ($version, $tag) {
|
|
wp_enqueue_style($tag, get_template_directory_uri() . '/dist' . $version);
|
|
});
|
|
continue;
|
|
}
|
|
|
|
if ($file == "/inline.css") {
|
|
add_action('wp_head', function () use ($file) {
|
|
$css = file_get_contents(get_template_directory() . "/dist" . $file);
|
|
echo "<style>".$css."</style>";
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
private function registerShortcodes()
|
|
{
|
|
if (!array_key_exists('shortcodes', $this->config)) {
|
|
return;
|
|
}
|
|
|
|
if (!is_array($this->config["shortcodes"])) {
|
|
return;
|
|
}
|
|
|
|
foreach ($this->config["shortcodes"] as $name => $callable) {
|
|
add_shortcode($name, $callable);
|
|
}
|
|
}
|
|
|
|
private function registerCPTs()
|
|
{
|
|
if (!array_key_exists('custom-post-types', $this->config)) {
|
|
return;
|
|
}
|
|
|
|
$cptTaxonomies = [];
|
|
foreach ($this->config['custom-post-types'] as $cpt) {
|
|
if (!isset($cpt['slug'])) {
|
|
$this->adminError("To register a new custom post type, you must set the `slug` key.");
|
|
continue;
|
|
}
|
|
|
|
// for convenience access
|
|
$this->cptSlugs[] = $cpt["slug"];
|
|
|
|
$options = $cpt['options'] ?? [];
|
|
if (isset($cpt["archive"]) && $cpt["archive"]) {
|
|
$options["has_archive"] = Util::slugify($this->humanize($cpt["slug"], true));
|
|
}
|
|
$names = $this->generateLabels($cpt['slug']);
|
|
|
|
$newCpt = new PostType($cpt['slug'], $options, $names);
|
|
|
|
if (isset($cpt['icon'])) {
|
|
$newCpt->icon($cpt['icon']);
|
|
}
|
|
|
|
if (isset($cpt["taxonomies"])) {
|
|
foreach ($cpt["taxonomies"] as $customTaxonomy) {
|
|
$cptTaxonomies[] = $customTaxonomy;
|
|
$newCpt->taxonomy($customTaxonomy);
|
|
}
|
|
}
|
|
$newCpt->register();
|
|
|
|
// disable post type things here
|
|
if (isset($cpt["disable"]) && is_array($cpt["disable"])) {
|
|
foreach ($cpt["disable"] as $feature) {
|
|
if (strtolower($feature) === "yoast") {
|
|
add_action('add_meta_boxes', fn () => remove_meta_box('wpseo_meta', $cpt['slug'], 'normal'), 100);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// set up any ACF CPT options pages here
|
|
if (isset($cpt["options-pages"])) {
|
|
if (!function_exists("acf_add_options_page")) {
|
|
$this->adminError("You can't register options pages without the Pro version of Advanced Custom Fields.");
|
|
continue;
|
|
}
|
|
foreach ($cpt["options-pages"] as $optionsPage) {
|
|
if (is_string($optionsPage)) {
|
|
acf_add_options_page([
|
|
"page_title" => $optionsPage,
|
|
"menu_title" => $optionsPage,
|
|
"menu_slug" => $this->stringify($optionsPage),
|
|
'capability' => 'edit_posts',
|
|
"position" => 100,
|
|
"parent_slug" => "edit.php?post_type=" . $cpt["slug"]
|
|
]);
|
|
continue;
|
|
}
|
|
|
|
if (is_array($optionsPage) && isset($optionsPage["name"]) && isset($optionsPage["parent_slug"])) {
|
|
acf_add_options_page([
|
|
"page_title" => $optionsPage["name"],
|
|
"menu_title" => $optionsPage["name"],
|
|
"menu_slug" => $this->stringify($optionsPage["name"]),
|
|
'capability' => 'edit_posts',
|
|
"position" => 100,
|
|
"parent_slug" => $optionsPage["parent_slug"],
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (count($cptTaxonomies)) {
|
|
foreach ($cptTaxonomies as $customTaxonomy) {
|
|
$tax = new Taxonomy($customTaxonomy);
|
|
$tax->options([
|
|
'hierarchical' => false,
|
|
]);
|
|
$tax->register();
|
|
}
|
|
}
|
|
}
|
|
|
|
private function disable(array $keys)
|
|
{
|
|
foreach ($keys as $key) {
|
|
if ($key === "editor") {
|
|
define('DISALLOW_FILE_EDIT', true);
|
|
continue;
|
|
}
|
|
if ($key === "gutenberg") {
|
|
add_filter('use_block_editor_for_post', '__return_false', 10);
|
|
add_action('wp_enqueue_scripts', function () {
|
|
wp_dequeue_style('wp-block-library');
|
|
wp_dequeue_style('wp-block-library-theme');
|
|
wp_dequeue_style('wc-blocks-style'); // Remove WooCommerce block CSS
|
|
wp_dequeue_style('global-styles');
|
|
}, 100);
|
|
continue;
|
|
}
|
|
if ($key === "patterns") {
|
|
add_action('after_setup_theme', function () {
|
|
remove_theme_support('core-block-patterns');
|
|
});
|
|
add_action('admin_init', function () {
|
|
remove_submenu_page('themes.php', 'edit.php?post_type=wp_block');
|
|
remove_submenu_page('themes.php', 'site-editor.php?p=/pattern');
|
|
remove_submenu_page('themes.php', 'site-editor.php?p=/patterns');
|
|
});
|
|
continue;
|
|
}
|
|
if ($key === "meta-generator") {
|
|
remove_action('wp_head', 'wp_generator');
|
|
add_filter('the_generator', '__return_null');
|
|
continue;
|
|
}
|
|
if ($key === "emojis") {
|
|
remove_action('wp_head', 'print_emoji_detection_script', 7);
|
|
remove_action('wp_print_styles', 'print_emoji_styles');
|
|
continue;
|
|
}
|
|
if ($key === "customizer") {
|
|
add_action('init', function () {
|
|
add_filter('map_meta_cap', function ($capabilities = [], $c = '', $user_id = 0, $args = []) {
|
|
if ($c == 'customize') {
|
|
return ['nope'];
|
|
}
|
|
return $capabilities;
|
|
}, 10, 4);
|
|
}, 10);
|
|
|
|
add_action('admin_init', function () {
|
|
remove_action('plugins_loaded', '_wp_customize_include', 10);
|
|
remove_action('admin_enqueue_scripts', '_wp_customize_loader_settings', 11);
|
|
|
|
add_action('load-customize.php', function () {
|
|
wp_die("The customizer is disabled via your current theme configuration.");
|
|
});
|
|
}, 10);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
private function addOptionsPages()
|
|
{
|
|
if (!array_key_exists('options-pages', $this->config)) {
|
|
return;
|
|
}
|
|
|
|
if (!function_exists("acf_add_options_page")) {
|
|
$this->adminError("You can't register options pages without the Pro version of Advanced Custom Fields.");
|
|
}
|
|
|
|
$i = 100;
|
|
foreach ($this->config["options-pages"] as $pageTitle) {
|
|
acf_add_options_page([
|
|
"page_title" => $pageTitle,
|
|
"menu_title" => $pageTitle,
|
|
"menu_slug" => $this->stringify($pageTitle),
|
|
'capability' => 'edit_posts',
|
|
"position" => $i,
|
|
]);
|
|
$i++;
|
|
}
|
|
}
|
|
|
|
private function enable(array $keys)
|
|
{
|
|
foreach ($keys as $key) {
|
|
if ($key === "post-thumbnails") {
|
|
add_theme_support('post-thumbnails');
|
|
continue;
|
|
}
|
|
if ($key === "menus") {
|
|
add_theme_support('menus');
|
|
continue;
|
|
}
|
|
if ($key === "styleselect") {
|
|
add_filter('mce_buttons_2', function ($buttons) {
|
|
return array_merge(['styleselect'], $buttons);
|
|
});
|
|
continue;
|
|
}
|
|
if ($key === "svg") {
|
|
add_filter('upload_mimes', function ($file_types) {
|
|
$file_types['svg'] = 'image/svg+xml';
|
|
return $file_types;
|
|
});
|
|
continue;
|
|
}
|
|
|
|
$this->adminError("Couldn't enable feature `$key`");
|
|
}
|
|
}
|
|
|
|
private function processFilters()
|
|
{
|
|
// handle "guest" html body class
|
|
$guestClass = "guest";
|
|
if (array_key_exists("guest-class", $this->config)) {
|
|
$guestClass = $this->config["guest-class"];
|
|
}
|
|
if (!is_null($guestClass)) {
|
|
add_filter('body_class', function ($classes) use ($guestClass) {
|
|
return in_array('logged-in', $classes) ? $classes : [$guestClass, ...$classes];
|
|
});
|
|
}
|
|
|
|
// change excerpt langth
|
|
if (array_key_exists("excerpt-length", $this->config)) {
|
|
add_filter("excerpt-length", fn () => (int) $this->config["excerpt-length"], 999);
|
|
}
|
|
}
|
|
|
|
private function registerMenuLocations()
|
|
{
|
|
if (!array_key_exists("menu-locations", $this->config)) {
|
|
return;
|
|
}
|
|
|
|
if (!is_array($this->config["menu-locations"])) {
|
|
// TODO: show a helpful error message that notifies the developer this is set up wrong
|
|
return;
|
|
}
|
|
|
|
$menus = $this->config["menu-locations"];
|
|
add_action('after_setup_theme', function () use ($menus) {
|
|
register_nav_menus($menus);
|
|
});
|
|
}
|
|
|
|
private function setUpHandlebars()
|
|
{
|
|
if ($this->config["handlebars"] === false) {
|
|
return;
|
|
}
|
|
|
|
$this->fileExtension = "tpl";
|
|
if (is_array($this->config["handlebars"]) &&
|
|
array_key_exists("template-extension", $this->config["handlebars"])
|
|
) {
|
|
$this->fileExtension = $this->config["handlebars"]["template-extension"];
|
|
}
|
|
|
|
$this->templateDirectory = "tpl";
|
|
if (is_array($this->config["handlebars"]) &&
|
|
array_key_exists("template-directory", $this->config["handlebars"])
|
|
) {
|
|
$this->templateDirectory = $this->config["handlebars"]["template-directory"];
|
|
}
|
|
|
|
$this->partialsDir = get_template_directory()."/".$this->templateDirectory;
|
|
if (!file_exists($this->partialsDir) && !is_dir($this->partialsDir)) {
|
|
// check for child theme?
|
|
$this->partialsDir = get_stylesheet_directory()."/".$this->templateDirectory;
|
|
|
|
if (!file_exists($this->partialsDir) && !is_dir($this->partialsDir)) {
|
|
$this->adminError("Your Handlebars directory is not setup correctly or doesn't exist.");
|
|
return;
|
|
}
|
|
$this->adminError("Your Handlebars directory is not setup correctly or doesn't exist.");
|
|
return;
|
|
}
|
|
$partialsLoader = new FilesystemLoader($this->partialsDir, ["extension" => $this->fileExtension]);
|
|
|
|
$this->hb = new Handlebars([
|
|
"loader" => $partialsLoader,
|
|
"partials_loader" => $partialsLoader,
|
|
"enableDataVariables" => true,
|
|
]);
|
|
|
|
// built in helpers
|
|
$helpers = [
|
|
"wp-header" => \ofc\RadThemeEngine::wpHeader(),
|
|
"wp-footer" => \ofc\RadThemeEngine::wpFooter(),
|
|
"wp-title" => \ofc\RadThemeEngine::wpTitle(),
|
|
"body-classes" => \ofc\RadThemeEngine::bodyClasses(),
|
|
"json-encode" => \ofc\RadThemeEngine::jsonEncode(),
|
|
"json-access" => \ofc\RadThemeEngine::jsonAccess(),
|
|
"flex" => \ofc\RadThemeEngine::processFlex(),
|
|
"nl2br" => \ofc\RadThemeEngine::nl2br(),
|
|
"assetURL" => \ofc\RadThemeEngine::assetURL(),
|
|
"assetUrl" => \ofc\RadThemeEngine::assetURL(),
|
|
"assetContents" => \ofc\RadThemeEngine::assetContents(),
|
|
];
|
|
foreach ($helpers as $name => $callback) {
|
|
$this->hb->addHelper($name, $callback);
|
|
}
|
|
|
|
// additional helpers defined in the config file
|
|
if (isset($this->config["handlebars"]["additional-helpers"])) {
|
|
foreach ($this->config["handlebars"]["additional-helpers"] as $name => $callback) {
|
|
$this->hb->addHelper($name, $callback);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* render
|
|
* See $this->view()
|
|
*
|
|
* @param string $fileName
|
|
* @param array $data
|
|
* @return string
|
|
*/
|
|
public function render(string $fileName, array $data = []) : string
|
|
{
|
|
return $this->view($fileName, $data);
|
|
}
|
|
|
|
/**
|
|
* view
|
|
* Invoke handlebar rendering engine for a given template file and data array
|
|
*
|
|
* @param string $fileName
|
|
* @param array $data
|
|
* @return string
|
|
*/
|
|
public function view(string $fileName, array $data = []) : string
|
|
{
|
|
$filePath = $this->partialsDir."/".$fileName.".".$this->fileExtension;
|
|
if (!file_exists($filePath)) {
|
|
if ($this->config["debug"] === true) {
|
|
return "<pre>View {$fileName}.{$this->fileExtension} does not exist.\nData:\n"
|
|
.print_r($data, true)
|
|
."</pre>";
|
|
}
|
|
return "";
|
|
}
|
|
return $this->hb->render($fileName, $data);
|
|
}
|
|
|
|
/**
|
|
* view
|
|
* Invoke handlebar rendering engine for a given template file and data array
|
|
*
|
|
* @param string $fileName
|
|
* @param array $data
|
|
* @return string
|
|
*/
|
|
public function renderTemplate(string $template, array $data = []) : string
|
|
{
|
|
$hb = new Handlebars();
|
|
$hb->addHelper("json-encode", \ofc\RadThemeEngine::jsonEncode());
|
|
return $hb->render($template, $data);
|
|
}
|
|
|
|
public function getCurrentPost($fields = [])
|
|
{
|
|
global $post;
|
|
return $this->getPost($post, $fields);
|
|
}
|
|
|
|
public function getPost($idOrPost, $fields = [])
|
|
{
|
|
if (is_numeric($idOrPost)) {
|
|
$p = get_post($idOrPost);
|
|
} elseif ($idOrPost instanceof WP_Post) {
|
|
$p = $idOrPost;
|
|
} else {
|
|
return [
|
|
"error" => "You must pass an ID or WP_Post to this method"
|
|
];
|
|
}
|
|
|
|
if ($fields == []) {
|
|
return [
|
|
'post' => $p,
|
|
'meta' => get_post_meta($p->ID),
|
|
];
|
|
}
|
|
|
|
$output = [];
|
|
$categories = [];
|
|
$taxonomies = [];
|
|
|
|
foreach ($fields as $key) {
|
|
// handle url/permalink
|
|
if ($key === "url" || $key === "permalink") {
|
|
$output[$key] = get_permalink($p->ID);
|
|
continue;
|
|
}
|
|
|
|
// handle post content
|
|
if ($key === "content") {
|
|
$output[$key] = apply_filters('the_content', get_the_content(null, false, $p->ID));
|
|
continue;
|
|
}
|
|
|
|
// handle post excerpt
|
|
if ($key === "excerpt") {
|
|
$output[$key] = apply_filters("the_content", get_the_excerpt($p->ID));
|
|
continue;
|
|
}
|
|
|
|
// handle adjacent posts
|
|
if ($key === "nextPost") {
|
|
$output[$key] = get_next_post();
|
|
continue;
|
|
}
|
|
if ($key === "previousPost") {
|
|
$output[$key] = get_previous_post();
|
|
continue;
|
|
}
|
|
|
|
// handle adjacent post attributes
|
|
if (substr($key, 0, 9) === "nextPost.") {
|
|
$fields = explode(",", substr($key, 9));
|
|
$output[substr($key, 0, 8)] = $this->getPost(get_next_post(), $fields);
|
|
continue;
|
|
}
|
|
if (substr($key, 0, 13) === "previousPost.") {
|
|
$fields = explode(",", substr($key, 13));
|
|
$output[substr($key, 0, 12)] = $this->getPost(get_previous_post(), $fields);
|
|
continue;
|
|
}
|
|
|
|
if (substr($key, 0, 7) === "parent.") {
|
|
if ($p->post_parent == 0) {
|
|
$output[substr($key, 0, 6)] = [];
|
|
continue;
|
|
}
|
|
$fields = explode(",", substr($key, 7));
|
|
$output[substr($key, 0, 6)] = $this->getPost($p->post_parent, $fields);
|
|
continue;
|
|
}
|
|
|
|
// handle meta keys
|
|
if (substr($key, 0, 5) === "meta.") {
|
|
$output[substr($key, 5)] = get_post_meta($p->ID, substr($key, 5), true);
|
|
continue;
|
|
}
|
|
|
|
// handle better wordpress fields
|
|
if (substr($key, 0, 4) === "rad.") {
|
|
if (substr($key, -5) === "-JSON") {
|
|
$key = substr($key, 0, strlen($key) - 5);
|
|
$output[substr($key, 4)] = json_decode(get_post_meta($p->ID, str_replace("rad.", "rad_", $key), true));
|
|
continue;
|
|
}
|
|
$output[substr($key, 4)] = get_post_meta($p->ID, str_replace("rad.", "rad_", $key), true);
|
|
continue;
|
|
}
|
|
|
|
if (substr($key, 0, 4) === "acf.") {
|
|
$output[substr($key, 4)] = get_field(substr($key, 4), $p->ID);
|
|
continue;
|
|
}
|
|
|
|
if (substr($key, 0, 11) === "categories.") {
|
|
if (count($categories) == 0) {
|
|
$categories = wp_get_post_categories($p->ID, ["fields" => "all"]);
|
|
$attrs = explode(",", substr($key, 11));
|
|
|
|
foreach ($categories as $cat) {
|
|
$dataToAppend = [];
|
|
foreach ($attrs as $key) {
|
|
$oldKey = $key;
|
|
if ($key == "id") {
|
|
$key = "term_id";
|
|
}
|
|
if (isset($cat->$key)) {
|
|
$dataToAppend[$oldKey] = $cat->$key;
|
|
}
|
|
}
|
|
$output["categories"][] = $dataToAppend;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (substr($key, 0, 9) === "taxonomy.") {
|
|
$taxonomyKey = $this->parseTaxKey($key);
|
|
$values = explode(",", $this->parseTaxValues($key));
|
|
if (!isset($taxonomies[$taxonomyKey])) {
|
|
$taxonomies[$taxonomyKey] = $this->getPostTaxonomy($p, $taxonomyKey);
|
|
$output[$taxonomyKey] = [];
|
|
}
|
|
|
|
if (!is_array($taxonomies[$taxonomyKey])) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($taxonomies[$taxonomyKey] as $tax) {
|
|
$taxonomyDTO = [];
|
|
foreach ($values as $val) {
|
|
if ($val === "id" || $val === "term_id") {
|
|
$taxonomyDTO["id"] = $tax->term_id;
|
|
continue;
|
|
}
|
|
|
|
if ($val === "link") {
|
|
$taxonomyDTO["link"] = get_term_link($tax);
|
|
continue;
|
|
}
|
|
|
|
if ($val === "url") {
|
|
$taxonomyDTO["url"] = get_term_link($tax);
|
|
continue;
|
|
}
|
|
|
|
if ($val === "permalink") {
|
|
$taxonomyDTO["permalink"] = get_term_link($tax);
|
|
continue;
|
|
}
|
|
|
|
if ($val === "name") {
|
|
$taxonomyDTO["name"] = $tax->name;
|
|
continue;
|
|
}
|
|
|
|
if ($val === "title") {
|
|
$taxonomyDTO["title"] = $tax->name;
|
|
continue;
|
|
}
|
|
|
|
if ($val === "slug") {
|
|
$taxonomyDTO["slug"] = $tax->slug;
|
|
continue;
|
|
}
|
|
|
|
if ($val === "description") {
|
|
$taxonomyDTO["description"] = $tax->description;
|
|
continue;
|
|
}
|
|
}
|
|
$output[$taxonomyKey][] = $taxonomyDTO;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// let's keep our sanity here...
|
|
// the following keys are here for convienance. WP names things so weirdly!
|
|
if ($key === "id") {
|
|
$output[$key] = $p->ID;
|
|
continue;
|
|
}
|
|
if ($key === "title" || $key === "name") {
|
|
$output[$key] = $p->post_title;
|
|
continue;
|
|
}
|
|
if (in_array($key, ["published", "publishedat", "publishedon", "published_at", "published_on"])) {
|
|
$output[$key] = $p->post_date;
|
|
continue;
|
|
}
|
|
if ($key === "thumbnail") {
|
|
$output[$key] = get_the_post_thumbnail_url($p->ID);
|
|
continue;
|
|
}
|
|
|
|
$output[$key] = $p->$key;
|
|
}
|
|
return $output;
|
|
}
|
|
|
|
public function getPosts($args = [], $fields = [])
|
|
{
|
|
$args = $this->processArgs($args);
|
|
$posts = get_posts($args);
|
|
|
|
if ($fields == []) {
|
|
return [
|
|
'posts' => $posts,
|
|
];
|
|
}
|
|
|
|
$output = [];
|
|
foreach ($posts as $p) {
|
|
$output[] = $this->getPost($p, $fields);
|
|
}
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* getCurrentPosts
|
|
* see getDefaultPosts
|
|
*
|
|
* @deprecated 1.0.15
|
|
*
|
|
* @param array $fields
|
|
* @return array
|
|
*/
|
|
public function getCurrentPosts($fields = []) : array
|
|
{
|
|
return $this->getDefaultPosts($fields);
|
|
}
|
|
|
|
/**
|
|
* getDefaultPosts
|
|
* get all the posts for the current page as defined by wordpress' weird rules for archive pages
|
|
*
|
|
* @param array $fields
|
|
* @return array
|
|
*/
|
|
public function getDefaultPosts($fields = []) : array
|
|
{
|
|
$output = [];
|
|
if (!have_posts()) {
|
|
return $output;
|
|
}
|
|
while (have_posts()) {
|
|
the_post();
|
|
global $post;
|
|
$output[] = $this->getPost($post, $fields);
|
|
}
|
|
return $output;
|
|
}
|
|
|
|
public function getTerm($slug, $fields = [])
|
|
{
|
|
$args = [
|
|
'taxonomy' => $slug,
|
|
'hide_empty' => false,
|
|
'suppress_filter' => true,
|
|
];
|
|
$results = get_terms($args);
|
|
|
|
if ($fields == []) {
|
|
return $results;
|
|
}
|
|
|
|
$output = [];
|
|
foreach ($results as $term) {
|
|
$append = [];
|
|
foreach ($fields as $key) {
|
|
$oldKey = $key;
|
|
if ($key == "id") {
|
|
$key = "term_id";
|
|
}
|
|
if ($key == "title") {
|
|
$key = "name";
|
|
}
|
|
|
|
if ($oldKey != $key) {
|
|
$append[$oldKey] = $term->$key;
|
|
continue;
|
|
}
|
|
|
|
if ($key === "url" || $key === "permalink") {
|
|
$append[$key] = get_term_link($term);
|
|
continue;
|
|
}
|
|
$append[$key] = $term->$key;
|
|
}
|
|
$output[] = $append;
|
|
}
|
|
return $output;
|
|
}
|
|
|
|
private function processArgs(array $args) : array
|
|
{
|
|
// wordpress default
|
|
$args = array_merge(
|
|
[
|
|
'numberposts' => 5,
|
|
'category' => 0,
|
|
'orderby' => 'date',
|
|
'order' => 'DESC',
|
|
'include' => array(),
|
|
'exclude' => array(),
|
|
'meta_key' => '',
|
|
'meta_value' => '',
|
|
'post_type' => 'post',
|
|
'suppress_filters' => true,
|
|
],
|
|
$args
|
|
);
|
|
|
|
if (isset($args["type"])) {
|
|
$args["post_type"] = $args["type"];
|
|
}
|
|
|
|
if (isset($args["title"])) {
|
|
$args["title"] = urldecode($args["title"]);
|
|
}
|
|
|
|
if (isset($args["s"])) {
|
|
$args["s"] = urldecode($args["s"]);
|
|
}
|
|
|
|
// process custom taxonomy
|
|
$taxQuery = [];
|
|
foreach ($args as $paramKey => $paramValue) {
|
|
if (!(substr($paramKey, 0, 4) === "tax." || substr($paramKey, 0, 9) === "taxonomy.")) {
|
|
continue;
|
|
}
|
|
|
|
if (substr($paramKey, 0, 4) === "tax.") {
|
|
$tax = substr($paramKey, 4);
|
|
}
|
|
|
|
if (substr($paramKey, 0, 9) === "taxonomy.") {
|
|
$tax = substr($paramKey, 9);
|
|
}
|
|
|
|
$field = "slug";
|
|
$values = explode(",", $paramValue);
|
|
$allValuesAreNumeric = false;
|
|
foreach ($values as $v) {
|
|
if (is_numeric($v)) {
|
|
$allValuesAreNumeric = true;
|
|
}
|
|
}
|
|
|
|
if ($allValuesAreNumeric) {
|
|
$field = "term_id";
|
|
}
|
|
|
|
$taxQuery[] = [
|
|
"taxonomy" => $tax,
|
|
"field" => $field,
|
|
"terms" => $values,
|
|
];
|
|
}
|
|
if ($taxQuery != []) {
|
|
$args['tax_query'] = $taxQuery;
|
|
}
|
|
|
|
return $args;
|
|
}
|
|
|
|
private function parseTaxKey(string $paramater) : string
|
|
{
|
|
$parts = explode(".", $paramater);
|
|
if (count($parts) != 3) {
|
|
return $paramater;
|
|
}
|
|
return $parts[1];
|
|
}
|
|
|
|
private function parseTaxValues(string $paramater) : string
|
|
{
|
|
$parts = explode(".", $paramater);
|
|
if (count($parts) != 3) {
|
|
return $paramater;
|
|
}
|
|
return $parts[2];
|
|
}
|
|
|
|
public function getPostTaxonomy($post, $taxonomyKey)
|
|
{
|
|
return get_the_terms($post->ID, $taxonomyKey);
|
|
}
|
|
|
|
public function setPostMeta($postID, $data)
|
|
{
|
|
foreach ($data as $metaKey => $metaValue) {
|
|
update_post_meta($postID, $metaKey, $metaValue);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* add an error to the wordpress backend
|
|
*
|
|
* @param string $message
|
|
*/
|
|
private function adminError(string $message)
|
|
{
|
|
$key = $this->stringify($message);
|
|
add_action('admin_notices', function ($messages) use ($message, $key) {
|
|
add_settings_error($key, '', "OFC Site Error: $message", 'error');
|
|
settings_errors($key);
|
|
return $messages;
|
|
});
|
|
}
|
|
|
|
public function renderMenu($menuLocation)
|
|
{
|
|
return wp_nav_menu([
|
|
"theme_location" => $menuLocation,
|
|
"echo" => false,
|
|
]);
|
|
}
|
|
|
|
public function menuArray($menuLocation)
|
|
{
|
|
$locations = get_nav_menu_locations();
|
|
$output = [];
|
|
if (!isset($locations[$menuLocation])) {
|
|
return $output;
|
|
}
|
|
|
|
foreach (wp_get_nav_menu_items($locations[$menuLocation]) as $menuItem) {
|
|
if ($menuItem->menu_item_parent === "0") {
|
|
$output[$menuItem->ID] = [
|
|
"id" => $menuItem->ID,
|
|
"title" => $menuItem->title,
|
|
"url" => $menuItem->url,
|
|
"hasChildren" => false,
|
|
"children" => [],
|
|
];
|
|
continue;
|
|
}
|
|
$output = $this->addMenuItemToChildren($output, $menuItem);
|
|
}
|
|
return $output;
|
|
}
|
|
|
|
private function addMenuItemToChildren(array $items, $childItem)
|
|
{
|
|
foreach ($items as $id => $menuItem) {
|
|
if ($id == $childItem->menu_item_parent) {
|
|
$items[$id]["hasChildren"] = true;
|
|
$items[$id]["children"][$childItem->ID] = [
|
|
"id" => $childItem->ID,
|
|
"title" => $childItem->title,
|
|
"url" => $childItem->url,
|
|
"hasChildren" => false,
|
|
"children" => [],
|
|
];
|
|
return $items;
|
|
}
|
|
}
|
|
|
|
foreach ($items as $id => $parent) {
|
|
foreach ($parent["children"] as $childID => $menuItem) {
|
|
$items[$id]["children"] = $this->addMenuItemToChildren($parent["children"], $childItem);
|
|
}
|
|
}
|
|
return $items;
|
|
}
|
|
|
|
private function stringify(string $thing) : string
|
|
{
|
|
$bad = [" ", "/"];
|
|
$good = ["_", ""];
|
|
return strtolower(str_replace($bad, $good, $thing));
|
|
}
|
|
|
|
public function getAssetURL(string $filename) : string
|
|
{
|
|
if (!file_exists(get_template_directory()."/assets/$filename")) {
|
|
$this->adminError("Requested asset $filename doesn't exist.");
|
|
return "";
|
|
}
|
|
|
|
return get_template_directory_uri()."/assets/$filename";
|
|
}
|
|
|
|
public function getAssetContents(string $filename) : string
|
|
{
|
|
if (!file_exists(get_template_directory()."/assets/$filename")) {
|
|
return "Asset doesn't exist: ".get_template_directory()."/assets/$filename";
|
|
}
|
|
|
|
return file_get_contents(get_template_directory()."/assets/$filename");
|
|
}
|
|
|
|
private function generateLabels(string|array $slug) : array
|
|
{
|
|
if (is_array($slug)) {
|
|
return $slug;
|
|
}
|
|
|
|
return [
|
|
'name' => $this->humanize($slug, true),
|
|
'singular_name' => $this->humanize($slug, false),
|
|
'menu_name' => $this->humanize($slug, true),
|
|
'all_items' => "All " . $this->humanize($slug, true),
|
|
'add_new' => "Add New {$this->humanize($slug, false)}",
|
|
'add_new_item' => "Add New {$this->humanize($slug, false)}",
|
|
'edit_item' => "Edit {$this->humanize($slug, false)}",
|
|
'new_item' => "New {$this->humanize($slug, false)}",
|
|
'view_item' => "View {$this->humanize($slug, false)}",
|
|
'search_items' => "Search {$this->humanize($slug, true)}",
|
|
'not_found' => "No {$this->humanize($slug, true)} found",
|
|
'not_found_in_trash' => "No {$this->humanize($slug, true)} found in Trash",
|
|
'parent_item_colon' => "Parent {$this->humanize($slug, false)}:",
|
|
];
|
|
}
|
|
|
|
private function humanize(string $word, bool $makePlural = false) : string
|
|
{
|
|
$humanized = ucwords(strtolower(str_replace(['-', '_'], ' ', $word)));
|
|
|
|
if (!$makePlural) {
|
|
return $humanized;
|
|
}
|
|
|
|
if (strtolower(substr($humanized, strlen($humanized)-1, 1)) == "s") {
|
|
return $humanized . "es";
|
|
}
|
|
if (strtolower(substr($humanized, strlen($humanized)-1, 1)) == "y") {
|
|
return substr($humanized, 0, strlen($humanized)-1) . "ies";
|
|
}
|
|
return $humanized . "s";
|
|
}
|
|
|
|
public function getPaginationLinks() : array
|
|
{
|
|
global $wp_query;
|
|
|
|
$output = [
|
|
"older" => false,
|
|
"newer" => false,
|
|
"totalPages" => 0,
|
|
"currentPage" => 0,
|
|
];
|
|
|
|
if (is_singular()) {
|
|
return $output;
|
|
}
|
|
|
|
$max_num_pages = (int) $wp_query->max_num_pages;
|
|
$paged = get_query_var('paged');
|
|
if (($paged + 1) < $max_num_pages) {
|
|
$output["older"] = next_posts(0, false);
|
|
}
|
|
|
|
if ($max_num_pages > 1 && $paged <= $max_num_pages && $paged > 0) {
|
|
$output["newer"] = previous_posts(false);
|
|
}
|
|
|
|
$output["totalPages"] = $max_num_pages;
|
|
$output["currentPage"] = $paged;
|
|
|
|
return $output;
|
|
}
|
|
|
|
private function processCustomFields()
|
|
{
|
|
$templateFields = [];
|
|
$tplFileContents;
|
|
|
|
$theme = wp_get_theme();
|
|
$templates = $theme->get_page_templates();
|
|
foreach ($templates as $filename => $templateName) {
|
|
$tplFileContents = file_get_contents(get_theme_file_path($filename));
|
|
$fields = explode("\$fields", $tplFileContents);
|
|
if (count($fields) < 2) {
|
|
continue;
|
|
}
|
|
$fields = explode(";", $fields[1]);
|
|
|
|
$fieldArray = eval("use ofc\RadField; return " . preg_replace('/=/', "", $fields[0], 1) . ";");
|
|
$templateFields = [...$templateFields, ...$fieldArray];
|
|
|
|
//add template name to first element of name so we can grab it in Util.php
|
|
//it will be removed when processing each field
|
|
array_unshift($templateFields, $templateName);
|
|
}
|
|
|
|
|
|
// some default admin ajax hooks for field processing
|
|
$existingCPTSlugs = $this->cptSlugs;
|
|
add_action('wp_ajax_rad_theme_engine_related', function () use ($existingCPTSlugs) {
|
|
$args = [
|
|
"type" => array_merge(["page", "post"], $existingCPTSlugs),
|
|
"limit" => 5,
|
|
"order" => "asc",
|
|
"orderby" => "title",
|
|
"s" => $_POST["q"] ?? "",
|
|
];
|
|
|
|
$results = site()->getPosts($args, ["title", "id", "url"]);
|
|
if (count($results) < 1) {
|
|
$results = [
|
|
[
|
|
"title" => "No results",
|
|
"id" => 0,
|
|
"url" => "#"
|
|
]
|
|
];
|
|
}
|
|
|
|
echo json_encode($results);
|
|
wp_die();
|
|
});
|
|
|
|
Util::processFieldGroup($templateFields);
|
|
}
|
|
|
|
private function checkfavicons()
|
|
{
|
|
if (!is_dir(get_template_directory() . "/assets")) {
|
|
return;
|
|
}
|
|
|
|
if (file_exists(get_template_directory() . "/assets/favicon.ico")) {
|
|
add_action('wp_head', function () {
|
|
echo "<link rel='icon' type='image/x-icon' href='".get_template_directory_uri()."/assets/favicon.ico'>".PHP_EOL;
|
|
});
|
|
}
|
|
|
|
if (file_exists(get_template_directory() . "/assets/favicon-16x16.png")) {
|
|
add_action('wp_head', function () {
|
|
echo "<link rel='icon' type='image/png' href='".get_template_directory_uri()."/assets/favicon-16x16.png'>".PHP_EOL;
|
|
});
|
|
}
|
|
|
|
if (file_exists(get_template_directory() . "/assets/favicon-32x32.png")) {
|
|
add_action('wp_head', function () {
|
|
echo "<link rel='icon' type='image/png' href='".get_template_directory_uri()."/assets/favicon-32x32.png'>".PHP_EOL;
|
|
});
|
|
}
|
|
|
|
if (file_exists(get_template_directory() . "/assets/apple-touch-icon.png")) {
|
|
add_action('wp_head', function () {
|
|
echo "<link rel='apple-touch-icon' type='image/png' href='".get_template_directory_uri()."/assets/apple-touch-icon.png'>".PHP_EOL;
|
|
});
|
|
}
|
|
|
|
if (file_exists(get_template_directory() . "/assets/site.webmanifest")) {
|
|
add_action('wp_head', function () {
|
|
echo "<link rel='manifest' href='".get_template_directory_uri()."/assets/site.webmanifest'>".PHP_EOL;
|
|
});
|
|
}
|
|
}
|
|
}
|