SarahMoyer
12/17/2019 - 7:11 PM

Use Divi "Projects" Post Type for Staff Directory / Team Members

If you are using the Divi Theme, from Elegant Themes, you can easily make a staff/faculty/team member directory from Divi's built-in Projects!

Advantages of re-skinning Projects:

  1. Use Divi's own "Portfolio" module to build your main directory page right in the Divi Builder.
  2. Use the "Filterable Portfolio" module to pull 1 or more staff departments onto other landing pages using the Divi Builder.

Recommended Plugins:

  1. Advanced Custom Fields Pro (ACF) for staff details
  2. Post Types Order for re-ordering staff members alphabetically (manually).

Ready to get started? Just go through each step, choosing or leaving the pieces you want to incorporate.

Overview of Steps

  1. Rename "Projects" to "Staff".
  2. Add custom templates.
  3. Set up "Staff Information" box.
  4. Display Job Title / Position in the Portfolio Divi Module.

Let's get started!

1. Rename "Projects" to "Staff".

  • Add functions.php code (file below) to your child theme's functions.php.
  • Check notes in the code for any changes you may wish to make.
  • Refresh the permalinks after adding functions.php code. To do so, simply click "Save" in WP Admin -> Settings -> Permalinks.

You may notice additional code in the functions.php file below. You can add that code in later steps.

2. Add custom templates.

  • Add single-project.php (file below) into your child theme, for individual staff posts.
  • Add taxonomy-project_category.php (file below) into your child theme, for staff categories or departments.
  • At some point, check notes in the code for any changes you may wish to make.
  • Add styles from style.css into your child theme's stylesheet. Modify as desired.

You may notice that I'm using ACF Pro plugin to build the staff details. You can add that in the next step, then return to these templates to check the notes.

3. Set up "Staff Information" box.

I use Advanced Custom Fields PRO WordPress plugin to build a nice "Staff Info" meta box for job title, phone, location, etc.

If you want to use ACF, skip to "Steps to build ACF staff info box." If you don't, see alternatives below.

To use ACF or not to use

Why I use ACF. Using ACF, staff details can be easily updated in the WP Admin. I love the simplicity and UI for my client websites. The plugin is very affordable and quite versatile. The templates above are ready to go with the ACF data fields that I share below. If you are already familiar with ACF, you can easily customize it to your needs.

Instead of ACF, you could do one of these:

  1. Activate the Divi Builder on "Projects" and make your layout there. The downside is if you have 100 people, and you want a new layout, you have to update it 100 times. No downside if you have just a few. My preference is to keep the Divi Builder out of posts.
  2. Make your own Staff Info box in the Editor. Same downside as above.
  3. Use custom fields straight from WordPress. The downside is for ease of use if it's a client's website.

Steps to build ACF staff info box

  1. Import metabox into ACF: acf-export-staff-information-metabox.json
  2. Replace ku_ with your website prefix. (Also replace in custom templates.)
  3. Remove any fields you won't need. (Then remove in custom templates.)
  4. Save your ACF Field Group.
  5. Finally, here is a screenshot of the ACF settings for Staff Information box:

4. Display Job Title / Position in the Portfolio Divi Module.

We know it's super easy to pull in staff using the Divi module "Portfolio" or "Filterable Portfolio."

But what if you want to display their position or job title?

That's where it gets tricky. To do so, we need to rebuild the module in our child theme, and then add custom code to display the position.

Here's how to do that:

"Filterable Portfolio" Divi module

  • Add divi-filterable-portfolio.php (file below) into your child theme's /lib/ folder. Create folder if needed. (This file replaces cfwpm.php file in the Divi theme.)
  • Make sure you have added the code from functions.php (file below) that overrides the original module code.
  • If you change the name or location of /your-child-theme/lib/divi-filterable-portfolio.php, you also need to update the code in functions.php as well.
  • In divi-filterable-portfolio.php, search for "NOTE" for 2 changes you will need to make.

