create a mostly read only CPT

I did something similar recently for a simple email log (for diagnosing some problems). I don’t have time to dissect the code, but hopefully it’ll be clear enough for you.

It creates a CPT for storing data that can be viewed in the admin (only), and allows the user to view and delete each post. NB: delete, not trash — no need for resurrection of deleted posts. Edit is changed to View, and metaboxes are replaced with a view template. This code is obviously missing the bits that write to the CPT and auto-purge old ones, but should be enough for your question.

/**
* custom post type for email log
*/
class LogEmailsPostTypeLog {

    /**
    * hooks
    */
    public function __construct() {
        add_action('admin_init', array($this, 'init'));
        add_action('init', array($this, 'register'));
    }

    /**
    * admin_init action
    */
    public function init() {
        global $typenow;

        if (empty($typenow)) {
            // try to pick it up from the query string
            if (!empty($_GET['post'])) {
                $post = get_post($_GET['post']);
                $typenow = $post->post_type;
            }
        }

        if ($typenow == 'log_emails_log') {
            add_filter('display_post_states', '__return_false');
            add_action('edit_form_after_title', array($this, 'adminEditAfterTitle'), 100);
            add_filter('post_row_actions', array($this, 'adminPostRowActions'), 10, 2);
            add_filter('bulk_actions-edit-log_emails_log', array($this, 'adminBulkActionsEdit'));
            add_filter('manage_log_emails_log_posts_columns', array($this, 'adminManageColumns'));
            add_action('manage_log_emails_log_posts_custom_column', array($this, 'adminManageCustomColumn'), 10, 2);
            add_action('admin_print_footer_scripts', array($this, 'adminPrintFooterScripts'));

            add_action('in_admin_header', array($this, 'adminScreenLayout'));
            add_filter('views_edit-log_emails_log', array($this, 'adminViewsEdit'));

            if (is_admin()) {
                add_filter('gettext', array($this, 'adminGetText'), 10, 3);
            }

            wp_enqueue_script('jquery');
        }
    }

    /**
    * register Custom Post Type
    */
    public function register() {
        // register the post type
        register_post_type('log_emails_log', array(
            'labels' => array (
                'name' => __('Email Logs', 'log-emails'),
                'singular_name' => __('Email Log', 'log-emails'),
                'add_new_item' => __('Add New Email Log', 'log-emails'),
                'edit_item' => __('View Email Log', 'log-emails'),
                'new_item' => __('New Email Log', 'log-emails'),
                'view_item' => __('View Email Log', 'log-emails'),
                'search_items' => __('Search Email Log', 'log-emails'),
                'not_found' => __('No email logs found', 'log-emails'),
                'not_found_in_trash' => __('No email logs found in Trash', 'log-emails'),
                'parent_item_colon' => __('Parent email logs', 'log-emails'),
            ),
            'description' => __('Email Logs, as a custom post type', 'log-emails'),
            'exclude_from_search' => true,
            'publicly_queryable' => false,
            'public' => false,
            'show_ui' => true,
            'show_in_admin_bar' => false,
            'menu_position' => 75,
            'hierarchical' => false,
            'has_archive' => false,
            'supports' => array('nada'),
            'rewrite' => false,
            'can_export' => false,
            'capabilities' => array (
                'create_posts' => false,
                'edit_post' => 'manage_options',
                'read_post' => 'manage_options',
                'delete_post' => 'manage_options',
                'edit_posts' => 'manage_options',
                'edit_others_posts' => 'manage_options',
                'publish_posts' => 'manage_options',
                'read_private_posts' => 'manage_options',
            ),
        ));
    }

    /**
    * change some text on admin pages
    * @param string $translation
    * @param string $text
    * @param string $domain
    * @return string
    */
    public function adminGetText($translation, $text, $domain) {
        if ($domain == 'default') {
            if ($text == 'Edit “%s”') {
                $translation = 'View “%s”';
            }
        }

        return $translation;
    }

    /**
    * remove views we don't need from post list
    * @param array $views
    * @return array
    */
    public function adminViewsEdit($views) {
        unset($views['publish']);
        unset($views['draft']);

        return $views;
    }

    /**
    * remove unwanted actions from post list
    * @param array $actions
    * @param WP_Post $post
    * @return array
    */
    public function adminPostRowActions($actions, $post) {
        unset($actions['inline hide-if-no-js']);        // "quick edit"
        unset($actions['trash']);
        unset($actions['edit']);

        if ($post && $post->ID) {
            // add View link
            $actions['view'] = sprintf('<a href="https://wordpress.stackexchange.com/questions/124868/%s" title="https://wordpress.stackexchange.com/questions/124868/%s">%s</a>',
                get_edit_post_link($post->ID),
                __('View', 'log-emails'), __('View', 'log-emails'));

            // add Delete link
            $actions['delete'] = sprintf('<a href="https://wordpress.stackexchange.com/questions/124868/%s" title="https://wordpress.stackexchange.com/questions/124868/%s" class="submitdelete">%s</a>',
                get_delete_post_link($post->ID, '', true),
                __('Delete', 'log-emails'), __('Delete', 'log-emails'));
        }

        return $actions;
    }

    /**
    * change the list of available bulk actions
    * @param array $actions
    * @return array
    */
    public function adminBulkActionsEdit($actions) {
        unset($actions['edit']);

        return $actions;
    }

    /**
    * filter to add columns to post list
    * @param array $posts_columns
    * @return array
    */
    public function adminManageColumns($posts_columns) {
        $posts_columns['title'] = 'Subject';
        $posts_columns['_log_emails_log_to'] = 'Recipients';

        return $posts_columns;
    }

    /**
    * action to add custom columns to post list
    * @param string $column_name
    * @param int $post_id
    */
    public function adminManageCustomColumn($column_name, $post_id) {
        switch ($column_name) {
            case '_log_emails_log_to':
                $post = get_post($post_id);
                if ($post) {
                    echo htmlspecialchars(get_post_meta($post_id, '_log_emails_log_to', true));
                }
                break;
        }
    }

    /**
    * change the screen layout
    */
    public function adminScreenLayout() {
        // set max / default layout as single column
        add_screen_option('layout_columns', array('max' => 1, 'default' => 1));
    }

    /**
    * drop all the metaboxes and output what we want to show
    */
    public function adminEditAfterTitle($post) {
        global $wp_meta_boxes;

        // remove all meta boxes
        $wp_meta_boxes = array('log_emails_log' => array(
            'advanced' => array(),
            'side' => array(),
            'normal' => array(),
        ));

        // show my admin form
        require LOG_EMAILS_PLUGIN_ROOT . 'views/log-detail.php';
    }

    /**
    * replace Trash bulk actions with Delete
    * NB: WP admin already handles the delete action, it just doesn't expose it as a bulk action
    */
    public function adminPrintFooterScripts() {
        ?>

        <script>
        jQuery("select[name="action"],select[name="action2"]").find("option[value="trash"]").each(function() {
            this.value="delete";
            jQuery(this).text("<?php esc_attr_e('Delete', 'log-emails'); ?>");
        });
        </script>

        <?php
    }

}

Leave a Comment