latest changes

This commit is contained in:
2025-06-17 08:01:10 -07:00
parent edcad561a5
commit 4c48d7ccbd
50 changed files with 1735 additions and 195 deletions

View File

@@ -1,37 +0,0 @@
#!/usr/bin/env php
<?php
$autoloadPaths = [
__DIR__ . '/../../../autoload.php', // typical path when symlinked in vendor/bin
__DIR__ . '/../vendor/autoload.php', // fallback if run directly from source
];
$found = false;
foreach ($autoloadPaths as $path) {
if (file_exists($path)) {
require $path;
$found = true;
break;
}
}
if (!$found) {
fwrite(STDERR, "Could not locate Composer autoloader.\n");
exit(1);
}
use ofc\IconGetter;
if ($argc !== 2) {
fwrite(STDERR, "Usage: getIcon <icon-name>\n");
exit(1);
}
$icon = trim(strtolower($argv[1]));
try {
IconGetter::get($icon);
} catch (Exception $e) {
fwrite(STDERR, "Error: ".$e->getMessage().PHP_EOL);
exit(1);
}

View File

@@ -0,0 +1,49 @@
#!/usr/bin/env php
<?php
$autoloadPaths = [
__DIR__ . '/../../../autoload.php', // typical path when symlinked in vendor/bin
__DIR__ . '/../vendor/autoload.php', // fallback if run directly from source
];
$found = false;
foreach ($autoloadPaths as $path) {
if (file_exists($path)) {
require $path;
$found = true;
break;
}
}
if (!$found) {
fwrite(STDERR, "Could not locate Composer autoloader.\n");
exit(1);
}
if ($argc < 2) {
fwrite(STDERR, "Usage: rad <command> [options]\n");
exit(1);
}
$command = strtolower(trim($argv[1]));
$arguments = array_slice($argv, 2);
$availableCommands = [
"get-icon" => \ofc\Commands\GetIconCommand::class,
// TODO: additional commands go below
// "swap-library" => \ofc\Commands\LibrarySwap::class,
];
if (!isset($availableCommands[$command])) {
fwrite(STDERR, "Error: Unknown command '$command'\n");
exit(1);
}
// If the user requested help
if (in_array('--help', $arguments)) {
echo $availableCommands[$command]::getHelp().PHP_EOL.PHP_EOL;
exit(0);
}
$availableCommands[$command]::run($arguments);

View File

@@ -2,7 +2,7 @@
"name": "open-function-computers-llc/rad-theme-engine",
"description": "A suite of classes to make WordPress theme development cleaner",
"require": {
"php": ">=7.4",
"php": ">=8.1",
"jjgrainger/posttypes": "^2.1",
"salesforce/handlebars-php": "^2.3"
},
@@ -25,6 +25,6 @@
"post-create-project-cmd": "ofc\\RadThemeEngine::setup"
},
"bin": [
"bin/getIcon"
"bin/rad"
]
}

View File

@@ -0,0 +1,35 @@
<?php
namespace ofc\Commands;
use ofc\IconGetter;
class GetIconCommand
{
public static function run(array $args): void
{
if (count($args) !== 1) {
fwrite(STDERR, "Usage: rad get-icon <icon-name>\n");
var_dump($args);
exit(1);
}
$icon = trim(strtolower($args[0]));
IconGetter::get($icon);
}
public static function getHelp(): string
{
return <<<HELP
Usage: rad get-icon <icon-name>
Description:
Download the SVG icon from https://icons.getbootstrap.com and place it in your theme assets folder.
TODO: link to the docs page.
Example:
rad get-icon phone
HELP;
}
}

View File

