Search code examples
phpwordpresswordpress-plugin-creationwordpress-hook

WordPress Plugin: Hook to post_row_actions function the content of a custom PHP


I created a WordPress Plugin, with a hook to create links below the overview of posts in the backend(for a custom post type).

Action

The link should redirect to a custom PHP-File. Which is currently solved like this (for Terminierungsverwaltung):

class CustomPlugin
{


function __construct(){

    #... more code

    /**
     * Terminverwaltung
     */
    add_filter( 'post_row_actions', array( $this,'termin_add_user_action_button'), 10, 2 );

    #... more code

}

#... more code

function termin_add_user_action_button($actions, $post){
    global $wpdb;
    $post_id = $post->ID;                       //post ID
    $poll_id;                                   //poll ID

    
    $wordpress_polls = $wpdb->get_results("SELECT wordpress_id, poll_id FROM " . self::WORDPRESS_TABLE_NAME . " WHERE wordpress_id = " . $post_id);
    $wordpress_polls_rows = $wpdb->num_rows;
    
    foreach($wordpress_polls as $wordpress_poll){
        $poll_id  = $wordpress_poll->poll_id;
    }

    if($wordpress_polls_rows === NULL || $wordpress_polls_rows === 0){
        return $actions;
    } else{
        if(get_post_type() === 'workshop'){
            $url = add_query_arg(
                array(
                  'post_id' => $post->ID,
                  'poll_id' => $poll_id,
                ), 
                plugins_url( '/attendees.php', __FILE__ )
              );
            $actions['termin_add_user'] = '<a href="' . $url . '" target="_blank">Teilnehmerverwaltung</a>';
        }
        return $actions;
    }
}

#... more code

}

So currently I am pointing directly to the source file attendees.php (which is located inside the plugin directory) and it is not protected.

My aim is to protect the attendees.php with:

if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly.
}

Obviously, I will need to hook the file somehow to the plugin or WordPress, so ABSPATH is defined. But I am not sure how to do it. Does anyone know how to get this done?

I am new to PHP and WordPress, so sorry if this is a newbie question.

Thanks in advance!

THE-E

In case the attendees.php is of importance, it has currently this structure, and should be displayed independently:

<?php
// if ( ! defined( 'ABSPATH' ) ) {
//  exit; // Exit if accessed directly.
// }
require '../../../wp-load.php';
require 'src/attendees_function.php';
?>

<!DOCTYPE html>
<html lang="de">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel = "stylesheet"
      type = "text/css"
      href = "style/attendeesStyle.css" />
    <title>Teilnehmerverwaltung: <?php echo get_the_title($_REQUEST['post_id']);?></title>
  </head>
  <body>
    <?php add_user_form(); ?>
    <?php delete_attendee(); ?>
    <?php upload(); ?>
    <H1>Teilnehmerverwaltung</H1>
    <H2><?php echo get_the_title($_REQUEST['post_id']);?></H2>
    <section>
    <div id="add-users-upload">
      <form method="POST" enctype="multipart/form-data" id="csv-form">
        <input type="file" name="teilnehmer-csv" accept=".csv"><br>
        <button type="submit" name="csv-upload" id="top-buttons">Teilnehmer CSV hochladen</button>
      </form>

      <form method="post">
        <label for="kuerzel">Kürzel:</label>
        <input type="hidden" name="poll-id" value="<?php $poll_id = $_REQUEST['poll_id']; echo $poll_id; ?>"/>
        <input type="hidden" name="table" value="<?php $table = "oc_polls_share"; echo $table; ?>"/>
        <input type="text" class="form-control"  id="input-kuerzel" name="kuerzel"><br>
        <button type="submit" name="add-user-form" id="top-buttons">Teilnehmer anlegen</button>
      </form>
    </div>
    </section>
    <div id="table-div">
        <table>
          </thead>
            <tr>
            <td id="index">#</td>
            <td id="index">Status</td>
            <td id="header">Kürzel</td>
            <td id="header">E-Mail</td>
            <td id="header">Vorname</td>
            <td id="header">Nachname</td>
            <td id="header">Geburtstag</td>
            <td id="header">Token</td>
            <td id="header">Löschen</td>
            <td id="header">Umfrage-Link</td>
            <td id="header">Webseiten-Link</td>
            <td id="header">Senden</td>
            </tr>
          </thead>
          <tbody>
            <?php
            
            $nextcloud_base_url = "https://example.com";
            $nextcloud_attendees_base_url = $nextcloud_base_url . "/apps/polls/s/";

            if(isset($_REQUEST['post_id']) && isset($_REQUEST['poll_id'])){
              $post_id = $_REQUEST['post_id'];                  //post ID
              $poll_id = $_REQUEST['poll_id'];                                  //poll ID
            }
            $wordpress_polls_table_name = 'wordpress_polls';    //storage of primary keys of post id and poll id
            $polls_share_table_name = 'oc_polls_share'; //attendees table
            
            $wordpress_polls = get_wordpress_polls($wordpress_polls_table_name, $post_id);
            $wordpress_polls_rows = get_wordpress_polls($wordpress_polls_table_name, $post_id);


            $poll_id = get_poll_id($wordpress_polls);

            if($wordpress_polls_rows === NULL || $wordpress_polls_rows === 0){
              return;
            } else {
              $attendees = get_attendees($polls_share_table_name, $poll_id);
              $attendees_rows = get_attendees_rows($polls_share_table_name, $poll_id);
              create_attendee_rows($attendees, $nextcloud_attendees_base_url); 

            }
            ?>
            </tbody>
            </table>
    </div>
    <div id="legende">
      <p>
        Status-Legende:<br>
        &#x274C; = Abstimmung nicht erfolgt<br>
        &#x2705; = Abstimmung erfolgt
      </p>
    </div>
  </body>
