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 ""; }); } } } 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 "
View {$fileName}.{$this->fileExtension} does not exist.\nData:\n"
                    .print_r($data, true)
                    ."
"; } 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 "".PHP_EOL; }); } if (file_exists(get_template_directory() . "/assets/favicon-16x16.png")) { add_action('wp_head', function () { echo "".PHP_EOL; }); } if (file_exists(get_template_directory() . "/assets/favicon-32x32.png")) { add_action('wp_head', function () { echo "".PHP_EOL; }); } if (file_exists(get_template_directory() . "/assets/apple-touch-icon.png")) { add_action('wp_head', function () { echo "".PHP_EOL; }); } if (file_exists(get_template_directory() . "/assets/site.webmanifest")) { add_action('wp_head', function () { echo "".PHP_EOL; }); } } }