How do I add custom meta-data to Units?

I am trying to address two very related issues.

1) How to provide a public description of the course units? I had hoped to extend the edit panel by adding support for the excerpt to the Unit post type but I can't figure out how to add the functionality to the admin edit page.

2) How do you add custom meta data to a course or course unit?

I would expect that extending the post-type meta-data would be a common necessary feature.

  • Vinod Dalvi

    Hi @Cagle Webmaster,

    I hope you are well today and thank you for your questions.

    1) How to provide a public description of the course units? I had hoped to extend the edit panel by adding support for the excerpt to the Unit post type but I can't figure out how to add the functionality to the admin edit page.

    Do you want to display some information for every respective course units in the front end which will be accessible foe public/non logged in users?

    2) How do you add custom meta data to a course or course unit?

    Currently CoursePress Pro doesn't support adding custom meta data to a course or course unit out of the box without custom coding.

    There is already a feature request created for this feature on the following thread and this feature may be added in future version of plugin but We don't publish ETAs to prevent disappointment if a deadline is missed(which in plugin development quite a frequent occurrence!).

    https://premium.wpmudev.org/forums/topic/can-you-add-fields-to-courses-and-units

    Kind Regards,
    Vinod Dalvi

  • Cagle Webmaster

    Do you want to display some information for every respective course units in the front end which will be accessible foe public/non logged in users?

    Yes. Having a public description of each unit is a necessity. We could just add these as listed items in the general course description but that doesn't make sense when we then display the course structure below; which would then be redundant.

    Having a long description and excerpt for each unit for use as public marketing content would be idea.

    As a note, I'm writing an extension plugin that allows editing the post content and excerpt for the Unit post type outside of the Course -> Unit admin pages to facilitate this along with adding custom write panels for extending the data that can be associated with the courses and units.

    It would be nice if there was some documentation on how to hook into the admin boxes you already have but I think that's already possible. I'm just not as familiar with the plugin hooks as I am the front-end. I'll post something when I have a solution.

  • Cagle Webmaster

    Currently CoursePress Pro doesn't support adding custom meta data to a course or course unit out of the box without custom coding.

    I wrote a quick little plugin that allows you to add both custom meta-data to Unit modules as well as expose both the Content and Excerpt fields. They are accessible through a new "Unit" management page separate from the Coursepress - Course/Unit management page.

    It displays the excerpt or content field in the modified shortcode. This forks the shortcode but nothing else. It could easily be converted to run in a functions.php file but I thought putting it in a plugin would make it easier to update later and add on additional extensions if users want their own custom extensions internally.

  • Cagle Webmaster

    Evidently it wouldn't let me attach the zip file, so here's the code for the plugin below:

    coursepress-extensions.php

    <?php
    /*
    Plugin Name: Coursepress Pro - Extensions
    Version: 0.1
    Plugin URI: *private*
    Description: Custom extensions for Coursepress Pro
    Author: Theo Tillotson (email@theotillotson.com)
    Author URI: http://www.theotillotson.com/
    */
    if ( !defined( 'ABSPATH' ) )
    
    	exit; // Exit if accessed directly
    
    ////////////////////////////////////////////////////////////////////////////////
    // Reconfigure Custom Post Type Settings
    // 10/08/2014 Theo Tillotson
    // Version: 0.01
    // Updated: 10/08/2014
    // Ref: http://wpsmith.net/2013/wp/use-registered_post_type-hook-to-modify-post-type-registration/
    ////////////////////////////////////////////////////////////////////////////////
    
    add_action( 'registered_post_type', 'cppe_register_post_type', 10, 2 );
    /**
     * Modify registered post type menu label
     *
     * @param string $post_type Registered post type name.
     * @param array $args Array of post type parameters.
     */
    function cppe_register_post_type( $post_type, $args ) {
        if ( 'course' === $post_type ) {
    
            global $wp_post_types;
    
    		$args->show_ui = true;
    		$args->show_in_menu = true;
    		$args->show_in_admin_bar = true;
    		$args->menu_position = '0';
    		$args->menu_icon = 'dashicons-welcome-learn-more';
    
            $wp_post_types[ $post_type ] = $args;
    
        } elseif ( 'unit' === $post_type ) {
    
    		global $wp_post_types;
    
    		$args->show_ui = true;
    		$args->show_in_menu = true;
    		$args->show_in_admin_bar = true;
    		$args->menu_position = '0';
    		$args->menu_icon = 'dashicons-exerpt-view';
    		$wp_post_types[ $post_type ] = $args;
    
    	}
    }
    
    add_action('init', 'cppe_init');
    function cppe_init() {
    	add_post_type_support( 'unit', 'excerpt' );
    	add_post_type_support( 'unit', 'custom-fields' );
    	add_post_type_support( 'course', 'excerpt' );
    	add_post_type_support( 'course', 'custom-fields' );
    
    }
    
    add_action( 'admin_menu', 'adjust_the_wp_menu', 999 );
    function adjust_the_wp_menu() {
      $page = remove_submenu_page( 'edit.php?post_type=unit', 'post-new.php?post_type=unit' );
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // Add Columns to the admin panels for Custom Post Types
    // 10/08/2014 Theo Tillotson
    // Version: 0.01
    // Updated: 10/09/2014
    // Ref: http://www.smashingmagazine.com/2013/12/05/modifying-admin-post-lists-in-wordpress/
    ////////////////////////////////////////////////////////////////////////////////
    
    function cppe_edit_unit_columns( $columns ) {
    
    	$columns = array(
    		'cb' => '<input type="checkbox" />',
    		'course' => __( 'Course' ),
    		'course_id' => __( 'CID' ),
    		'title' => __( 'Unit Module' ),
    		'excerpt' => __( 'Excerpt' ),
    	);
    
    	return $columns;
    }
    
    add_filter('manage_course_posts_columns', 'cppe_course_table_head');
    function cppe_course_table_head( $columns ) {
        $columns['id']  = 'ID';
        $columns['course-name']    = 'Course Name';
        return $columns;
    }
    
    add_action( 'manage_course_posts_custom_column', 'cppe_course_table_content', 10, 2 );
    
    function cppe_course_table_content( $column, $post_id ) {
    	$post = get_post($post_id);
    
    	switch($column) {
    
    		case 'id' :
    			echo $post_id;
    			break;
    
    		case 'course-name' :
    			echo $post->post_title;
    			break;
    
    		default :
    			break;
    	}
    
    }
    
    function get_excerpt_by_id($post_id){
        $the_post = get_post($post_id);
        $the_excerpt = $the_post->post_excerpt;
        return $the_excerpt;
    }
    
    function get_content_by_id($post_id){
        $the_post = get_post($post_id);
        $the_content = $the_post->post_content;
        return $the_content;
    }
    
    function cppe_unit_summary($post_id) {
    	$content = get_content_by_id($post_id);
    	if ( !empty($content) ) { echo '<div id="unit-summary-'.$post_id.'" class="course-structure-unit-summary">'.$content.'</div>'; }
    }
    
    /**
     * Proper way to enqueue scripts and styles
     */
    
    function cppe_front_scripts() {
    	/*styles*/
    	wp_register_style( 'cppe-stylesheet', plugins_url('coursepress-extensions.css', __FILE__) );
    	wp_enqueue_style( 'cppe-stylesheet' );
    	/*scripts*/
    	//wp_enqueue_script( 'script-name', get_template_directory_uri() . '/js/example.js', array(), '1.0.0', true );
    }
    add_action( 'wp_enqueue_scripts', 'cppe_front_scripts' );
    
     function get_coursepress_course_structure() {
    	return do_shortcode( '[course_structure label="" show_title="no" show_divider="yes"]' );
     }
    
    ////////////////////////////////////////////////////////////////////////////////
    // Course Structure Shortcode override
    // Origin: coursepress/includes/class.shortcodes.php :: course_structure( $atts )
    // 10/08/2014 Theo Tillotson
    // Version: 0.01
    // Updated: 10/09/2014
    // Ref: http://www.smashingmagazine.com/2013/12/05/modifying-admin-post-lists-in-wordpress/
    ////////////////////////////////////////////////////////////////////////////////
    
     	/**
    
    		 * Shows the course structure.
    
    		 *
    
    		 * @since 1.0.0
    
    		 */
    
    		function cppe_course_structure( $atts ) {
    			echo get_cppe_course_structure( $atts );
    		}
    
    		function get_cppe_course_structure( $atts ) {
    
    			extract( shortcode_atts( array(
    
    				'course_id'			 => in_the_loop() ? get_the_ID() : '',
    
    				'course'			 => false,
    
    				'free_text'			 => __( 'Free', 'cp' ),
    
    				'free_show'			 => true,
    
    				'show_title'		 => 'no',
    
    				'show_label'		 => 'no',
    
    				'label_delimeter'	 => ': ',
    
    				'label_tag'			 => 'h2',
    
    				'show_divider'		 => 'yes',
    
    				'label'				 => __( 'Course Structure', 'cp' ),
    
    				'class'				 => '',
    
    			), $atts, 'course_structure' ) );
    
    			$course_id		 = (int) $course_id;	
    
    			$free_text		 = sanitize_text_field( $free_text );
    
    			$free_show		 = (bool) $free_show;
    
    			$show_title		 = sanitize_html_class( $show_title );
    
    			$show_label		 = sanitize_html_class( $show_label );
    
    			$label_delimeter = sanitize_html_class( $label_delimeter );
    
    			$label_tag		 = sanitize_html_class( $label_tag );
    
    			$show_divider	 = sanitize_html_class( $show_divider );
    
    			$label			 = sanitize_text_field( $label );
    
    			$class			 = sanitize_html_class( $class );
    
    // Saves some overhead by not loading the post again if we don't need to.
    
    			$course			 = empty( $course ) ? new Course( $course_id ) : object_decode( $course, 'Course' );
    
    			$class			 = sanitize_html_class( $class );
    
    			$label_tag		 = sanitize_html_class( $label_tag );
    
    			$label_delimeter = sanitize_html_class( $label_delimeter );
    
    			if ( $course->details->course_structure_options == 'on' ) {
    
    				$content = '';
    
    				$student			 = new Student( get_current_user_id() );
    
    				$existing_student	 = $student->has_access_to_course( $course_id );
    
    				$show_unit		 = $course->details->show_unit_boxes;
    
    				$preview_unit	 = $course->details->preview_unit_boxes;
    
    				$show_page		 = $course->details->show_page_boxes;
    
    				$preview_page	 = $course->details->preview_page_boxes;
    
    				$units = $course->get_units();
    
    				$content .= '<div class="course-structure-block course-structure-block-' . $course_id . '">';
    
    				if ( !empty( $label ) ) {
    
    					$content .= '<' . $label_tag . ' class="label">' . $label . $label_delimeter . '</' . $label_tag . '>';
    
    				}
    
    				$content .= 'yes' == $show_title ? '<label>' . $course->details->post_title . '</label>' : '';
    
    				if ( $units ) {
    
    					ob_start();
    
    					?>
    
    					<ul class="tree">
    
    						<li>
    
    							<ul>
    
    								<?php
    
    								$module = new Unit_Module();
    
    								foreach ( $units as $unit ) {
    
    									$unit_class	 = new Unit( $unit->ID );
    
    									$unit_pages	 = $unit_class->get_number_of_unit_pages();
    
    									$modules = $module->get_modules( $unit->ID );
    
    									if ( isset( $show_unit[ $unit->ID ] ) && $show_unit[ $unit->ID ] == 'on' && $unit->post_status == 'publish' ) {
    
    										?>
    
    										<li>
    
    											<label for="unit_<?php echo $unit->ID; ?>" class="course_structure_unit_label <?php echo $existing_student ? 'single_column' : ''; ?>">
    
    												<?php
    
    												$title = '';
    
    												if ( $existing_student ) {
    
    													$title = '<a href="' . $unit_class->get_permalink() . '">' . $unit->post_title . '</a>';
    
    												} else {
    
    													$title = $unit->post_title;
    
    												}
    
    												?>
    
    												<div class="tree-unit-left"><?php echo $title; ?></div>
    
    												<div class="tree-unit-right">
    
    													<?php if ( $course->details->course_structure_time_display == 'on' ) { ?>
    
    														<span><?php echo $unit_class->get_unit_time_estimation( $unit->ID ); ?></span>
    
    													<?php } ?>
    
    													<?php
    
    													if ( isset( $preview_unit[ $unit->ID ] ) && $preview_unit[ $unit->ID ] == 'on' && $unit_class->get_permalink() && !$existing_student ) {
    
    														?>
    
    														<a href="<?php echo $unit_class->get_permalink(); ?>?try" class="preview_option"><?php echo $free_text; ?></a>
    
    													<?php } ?>
    
    												</div>
    
    											</label>
    
    											<ul>
    
    												<?php
    
    												for ( $i = 1; $i <= $unit_pages; $i++ ) {
    
    													if ( isset( $show_page[ $unit->ID . '_' . $i ] ) && $show_page[ $unit->ID . '_' . $i ] == 'on' ) {
    
    														?>
    
    														<li class="course_structure_page_li <?php echo $existing_student ? 'single_column' : ''; ?>">
    
    															<?php
    
    															$pages_num	 = 1;
    
    															$page_title	 = $unit_class->get_unit_page_name( $i );
    
    															?>
    
    															<label for="page_<?php echo $unit->ID . '_' . $i; ?>">
    
    																<?php
    
    																$title		 = '';
    
    																if ( $existing_student ) {
    
    																	$p_title = isset( $page_title ) && $page_title !== '' ? $page_title : __( 'Untitled Page', 'cp' );
    
    																	$title	 = '<a href="' . $unit_class->get_permalink() . '/' . 'page/' . $i . '">' . $p_title . '</a>';
    
    																} else {
    
    																	$title = isset( $page_title ) && $page_title !== '' ? $page_title : __( 'Untitled Page', 'cp' );
    
    																}
    
    																?>
    
    																<div class="tree-page-left">
    
    																	<?php echo $title; ?>
    
    																</div>
    
    																<div class="tree-page-right">
    
    																	<?php if ( $course->details->course_structure_time_display == 'on' ) { ?>
    
    																		<span><?php echo $unit_class->get_unit_page_time_estimation( $unit->ID, $i ); ?></span>
    
    																	<?php } ?>
    
    																	<?php
    
    																	if ( isset( $preview_page[ $unit->ID . '_' . $i ] ) && $preview_page[ $unit->ID . '_' . $i ] == 'on' && $unit_class->get_permalink() && !$existing_student ) {
    
    																		?>
    
    																		<a href="<?php echo $unit_class->get_permalink(); ?>page/<?php echo $i; ?>?try" class="preview_option"><?php echo $free_text; ?></a>
    
    																	<?php } ?>
    
    																</div>
    
                                                                    <?php cppe_unit_summary($unit->ID) ?>
    
    															</label>
    
    															<?php ?>
    
    														</li>
    
    														<?php
    
    													}
    
    												}//page visible
    
    												?>
    
    											</ul>
    
    										</li>
    
    										<?php
    
    									}//unit visible
    
    								} // foreach
    
    								?>
    
    							</ul>
    
    						</li>
    
    					</ul>
    
    					<?php if ( $show_divider == 'yes' ) { ?>
    
    						<div class="divider"></div>
    
    					<?php } ?>
    
    					<?php
    
    					$content .= trim( ob_get_clean() );
    
    				} else {
    
    				}
    
    				$content .= '</div>';
    
    				return $content;
    
    			}
    
    		}
    
    function cppe_register_shortcodes(){
    	add_shortcode( 'course_structure', 'cppe_course_structure' );
    }
    
    add_action( 'init', 'cppe_register_shortcodes', 1000);
    
    ?>

    coursepress-extensions.css

    /* CSS Document */
    
    .course-structure-unit-summary {
    	white-space: normal;
    	width: 85%;
    	display: block;
    	float: left;
    	padding-left: 5%;
    	padding-right: 35px;
    	font-size: .95em;
    
    }
  • Cagle Webmaster

    Thanks!

    So I ran into a hitch. The solution works great so long as I'm logged in as the admin. If I'm logged in as an Editor or Author, the menu's I've added don't work. I think this is because of the underlying CoursePress capabilities setting, which is a bit beyond me.

    Any idea what might prevent that from working?

    Any clue if we could just get the post_content and post_excerpt fields added as metaboxes in the actual admin pages for the Units and Unit Pages. Doesn't seem like it would be that difficult to add and I'm sure people would love to be able to use those fields for any number of things, especially providing general outlines of courses.

  • Hoang Ngo

    Hi @Cagle Webmaster,

    I hope you are well today and I'm sorry for the delay.

    Because they using capabilities with the wordpress custom post type, so this code can help, but with limit ability (edit their own unit, publish)

    add_action('init', 'determine_unit_cap',9999);
    function determine_unit_cap()
    {
        $author = get_role('author');;
        //enable it for edit but can not publish
        $author->add_cap('edit_unit');
        $author->add_cap('edit_units');
        //enable it for can publish
        $author->add_cap('publish_units');
    
        $editor = get_role('editor');
        $editor->add_cap('edit_unit');
        $editor->add_cap('edit_units');
    
        $editor->add_cap('publish_units');
    }

    For extend it, this info will help you

    read - Controls whether objects of this post type can be read.
    delete_posts - Controls whether objects of this post type can be deleted.
    delete_private_posts - Controls whether private objects can be deleted.
    delete_published_posts - Controls whether published objects can be deleted.
    delete_others_posts - Controls whether objects owned by other users can be can be deleted. If the post type does not support an author, then this will behave like delete_posts.
    edit_private_posts - Controls whether private objects can be edited.
    edit_published_posts - Controls whether published objects can be edited.

    Also, you will need to check this document http://codex.wordpress.org/Function_Reference/register_post_type if you need more information.

    And lastly, if you want to extend the course's capabilities, so you this is the custom post type name course

    If you have any issues please don't hesitate to let us know so we can assist

    Best regards,
    Hoang Ngo

Thank NAME, for their help.

Let NAME know exactly why they deserved these points.

Gift a custom amount of points.