(Reference: I used this helpful tutorial here: https://quiroz.co/add-excerpts-to-your-filterable-portfolio)

"Portfolio" Divi module

The helpful tutorial above showed me how to do the Filterable Portfolio module, but not the Portfolio module.

Instead of wasting time trying to do it myself, I decided NOT to use the Portfolio module, and ONLY use the Filterable Portfolio module in the Divi Builder.

But... the "filter" is unnecessary when, for example, displaying Science department faculty on the Science landing page.

In that case, add CSS from style.css to hide the filter of Divi module "Filterable Portfolio."

<?php // put this code into your child theme's functions.php file. See NOTES below for any changes you may wish to make.

//* Rename "Project" post type to "Staff"
// @link https://support.opte.network/topic/rename-project-custom-post-type-and-slug-with-divi-theme/
add_action( 'init', 'jennywren_rename_project_cpt' );
function jennywren_rename_project_cpt() {
	register_post_type( 'project',
		array(
		'labels' => array(
			'name'          => __( 'Staff', 'divi' ), // NOTE: Choose your directory name here.
			'singular_name' => __( 'Staff Member', 'divi' ), // NOTE: Choose singular name here.
		),
		'has_archive'  => true,
		'hierarchical' => false, // NOTE: Choose false if using "Post Types Order" plugin.
	    'menu_icon'    => 'dashicons-groups',  // NOTE: Choose dashicon here.
		'public'       => true,
		'rewrite'      => array( 'slug' => 'staff', 'with_front' => false ), // NOTE: Choose staff slug here (i.e. website.com/staff/jennry-wren).
		'supports'     => array(),
	         
	));
}

//* Change taxonomy slug "/project_category/" to "/departments/"
// @link https://wordpress.stackexchange.com/questions/161788/how-to-modify-a-taxonomy-thats-already-registered
add_action( 'init', 'jennywren_modify_taxonomy', 99 ); // 11 to override the original register_taxonomy function
function jennywren_modify_taxonomy() {
    $department_args = get_taxonomy( 'project_category' ); // returns an object
    $department_args->rewrite['slug'] = 'department'; // NOTE: Choose category slug here (i.e. website.com/department/english).
    register_taxonomy( 'project_category', 'project', (array) $department_args );
}


//* Remove taxonomy metabox "project_tag" for Edit screens
// @link https://wordpress.stackexchange.com/questions/102655/remove-custom-taxonomy-metabox-from-custom-post-type-screen
// NOTE: Use this code block if you don't want webmasters to use staff tags.
add_action( 'admin_menu' , 'jennywren_remove_taxonomies_metaboxes' );
function jennywren_remove_taxonomies_metaboxes() {
    remove_meta_box( 'tagsdiv-project_tag', 'project', 'normal' );
}


//* Customize "Filterable Portfolio" module for staff members
// @link https://quiroz.co/add-excerpts-to-your-filterable-portfolio/
// NOTE: Use this code block if you are using the file "divi-filterable-portfolio" to modify the Divi module Filterable Portfolio.
add_action( 'wp', 'divi_child_theme_setup', 9999 );
function divi_child_theme_setup() {
    if ( ! class_exists('ET_Builder_Module') ) {
        return;
    }
    get_template_part( 'lib/divi-filterable-portfolio' ); // NOTE: Change if your file is not named divi-filterable-portfolio.php in /your-child-theme/lib/ folder.
    $cfwpm = new Custom_ET_Builder_Module_Filterable_Portfolio();
    remove_shortcode( 'et_pb_filterable_portfolio' );
    add_shortcode( 'et_pb_filterable_portfolio', array($cfwpm, '_shortcode_callback') );
}


//* Change sort order to menu order for archives for "Project" post type
// @link https://wordpress.stackexchange.com/questions/39817/sort-results-by-name-asc-order-on-archive-php
// NOTE: Use this code block if using "Post Types Order" plugin
add_action( 'pre_get_posts', 'jennywren_change_sort_order'); 
function jennywren_change_sort_order( $query ){
    if( is_tax( 'project_category') ) {
    	$query->set('order', 'ASC'); 	
		$query->set( 'orderby', 'menu_order' );
    }   
};
<?php // put this file into your child theme. See NOTES below for any changes you may need to make.

/**
 * Single Staff Post, for Staff Directory ("Project" CPT, rewrite to /staff/)
 */

get_header(); ?>

<div id="main-content">
	<div class="container">
		<div id="content-area" class="clearfix">

			<?php while ( have_posts() ) : the_post(); ?>
			<article id="post-<?php the_ID(); ?>" <?php post_class( 'et_pb_post' ); ?>>

				<?php // Breadcrumbs ?>
				<div class="breadcrumbs directory-link-top">
				  <?php // (NOTE: Change "/directory" link below to your main directory page.) ?>
					<a href="/directory">Staff Directory</a> / <?php the_terms( $post->ID, 'project_category', ' ', ', ', ' ' ); ?> / <span class="breadcrumb_last"><?php the_title(); ?></span>
				</div>		
				
				<?php // Start Schema ?>
				<div class="staff-details-wrap" itemscope="" itemtype="http://schema.org/Person">
				
					<?php // Photo Headshot ?>
					<div class="staff-photo">		
						<?php // Variables for Staff Photo
						$staff_id = get_post_thumbnail_id();
						$staff_url = wp_get_attachment_image_src( $staff_id, 'staff-photo', true );
						$staff_thumb = $staff_url[0];
						$staff_alt = get_post_meta( $staff_id, '_wp_attachment_image_alt', true );
						?>
						
						<?php if ( has_post_thumbnail() ) : ?>
					    <img src="<?php echo $staff_thumb; ?>" alt="<?php echo $staff_alt; ?>" itemprop="image"/>
					    	<?php else : 
					    	// (NOTE: Change "placeholder-staff-photo.jpg" below to your placeholder photo url.) ?> 
								<img src="<?php echo site_url(); ?>/wp-content/uploads/placeholder-staff-photo.jpg" class="no-image-found"/>
						<?php endif; ?>
					</div>
					
					<?php // Staff Details ?>
					<div class="staff-details">
						
						<h1 class="name" itemprop="name"><?php the_title(); ?></h1>
						<hr/>
						
						<?php // Variables for Staff Details
						// (NOTE: Change all "ku_" below to the same prefix as your ACF fields. Remove variables that you do not have.)
						$position 	= get_post_meta( get_the_ID(), 'ku_staff_position', true );
						$phone 		= get_post_meta( get_the_ID(), 'ku_staff_phone', true );
						$phone_link = preg_replace("/[^0-9]/", "", $phone); // keep if using $phone
						$fax 		= get_post_meta( get_the_ID(), 'ku_staff_fax', true );
						$email 		= get_post_meta( get_the_ID(), 'ku_staff_email', true );
						$location 	= get_post_meta( get_the_ID(), 'ku_staff_location', true );
						$url 		= get_post_meta( get_the_ID(), 'ku_staff_url', true );
						?>
						
					<?php // (NOTE: Remove details (fax, location, etc.) that you do not have.) ?>
					<?php if( $position ) { ?>
						<h2 class="position" itemprop="jobTitle">
				            <?php echo esc_html( $position ); ?>
				        </h2><?php
				        } ?>
				        	
						<?php if( $phone ) { ?>
						<div class="phone staff-meta" itemprop="telephone"><?php
							echo '<a href="tel:' . esc_html( $phone_link ) . '" target="_blank">' . esc_html( $phone ) . '</a>'; ?>
						</div><?php
						} ?>
						
						<?php if( $fax ) { ?>
						<div class="fax staff-meta"><?php 
							echo esc_html( $fax ); ?>
						</div><?php
						} ?>
				
						<?php if( $email ) { ?>
						<div class="email staff-meta" itemprop="email"><?php 
							echo '<a href="mailto:' . esc_html( $email ) . '" target="_blank">' . esc_html( $email ) . '</a>'; ?>
					    </div><?php
						} ?>
				
						<?php if( $location ) { ?>
						<div class="location staff-meta" itemprop="workLocation"><?php 
							echo esc_html( $location ); ?>
					    </div><?php
						} ?>
					
						<?php if( $url ) { ?>
						<div class="url staff-meta" itemprop="url"><?php
							echo '<a href="' . esc_html( $url ) . '" target="_blank">' . esc_html( $url ) . '</a>'; ?>
						</div><?php
						} ?>
				
						<div class="department">
							<?php the_terms( $post->ID, 'project_category', ' ', ' ', ' ' ); ?>
						</div>
						
					</div>
				</div><?php // End Schema ?>
	
	
				<?php // Bio / Main Content ?>
				<div class="entry-content"><?php
					the_content(); ?>		
				</div>
					
			</article>
			<?php endwhile; ?>

		</div> <!-- #content-area -->
	</div> <!-- .container -->
</div> <!-- #main-content -->

<?php get_footer(); ?>
<?php // put this file into your child theme. See NOTES below for any changes you may need to make.

/**
 * Department Listings, for Staff Directory ("project_category" taxonomy, rewrite to /department/)
 */

get_header(); ?>

<div id="main-content">
	<div class="container">
		<div id="content-area" class="clearfix">
			
			<header class="archive-title">
				
				<?php // Breadcrumbs
					$tax = $wp_query->get_queried_object(); ?>
				<div class="breadcrumbs directory-link-top">
				  <?php // (NOTE: Change "/directory" link below to your main directory page) ?>
					<a href="/directory">Staff Directory</a> / <span class="breadcrumb_last"><?php echo $tax->name;?></span>
				</div>
				
				<?php // Archive Title ?>
				<h1><?php single_cat_title(); ?></h1>
				<hr/>

			</header>

			<?php if ( have_posts() ) :
			while ( have_posts() ) : the_post(); ?>

			<article id="post-<?php the_ID(); ?>" <?php post_class( 'et_pb_post' ); ?>>

				<?php // Photo Headshot ?>
				<a class="staff-photo" href="<?php the_permalink(); ?>">
					<?php if ( has_post_thumbnail() ) { 
					  // (NOTE: Change 'staff-photo' below to 'thumb' or 'small' or whatever image size you need.)
						echo get_the_post_thumbnail( $post->ID, 'staff-photo', array( 'class' => 'staff-photo-img' ) );
					} else { 
					  // (NOTE: Change "placeholder-staff-photo.jpg" below to your placeholder photo url) ?>
						<img src="<?php echo site_url(); ?>/wp-content/uploads/placeholder-staff-photo.jpg" class="no-image-found"/><?php
					} ?>
				</a>

				<?php // Name ?>
				<h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
		
				<?php // Position / Job Title 
				  // (NOTE: Change "ku_" below to the same prefix as your ACF fields. Remove if you do not have.)
					$position  = get_post_meta( get_the_ID(), 'ku_staff_position', true ); ?>
				<div class="position"><?php 
		            echo esc_html( $position ); ?>
		        </div>
		
			</article> <!-- .et_pb_post -->
			
			<?php endwhile; ?>
			<?php endif; ?>

		</div> <!-- #content-area -->
	</div> <!-- .container -->
</div> <!-- #main-content -->

<?php get_footer(); ?>
/*******************************************/
/* Staff Directory (Divi's "Project" post type)
/*******************************************/

/* Resets
-------------------------------------------------------------------------------- */

/* Remove custom bullets, if custom bullets were used on site */

.entry-content ul li.et_pb_portfolio_filter::before,
.entry-content .et_pb_portofolio_pagination ul li::before {
	content: none;
}

/* Remove Divi sidebar border, since staff is fullwidth */

.single-project  #main-content .container::before,
.tax-project_category  #main-content .container::before {
	content: none;
}

