Skip to content

Tutorial: Creating a Custom Plugin to Extend the CYOA Plugin

Seth Shoultes edited this page Oct 27, 2024 · 5 revisions

Creating a custom WordPress plugin that adds new entity types—such as Items, Weapons, Vehicles, Locations, Lore, Organizations, and Laws—to your CYOA (Choose Your Own Adventure) Story Builder plugin using Advanced Custom Fields (ACF). This approach allows us to extend the functionality without modifying the original plugin.


Creating the Custom Plugin: CYOA Entities

This plugin will:

  • Register custom post types for your entities (Items, Weapons, Vehicles, Locations, Lore, Organizations, and Laws).
  • Use Advanced Custom Fields (ACF) to add custom fields to these entities.
  • Integrate with your existing templates to display entity information.

Step 1: Set Up the Plugin Structure

1. Create the Plugin Folder and File

  • Navigate to your WordPress installation's wp-content/plugins directory.
  • Create a new folder named cyoa-entities.
  • Inside this folder, create a file named cyoa-entities.php.

2. Add the Plugin Header Information

In cyoa-entities.php, add the following code at the top:

<?php
/*
Plugin Name: CYOA Entities
Description: Adds custom post types and integrates entities with the CYOA Story Builder plugin.
Version: 1.0
Author: Your Name
Text Domain: cyoa-entities
*/

This header provides WordPress with the necessary information about your plugin.


Step 2: Register Custom Post Types (CPTs)

We'll register CPTs for each entity type using the register_post_type function.

Add the following code to cyoa-entities.php:

// Prevent direct access
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

// Register custom post types
function cyoa_register_custom_post_types() {
    $entity_types = [
        'iasb_lore'          => 'Lore',
        'iasb_organization'  => 'Organization',
        'iasb_law'           => 'Law',
    ];

    foreach ( $entity_types as $slug => $singular ) {
        $plural = $singular . 's';

        $labels = [
            'name'                  => $plural,
            'singular_name'         => $singular,
            'menu_name'             => $plural,
            'name_admin_bar'        => $singular,
            'add_new'               => 'Add New',
            'add_new_item'          => 'Add New ' . $singular,
            'new_item'              => 'New ' . $singular,
            'edit_item'             => 'Edit ' . $singular,
            'view_item'             => 'View ' . $singular,
            'all_items'             => 'All ' . $plural,
            'search_items'          => 'Search ' . $plural,
            'not_found'             => 'No ' . strtolower( $plural ) . ' found.',
            'not_found_in_trash'    => 'No ' . strtolower( $plural ) . ' found in Trash.',
            'parent_item_colon'     => '',
            'menu_name'             => $plural,
        ];

        $args = [
            'labels'             => $labels,
            'public'             => true,
            'has_archive'        => true,
            'rewrite'            => [ 'slug' => strtolower( $plural ) ],
            'supports'           => [ 'title', 'editor', 'thumbnail', 'excerpt', 'custom-fields' ],
            'show_in_rest'       => true, // Enables Gutenberg editor support
            'taxonomies'         => [],
        ];

        register_post_type( $slug, $args );
    }
}
add_action( 'init', 'cyoa_register_custom_post_types' );

Explanation:

  • Prevent Direct Access: The check for ABSPATH ensures the file is not directly accessed.
  • Function cyoa_register_custom_post_types(): Registers each entity type as a custom post type.
  • Entity Types: Uses your specified slugs with the iasb_ prefix to avoid conflicts.
  • Labels and Arguments: Sets up the necessary labels and arguments for each CPT.

Step 3: Integrate Advanced Custom Fields (ACF)

Since you want to use ACF to add custom fields without adding more features to the plugin, we won't include code for custom fields in the plugin. Instead, we'll guide you on setting up ACF fields.

1. Install and Activate ACF

  • Go to Plugins > Add New in your WordPress dashboard.
  • Search for Advanced Custom Fields.
  • Install and activate the plugin by WP Engine.

2. Create Custom Fields for Entities

You'll need to create field groups for each entity type.

Example: Creating Fields for Items

  1. Navigate to: Custom Fields > Add New.

  2. Field Group Title: Item Fields.

  3. Location Rules: Show this field group if Post Type is equal to Item (iasb_item).

  4. Add Fields:

    • Item Type

      • Field Label: Item Type
      • Field Name: item_type
      • Field Type: Select
      • Choices: Add options like Consumable, Equipment, Quest Item, etc.
    • Rarity

      • Field Label: Rarity
      • Field Name: rarity
      • Field Type: Select
      • Choices: Common, Uncommon, Rare, Epic, Legendary
    • Value

      • Field Label: Value
      • Field Name: value
      • Field Type: Number
    • Description

      • Field Label: Description
      • Field Name: description
      • Field Type: Textarea
  5. Publish the field group.

Repeat this process for other entity types, adding relevant fields.


Step 4: Update Templates to Display Entities