</html>

EDIT: I am looking into the Custom Endpoints Documentation, but couldn't get it to work for now.

Meanwhile a picture of the content I want to integrate (attendees.php) enter image description here


Solution

  • There's a bunch of ways to solve this which I'll try to outline. These are not in order of recommendation, just stream on consciousness. I would recommend #2, if it is viable, however.

    1 - Manually boot WordPress

    This question shows shows how to do this and this answer gives pros and cons for each, but the general idea is to require_once either the wp-load.php or the wp-blog-header.php files from root, and you'd probably want the former. Doing this, however, is usually discouraged and I would only recommend it for cases where you need access to basic WordPress functions. If you do this, WordPress doesn't know what your security requirements are, so it will be up to you to perform access control using things like current_user_can.

    Although I posted this first, I would strongly recommended avoiding this, and if you are distributing your plugin, especially in the WordPress.org repo, absolutely don't do this because I'm pretty certain it would be rejected.

    2 - Create a true WordPress admin page

    If you are willing so sacrifice some pixel space (for the admin toolbars), this is the recommended way. Just register a menu using add_menu_page and you can either echo or include/require your code:

    add_action(
        'admin_menu',
        static function () {
            add_menu_page(
                'My Plugin',
                'My Plugin',
                'manage_options',
                'my-plugin',
                static function () {
                    // Either echo here or use an include/require
                    echo '<h1>Hello world</h1>';
                }
            );
        }
    );
    

    The URL to this page is based on the fourth parameter to that function, which using the above would be /wp-admin/admin.php?page=my-plugin. Access control is handled automatically with the third parameter which makes certain that the user has the manage_options permission. You can customize that as needed, and optionally create your own permissions, too.

    3 - Inject via templates

    The template_include function is used to determine which front-end WordPress template to use. Personally, I don't like using this for admin stuff because I like to keep things separate from each other, but that's just me. In this code sample below I'm setting a template in the theme's folder, but it could just as easily be in the plugin's folder.

    add_filter(
        'template_include',
        static function ($template) {
            // Pick whatever logic you want here, it could be `_GET` or `_POST` or pretty much anything
            if ('my-plugin' === ($_GET['action'] ?? null)) {
                $template = get_template_directory() . '/searchform.php';
            }
            return $template;
        }
    );
    

    The if logic is where you do whatever you want. I'm just showing a simple _GET check where the URL would be /?action=my-plugin. Security, once again, is up to you, but you have full access to WordPress core functions like current_user_can to do whatever is necessary.

    4 - Rewrite endpoints

    This is actually a version of #3 because you are just making the URL prettier. I personally think it is over-complicated because it is a multi-step process with a lot of moving parts, but if the URL is important to you it might work. Make sure to flush your permalinks! The key to this is rewrite_rules_array where you register a custom regex for the URL and you can provide whatever logic you need, including querystrings and whatever.

    // Register a global variable for the URL so that WordPress is aware that you care about it
    add_filter(
        'query_vars',
        static function ($vars) {
            $vars[] = 'my-plugin';
            return $vars;
        }
    );
    
    // Add special regex-like syntax for URLs. You can go wild here, too
    add_action(
        'rewrite_rules_array',
        static function ($rules) {
            $newrules = array();
            // The true isn't important, it could be anything. The my-plugin is what
            // matters. Make sure to flush your permalinks!
            $newrules['my-plugin/?$'] = 'index.php?my-plugin=true';
    
            return $newrules + $rules;
        }
    );
    
    add_action(
        'template_include',
        static function ($template) {
            if (get_query_var('my-plugin')) {
                $template = get_template_directory() . '/searchform.php';
            }
    
            return $template;
        }
    );