/* Overall photo treatment
-------------------------------------------------------------------------------- */

.staff-photo img,
.et_pb_portfolio_item .et_portfolio_image img {
  border: 1px solid #666;
}

.staff-photo img:hover,
.et_pb_portfolio_item .et_portfolio_image img:hover {
	opacity: 0.7;
  transform: rotate(-1deg);
	}
}

/* Individual Posts - Single
-------------------------------------------------------------------------------- */

/* Staff Photos / Headshots */

.single .staff-photo {
    max-width: 326px; /* 300px + 13px padding left and right */
    display: inline-block;
    vertical-align: top;
}

/* Staff Details & Meta */

.single .staff-details {
    margin-top: 30px;
}

.single h2.position {
	font-size: 25px !important;
	font-style: italic;
  padding-top: .5em;
}
	
.single .staff-meta {
	padding: 10px 0 10px 40px;
	line-height: 1.5;
	display: block;	
	a {
    	text-decoration: underline;
		&:hover {
    		text-decoration: none;
    	}
    }
}

.single .phone 		{ background: url(../images/icon-chat.png) left center no-repeat; }
.single .fax 		{ background: url(../images/icon-notebook.png) left center no-repeat; }
.single .email 		{ background: url(../images/icon-email.png) left center no-repeat; }
.single .location 	{ background: url(../images/icon-notebook.png) left center no-repeat; }
.single .url 		{ background: url(../images/icon-web.png) left center no-repeat; }

