diff options
| author | Daniel Weipert <code@drogueronin.de> | 2020-10-26 18:03:48 +0100 | 
|---|---|---|
| committer | Daniel Weipert <code@drogueronin.de> | 2020-10-26 18:03:48 +0100 | 
| commit | 992a9d6ca186bb4399143dd5ee72a27a3b4909aa (patch) | |
| tree | e9da01ac2ed233308b1477de5463ae80065afcd4 /src | |
Initial commitv1.0.0
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', +];  | 
