Add Sources via Metabox

Hi,

to structure our posts better and have a clearer division between the content, we’ve added a sources meta box that automatically appends them at the end of the post, linking to them via a target=”_blank” solution.

NOTE: If you use custom post types, use the following code. If not, scroll down to “Snippet”.

function add_sources_meta_box() {
    $post_types = ['post', 'glossary']; // Add your custom post types here
    
    foreach ($post_types as $post_type) {
        add_meta_box(
            'post_sources',
            'Sources',
            'render_sources_meta_box',
            $post_type,
            'normal',
            'high'
        );
    }
}
add_action('add_meta_boxes', 'add_sources_meta_box');

// Render the meta box content
function render_sources_meta_box($post) {
    wp_nonce_field('sources_meta_box', 'sources_meta_box_nonce');
    
    $sources = get_post_meta($post->ID, '_post_sources', true);
    if (!is_array($sources)) {
        $sources = array();
    }
    ?>
    <div id="sources-container">
        <?php 
        if (!empty($sources)) {
            foreach ($sources as $index => $source) {
                ?>
                <div class="source-row">
                    <input type="text" name="sources[<?php echo $index; ?>][name]" 
                           value="<?php echo esc_attr($source['name']); ?>" 
                           placeholder="Source Name" style="width: 40%;">
                    <input type="url" name="sources[<?php echo $index; ?>][url]" 
                           value="<?php echo esc_url($source['url']); ?>" 
                           placeholder="Source URL" style="width: 40%;">
                    <button type="button" class="remove-source button">-</button>
                </div>
                <?php
            }
        }
        ?>
    </div>
    <button type="button" id="add-source" class="button">+ Add Source</button>

    <style>
        .source-row { margin-bottom: 10px; }
        .source-row input { margin-right: 10px; }
        #add-source { margin-top: 10px; }
    </style>

    <script>
        jQuery(document).ready(function($) {
            var container = $('#sources-container');
            var count = $('.source-row').length;

            $('#add-source').on('click', function() {
                var row = $('<div class="source-row">' +
                    '<input type="text" name="sources[' + count + '][name]" placeholder="Source Name" style="width: 40%;">' +
                    '<input type="url" name="sources[' + count + '][url]" placeholder="Source URL" style="width: 40%;">' +
                    '<button type="button" class="remove-source button">-</button>' +
                    '</div>');
                container.append(row);
                count++;
            });

            $(document).on('click', '.remove-source', function() {
                $(this).parent().remove();
            });
        });
    </script>
    <?php
}

// Save the meta box data
function save_sources_meta_box($post_id) {
    if (!isset($_POST['sources_meta_box_nonce']) || 
        !wp_verify_nonce($_POST['sources_meta_box_nonce'], 'sources_meta_box')) {
        return;
    }

    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
    }

    if (!current_user_can('edit_post', $post_id)) {
        return;
    }

    if (isset($_POST['sources'])) {
        $sources = array_values(array_filter($_POST['sources'], function($source) {
            return !empty($source['name']) || !empty($source['url']);
        }));
        update_post_meta($post_id, '_post_sources', $sources);
    } else {
        delete_post_meta($post_id, '_post_sources');
    }
}
add_action('save_post', 'save_sources_meta_box');

// Display sources at the end of post content
function display_post_sources($content) {
    $supported_post_types = ['post', 'glossary']; // Add your custom post types here
    
    if (!is_singular($supported_post_types)) {
        return $content;
    }
    
    $post_id = get_the_ID();
    $sources = get_post_meta($post_id, '_post_sources', true);
    
    // Force array type
    $sources = (array) $sources;
    
    // Filter out empty sources
    $valid_sources = array_filter($sources, function($source) {
        return !empty($source['name']) || !empty($source['url']);
    });
    
    // Only display sources if there are valid sources
    if (empty($valid_sources)) {
        return $content;
    }
    
    $sources_html = '<div class="post-sources">';
    $sources_html .= '<h3>Quellen</h3>'; // German translation for Sources
    $sources_html .= '<ul>';
    
    foreach ($valid_sources as $source) {
        $sources_html .= '<li>';
        if (!empty($source['url'])) {
            $sources_html .= sprintf(
                '<a href="%s" target="_blank" rel="nofollow noopener">%s</a>',
                esc_url($source['url']),
                esc_html($source['name'] ?? $source['url'])
            );
        } else {
            $sources_html .= esc_html($source['name']);
        }
        $sources_html .= '</li>';
    }
    
    $sources_html .= '</ul></div>';
    
    return $content . $sources_html;
}
add_filter('the_content', 'display_post_sources', 999);

Snippet

Use the following Snippet in your functions.php:

function add_sources_meta_box() {
    add_meta_box(
        'post_sources',
        'Sources',
        'render_sources_meta_box',
        'post',
        'normal',
        'high'
    );
}
add_action('add_meta_boxes', 'add_sources_meta_box');

// Render the meta box content
function render_sources_meta_box($post) {
    wp_nonce_field('sources_meta_box', 'sources_meta_box_nonce');
    
    $sources = get_post_meta($post->ID, '_post_sources', true);
    if (!is_array($sources)) {
        $sources = array();
    }
    ?>
    <div id="sources-container">
        <?php 
        if (!empty($sources)) {
            foreach ($sources as $index => $source) {
                ?>
                <div class="source-row">
                    <input type="text" name="sources[<?php echo $index; ?>][name]" 
                           value="<?php echo esc_attr($source['name']); ?>" 
                           placeholder="Source Name" style="width: 40%;">
                    <input type="url" name="sources[<?php echo $index; ?>][url]" 
                           value="<?php echo esc_url($source['url']); ?>" 
                           placeholder="Source URL" style="width: 40%;">
                    <button type="button" class="remove-source button">-</button>
                </div>
                <?php
            }
        }
        ?>
    </div>
    <button type="button" id="add-source" class="button">+ Add Source</button>

    <style>
        .source-row { margin-bottom: 10px; }
        .source-row input { margin-right: 10px; }
        #add-source { margin-top: 10px; }
    </style>

    <script>
        jQuery(document).ready(function($) {
            var container = $('#sources-container');
            var count = $('.source-row').length;

            $('#add-source').on('click', function() {
                var row = $('<div class="source-row">' +
                    '<input type="text" name="sources[' + count + '][name]" placeholder="Source Name" style="width: 40%;">' +
                    '<input type="url" name="sources[' + count + '][url]" placeholder="Source URL" style="width: 40%;">' +
                    '<button type="button" class="remove-source button">-</button>' +
                    '</div>');
                container.append(row);
                count++;
            });

            $(document).on('click', '.remove-source', function() {
                $(this).parent().remove();
            });
        });
    </script>
    <?php
}

// Save the meta box data
function save_sources_meta_box($post_id) {
    if (!isset($_POST['sources_meta_box_nonce']) || 
        !wp_verify_nonce($_POST['sources_meta_box_nonce'], 'sources_meta_box')) {
        return;
    }

    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
    }

    if (!current_user_can('edit_post', $post_id)) {
        return;
    }

    if (isset($_POST['sources'])) {
        $sources = array_values(array_filter($_POST['sources'], function($source) {
            return !empty($source['name']) || !empty($source['url']);
        }));
        update_post_meta($post_id, '_post_sources', $sources);
    } else {
        delete_post_meta($post_id, '_post_sources');
    }
}
add_action('save_post', 'save_sources_meta_box');

// Display sources at the end of post content
function display_post_sources($content) {
    if (!is_singular('post')) {
        return $content;
    }
    
    $post_id = get_the_ID();
    $sources = get_post_meta($post_id, '_post_sources', true);
    
    // Force array type
    $sources = (array) $sources;
    
    // Filter out empty sources
    $valid_sources = array_filter($sources, function($source) {
        return !empty($source['name']) || !empty($source['url']);
    });
    
    // Only display sources if there are valid sources
    if (empty($valid_sources)) {
        return $content;
    }
    
    $sources_html = '<div class="post-sources">';
    $sources_html .= '<h3>Quellen</h3>'; // German translation for Sources
    $sources_html .= '<ul>';
    
    foreach ($valid_sources as $source) {
        $sources_html .= '<li>';
        if (!empty($source['url'])) {
            $sources_html .= sprintf(
                '<a href="%s" target="_blank" rel="nofollow noopener">%s</a>',
                esc_url($source['url']),
                esc_html($source['name'] ?? $source['url'])
            );
        } else {
            $sources_html .= esc_html($source['name']);
        }
        $sources_html .= '</li>';
    }
    
    $sources_html .= '</ul></div>';
    
    return $content . $sources_html;
}
add_filter('the_content', 'display_post_sources', 999);

CSS

Additionally, add the following CSS to your Theme:

.post-sources {
    margin-top: 40px;
    padding-top: 20px;
    border-top: 1px solid #ddd;
}

.post-sources h3 {
    margin-top: 1.5em; /* Added margin for the heading */
    margin-bottom: 15px;
}

.post-sources ul {
    list-style: none;
    padding-left: 0;
}

.post-sources li {
    margin-bottom: 10px;
}

.post-sources a {
    color: #0073aa;
    text-decoration: none;
}

.post-sources a:hover {
    text-decoration: underline;
}

Posted

in

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *