diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/MetaBox.php | 81 | ||||
-rw-r--r-- | src/Settings.php | 97 | ||||
-rw-r--r-- | src/ThemeEditor.php | 73 | ||||
-rw-r--r-- | src/TimberEditor.php | 148 | ||||
-rw-r--r-- | src/codemirror-themes.php | 67 |
5 files changed, 466 insertions, 0 deletions
diff --git a/src/MetaBox.php b/src/MetaBox.php new file mode 100644 index 0000000..d11336f --- /dev/null +++ b/src/MetaBox.php @@ -0,0 +1,81 @@ +<?php + +namespace TimberEditor; + +class MetaBox +{ + /** + * Metabox constructor. + */ + public function __construct() + { + add_action('add_meta_boxes', [$this, 'addMetaBoxes']); + + foreach (Settings::getGeneralSupportedPostTypes() as $postType) { + add_action("save_post_{$postType}", [$this, 'savePost']); + } + } + + /** + * add_meta_boxes action callback + * + * @param $postType + */ + public function addMetaBoxes($postType) + { + if (! in_array($postType, Settings::getGeneralSupportedPostTypes())) { + return; + } + + add_meta_box('timber-editor', 'Timber Editor', [$this, 'metaBoxTimberEditor'], '', 'advanced', 'high'); + } + + /** + * save_post action callback + * Writes the content to file + * + * @param $postId + */ + public function savePost($postId) + { + if ( + (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) || + (! isset($_POST['post_ID']) || $_POST['post_ID'] != $postId) || + ! check_admin_referer('metaBoxTimberEditor', 'metaBoxTimberEditor') || + ! isset($_POST['timber-editor_content']) + ) { + return; + } + + file_put_contents(TimberEditor::getTemplateFilePath($postId), $_POST['timber-editor_content']); + if (empty($_POST['timber-editor_content'])) { + wp_delete_file(TimberEditor::getTemplateFilePath($postId)); + } + } + + /** + * add_meta_box callback + */ + public function metaBoxTimberEditor() + { + $file = TimberEditor::getTemplateFilePath(); + if (file_exists($file)) { + $f = fopen($file, 'r'); + $content = fread($f, filesize($file)); + fclose($f); + } + + wp_nonce_field('metaBoxTimberEditor', 'metaBoxTimberEditor'); + ?> + <textarea name="timber-editor_content" id="timber-editor_content"><?= esc_textarea($content ?? '') ?></textarea> + <?php + + $settings = wp_enqueue_code_editor([ + 'file' => $file, + 'codemirror' => [ + 'theme' => Settings::getCodeMirrorTheme(), + ], + ]); + wp_add_inline_script('code-editor', sprintf('jQuery( function() { wp.codeEditor.initialize( "timber-editor_content", %s ); } );', wp_json_encode($settings))); + } +} diff --git a/src/Settings.php b/src/Settings.php new file mode 100644 index 0000000..d9947f8 --- /dev/null +++ b/src/Settings.php @@ -0,0 +1,97 @@ +<?php + +namespace TimberEditor; + +class Settings +{ + /** + * Settings constructor. + */ + public function __construct() + { + add_action('admin_menu', [$this, 'adminMenu']); + add_action('admin_init', [$this, 'adminInit']); + } + + /** + * admin_menu action callback + */ + public function adminMenu() + { + add_submenu_page('options-general.php', 'Timber Editor', 'Timber Editor', 'manage_options', 'timber-editor', [$this, 'addSubmenuPage']); + } + + /** + * add_submenu_page callback + */ + public function addSubmenuPage() + { + ?> + <div class="wrap"> + <h1><?= esc_html(get_admin_page_title()) ?></h1> + <form action="options.php" method="post"> + <?php + settings_fields('timber-editor'); + do_settings_sections('timber-editor'); + submit_button(); + ?> + </form> + </div> + <?php + } + + /** + * admin_init action callback + */ + public function adminInit() + { + register_setting('timber-editor', 'timber-editor_general_supported-post-types'); + add_settings_section('timber-editor_general', __('General'), function () {}, 'timber-editor'); + add_settings_field('timber-editor_general_supported-post-types', __('Supported Post Types'), function () { + $postTypes = get_post_types([], 'objects'); + $supportedPostTypes = self::getGeneralSupportedPostTypes(); + ?> + <select name="timber-editor_general_supported-post-types[]" id="timber-editor_general_supported-post-types" multiple> + <?php foreach ($postTypes as $pt): ?> + <option value="<?= $pt->name ?>" <?= in_array($pt->name, $supportedPostTypes) ? 'selected' : '' ?>><?= $pt->label ?></option> + <?php endforeach; ?> + </select> + <?php + }, 'timber-editor', 'timber-editor_general', ['label_for' => 'timber-editor_general_supported-post-types']); + + register_setting('timber-editor', 'timber-editor_codemirror_theme'); + add_settings_section('timber-editor_codemirror', 'CodeMirror', function () {}, 'timber-editor'); + add_settings_field('timber-editor_codemirror_theme', 'Theme', function () { + $theme = self::getCodeMirrorTheme(); + $themes = include_once 'codemirror-themes.php'; + ?> + <select name="timber-editor_codemirror_theme" id="timber-editor_codemirror_theme"> + <?php foreach ($themes as $t): ?> + <option value="<?= $t ?>" <?php selected($theme, $t) ?>><?= $t ?></option> + <?php endforeach; ?> + </select> + <p class="description"> + <a href="https://codemirror.net/demo/theme.html#<?= $theme ?>" target="_blank"> + <?= __('Preview') ?> + </a> + </p> + <?php + }, 'timber-editor', 'timber-editor_codemirror', ['label_for' => 'timber-editor_codemirror_theme']); + } + + /** + * @return string[] + */ + public static function getGeneralSupportedPostTypes() + { + return get_option('timber-editor_general_supported-post-types', ['page']) ?: []; + } + + /** + * @return string + */ + public static function getCodeMirrorTheme() + { + return get_option('timber-editor_codemirror_theme', 'default'); + } +} diff --git a/src/ThemeEditor.php b/src/ThemeEditor.php new file mode 100644 index 0000000..0b62264 --- /dev/null +++ b/src/ThemeEditor.php @@ -0,0 +1,73 @@ +<?php + +namespace TimberEditor; + +class ThemeEditor +{ + /** + * ThemeEditor constructor. + */ + public function __construct() + { + add_filter('wp_theme_editor_filetypes', [$this, 'editableExtensions']); + add_filter('editable_extensions', [$this, 'editableExtensions']); + add_filter('wp_code_editor_settings', [$this, 'codeEditorSettings'], 10, 2); + add_action('wp_enqueue_code_editor', [$this, 'enqueueCodeEditor']); + } + + /** + * wp_theme_editor_filetypes and editable_extensions filter callback + * Adds twig extension support + * + * @param $types + * + * @return array + */ + public function editableExtensions($types) + { + $types[] = 'twig'; + + return $types; + } + + /** + * wp_code_editor_settings filter callback + * Adds twig support and sets theme + * + * @param $settings + * @param $args + * + * @return array + */ + public function codeEditorSettings($settings, $args) { + if (strpos($args['file'], '.twig') !== false) { + $settings['codemirror']['mode'] = ['name' => 'twig', 'base' => 'text/html']; + } + + $settings['codemirror']['theme'] = Settings::getCodeMirrorTheme(); + + return $settings; + } + + /** + * wp_enqueue_code_editor action callback + * Adds twig and custom mode support + * Adds selected theme css + * + * @param $settings + */ + public function enqueueCodeEditor($settings) { + if (isset($settings['codemirror']['mode']['name']) && $settings['codemirror']['mode']['name'] == 'twig') { + wp_add_inline_script( # fix as described here: https://make.wordpress.org/core/2017/10/22/code-editing-improvements-in-wordpress-4-9/ + 'wp-codemirror', + 'window.CodeMirror = wp.CodeMirror;' + ); + wp_enqueue_script('mode-twig', 'https://unpkg.com/codemirror@5/mode/twig/twig.js', ['wp-codemirror']); + } + + $theme = $settings['codemirror']['theme']; + if ($theme != 'default') { + wp_enqueue_style('codemirror-theme', "https://unpkg.com/codemirror@5/theme/$theme.css", ['wp-codemirror']); + } + } +} diff --git a/src/TimberEditor.php b/src/TimberEditor.php new file mode 100644 index 0000000..2eb3b36 --- /dev/null +++ b/src/TimberEditor.php @@ -0,0 +1,148 @@ +<?php + +namespace TimberEditor; + +use Timber\Loader; +use Timber\Post; +use Timber\Timber; + +class TimberEditor +{ + /** + * TimberEditor constructor. + */ + public function __construct() + { + if (! class_exists(Timber::class)) { + add_action('admin_notices', [$this, 'adminNoticeTimberLibraryMissing']); + return; + } + if (! class_exists(\Classic_Editor::class)) { + add_action('admin_notices', [$this, 'adminNoticeClassicEditor']); + } + + $this->run(); + } + + /** + * admin_notices action callback for missing Timber Library + */ + public function adminNoticeTimberLibraryMissing() + { + ?> + <div class="notice notice-error"> + <p> + <a href="https://wordpress.org/plugins/timber-library/" target="_blank">Timber</a> + (<a href="http://word.press/wp-admin/plugin-install.php?s=timber&tab=search&type=term">install</a>) + needs to be installed and active. + </p> + </div> + <?php + } + + /** + * admin_notices action callback for missing Classic Editor + */ + public function adminNoticeClassicEditor() + { + ?> + <div class="notice notice-warning is-dismissible"> + <p> + <a href="https://wordpress.org/plugins/classic-editor/" target="_blank">Classic Editor</a> + (<a href="http://word.press/wp-admin/plugin-install.php?s=classic+editor&tab=search&type=term">install</a>) + should be installed and active, because the <b>Gutenberg Editor</b> doesn't play well with <b>CodeMirror</b> <i>currently</i>. + </p> + </div> + <?php + } + + /** + * Run the plugin + */ + public function run() + { + if (is_null(Timber::$locations)) { + Timber::$locations = self::getTemplatesLocation(); + } + + new Settings(); + new ThemeEditor(); + new MetaBox(); + } + + /** + * Get the plugins' templates location + * try to use user provided location via Timber::$locations + * or fall back to uploads folder + * + * @return string + */ + public static function getTemplatesLocation() + { + $location = Timber::$locations; + if (is_array($location)) { + $location = $location[array_key_first($location)]; + } else if (is_null($location)) { + $location = wp_upload_dir()['basedir'] . '/timber-editor'; + } + + return apply_filters('TimberEditor/getTemplatesLocation', $location); + } + + /** + * Get the current posts' template filename + * + * @param int|null $postId + * + * @return string + */ + public static function getTemplateFilename($postId = null) + { + return apply_filters('TimberEditor/getTemplateFilename', ($postId ?? get_the_ID()) . '.twig', $postId); + } + + /** + * Get the current posts' template filepath + * + * @param int|null $postId + * + * @return string + */ + public static function getTemplateFilePath($postId = null) + { + return self::getTemplatesLocation() . '/' . self::getTemplateFilename($postId); + } + + /** + * @param array|string $filenames + * @param array $context + * @param bool $expires + * @param string $cacheMode + * + * @return bool|string + */ + public static function render($filenames = [], $context = [], $expires = false, $cacheMode = Loader::CACHE_USE_DEFAULT) + { + $filenames = (array)$filenames; + array_unshift($filenames, self::getTemplateFilename()); + + return Timber::render($filenames, $context, $expires, $cacheMode); + } + + /** + * @param array|string $filenames + * @param array $context + * @param bool $expires + * @param string $cacheMode + * + * @return bool|string + */ + public static function renderPost($filenames = [], $context = [], $expires = false, $cacheMode = Loader::CACHE_USE_DEFAULT) + { + if (! isset($context['post'])) { + $context['post'] = new Post(); + } + + return self::render($filenames, $context, $expires, $cacheMode); + } +} diff --git a/src/codemirror-themes.php b/src/codemirror-themes.php new file mode 100644 index 0000000..a51779d --- /dev/null +++ b/src/codemirror-themes.php @@ -0,0 +1,67 @@ +<?php + +return [ + 'default', + '3024-day', + '3024-night', + 'abcdef', + 'ambiance', + 'ayu-dark', + 'ayu-mirage', + 'base16-dark', + 'base16-light', + 'bespin', + 'blackboard', + 'cobalt', + 'colorforth', + 'darcula', + 'dracula', + 'duotone-dark', + 'duotone-light', + 'eclipse', + 'elegant', + 'erlang-dark', + 'gruvbox-dark', + 'hopscotch', + 'icecoder', + 'idea', + 'isotope', + 'lesser-dark', + 'liquibyte', + 'lucario', + 'material', + 'material-darker', + 'material-palenight', + 'material-ocean', + 'mbo', + 'mdn-like', + 'midnight', + 'monokai', + 'moxer', + 'neat', + 'neo', + 'night', + 'nord', + 'oceanic-next', + 'panda-syntax', + 'paraiso-dark', + 'paraiso-light', + 'pastel-on-dark', + 'railscasts', + 'rubyblue', + 'seti', + 'shadowfox', + 'solarized dark', + 'solarized light', + 'the-matrix', + 'tomorrow-night-bright', + 'tomorrow-night-eighties', + 'ttcn', + 'twilight', + 'vibrant-ink', + 'xq-dark', + 'xq-light', + 'yeti', + 'yonce', + 'zenburn', +]; |