Now, we'll integrate your provided item and story templates.

1. Create Template Files in Your Theme

  • Item Template: single-iasb_item.php
  • Story Template: single-iasb_story.php

Place these files in your theme's folder. If your theme doesn't support custom templates for CPTs, you may need to modify it or use a child theme.

2. Item Template

Use your provided item template code. Create a file named single-iasb_item.php in your theme directory and add the following code:

<?php
// single-iasb_item.php
get_header();

if (have_posts()) :
    while (have_posts()) : the_post();
        ?>
        <article id="post-<?php the_ID(); ?>" <?php post_class('story_builder item_builder'); ?>>
            <!-- Title -->
            <h1 class="story-story-title"><?php the_title(); ?></h1>
            
            <!-- Featured Image (Optional) -->
            <?php if (has_post_thumbnail()) : ?>
                <div class="item-image">
                    <?php the_post_thumbnail('large', array('alt' => get_the_title())); ?>
                </div>
            <?php endif; ?>
            
            <!-- Meta Information -->
            <div class="story-story-meta item-meta">
                <?php
                // Display item type from ACF field
                $item_type = get_field('item_type');
                if ($item_type) {
                    echo '<span class="item-type"><i class="fas fa-archive"></i> <strong>' . __('Type:', 'story-builder') . '</strong> ' . esc_html($item_type) . '</span>';
                }
                ?>
            </div>

            <!-- Content -->
            <div class="story-content item-content">
                <?php
                // Display the description from ACF field
                $description = get_field('description');
                if ($description) {
                    echo wpautop($description);
                } else {
                    the_content();
                }
                ?>
            </div>
            
            <?php
            // Allow themes/plugins to add additional content after item content
            do_action('iasb_after_item_content', get_the_ID());
            ?>
        </article>
        <?php
    endwhile;
endif;

get_footer();

Explanation:

  • Meta Information: Retrieves and displays the item_type field from ACF.
  • Content: Displays the description field from ACF if available; otherwise, it displays the post content.

3. Story Template

Create a file named single-iasb_story.php in your theme directory and add the following code:

<?php
// single-iasb_story.php
get_header();

if (have_posts()) :
    while (have_posts()) : the_post();

        // Get the current post ID and user ID
        $post_id = get_the_ID();
        $user_id = get_current_user_id();

        // Existing code to handle universes and user progress...

        ?>
        <article id="post-<?php the_ID(); ?>" <?php post_class('story_builder'); ?>>
            <h1 class="story-story-title"><?php the_title(); ?></h1>
            <div class="story-story-meta">
                <?php
                // Display breadcrumb navigation
                // iasb_display_breadcrumbs($post_id);
                ?>
            </div>
            <?php if (has_post_thumbnail()) : ?>
                <div class="single-story-image">
                    <?php the_post_thumbnail('large', array('alt' => get_the_title())); ?>
                </div>
            <?php endif; ?>

            <div class="story-content">
                <?php the_content(); ?>
            </div>

            <?php
            // Existing code for universes, user progress, etc.

            // Display Entities Associated with the Story
            ?>
            <div class="story-entities">
                <?php
                // Function to display entities
                function iasb_display_entities($post_id, $field_name, $entity_label) {
                    $entities = get_field($field_name, $post_id);
                    if ($entities) {
                        echo '<h3>' . sprintf(__('Related %s:', 'story-builder'), $entity_label) . '</h3>';
                        echo '<ul>';
                        foreach ($entities as $entity) {
                            echo '<li><a href="' . get_permalink($entity->ID) . '">' . get_the_title($entity->ID) . '</a></li>';
                        }
                        echo '</ul>';
                    }
                }

                // Display Items
                iasb_display_entities($post_id, 'iasb_story_items', 'Items');

                // Display Weapons
                iasb_display_entities($post_id, 'iasb_story_weapons', 'Weapons');

                // Display Vehicles
                iasb_display_entities($post_id, 'iasb_story_vehicles', 'Vehicles');

                // Display Locations
                iasb_display_entities($post_id, 'iasb_story_locations', 'Locations');

                // Display Lore
                iasb_display_entities($post_id, 'iasb_story_lore', 'Lore');

                // Display Organizations
                iasb_display_entities($post_id, 'iasb_story_organizations', 'Organizations');

                // Display Laws
                iasb_display_entities($post_id, 'iasb_story_laws', 'Laws');
                ?>
            </div>

        </article>

        <?php

    endwhile;
endif;

get_footer();

Explanation:

  • Function iasb_display_entities(): A helper function to display entities associated with the story.
  • Displaying Entities: Calls iasb_display_entities() for each entity type, passing the ACF field name and label.
  • ACF Fields in Story: Ensure you've added ACF relationship fields to associate entities with stories (covered in Step 5).

Step 5: Add ACF Relationship Fields to Stories

To associate entities with stories, we'll add relationship fields to the Story post type.