/* Department Button */

.single .department {
	margin: 30px 0 10px;
	a {
		border: 2px solid #024 !important;
		color: #024 !important;
	    padding: .4em 1.5em !important;
		&:hover {
			background: #024 !important;
			color: white !important;
		}
	}		
}

/* 2 Columns on Desktop */

@media (min-width: 768px) {	
	.single .staff-photo {
	    margin-right: 5%;
	    max-width: 40%;
	}
	.single .staff-details {
		display: inline-block;
	    margin-top: 0;
	    max-width: 54%;
	    vertical-align: top;
	}
}
@media (min-width: 981) {	
	.single .staff-photo {
	    max-width: 326px; /* 300px + 13px padding left and right */
	}
	.single .staff-details {
	    width: calc(95% - 326px); /* 100% - 5% margin = 95% */
	}
}

/* Department Listings - Taxonomy project_category
 * Staff Directory - Divi module "Filterable Portfolio"
-------------------------------------------------------------------------------- */

/* Staff Name */

.tax-project_category h2,
.et_pb_filterable_portfolio h2 {
	font-size: 30px;
	margin: 10px 0 5px;
	max-width: 240px;
	padding: 0;
	a:hover {
		color: #999;
	}
}

/* Staff Position */

.tax-project_category .position,
.et_pb_filterable_portfolio .position {
    margin: inherit !important;
    line-height: 1.2em;
}