@@ -85,6 +85,39 @@ class RadThemeEngine
};
}
public static function pagination()
{
return function ($template, $context, $args, $source) {
return site()->renderTemplate(<<<HTML
<nav class="pagination-links">
<ul>
{{#if older }}
<li><a href="{{ older }}">Next</a></li>
{{/if}}
{{#if newer }}
<li><a href="{{ newer }}">Previous</a></li>
{{/if}}
</ul>
</nav>
HTML, site()->getPaginationLinks());
};
}
public static function count()
{
return function ($template, $context, $args, $source) {
return count($context->get($args));
};
}
public static function queryCount()
{
global $wp_query;
return function ($template, $context, $args, $source) use ($wp_query) {
return $wp_query->found_posts;
};
}
public static function assetURL()
{
return function ($template, $context, $args, $source) {

View File

@@ -2,6 +2,7 @@
namespace ofc;
use Exception;
use PostTypes\PostType;
use Handlebars\Handlebars;
use Handlebars\Loader\FilesystemLoader;
@@ -77,7 +78,8 @@ class Site
$this->registerCPTs();
// register custom hook callbacks
$this->processHooks();
$this->processActions();
$this->processFilters(); // TODO
// add custom shortcodes
$this->registerShortcodes();
@@ -432,6 +434,10 @@ class Site
define('DISALLOW_FILE_EDIT', true);
continue;
}
if ($key === "revisions") {
add_filter('wp_revisions_to_keep', fn ($num, $post) => 0, 10, 2);
continue;
}
if ($key === "gutenberg") {
add_filter('use_block_editor_for_post', '__return_false', 10);
add_action('wp_enqueue_scripts', function () {
@@ -481,6 +487,25 @@ class Site
add_action('init', fn () => add_filter('woocommerce_show_page_title', '__return_false'));
continue;
}
if ($key === "archive_description") {
add_action('init', fn () => remove_action('woocommerce_archive_description', 'woocommerce_taxonomy_archive_description', 10));
continue;
}
if ($key === "reviews") {
add_action('init', function () {
remove_post_type_support('product', 'comments');
});
add_filter('woocommerce_product_reviews_enabled', '__return_false');
add_filter('comments_array', function ($comments, $post_id) {
if (get_post_type($post_id) === 'product') {
return [];
}
return $comments;
}, 10, 2);
continue;
}
continue;
}
@@ -541,6 +566,11 @@ class Site
add_theme_support('menus');
continue;
}
if ($key === "excerpts" || $key === "excerpt") {
add_action('init', fn () => add_post_type_support('page', ['excerpt']));
add_action('init', fn () => add_post_type_support('post', ['excerpt']));
continue;
}
if ($key === "styleselect") {
add_filter('mce_buttons_2', function ($buttons) {
return array_merge(['styleselect'], $buttons);
@@ -556,6 +586,9 @@ class Site
}
if ($key === "woocommerce") {
add_theme_support('woocommerce');
add_theme_support('wc-product-gallery-zoom');
add_theme_support('wc-product-gallery-lightbox');
add_theme_support('wc-product-gallery-slider');
continue;
}
@@ -655,6 +688,10 @@ class Site
"assetURL" => \ofc\RadThemeEngine::assetURL(),
"assetUrl" => \ofc\RadThemeEngine::assetURL(),
"assetContents" => \ofc\RadThemeEngine::assetContents(),
"count" => \ofc\RadThemeEngine::count(),
"length" => \ofc\RadThemeEngine::count(),
"paginationLinks" => \ofc\RadThemeEngine::pagination(),
"queryCount" => \ofc\RadThemeEngine::queryCount(),
];
foreach ($helpers as $name => $callback) {
$this->hb->addHelper($name, $callback);
@@ -700,7 +737,20 @@ class Site
}
return "";
}
return $this->hb->render($fileName, $data);
try {
return $this->hb->render($fileName, $data);
} catch (Exception $e) {
$template = <<<HTML
<div style="padding: 1rem; text-align: center; border: 1px solid #da3636; border-radius: 5px; margin: 1rem;">{{{ message }}}</div>
HTML;
$message = isset($this->config["debug"]) && $this->config["debug"] === true ?
"There was an error. Please see the message below:<br /><strong>" . $e->getMessage() ."</strong>":
"There was an error rendering this page. Please reach out to the site owner or try again later.";
return $this->renderTemplate($template, ["message" => $message]);
}
}
/**
@@ -1012,6 +1062,19 @@ class Site
return $output;
}
public function getQueriedObject($term = null, $fields = [])
{
if (is_null($term)) {
$term = get_queried_object();
}
if ($fields === []) {
return $term;
}
return $this->getFieldsForTerm($fields, $term);
}
public function getTerm($slug, $fields = [])
{
$args = [
@@ -1027,32 +1090,51 @@ class Site
$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;
}
$append = $this->getFieldsForTerm($fields, $term);
$output[] = $append;
}
return $output;
}
private function getFieldsForTerm(array $fields, $term)
{
$output = [];
foreach ($fields as $key) {
if ($key === "id" || $key === "ID" || $key === "term_id") {
$output[$key] = $term->term_id;
continue;
}
if ($key === "title" || $key === "name") {
$output[$key] = $term->name;
continue;
}
if ($key === "url" || $key === "permalink") {
$output[$key] = get_term_link($term);
continue;
}
if ($key === "thumbnail") {
$thumbnailID = get_term_meta($term->term_id, 'thumbnail_id', true);
if (!$thumbnailID) {
$output[$key] = "";
continue;
}
$output[$key] = wp_get_attachment_url($thumbnailID);
continue;
}
if (substr($key, 0, 4) === "acf.") {
$key = str_replace("acf.", "", $key);
$output[$key] = get_field($key, $term->taxonomy . "_" . $term->term_id);
continue;
}
$output[$key] = $term->$key;
}
return $output;
}
private function processArgs(array $args) : array
{
// wordpress default
@@ -1413,19 +1495,51 @@ class Site
}
}
private function processHooks()
private function processActions()
{
if (!isset($this->config["hooks"]) || !is_array($this->config["hooks"])) {
if (!isset($this->config["actions"]) || !is_array($this->config["actions"])) {
return;
}
$defaultPriorities = [
'woocommerce_before_main_content' => 5,
'woocommerce_after_main_content' => 50,
];
foreach ($this->config["hooks"] as $hookName => $callback) {
$priority = $defaultPriorities[$hookName] ?? 99;
add_action($hookName, $callback, $priority);
foreach ($this->config["actions"] as $hookName => $callback) {
if (is_array($callback) && array_is_list($callback)) {
$priority = $callback[1];
$callback = $callback[0];
add_action($hookName, $callback, $priority);
continue;
}
if (is_array($callback) && isset($callback["hook"]) && isset($callback["callback"])) {
add_action($callback["hook"], $callback["callback"], $callback["priority"] ?? 99);
continue;
}
add_action($hookName, $callback, 99);
}
}
/**
* parseArgs
* Used in conjuction with the handlebars helpers to grab all the different args
*
* @param string $args
* @return array
*/
public function parseArgs(string $args): array
{
$parsed = [];
// Match key="value", key='value', or key=value
preg_match_all('/(\w+)=(".*?"|\'.*?\'|\S+)/', $args, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$key = $match[1];
$value = $match[2];
// Strip quotes if present
$value = trim($value, "\"'");
$parsed[$key] = $value;
}
return $parsed;
}
}