pods-framework / pods

The Pods Framework is a Content Development Framework for WordPress - It lets you create and extend content types that can be used for any project. Add fields of various types we've built in, or add your own with custom inputs, you have total control.
https://pods.io/
GNU General Public License v2.0
1.07k stars 264 forks source link

Ability to load templates from filesystem #7013

Open xlotlu opened 1 year ago

xlotlu commented 1 year ago

Problem to Solve

I see the following advantages to having templates stored on the filesystem:

This request was triggered after working for a long time on a template on my dev environment, then re-setting the database to production data. Luckily I did not lose my work, but the near-miss was incentivising.

Proposed Solution

I think the simplest way would be to mark the template as "filesystem type" during creation, and provide the path to it. Then extend the existing code-path with reading from fs instead of returning the post_content column.

How this should happen technically, or even UI-wise, I don't know. Given my limited WP knowledge, I think the most straightforward way would be to create a new type, e.g. "_pods_template_fs", and handle it fully separately.

I would like to have a unified interface for these, maybe two buttons, "Add new DB template" / "Add new FS template", but that would probably require jumping through too many hoops.

Possible Workaround

Hook into pods_templates_pre_template and return file contents. Example below.

xlotlu commented 1 year ago

I did not write my workaround together with the issue report, because it's more of a sideways hack, but I'll leave it here in case it's useful for somebody:

What I did was write a script to update the db from the command-line, and run it after saving files locally. I automated this with inotifywait:

while true; do
  inotifywait -e modify,create,delete,move my-template.html
  echo "update wp_posts set post_content='$(cat my-template.html)' where post_type='_pods_template' and post_name='my-template'" | mysql wp_db
done

I also wrapped this in a script that refreshes the browser automatically using xdotool, and now: much happier.

xlotlu commented 1 year ago

To answer myself (again), here is a workaround, inspired by this example:

add_filter( 'pods_templates_pre_template', 'pods_fs_templates', 10, 2);
function pods_fs_templates( $output, $data ) {
    // don't touch existing content
    if ($output != "")
        return $output;

    $tpl = __DIR__ . "/templates/{$data['name']}";

    if (file_exists($tpl))
        return file_get_contents($tpl);
}
lkraav commented 1 year ago

Great work here. I did something similar for Toolset Views

<?php
/*
Plugin Name: Toolset Views External Layout Meta HTML
Plugin URI: https://conversionready.com
Description: Filter view layout HTML definition with an external source
Version: 2016.10.04
Author: Leho Kraav
Author URI: https://conversionready.com
*/

if ( is_admin() ) {
    return;
}

final class WPV_External_Layout_Meta_HTML {

    private static $layout_meta_html_by_theme_directory = 'wp-views';

    public static function plugins_loaded() {

        if ( ! defined( 'WPV_VERSION' ) ) {
            return;
        }

        # plugins/wp-views/embedded/inc/wpv.class.php WP_Views::get_view_layout_settings()
        add_filter( 'wpv_filter_override_view_layout_settings', [ __CLASS__, 'layout_meta_html_by_theme' ], 10, 2 );

    }

    # implements known theme location backend
    public static function layout_meta_html_by_theme( $view_layout_settings, $view_id ) {

        $view_post_name = get_post_field( 'post_name', $view_id );

        $layout_meta_html_file = sprintf( '%s/%s/%s.html',
            get_stylesheet_directory(),
            static::$layout_meta_html_by_theme_directory,
            $view_post_name
        );

        $layout_meta_html_file = apply_filters( 'wpv_external_layout_meta_html_file',
            $layout_meta_html_file,
            $view_layout_settings,
            $view_id
        );

        if ( $layout_meta_html = @file_get_contents( $layout_meta_html_file ) ) {

            $layout_meta_html_debug = sprintf( "<!-- %s::%s -> %s -->\n",
                __CLASS__,
                __FUNCTION__,
                str_replace( ABSPATH, '', $layout_meta_html_file )
            );

            $layout_meta_html = $layout_meta_html_debug . $layout_meta_html;

            $layout_meta_html = implode( "\n", array_map( 'trim', explode( "\n", $layout_meta_html ) ) );

            $view_layout_settings['layout_meta_html'] = $layout_meta_html;

        }

        return $view_layout_settings;

    }

}

add_action( 'plugins_loaded', [ 'WPV_External_Layout_Meta_HTML', 'plugins_loaded' ] );
sc0ttkclark commented 1 year ago

I'd like to think about this further, I like the idea but think that if someone has the files in their code then the solution should:

  1. Automatically detect the files in a specific directory as Pods Templates and have them selectable (alongside existing Pods Templates stored in the DB -- if any)
  2. Allow you to register a template (provide a label and the file location) via code

Right now, you can register templates via JSON files like this: https://docs.pods.io/code/registering-configurations/#Registering_Pods_with_JSONYML_files

But that isn't great for things that contain HTML/code, Pods Templates (and Pods Pages) may be better suited having their own registration functions like pods_register_template( $name, $label, $file ) and pods_register_page( $uri, $file )

Maybe even having a way to register the file and pull metadata from the PHP doc tag like WP Page Templates have for template name etc.

What do ya'll think about this?