diff --git a/app/admin.php b/app/admin.php new file mode 100644 index 0000000..74fa2c4 --- /dev/null +++ b/app/admin.php @@ -0,0 +1,15 @@ +get_setting('blogname')->transport = 'postMessage'; +}); + +/** + * Customizer JS + */ +add_action('customize_preview_init', function () { + wp_enqueue_script('sage/customizer', asset_path('scripts/customizer.js'), ['customize-preview'], null, true); +}); diff --git a/app/filters.php b/app/filters.php new file mode 100644 index 0000000..a631da6 --- /dev/null +++ b/app/filters.php @@ -0,0 +1,55 @@ + classes + */ +add_filter('body_class', function (array $classes) { + // Add page slug if it doesn't exist + if (is_single() || is_page() && !is_front_page()) { + if (!in_array(basename(get_permalink()), $classes)) { + $classes[] = basename(get_permalink()); + } + } + + // Add class if sidebar is active + if (display_sidebar()) { + $classes[] = 'sidebar-primary'; + } + + return $classes; +}); + +/** + * Clean up the_excerpt() + */ +add_filter('excerpt_more', function () { + return ' … ' . __('Continued', 'sage') . ''; +}); + + +/** + * Use Wrapper by default + */ +add_filter('template_include', function ($main) { + if (!is_string($main) || !(string) $main) { + return $main; + } + $main = basename($main, '.php'); + return Template::wrap(new Wrapper($main, 'layouts/base.php'))->locate(); +}, 109); diff --git a/app/helpers.php b/app/helpers.php new file mode 100644 index 0000000..395744a --- /dev/null +++ b/app/helpers.php @@ -0,0 +1,77 @@ +locate()) { + /** @noinspection PhpIncludeInspection */ + include $file; + } +} + +/** + * @param array $context + */ +function template_sidebar($context = []) { + template_part('sidebar', $context); +} + +/** + * @param $template + * @param array $context + */ +function template_part($template, $context = []) { + if ($file = (new Template($template, $context))->locate()) { + /** @noinspection PhpIncludeInspection */ + include $file; + } +} + +/** + * @param $filename + * @return string + */ +function asset_path($filename) { + static $manifest; + isset($manifest) || $manifest = new JsonManifest(get_template_directory() . '/' . Asset::$dist . '/assets.json'); + return (string) new Asset($filename, $manifest); +} + +/** + * Determine whether to show the sidebar + * @return bool + */ +function display_sidebar() { + static $display; + isset($display) || $display = apply_filters('sage/display_sidebar', true); + return $display; +} + +/** + * Page titles + * @return string + */ +function title() { + if (is_home()) { + if ($home = get_option('page_for_posts', true)) { + return get_the_title($home); + } + return __('Latest Posts', 'sage'); + } + if (is_archive()) { + return get_the_archive_title(); + } + if (is_search()) { + return sprintf(__('Search Results for %s', 'sage'), get_search_query()); + } + if (is_404()) { + return __('Not Found', 'sage'); + } + return get_the_title(); +} diff --git a/app/lib/Sage/Asset.php b/app/lib/Sage/Asset.php new file mode 100644 index 0000000..fd47a7b --- /dev/null +++ b/app/lib/Sage/Asset.php @@ -0,0 +1,35 @@ +manifest = $manifest; + $this->asset = basename($file); + $this->dir = dirname($file) != '.' ? dirname($file) : ''; + } + + public function __toString() { + return $this->getUri(); + } + + public function getUri() { + $file = self::$dist . '/' . $this->dir . '/' . ($this->manifest ? $this->manifest->get($this->asset) : $this->asset); + return get_template_directory_uri() . $file; + } +} diff --git a/app/lib/Sage/Assets/IManifest.php b/app/lib/Sage/Assets/IManifest.php new file mode 100644 index 0000000..62b5134 --- /dev/null +++ b/app/lib/Sage/Assets/IManifest.php @@ -0,0 +1,26 @@ +manifest = file_exists($manifestPath) ? json_decode(file_get_contents($manifestPath), true) : []; + } + + /** @inheritdoc */ + public function get($file) { + return isset($this->manifest[$file]) ? $this->manifest[$file] : $file; + } + + /** @inheritdoc */ + public function getAll() { + return $this->manifest; + } +} diff --git a/app/lib/Sage/Template.php b/app/lib/Sage/Template.php new file mode 100644 index 0000000..3655e76 --- /dev/null +++ b/app/lib/Sage/Template.php @@ -0,0 +1,151 @@ +getSlug()] = $wrapper; + return new static($wrapper->getWrappers(), $context); + } + + /** + * @param string $slug + * @param array $context + * @return static + */ + public static function unwrap($slug = '', $context = []) { + if (!$slug) { + // If no slug is specified, we grab the most recently wrapped item + end(self::$wrappers); + $slug = key(self::$wrappers); + } + return new static(self::$wrappers[$slug]->getTemplate(), $context); + } + + /** + * Converts a delimeted template file into an array of parts + * + * Example: + * Template::getConvertedTemplateParts('content-single-audio.php'); + * => ['content-single-audio.php', 'content-single.php', 'content.php'] + * + * The returned value can then be passed to WordPress's locate_template. + * + * @param string $template + * @param string $delimeter + * @return array + */ + public static function convertParts($template, $delimeter = '-') { + $templateParts = explode($delimeter, str_replace('.php', '', (string) $template)); + $templates[] = array_shift($templateParts); + foreach ($templateParts as $i => $templatePart) { + $templates[] = $templates[$i] . $delimeter . $templatePart; + } + return array_reverse($templates); + } + + /** + * Template constructor + * @param string|string[] $template + * @param array $context + */ + public function __construct($template, array $context = []) { + $this->set($template); + $this->context = $context; + } + + /** + * @return string HTML + * @see get + */ + public function __toString() { + return $this->get(); + } + + /** + * Echoes the output HTML + * @see get + */ + public function render() { + echo $this->get(); + } + + /** + * @return string HTML + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + */ + public function get() { + /** @noinspection PhpUnusedLocalVariableInspection $context is passed to the included template */ + $context = $this->context; + extract($this->context); + ob_start(); + if ($template = $this->locate()) { + /** @noinspection PhpIncludeInspection */ + include $template; + } + $this->html = ob_get_clean() ?: ''; + return $this->html; + } + + /** + * @param string[]|string $template + */ + public function set($template) { + if (is_array($template)) { + $this->templates = self::format($template); + return; + } + if (!is_string($template) || !(string) $template) { + return; + } + // At this point, we assume it's something like `content-single.php` or `content-single-audio.php` + $this->templates = self::format(self::convertParts($template)); + } + + /** + * Ensures that each template in $this->templates is appended with `.php` + * @param $templates + * @return array + */ + protected static function format($templates) { + return array_map(function ($template) { + if (substr($template, -4, 4) === '.php') { + return $template; + } + return $template . '.php'; + }, $templates); + } + + /** + * @param string $templateDir Specify a template directory relative to your theme directory; e.g., `templates/`, `templates/partials/`, `woocommerce/` + * @return string Filename + */ + public function locate($templateDir = '') { + $templates = array_map(function ($template) use ($templateDir) { + return ($templateDir ?: self::$root) . $template; + }, $this->templates); + $template = locate_template($templates); + return apply_filters('sage/locate_template', $template, $templates) ?: $template; + } +} diff --git a/app/lib/Sage/Template/IWrapper.php b/app/lib/Sage/Template/IWrapper.php new file mode 100644 index 0000000..d785988 --- /dev/null +++ b/app/lib/Sage/Template/IWrapper.php @@ -0,0 +1,27 @@ +slug = sanitize_title(basename($base, '.php')); + $this->wrappers = [$base]; + $this->template = $templateSlug; + $str = substr($base, 0, -4); + array_unshift($this->wrappers, sprintf($str . '-%s.php', $templateSlug)); + } + + /** {@inheritdoc} */ + public function getWrappers() { + $this->wrappers = apply_filters('sage/wrap_' . $this->slug, $this->wrappers) ?: $this->wrappers; + return $this->wrappers; + } + + /** {@inheritdoc} */ + public function getSlug() { + return $this->slug; + } + + /** {@inheritdoc} */ + public function getTemplate() + { + return $this->template; + } +} diff --git a/app/setup.php b/app/setup.php new file mode 100644 index 0000000..1127939 --- /dev/null +++ b/app/setup.php @@ -0,0 +1,96 @@ + __('Primary Navigation', 'sage') + ]); + + /** + * Enable post thumbnails + * @link http://codex.wordpress.org/Post_Thumbnails + * @link http://codex.wordpress.org/Function_Reference/set_post_thumbnail_size + * @link http://codex.wordpress.org/Function_Reference/add_image_size + */ + add_theme_support('post-thumbnails'); + + /** + * Enable post formats + * @link http://codex.wordpress.org/Post_Formats + */ + add_theme_support('post-formats', ['aside', 'gallery', 'link', 'image', 'quote', 'video', 'audio']); + + /** + * Enable HTML5 markup support + * @link http://codex.wordpress.org/Function_Reference/add_theme_support#HTML5 + */ + add_theme_support('html5', ['caption', 'comment-form', 'comment-list', 'gallery', 'search-form']); + + /** + * Use main stylesheet for visual editor + * @see /assets/styles/layouts/_tinymce.scss + */ + add_editor_style(asset_path('styles/main.css')); +}); + +/** + * Register sidebars + */ +add_action('widgets_init', function () { + $config = function ($name, $id = '') { + return [ + 'name' => __($name, 'sage'), + 'id' => 'sidebar-' . $id ?: sanitize_title($name), + 'before_widget' => '
', + 'after_widget' => '
', + 'before_title' => '

', + 'after_title' => '

' + ]; + }; + + register_sidebar($config('Primary')); + register_sidebar($config('Footer')); +}); + +/** + * Theme assets + */ +add_action('wp_enqueue_scripts', function () { + wp_enqueue_style('sage/css', asset_path('styles/main.css'), false, null); + + if (is_single() && comments_open() && get_option('thread_comments')) { + wp_enqueue_script('comment-reply'); + } + + wp_enqueue_script('sage/js', asset_path('scripts/main.js'), ['jquery'], null, true); +}, 100); diff --git a/gulpfile.js b/gulpfile.js index d270e96..ef543a7 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -12,7 +12,7 @@ var eslint = require('gulp-eslint'); var lazypipe = require('lazypipe'); var less = require('gulp-less'); var merge = require('merge-stream'); -var cssNano = require('gulp-cssnano'); +var minifyCss = require('gulp-minify-css'); var plumber = require('gulp-plumber'); var rev = require('gulp-rev'); var runSequence = require('run-sequence'); diff --git a/package.json b/package.json index 67ac572..02769e5 100644 --- a/package.json +++ b/package.json @@ -40,11 +40,11 @@ "gulp-imagemin": "^2.4.0", "gulp-less": "^3.0.5", "gulp-cssnano": "^2.1.0", - "gulp-flatten": "0.1.1", - "gulp-if": "^1.2.5", - "gulp-imagemin": "^2.3.0", - "gulp-jshint": "^1.11.2", - "gulp-less": "^3.0.3", + "gulp-eslint": "^1.1.1", + "gulp-flatten": "0.2.0", + "gulp-if": "^2.0.0", + "gulp-imagemin": "^2.4.0", + "gulp-less": "^3.0.5", "gulp-plumber": "^1.0.1", "gulp-rename": "^1.2.2", "gulp-rev": "^6.0.1", diff --git a/ruleset.xml b/ruleset.xml new file mode 100644 index 0000000..8fb7a9c --- /dev/null +++ b/ruleset.xml @@ -0,0 +1,43 @@ + + + Roots Coding Standards + + + + + + + + + + + + + + + + + + + + + + templates/* + + + + + templates/* + + + + + + + + + + + + +