/* Multiple Columns on Desktop */

@media (min-width: 480px) {
	.archive .et_pb_post.project {
	    float: left;
		margin-right: 5%;
		margin-bottom: 5%;
		width: 45%;
	}
}
@media (min-width: 768px) {
	.archive .et_pb_post.project {
		width: 28.333%;
	}
}
@media (min-width: 981px) {
	.archive .et_pb_post.project {
		width: 20%;
	}
}
@media (min-width: 480px) and (max-width: 767px) {
	.archive .et_pb_post.project:nth-child(2n+2) {
		clear: both;
	}	
}
@media (min-width: 768px) and (max-width: 980px) {
	.archive .et_pb_post.project:nth-child(3n+2) {
		clear: both;
	}	
}
@media (min-width: 981px) {
	.archive .et_pb_post.project:nth-child(4n+2) {
		clear: both;
	}	
}

/* Staff Directory - Divi module "Filterable Portfolio"
-------------------------------------------------------------------------------- */

/* Hide filter, if .hideCategories class is added.
	Add "hideCategories" to Filterable Portfolio module -> Settings -> CSS IDs & Classes -> Classes */

.hideCategories.et_pb_filterable_portfolio .et_pb_portfolio_filters {
  display: none;
}

/* Fix Pagination */

.et_pb_gallery .et_pb_gallery_pagination, 
.et_pb_portfolio_grid .et_pb_portofolio_pagination,
.et_pb_filterable_portfolio .et_pb_portofolio_pagination {
	border: none;
	clear: both;
}
<?php // put this file into your child theme's /lib/ folder. Create folder if needed. Search for "NOTE" for 2 changes you will need to make.	

/**
 * Divi module "Filterable Portfolio" for Staff Directory ("Project" CPT, converted to staff directory)
 * Replaces the Divi theme file "cfwpm.php", see child theme's functions.php file.
 * @link https://quiroz.co/add-excerpts-to-your-filterable-portfolio/
 *
 * To see changes in this file, search for "EDIT".
 */

class Custom_ET_Builder_Module_Filterable_Portfolio extends ET_Builder_Module {
    function init() {
        $this->name = __( 'Filterable Portfolio', 'et_builder' );
        $this->slug = 'et_pb_filterable_portfolio';
 
        $this->whitelisted_fields = array(
            'fullwidth',
            'posts_number',
            'include_categories',
            'show_title',
            'show_categories',
            'show_pagination',
            'background_layout',
            'admin_label',
            'module_id',
            'module_class',
            'hover_icon',
            'zoom_icon_color',
            'hover_overlay_color',
        );
 
        $this->fields_defaults = array(
            'fullwidth'         => array( 'on' ),
            'posts_number'      => array( 10, 'add_default_setting' ),
            'show_title'        => array( 'on' ),
            'show_categories'   => array( 'on' ),
            'show_pagination'   => array( 'on' ),
            'background_layout' => array( 'light' ),
        );
 
        $this->main_css_element = '%%order_class%%.et_pb_filterable_portfolio';
        $this->advanced_options = array(
            'fonts' => array(
                'title'   => array(
                    'label'    => __( 'Title', 'et_builder' ),
                    'css'      => array(
                        'main' => "{$this->main_css_element} h2",
                        'important' => 'all',
                    ),
                ),
                'caption' => array(
                    'label'    => __( 'Meta', 'et_builder' ),
                    'css'      => array(
                        'main' => "{$this->main_css_element} .post-meta, {$this->main_css_element} .post-meta a",
                    ),
                ),
                'filter' => array(
                    'label'    => __( 'Filter', 'et_builder' ),
                    'css'      => array(
                        'main' => "{$this->main_css_element} .et_pb_portfolio_filter",
                    ),
                ),
            ),
            'background' => array(
                'settings' => array(
                    'color' => 'alpha',
                ),
            ),
            'border' => array(
                'css' => array(
                    'main' => "{$this->main_css_element} .et_pb_portfolio_item",
                ),
            ),
        );
        $this->custom_css_options = array(
            'portfolio_filters' => array(
                'label'    => __( 'Portfolio Filters', 'et_builder' ),
                'selector' => '.et_pb_filterable_portfolio .et_pb_portfolio_filters',
            ),
            'active_portfolio_filter' => array(
                'label'    => __( 'Active Portfolio Filter', 'et_builder' ),
                'selector' => '.et_pb_filterable_portfolio .et_pb_portfolio_filters li a.active',
            ),
            'portfolio_image' => array(
                'label'    => __( 'Portfolio Image', 'et_builder' ),
                'selector' => '.et_portfolio_image',
            ),
            'overlay' => array(
                'label'    => __( 'Overlay', 'et_builder' ),
                'selector' => '.et_overlay',
            ),
            'overlay_icon' => array(
                'label'    => __( 'Overlay Icon', 'et_builder' ),
                'selector' => '.et_overlay:before',
            ),
            'portfolio_title' => array(
                'label'    => __( 'Portfolio Title', 'et_builder' ),
                'selector' => '.et_pb_portfolio_item h2',
            ),
            'portfolio_post_meta' => array(
                'label'    => __( 'Portfolio Post Meta', 'et_builder' ),
                'selector' => '.et_pb_portfolio_item .post-meta',
            ),
        );
    }
 