1. Create Story Entities Field Group

  1. Navigate to: Custom Fields > Add New.

  2. Field Group Title: Story Entities.

  3. Location Rules: Show this field group if Post Type is equal to Story (iasb_story or your story CPT).

  4. Add Fields:

    • Items

      • Field Label: Items
      • Field Name: iasb_story_items
      • Field Type: Relationship
      • Post Type Filter: Select Items (iasb_item).
    • Weapons

      • Field Label: Weapons
      • Field Name: iasb_story_weapons
      • Field Type: Relationship
      • Post Type Filter: Select Weapons (iasb_weapon).
    • Vehicles

      • Field Label: Vehicles
      • Field Name: iasb_story_vehicles
      • Field Type: Relationship
      • Post Type Filter: Select Vehicles (iasb_vehicle).
    • Locations

      • Field Label: Locations
      • Field Name: iasb_story_locations
      • Field Type: Relationship
      • Post Type Filter: Select Locations (iasb_location).
    • Lore

      • Field Label: Lore
      • Field Name: iasb_story_lore
      • Field Type: Relationship
      • Post Type Filter: Select Lore (iasb_lore).
    • Organizations

      • Field Label: Organizations
      • Field Name: iasb_story_organizations
      • Field Type: Relationship
      • Post Type Filter: Select Organizations (iasb_organization).
    • Laws

      • Field Label: Laws
      • Field Name: iasb_story_laws
      • Field Type: Relationship
      • Post Type Filter: Select Laws (iasb_law).
  5. Publish the field group.


Step 6: Activate the Plugin

  • Go to Plugins in your WordPress dashboard.
  • Find CYOA Entities in the list.
  • Click Activate.

Step 7: Add Content to Your Entities

1. Add Items

  • Go to Items > Add New.
  • Enter the item title.
  • Fill in the ACF fields (Item Type, Rarity, Value, Description).
  • Add a featured image if desired.
  • Publish the item.

2. Add Other Entities

Repeat similar steps for Weapons, Vehicles, Locations, Lore, Organizations, and Laws.


Step 8: Associate Entities with Stories

  • Edit a story post.
  • In the Story Entities metabox (added via ACF), select the entities related to the story.
  • Update or publish the story.

Step 9: Test the Implementation

  • View a story on the frontend.
  • Confirm that the associated entities are displayed correctly in the story-entities section.
  • View an item page to ensure the custom fields are displayed as per the item template.

Full Plugin Code

Here's the complete cyoa-entities.php file:

<?php
/*
Plugin Name: CYOA Entities
Description: Adds custom post types and integrates entities with the CYOA Story Builder plugin.
Version: 1.0
Author: Your Name
Text Domain: cyoa-entities
*/

// Prevent direct access
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

// Register custom post types
function cyoa_register_custom_post_types() {
    $entity_types = [
        'iasb_lore'          => 'Lore',
        'iasb_organization'  => 'Organization',
        'iasb_law'           => 'Law',
    ];

    foreach ( $entity_types as $slug => $singular ) {
        $plural = $singular . 's';

        $labels = [
            'name'                  => $plural,
            'singular_name'         => $singular,
            'menu_name'             => $plural,
            'name_admin_bar'        => $singular,
            'add_new'               => 'Add New',
            'add_new_item'          => 'Add New ' . $singular,
            'new_item'              => 'New ' . $singular,
            'edit_item'             => 'Edit ' . $singular,
            'view_item'             => 'View ' . $singular,
            'all_items'             => 'All ' . $plural,
            'search_items'          => 'Search ' . $plural,
            'not_found'             => 'No ' . strtolower( $plural ) . ' found.',
            'not_found_in_trash'    => 'No ' . strtolower( $plural ) . ' found in Trash.',
            'parent_item_colon'     => '',
            'menu_name'             => $plural,
        ];

        $args = [
            'labels'             => $labels,
            'public'             => true,
            'has_archive'        => true,
            'rewrite'            => [ 'slug' => strtolower( $plural ) ],
            'supports'           => [ 'title', 'editor', 'thumbnail', 'excerpt', 'custom-fields' ],
            'show_in_rest'       => true,
            'taxonomies'         => [],
        ];

        register_post_type( $slug, $args );
    }
}
add_action( 'init', 'cyoa_register_custom_post_types' );

Additional Notes

  • Custom Taxonomies: If you need taxonomies (e.g., categories or tags) for your entities, you can register them similarly using register_taxonomy.
  • Template Hierarchy: Ensure your theme supports custom templates for CPTs. If not, you may need to create a child theme or modify your theme.
  • Styling: Add CSS styles to your theme or plugin to style the entity displays.
  • ACF Pro Features: If you have ACF Pro, you can use features like Repeater Fields and Flexible Content for more advanced custom fields.

Conclusion

By creating this custom plugin, you've extended the functionality of your CYOA Story Builder plugin to include new entity types, leveraging ACF for custom fields. This approach keeps your customizations modular and maintainable.