    function get_fields() {
        $fields = array(
            'fullwidth' => array(
                'label'           => __( 'Layout', 'et_builder' ),
                'type'            => 'select',
                'option_category' => 'layout',
                'options'         => array(
                    'on'  => __( 'Fullwidth', 'et_builder' ),
                    'off' => __( 'Grid', 'et_builder' ),
                ),
                'description'        => __( 'Choose your desired portfolio layout style.', 'et_builder' ),
            ),
            'posts_number' => array(
                'label'             => __( 'Posts Number', 'et_builder' ),
                'type'              => 'text',
                'option_category'   => 'configuration',
                'description'       => __( 'Define the number of projects that should be displayed per page.', 'et_builder' ),
            ),
            'include_categories' => array(
                'label'           => __( 'Include Categories', 'et_builder' ),
                'renderer'        => 'et_builder_include_categories_option',
                'option_category' => 'basic_option',
                'description'     => __( 'Select the categories that you would like to include in the feed.', 'et_builder' ),
            ),
            'show_title' => array(
                'label'             => __( 'Show Title', 'et_builder' ),
                'type'              => 'yes_no_button',
                'option_category'   => 'configuration',
                'options'           => array(
                    'on'  => __( 'Yes', 'et_builder' ),
                    'off' => __( 'No', 'et_builder' ),
                ),
                'description'        => __( 'Turn project titles on or off.', 'et_builder' ),
            ),
            'show_categories' => array(
                'label'             => __( 'Show Categories', 'et_builder' ),
                'type'              => 'yes_no_button',
                'option_category'   => 'configuration',
                'options'           => array(
                    'on'  => __( 'Yes', 'et_builder' ),
                    'off' => __( 'No', 'et_builder' ),
                ),
                'description'        => __( 'Turn the category links on or off.', 'et_builder' ),
            ),
            'show_pagination' => array(
                'label'             => __( 'Show Pagination', 'et_builder' ),
                'type'              => 'yes_no_button',
                'option_category'   => 'configuration',
                'options'           => array(
                    'on'  => __( 'Yes', 'et_builder' ),
                    'off' => __( 'No', 'et_builder' ),
                ),
                'description'        => __( 'Enable or disable pagination for this feed.', 'et_builder' ),
            ),
            'background_layout' => array(
                'label'           => __( 'Text Color', 'et_builder' ),
                'type'            => 'select',
                'option_category' => 'color_option',
                'options' => array(
                    'light'  => __( 'Dark', 'et_builder' ),
                    'dark' => __( 'Light', 'et_builder' ),
                ),
                'description'        => __( 'Here you can choose whether your text should be light or dark. If you are working with a dark background, then your text should be light. If your background is light, then your text should be set to dark.', 'et_builder' ),
            ),
            'admin_label' => array(
                'label'       => __( 'Admin Label', 'et_builder' ),
                'type'        => 'text',
                'description' => __( 'This will change the label of the module in the builder for easy identification.', 'et_builder' ),
            ),
            'module_id' => array(
                'label'           => __( 'CSS ID', 'et_builder' ),
                'type'            => 'text',
                'option_category' => 'configuration',
                'description'     => __( 'Enter an optional CSS ID to be used for this module. An ID can be used to create custom CSS styling, or to create links to particular sections of your page.', 'et_builder' ),
            ),
            'module_class' => array(
                'label'           => __( 'CSS Class', 'et_builder' ),
                'type'            => 'text',
                'option_category' => 'configuration',
                'description'     => __( 'Enter optional CSS classes to be used for this module. A CSS class can be used to create custom CSS styling. You can add multiple classes, separated with a space.', 'et_builder' ),
            ),
            'hover_icon' => array(
                'label'               => __( 'Hover Icon Picker', 'et_builder' ),
                'type'                => 'text',
                'option_category'     => 'configuration',
                'class'               => array( 'et-pb-font-icon' ),
                'renderer'            => 'et_pb_get_font_icon_list',
                'renderer_with_field' => true,
                'tab_slug'            => 'advanced',
            ),
            'zoom_icon_color' => array(
                'label'             => __( 'Zoom Icon Color', 'et_builder' ),
                'type'              => 'color',
                'custom_color'      => true,
                'tab_slug'          => 'advanced',
            ),
            'hover_overlay_color' => array(
                'label'             => __( 'Hover Overlay Color', 'et_builder' ),
                'type'              => 'color-alpha',
                'custom_color'      => true,
                'tab_slug'          => 'advanced',
            ),
        );
        return $fields;
    }
 
    function shortcode_callback( $atts, $content = null, $function_name ) {
        $module_id          = $this->shortcode_atts['module_id'];
        $module_class       = $this->shortcode_atts['module_class'];
        $fullwidth          = $this->shortcode_atts['fullwidth'];
        $posts_number       = $this->shortcode_atts['posts_number'];
        $include_categories = $this->shortcode_atts['include_categories'];
        $show_title         = $this->shortcode_atts['show_title'];
        $show_categories    = $this->shortcode_atts['show_categories'];
        $show_pagination    = $this->shortcode_atts['show_pagination'];
        $background_layout  = $this->shortcode_atts['background_layout'];
        $hover_icon          = $this->shortcode_atts['hover_icon'];
        $zoom_icon_color     = $this->shortcode_atts['zoom_icon_color'];
        $hover_overlay_color = $this->shortcode_atts['hover_overlay_color'];
 
        $module_class = ET_Builder_Element::add_module_order_class( $module_class, $function_name );
 
        wp_enqueue_script( 'hashchange' );
 
        $args = array();
 
        if ( '' !== $zoom_icon_color ) {
            ET_Builder_Element::set_style( $function_name, array(
                'selector'    => '%%order_class%% .et_overlay:before',
                'declaration' => sprintf(
                    'color: %1$s !important;',
                    esc_html( $zoom_icon_color )
                ),
            ) );
        }
 
        if ( '' !== $hover_overlay_color ) {
            ET_Builder_Element::set_style( $function_name, array(
                'selector'    => '%%order_class%% .et_overlay',
                'declaration' => sprintf(
                    'background-color: %1$s;
                    border-color: %1$s;',
                    esc_html( $hover_overlay_color )
                ),
            ) );
        }
 
        if( 'on' === $show_pagination ) {
            $args['nopaging'] = true;
        } else {
            $args['posts_per_page'] = (int) $posts_number;
        }
 
        if ( '' !== $include_categories ) {
            $args['tax_query'] = array(
                array(
                    'taxonomy' => 'project_category',
                    'field' => 'id',
                    'terms' => explode( ',', $include_categories ),
                    'operator' => 'IN',
                )
            );
        }
 
        $projects = et_divi_get_projects( $args );
 
        $categories_included = array();
        ob_start();
        if( $projects->post_count > 0 ) {
            while ( $projects->have_posts() ) {
                $projects->the_post();
 
                $category_classes = array();
                $categories = get_the_terms( get_the_ID(), 'project_category' );
                if ( $categories ) {
                    foreach ( $categories as $category ) {
                        $category_classes[] = 'project_category_' . urldecode( $category->slug );
                        $categories_included[] = $category->term_id;
                    }
                }
 
                $category_classes = implode( ' ', $category_classes );
 
                $main_post_class = sprintf(
                    'et_pb_portfolio_item%1$s %2$s',
                    ( 'on' !== $fullwidth ? ' et_pb_grid_item' : '' ),
                    $category_classes
                );
 
                ?>
                <div id="post-<?php the_ID(); ?>" <?php post_class( $main_post_class ); ?>>
                <?php
                    $thumb = '';
 
                    $width = 'on' === $fullwidth ?  1080 : 400;
                    $width = (int) apply_filters( 'et_pb_portfolio_image_width', $width );
 
                    $height = 'on' === $fullwidth ?  9999 : 284;
                    $height = (int) apply_filters( 'et_pb_portfolio_image_height', $height );
                    $classtext = 'on' === $fullwidth ? 'et_pb_post_main_image' : '';
                    $titletext = get_the_title();
                    $thumbnail = get_thumbnail( $width, $height, $classtext, $titletext, $titletext, false, 'Blogimage' );
                    $thumb = $thumbnail["thumb"];
 
// EDIT: Changed : to {
                    if ( '' !== $thumb ) { ?>
                        <a href="<?php the_permalink(); ?>">
                        <?php if ( 'on' !== $fullwidth ) : ?>
                            <span class="et_portfolio_image">
                        <?php endif; ?>
                                <?php print_thumbnail( $thumb, $thumbnail["use_timthumb"], $titletext, $width, $height ); ?>
                        <?php if ( 'on' !== $fullwidth ) :
 
                                $data_icon = '' !== $hover_icon
                                    ? sprintf(
                                        ' data-icon="%1$s"',
                                        esc_attr( et_pb_process_font_icon( $hover_icon ) )
                                    )
                                    : '';
 
                                printf( '<span class="et_overlay%1$s"%2$s></span>',
                                    ( '' !== $hover_icon ? ' et_pb_inline_icon' : '' ),
                                    $data_icon
                                );
 
                        ?>
                            </span>
                        <?php endif; ?>
                        </a><?php 

// EDIT: Added placeholder image if no staff photo.
	                }
                    else { // NOTE: Change "placeholder-staff-photo.jpg" below to your placeholder photo ?>
						<img src="<?php echo site_url(); ?>/wp-content/uploads/placeholder-staff-photo.jpg" class="no-image-found"/><?php
					}
                ?>
                 
                <?php if ( 'on' === $show_title ) : ?>
                    <h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
                <?php endif;
                
// EDIT: Added staff position. ?>
	            <div class="position"><?php // NOTE: Change "ku_" below to the same prefix as your ACF field. ?>
		            <?php $text  = get_post_meta( get_the_ID(), 'ku_staff_position', true );
					echo esc_html( $text ); ?>
                </div>
 
                <?php if ( 'on' === $show_categories ) : ?>
                    <p class="post-meta"><?php echo get_the_term_list( get_the_ID(), 'project_category', '', ', ' ); ?></p>
                <?php endif; ?>
                
                </div><!-- .et_pb_portfolio_item -->
                <?php
            }
        }
 
        wp_reset_postdata();
 
        $posts = ob_get_clean();
 
        $categories_included = explode ( ',', $include_categories );
        $terms_args = array(
            'include' => $categories_included,
            'orderby' => 'name',
            'order' => 'ASC',
        );
        $terms = get_terms( 'project_category', $terms_args );
 
        $category_filters = '<ul class="clearfix">';
        $category_filters .= sprintf( '<li class="et_pb_portfolio_filter et_pb_portfolio_filter_all"><a href="#" class="active" data-category-slug="all">%1$s</a></li>',
            esc_html__( 'All', 'et_builder' )
        );
        foreach ( $terms as $term  ) {
            $category_filters .= sprintf( '<li class="et_pb_portfolio_filter"><a href="#" data-category-slug="%1$s">%2$s</a></li>',
                esc_attr( urldecode( $term->slug ) ),
                esc_html( $term->name )
            );
        }
        $category_filters .= '</ul>';
 
        $class = " et_pb_module et_pb_bg_layout_{$background_layout}";
 
        $output = sprintf(
            '<div%5$s class="et_pb_filterable_portfolio %1$s%4$s%6$s" data-posts-number="%7$d"%10$s>
                <div class="et_pb_portfolio_filters clearfix">%2$s</div><!-- .et_pb_portfolio_filters -->
 
                <div class="et_pb_portfolio_items_wrapper %8$s">
                    <div class="et_pb_portfolio_items">%3$s</div><!-- .et_pb_portfolio_items -->
                </div>
                %9$s
            </div> <!-- .et_pb_filterable_portfolio -->',
            ( 'on' === $fullwidth ? 'et_pb_filterable_portfolio_fullwidth' : 'et_pb_filterable_portfolio_grid clearfix' ),
            $category_filters,
            $posts,
            esc_attr( $class ),
            ( '' !== $module_id ? sprintf( ' id="%1$s"', esc_attr( $module_id ) ) : '' ),
            ( '' !== $module_class ? sprintf( ' %1$s', esc_attr( $module_class ) ) : '' ),
            esc_attr( $posts_number),
            ('on' === $show_pagination ? '' : 'no_pagination' ),
            ('on' === $show_pagination ? '<div class="et_pb_portofolio_pagination"></div>' : '' ),
            is_rtl() ? ' data-rtl="true"' : ''
        );
 
        return $output;
    }
}