Adding custom modules to Divi

This is a little bit more advanced than our average tutorial. Proceed with caution and enthusiasm πŸ™‚

Whilst helping out in my favourite Divi theme groupΒ recently a case came up for making small structural changes to a Divi page builder module. In this case, we needed to place the grid blog module header above the featured image, like so:

modified blog module
  • Google+
  • Facebook
  • Twitter

It occurred to me that others may be interested in editing modules in this way, so here’s how you do it.

Extending the page builder

At this point we’re assuming you have a child theme with its own functions.php set up. Add this code to your functions.php to extend the page builder module to a new file that we’re going to add to our child theme called ‘ds-custom-modules.php’…

function DS_Custom_Modules(){
 if(class_exists("ET_Builder_Module")){
 include("ds-custom-modules.php");
 }
}

function Prep_DS_Custom_Modules(){
 global $pagenow;

$is_admin = is_admin();
 $action_hook = $is_admin ? 'wp_loaded' : 'wp';
 $required_admin_pages = array( 'edit.php', 'post.php', 'post-new.php', 'admin.php', 'customize.php', 'edit-tags.php', 'admin-ajax.php', 'export.php' ); // list of admin pages where we need to load builder files
 $specific_filter_pages = array( 'edit.php', 'admin.php', 'edit-tags.php' );
 $is_edit_library_page = 'edit.php' === $pagenow && isset( $_GET['post_type'] ) && 'et_pb_layout' === $_GET['post_type'];
 $is_role_editor_page = 'admin.php' === $pagenow && isset( $_GET['page'] ) && 'et_divi_role_editor' === $_GET['page'];
 $is_import_page = 'admin.php' === $pagenow && isset( $_GET['import'] ) && 'wordpress' === $_GET['import']; 
 $is_edit_layout_category_page = 'edit-tags.php' === $pagenow && isset( $_GET['taxonomy'] ) && 'layout_category' === $_GET['taxonomy'];

if ( ! $is_admin || ( $is_admin && in_array( $pagenow, $required_admin_pages ) && ( ! in_array( $pagenow, $specific_filter_pages ) || $is_edit_library_page || $is_role_editor_page || $is_edit_layout_category_page || $is_import_page ) ) ) {
 add_action($action_hook, 'DS_Custom_Modules', 9789);
 }
}
Prep_DS_Custom_Modules();

The next step is to create the file called ds-custom-modules.php and add it to our child theme root alongside functions.php and any other files we have in there.

Housing new modules

Now the next part depends on which module you’d like to edit. In the Divi theme files includes > builder > main-modules.php you’ll find all of the modules. In our case we wanted to edit the blog module so we copied everything between these two lines –

class ET_Builder_Module_Blog extends ET_Builder_Module {
EVERYTHING IN HERE GOT COPIED
new ET_Builder_Module_Blog;

Each module will start and end like this so it’s easy to know what you need to copy. There are three things that you must change in your new blog module: The class, the name and the slug. You’ll find those lines at the top of your module –

class DS_Custom_Module_Blog extends ET_Builder_Module {
 function init() {
 $this->name = esc_html__( 'Blog - Custom Grid', 'et_builder' );
 $this->slug = 'et_pb_blog_2';

You can see in our example we kept it simple and renamed it ‘Blog – Custom Grid’ and changed the slug to ‘et_pb_blog_2’, but you can change it to whatever you want to. We renamed our class to ‘DS_Custom_Module_blog’.

As you can see, the new module now appears alongside our normal ones:

grid example
  • Google+
  • Facebook
  • Twitter

The only other changes you should need to make is to adjust the structure or design as needed. If you plan on making CSS changes then you may want to add new classes to the various elements so you can target them separately to the standard modules, but that CSS can then be added in your style sheet as normal.

For reference and testing, here is the code from our custom module –

<?php

class DS_Custom_Module_Blog extends ET_Builder_Module {
 function init() {
 $this->name = esc_html__( 'Blog - Custom Grid', 'et_builder' );
 $this->slug = 'et_pb_blog_2';

 $this->whitelisted_fields = array(
 'fullwidth',
 'posts_number',
 'include_categories',
 'meta_date',
 'show_thumbnail',
 'show_content',
 'show_more',
 'show_author',
 'show_date',
 'show_categories',
 'show_comments',
 'show_pagination',
 'offset_number',
 'background_layout',
 'admin_label',
 'module_id',
 'module_class',
 'masonry_tile_background_color',
 'use_dropshadow',
 'use_overlay',
 'overlay_icon_color',
 'hover_overlay_color',
 'hover_icon',
 );

 $this->fields_defaults = array(
 'fullwidth' => array( 'off' ),
 'posts_number' => array( 10, 'add_default_setting' ),
 'meta_date' => array( 'M j, Y', 'add_default_setting' ),
 'show_thumbnail' => array( 'on' ),
 'show_content' => array( 'off' ),
 'show_more' => array( 'off' ),
 'show_author' => array( 'on' ),
 'show_date' => array( 'on' ),
 'show_categories' => array( 'on' ),
 'show_comments' => array( 'off' ),
 'show_pagination' => array( 'on' ),
 'offset_number' => array( 0, 'only_default_setting' ),
 'background_layout' => array( 'light' ),
 'use_dropshadow' => array( 'off' ),
 'use_overlay' => array( 'off' ),
 );

 $this->main_css_element = '%%order_class%% .et_pb_post';
 $this->advanced_options = array(
 'fonts' => array(
 'header' => array(
 'label' => esc_html__( 'Header', 'et_builder' ),
 'css' => array(
 'main' => "{$this->main_css_element} h2",
 'important' => 'all',
 ),
 ),
 'meta' => array(
 'label' => esc_html__( 'Meta', 'et_builder' ),
 'css' => array(
 'main' => "{$this->main_css_element} .post-meta",
 ),
 ),
 'body' => array(
 'label' => esc_html__( 'Body', 'et_builder' ),
 'css' => array(
 'line_height' => "{$this->main_css_element} p",
 ),
 ),
 ),
 'border' => array(),
 );
 $this->custom_css_options = array(
 'title' => array(
 'label' => esc_html__( 'Title', 'et_builder' ),
 'selector' => '.et_pb_post h2',
 ),
 'post_meta' => array(
 'label' => esc_html__( 'Post Meta', 'et_builder' ),
 'selector' => '.et_pb_post .post-meta',
 ),
 'pagenavi' => array(
 'label' => esc_html__( 'Pagenavi', 'et_builder' ),
 'selector' => '.wp_pagenavi',
 ),
 'featured_image' => array(
 'label' => esc_html__( 'Featured Image', 'et_builder' ),
 'selector' => '.et_pb_image_container',
 ),
 'read_more' => array(
 'label' => esc_html__( 'Read More Button', 'et_builder' ),
 'selector' => '.et_pb_post .more-link',
 ),
 );
 }

 function get_fields() {
 $fields = array(
 'posts_number' => array(
 'label' => esc_html__( 'Posts Number', 'et_builder' ),
 'type' => 'text',
 'option_category' => 'configuration',
 'description' => esc_html__( 'Choose how much posts you would like to display per page.', 'et_builder' ),
 ),
 'include_categories' => array(
 'label' => esc_html__( 'Include Categories', 'et_builder' ),
 'renderer' => 'et_builder_include_categories_option',
 'option_category' => 'basic_option',
 'renderer_options' => array(
 'use_terms' => false,
 ),
 'description' => esc_html__( 'Choose which categories you would like to include in the feed.', 'et_builder' ),
 ),
 'meta_date' => array(
 'label' => esc_html__( 'Meta Date Format', 'et_builder' ),
 'type' => 'text',
 'option_category' => 'configuration',
 'description' => esc_html__( 'If you would like to adjust the date format, input the appropriate PHP date format here.', 'et_builder' ),
 ),
 'show_thumbnail' => array(
 'label' => esc_html__( 'Show Featured Image', 'et_builder' ),
 'type' => 'yes_no_button',
 'option_category' => 'configuration',
 'options' => array(
 'on' => esc_html__( 'Yes', 'et_builder' ),
 'off' => esc_html__( 'No', 'et_builder' ),
 ),
 'description' => esc_html__( 'This will turn thumbnails on and off.', 'et_builder' ),
 ),
 'show_content' => array(
 'label' => esc_html__( 'Content', 'et_builder' ),
 'type' => 'select',
 'option_category' => 'configuration',
 'options' => array(
 'off' => esc_html__( 'Show Excerpt', 'et_builder' ),
 'on' => esc_html__( 'Show Content', 'et_builder' ),
 ),
 'affects' => array(
 '#et_pb_show_more',
 ),
 'description' => esc_html__( 'Showing the full content will not truncate your posts on the index page. Showing the excerpt will only display your excerpt text.', 'et_builder' ),
 ),
 'show_more' => array(
 'label' => esc_html__( 'Read More Button', 'et_builder' ),
 'type' => 'yes_no_button',
 'option_category' => 'configuration',
 'options' => array(
 'off' => esc_html__( 'Off', 'et_builder' ),
 'on' => esc_html__( 'On', 'et_builder' ),
 ),
 'depends_show_if' => 'off',
 'description' => esc_html__( 'Here you can define whether to show "read more" link after the excerpts or not.', 'et_builder' ),
 ),
 'show_author' => array(
 'label' => esc_html__( 'Show Author', 'et_builder' ),
 'type' => 'yes_no_button',
 'option_category' => 'configuration',
 'options' => array(
 'on' => esc_html__( 'Yes', 'et_builder' ),
 'off' => esc_html__( 'No', 'et_builder' ),
 ),
 'description' => esc_html__( 'Turn on or off the author link.', 'et_builder' ),
 ),
 'show_date' => array(
 'label' => esc_html__( 'Show Date', 'et_builder' ),
 'type' => 'yes_no_button',
 'option_category' => 'configuration',
 'options' => array(
 'on' => esc_html__( 'Yes', 'et_builder' ),
 'off' => esc_html__( 'No', 'et_builder' ),
 ),
 'description' => esc_html__( 'Turn the date on or off.', 'et_builder' ),
 ),
 'show_categories' => array(
 'label' => esc_html__( 'Show Categories', 'et_builder' ),
 'type' => 'yes_no_button',
 'option_category' => 'configuration',
 'options' => array(
 'on' => esc_html__( 'Yes', 'et_builder' ),
 'off' => esc_html__( 'No', 'et_builder' ),
 ),
 'description' => esc_html__( 'Turn the category links on or off.', 'et_builder' ),
 ),
 'show_comments' => array(
 'label' => esc_html__( 'Show Comment Count', 'et_builder' ),
 'type' => 'yes_no_button',
 'option_category' => 'configuration',
 'options' => array(
 'on' => esc_html__( 'Yes', 'et_builder' ),
 'off' => esc_html__( 'No', 'et_builder' ),
 ),
 'description' => esc_html__( 'Turn comment count on and off.', 'et_builder' ),
 ),
 'show_pagination' => array(
 'label' => esc_html__( 'Show Pagination', 'et_builder' ),
 'type' => 'yes_no_button',
 'option_category' => 'configuration',
 'options' => array(
 'on' => esc_html__( 'Yes', 'et_builder' ),
 'off' => esc_html__( 'No', 'et_builder' ),
 ),
 'description' => esc_html__( 'Turn pagination on and off.', 'et_builder' ),
 ),
 'offset_number' => array(
 'label' => esc_html__( 'Offset Number', 'et_builder' ),
 'type' => 'text',
 'option_category' => 'configuration',
 'description' => esc_html__( 'Choose how many posts you would like to offset by', 'et_builder' ),
 ),
 'use_overlay' => array(
 'label' => esc_html__( 'Featured Image Overlay', 'et_builder' ),
 'type' => 'yes_no_button',
 'option_category' => 'layout',
 'options' => array(
 'off' => esc_html__( 'Off', 'et_builder' ),
 'on' => esc_html__( 'On', 'et_builder' ),
 ),
 'affects' => array(
 '#et_pb_overlay_icon_color',
 '#et_pb_hover_overlay_color',
 '#et_pb_hover_icon',
 ),
 'description' => esc_html__( 'If enabled, an overlay color and icon will be displayed when a visitors hovers over the featured image of a post.', 'et_builder' ),
 ),
 'overlay_icon_color' => array(
 'label' => esc_html__( 'Overlay Icon Color', 'et_builder' ),
 'type' => 'color',
 'custom_color' => true,
 'depends_show_if' => 'on',
 'description' => esc_html__( 'Here you can define a custom color for the overlay icon', 'et_builder' ),
 ),
 'hover_overlay_color' => array(
 'label' => esc_html__( 'Hover Overlay Color', 'et_builder' ),
 'type' => 'color-alpha',
 'custom_color' => true,
 'depends_show_if' => 'on',
 'description' => esc_html__( 'Here you can define a custom color for the overlay', 'et_builder' ),
 ),
 'hover_icon' => array(
 'label' => esc_html__( '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,
 'depends_show_if' => 'on',
 'description' => esc_html__( 'Here you can define a custom icon for the overlay', 'et_builder' ),
 ),
 'background_layout' => array(
 'label' => esc_html__( 'Text Color', 'et_builder' ),
 'type' => 'select',
 'option_category' => 'color_option',
 'options' => array(
 'light' => esc_html__( 'Dark', 'et_builder' ),
 'dark' => esc_html__( 'Light', 'et_builder' ),
 ),
 'depends_default' => true,
 'description' => esc_html__( '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' ),
 ),
 'masonry_tile_background_color' => array(
 'label' => esc_html__( 'Grid Tile Background Color', 'et_builder' ),
 'type' => 'color-alpha',
 'custom_color' => true,
 'tab_slug' => 'advanced',
 'depends_show_if' => 'off',
 ),
 'use_dropshadow' => array(
 'label' => esc_html__( 'Use Dropshadow', 'et_builder' ),
 'type' => 'yes_no_button',
 'option_category' => 'layout',
 'options' => array(
 'off' => esc_html__( 'Off', 'et_builder' ),
 'on' => esc_html__( 'On', 'et_builder' ),
 ),
 'tab_slug' => 'advanced',
 'depends_show_if' => 'off',
 ),
 'disabled_on' => array(
 'label' => esc_html__( 'Disable on', 'et_builder' ),
 'type' => 'multiple_checkboxes',
 'options' => array(
 'phone' => esc_html__( 'Phone', 'et_builder' ),
 'tablet' => esc_html__( 'Tablet', 'et_builder' ),
 'desktop' => esc_html__( 'Desktop', 'et_builder' ),
 ),
 'additional_att' => 'disable_on',
 'option_category' => 'configuration',
 'description' => esc_html__( 'This will disable the module on selected devices', 'et_builder' ),
 ),
 'admin_label' => array(
 'label' => esc_html__( 'Admin Label', 'et_builder' ),
 'type' => 'text',
 'description' => esc_html__( 'This will change the label of the module in the builder for easy identification.', 'et_builder' ),
 ),
 'module_id' => array(
 'label' => esc_html__( 'CSS ID', 'et_builder' ),
 'type' => 'text',
 'option_category' => 'configuration',
 'tab_slug' => 'custom_css',
 'option_class' => 'et_pb_custom_css_regular',
 ),
 'module_class' => array(
 'label' => esc_html__( 'CSS Class', 'et_builder' ),
 'type' => 'text',
 'option_category' => 'configuration',
 'tab_slug' => 'custom_css',
 'option_class' => 'et_pb_custom_css_regular',
 ),
 );
 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'];
 $meta_date = $this->shortcode_atts['meta_date'];
 $show_thumbnail = $this->shortcode_atts['show_thumbnail'];
 $show_content = $this->shortcode_atts['show_content'];
 $show_author = $this->shortcode_atts['show_author'];
 $show_date = $this->shortcode_atts['show_date'];
 $show_categories = $this->shortcode_atts['show_categories'];
 $show_comments = $this->shortcode_atts['show_comments'];
 $show_pagination = $this->shortcode_atts['show_pagination'];
 $background_layout = $this->shortcode_atts['background_layout'];
 $show_more = $this->shortcode_atts['show_more'];
 $offset_number = $this->shortcode_atts['offset_number'];
 $masonry_tile_background_color = $this->shortcode_atts['masonry_tile_background_color'];
 $use_dropshadow = $this->shortcode_atts['use_dropshadow'];
 $overlay_icon_color = $this->shortcode_atts['overlay_icon_color'];
 $hover_overlay_color = $this->shortcode_atts['hover_overlay_color'];
 $hover_icon = $this->shortcode_atts['hover_icon'];
 $use_overlay = $this->shortcode_atts['use_overlay'];

 global $paged;

 $module_class = ET_Builder_Element::add_module_order_class( $module_class, $function_name );

 $container_is_closed = false;

 // remove all filters from WP audio shortcode to make sure current theme doesn't add any elements into audio module
 remove_all_filters( 'wp_audio_shortcode_library' );
 remove_all_filters( 'wp_audio_shortcode' );
 remove_all_filters( 'wp_audio_shortcode_class');

 if ( '' !== $masonry_tile_background_color ) {
 ET_Builder_Element::set_style( $function_name, array(
 'selector' => '%%order_class%%.et_pb_blog_grid .et_pb_post',
 'declaration' => sprintf(
 'background-color: %1$s;',
 esc_html( $masonry_tile_background_color )
 ),
 ) );
 }

 if ( '' !== $overlay_icon_color ) {
 ET_Builder_Element::set_style( $function_name, array(
 'selector' => '%%order_class%% .et_overlay:before',
 'declaration' => sprintf(
 'color: %1$s !important;',
 esc_html( $overlay_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;',
 esc_html( $hover_overlay_color )
 ),
 ) );
 }

 if ( 'on' === $use_overlay ) {
 $data_icon = '' !== $hover_icon
 ? sprintf(
 ' data-icon="%1$s"',
 esc_attr( et_pb_process_font_icon( $hover_icon ) )
 )
 : '';

 $overlay_output = sprintf(
 '<span class="et_overlay%1$s"%2$s></span>',
 ( '' !== $hover_icon ? ' et_pb_inline_icon' : '' ),
 $data_icon
 );
 }

 $overlay_class = 'on' === $use_overlay ? ' et_pb_has_overlay' : '';

 if ( 'on' !== $fullwidth ){
 if ( 'on' === $use_dropshadow ) {
 $module_class .= ' et_pb_blog_grid_dropshadow';
 }

 wp_enqueue_script( 'salvattore' );

 $background_layout = 'light';
 }

 $args = array( 'posts_per_page' => (int) $posts_number );

 $et_paged = is_front_page() ? get_query_var( 'page' ) : get_query_var( 'paged' );

 if ( is_front_page() ) {
 $paged = $et_paged;
 }

 if ( '' !== $include_categories )
 $args['cat'] = $include_categories;

 if ( ! is_search() ) {
 $args['paged'] = $et_paged;
 }

 if ( '' !== $offset_number && ! empty( $offset_number ) ) {
 /**
 * Offset + pagination don't play well. Manual offset calculation required
 * @see: https://codex.wordpress.org/Making_Custom_Queries_using_Offset_and_Pagination
 */
 if ( $paged > 1 ) {
 $args['offset'] = ( ( $et_paged - 1 ) * intval( $posts_number ) ) + intval( $offset_number );
 } else {
 $args['offset'] = intval( $offset_number );
 }
 }

 if ( is_single() && ! isset( $args['post__not_in'] ) ) {
 $args['post__not_in'] = array( get_the_ID() );
 }

 ob_start();

 query_posts( $args );

 if ( have_posts() ) {
 while ( have_posts() ) {
 the_post();

 $post_format = et_pb_post_format();

 $thumb = '';

 $width = 'on' === $fullwidth ? 1080 : 400;
 $width = (int) apply_filters( 'et_pb_blog_image_width', $width );

 $height = 'on' === $fullwidth ? 675 : 250;
 $height = (int) apply_filters( 'et_pb_blog_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"];

 $no_thumb_class = '' === $thumb || 'off' === $show_thumbnail ? ' et_pb_no_thumb' : '';

 if ( in_array( $post_format, array( 'video', 'gallery' ) ) ) {
 $no_thumb_class = '';
 } ?>

 <article id="post-<?php the_ID(); ?>" <?php post_class( 'et_pb_post' . $no_thumb_class . $overlay_class ); ?>>
 
 <h2 class="entry-title blog-2" style="position: relative; top: -20px; margin-bottom: 10px;"><a href="<?php esc_url( the_permalink() ); ?>"><?php the_title(); ?></a></h2>

 <?php
 et_divi_post_format_content();

 if ( ! in_array( $post_format, array( 'link', 'audio', 'quote' ) ) ) {
 if ( 'video' === $post_format && false !== ( $first_video = et_get_first_video() ) ) :
 printf(
 '<div class="et_main_video_container">
 %1$s
 </div>',
 $first_video
 );
 elseif ( 'gallery' === $post_format ) :
 et_pb_gallery_images( 'slider' );
 elseif ( '' !== $thumb && 'on' === $show_thumbnail ) :
 if ( 'on' !== $fullwidth ) echo '<div class="et_pb_image_container">'; ?>
 <a href="<?php esc_url( the_permalink() ); ?>" class="entry-featured-image-url">
 <?php print_thumbnail( $thumb, $thumbnail["use_timthumb"], $titletext, $width, $height ); ?>
 <?php if ( 'on' === $use_overlay ) {
 echo $overlay_output;
 } ?>
 </a>
 <?php
 if ( 'on' !== $fullwidth ) echo '</div> <!-- .et_pb_image_container -->';
 endif;
 } ?>

 <?php if ( 'off' === $fullwidth || ! in_array( $post_format, array( 'link', 'audio', 'quote' ) ) ) { ?>
 <?php if ( ! in_array( $post_format, array( 'link', 'audio' ) ) ) { ?>
 <?php } ?>

 <?php
 if ( 'on' === $show_author || 'on' === $show_date || 'on' === $show_categories || 'on' === $show_comments ) {
 printf( '<p class="post-meta">%1$s %2$s %3$s %4$s %5$s %6$s %7$s</p>',
 (
 'on' === $show_author
 ? sprintf( __( 'by %s', 'et_builder' ), '<span class="author vcard">' . et_pb_get_the_author_posts_link() . '</span>' ) 
 : ''
 ),
 (
 ( 'on' === $show_author && 'on' === $show_date )
 ? ' | '
 : ''
 ),
 (
 'on' === $show_date
 ? sprintf( __( '%s', 'et_builder' ), '<span class="published">' . esc_html( get_the_date( $meta_date ) ) . '</span>' ) 
 : ''
 ),
 (
 (( 'on' === $show_author || 'on' === $show_date ) && 'on' === $show_categories)
 ? ' | '
 : ''
 ),
 (
 'on' === $show_categories
 ? get_the_category_list(', ')
 : ''
 ),
 (
 (( 'on' === $show_author || 'on' === $show_date || 'on' === $show_categories ) && 'on' === $show_comments)
 ? ' | '
 : ''
 ),
 (
 'on' === $show_comments
 ? sprintf( esc_html( _nx( '1 Comment', '%s Comments', get_comments_number(), 'number of comments', 'et_builder' ) ), number_format_i18n( get_comments_number() ) )
 : ''
 )
 );
 }

 $post_content = get_the_content();

 // do not display the content if it contains Blog, Post Slider, Fullwidth Post Slider, or Portfolio modules to avoid infinite loops
 if ( ! has_shortcode( $post_content, 'et_pb_blog' ) && ! has_shortcode( $post_content, 'et_pb_portfolio' ) && ! has_shortcode( $post_content, 'et_pb_post_slider' ) && ! has_shortcode( $post_content, 'et_pb_fullwidth_post_slider' ) ) {
 if ( 'on' === $show_content ) {
 global $more;

 // page builder doesn't support more tag, so display the_content() in case of post made with page builder
 if ( et_pb_is_pagebuilder_used( get_the_ID() ) ) {
 $more = 1;
 the_content();
 } else {
 $more = null;
 the_content( esc_html__( 'read more...', 'et_builder' ) );
 }
 } else {
 if ( has_excerpt() ) {
 the_excerpt();
 } else {
 truncate_post( 270 );
 }
 }
 } else if ( has_excerpt() ) {
 the_excerpt();
 }

 if ( 'on' !== $show_content ) {
 $more = 'on' == $show_more ? sprintf( ' <a href="%1$s" class="more-link" >%2$s</a>' , esc_url( get_permalink() ), esc_html__( 'read more', 'et_builder' ) ) : '';
 echo $more;
 }
 ?>
 <?php } // 'off' === $fullwidth || ! in_array( $post_format, array( 'link', 'audio', 'quote', 'gallery' ?>

 </article> <!-- .et_pb_post -->
 <?php
 } // endwhile

 if ( 'on' === $show_pagination && ! is_search() ) {
 echo '</div> <!-- .et_pb_posts -->';

 $container_is_closed = true;

 if ( function_exists( 'wp_pagenavi' ) ) {
 wp_pagenavi();
 } else {
 if ( et_is_builder_plugin_active() ) {
 include( ET_BUILDER_PLUGIN_DIR . 'includes/navigation.php' );
 } else {
 get_template_part( 'includes/navigation', 'index' );
 }
 }
 }

 wp_reset_query();
 } else {
 if ( et_is_builder_plugin_active() ) {
 include( ET_BUILDER_PLUGIN_DIR . 'includes/no-results.php' );
 } else {
 get_template_part( 'includes/no-results', 'index' );
 }
 }

 $posts = ob_get_contents();

 ob_end_clean();

 $class = " et_pb_module et_pb_bg_layout_{$background_layout}";

 $output = sprintf(
 '<div%5$s class="%1$s%3$s%6$s"%7$s>
 %2$s
 %4$s',
 ( 'on' === $fullwidth ? 'et_pb_posts' : 'et_pb_blog_grid clearfix' ),
 $posts,
 esc_attr( $class ),
 ( ! $container_is_closed ? '</div> <!-- .et_pb_posts -->' : '' ),
 ( '' !== $module_id ? sprintf( ' id="%1$s"', esc_attr( $module_id ) ) : '' ),
 ( '' !== $module_class ? sprintf( ' %1$s', esc_attr( $module_class ) ) : '' ),
 ( 'on' !== $fullwidth ? ' data-columns' : '' )
 );

 if ( 'on' !== $fullwidth )
 $output = sprintf( '<div class="et_pb_blog_grid_wrapper">%1$s</div>', $output );

 return $output;
 }
}
new DS_Custom_Module_Blog;

 

 

Previous post
Next post

48 Comments

  1. Rebekah

    Hi, SJ. So, this isn’t adding a brand new module but commandeering one that already exists? The old blog module has been replaced with the new one? There aren’t two options for the module, correct?

    Thank you,
    rbkh

    Reply
    • Stephen

      Hey πŸ™‚ No, this would create a new module it’s just easier to use the existing setup of a module for reference, but not crucial.

    • Stephen

      Hey πŸ™‚ No, this would create a new module it’s just easier to use the existing setup of a module for reference, but not crucial.

  2. Adam

    Great stuff, like always! I’ll give it a try my next Divi customization.
    Thanks, SJ πŸ™‚

    Reply
  3. Dave

    This was great SJ!
    I have a question, how did you work out what is needed for the functions.php file.
    If I wanted to make another blurb type module,where would I look or how would I determine the correct code for the functions.php part of this tutorial?
    Thank you.
    D.

    Reply
  4. Horia

    Hello SJ, thanks for all the great Divi tutorials and documentation you offer. I am a junior Web Dev taking my first steps through WordPress and I only recently discovered the Divi theme. Please tell me, is there any way I can use the Divi Builder to generate content and then populate it with WordPress content (f.e. Custom Post Types) through a WP_Query ? I have searched the internet for 2 days now and I think your article is the closest I got to what I want. Looking forward in your reply! Cheers!

    Reply
  5. Horia

    Ok. So I have created the Blog – Custom Grid module and it appears in the builder as it should. Can you tell me if I can tie my custom module to bring in content from a Custom Post Type in order to generate dynamic content ? It would be awesome.

    Reply
    • Ben

      Hey Horia,

      I like the sound of what you mentioned and I am working on something similar. If you want to drop me an email then we could discuss what you had in mind.

      Ben
      ben[@]noou[.]co[.]uk

    • Ollie

      Hi Horia and Ben, your comments are exactly what I am trying to achieve. I would love to know how you’re going with this.

      You can email me at ocoady[@]gmail[.]com

    • Chris

      Have any of you developed a solution for this that you could point me to also? I am also attempting the same and would greatly value if you can move me further down the field faster.

      If you don’t mind, could you email me at chris[@]hisandheranderson[.]com

  6. Martin conde

    Great tutorial, thanks a lot!

    I was wondering.. Would be possible to somehow copy the code of some of the extra theme blog modules and inject it into divi using the steps of your tutorial? That’d be gold:)

    Reply
    • Stephen

      In theory, there’s no reason you couldn’t do this πŸ™‚ I’m sure it would be a great option for many.

    • Adam

      That’s exactly what I was trying to do, since I am dissapointed how Extra Theme restricts usage of the Divi builder… But that is a little more complicated. I didn’t find where the Extra modules resides. In the …>includes>builder>main-modules.php there are only the native Divi modules. Can anyone be so kind and tell me where to look for the Extra modules? Thanks πŸ™‚

  7. Mhluzi

    Hi Stephen

    This looks like exactly what I need – I have copied and pasted your code but the new module is not showing up.

    I have checked and double checked my code and the only thing I can think of at this stage is a possible version issue.

    I am using Divi 2.7.3 with a simple Divi Child theme.. do you think that could be an issue?

    Any ideas would be greatly appreciated!

    Cheers
    M

    Reply
  8. Mhluzi

    Hi Stephen

    I reached out for help too soon!

    Seems to be working now so all good!

    Cheers

    M

    Reply
  9. Stephen

    Stephen, I’m so happy I found your website. I’m currently customizing a header nav to function as a sticky menu at the foot of the site. I’ve been eyeing all your articles and I’m ecstatic. I can’t believe I never thought of customizing divi modules like you have in this tutorial. You make it look so easy. Thank you so much πŸ™‚

    Reply
    • Mhluzi

      Hi Stephen

      I had got this up and running and have noticed one thing. When inserting a new module it appears fine but in the Blog Module Settings, General Settings tab it doesn’t have the option “Layout” to choose between Fullwidth and Grid.

      I have changed the line in the DS_Custom_Module_Blog class to read:
      ‘fullwidth’ => array( ‘on’ ),

      But that didn’t seem to help.

      But, if, in the DS_Custom_Module_Blog class, I change the line :
      $this->slug = ‘et_pb_blog_2’;
      to
      $this->slug = ‘et_pb_blog’;

      then the “Layout” option appears but the original Blog module then disappears.

      Have you experienced that and if so do you perhaps know a way around it?

      Cheers

      M.

  10. Dawson

    For all the novice php folks out there: I just spent an hour not sure why it wouldn’t work, finally realized that of course you have to change the class in the last line of the function as well (new [class]), so for anyone having trouble don’t forget to do that!

    This is hugely useful, thanks for the great post!

    Reply
  11. David

    HI Mhluzi,

    I don’t know if you found your answer to your question yet or not.

    You need to use a name other than “blog”. The code is setup to not give access to certain features if you use the words, “blog, Post Slider, Fullwidth Post Slider, or Portfolio” to avoid creating an infinite loop.

    Just use something else in its place and everything will work correctly.

    Hope that helps.

    Regards,
    David

    Reply
  12. Terry

    SJ, thanks a ton for providing this. It opens up a whole new world of opportunities.

    One thing I’ve found is that the slug name *must* begin with “et_pb_”. Otherwise the shortcode callbacks don’t work, and the module will not be saved to the builder.

    For example, I used “n10s_pb_image” as the slug. The module would appear in the builder with all the custom options, but it would not save.

    Took several hours over three days to figure that out lol. πŸ™‚

    Reply
    • Denis

      Thank you Terry! You saved my day!:)

  13. Thomas

    I explored the Divi theme to create a new module by duplicating an existing one. But got an issue. I even could insert the new module and it was displayed on the page but the generated code has not the closing shortcode.

    I checked the database table to get the code: Normal module is coded like: [module_slug]content[/module_slug]
    But the code of my new module I created is: [my_new_module_slug]content
    {without the closing shortcode tag}
    This caused, that in the backend, when editing the page, my new module isn’t displayed, even the code contains still the shortcode of my new module.
    So I was happy to discover your article here. But after applying your steps I’m wondering now, why I still have the same problem, inspite I followed exactly the steps you are explaining here – OK the most are identical with what I did, but there are quite some differences.

    No one here seems to have the issue I have – so I don’t understand it. May it depend on the newest version 2.7.5 of DIVI, I’m using?

    Reply
    • Stephen

      Hi Thomas, it could be.. I haven’t tested this with the latest version. i’ll give it a try and let you know what I come up with πŸ™‚

    • Thomas

      Now it seems to work. But I don’t know why. In the first module I created, I changed more text items starting with ‘et_pb_’ to something like ‘t3d_’ as my own identifier. In the second one I left the most of all as it was in the original Divi module. Now the issue has gone. I can insert and save the new module and it is still diplayed even in the backend of an edited page.

      I want to build a filterable service module for a seperately defined custom post type ‘service’ with a divi type taxonomy ‘service-type’. Now I discover several dependencies and a lot of things that has to be changed and added in comparison to the original module I used as base, the filterable portfolio module which uses the project post type of Divi. Now it seems to me to be possible to do that, but it’s probably much more work than I had expected. Anyway exiting job!

  14. Bill

    Using latest wp and Divi 2.7 with a child theme. So far so good. I’m going to try customizing the blurb module wrapping it in a div so the whole div is clickable and on hover animate the elements inside, title, icon etc.
    Thanks for this code Stephen. Do you think it will need to change with 3.0?

    Reply
  15. Reak

    Greate tutorial, how put author name above the featured image to?
    what You changed to put titele above image?

    Reply
  16. Kim Joy Fox

    Your instructions were great. I got it all working, with the new module showing up. But when I tried to add another text field, I can’t get the extra field to show up. I tried several different names for the field, added it to the “$this->whitelisted_fields” array, the “$fields” array, the function “shortcode_callback”, and the output, but the field won’t show up in the module in the backend of the site. Do you know if it’s possible to add more fields to a module?

    Reply
  17. fxbodin

    Hi!
    First, thanks for sharing the method.

    I (believe I) have followed the steps, and near everything works well, except that, in the WP page editor after I submit the page including he custom module, when refreshing after submit, the DIVI editor doesn’t show the module no more. BUT, the website displays the custom module content.
    Have you any idea on what I did wrong?
    DIVI Version: 2.7.5
    Wordpress Version 4.5.3
    Thanks in advance and best regards.

    fxbodin

    Reply
  18. Theresa

    Hi is this the same way to add a custom post type to look like the blog grid or do you have to create a page template. I want to build my own custom post types not use a third party plugin if possible but it is really hard to get it to use as a module.

    Reply
  19. Theresa

    Hi I tried to change this for a custom post type and it works till a point. I modelled off this one and the filtered portfolio module but it won’t get the categories or taxonomy terms from my custom post type only the blog or portfolio. I changed the names in the new module but it isn’t working. Do you have any idea why it isn’t grabbing it?

    Reply
  20. Andrea

    Hi Stephen,
    Do you reckon this is something which we’ll be compatible with Divi 3.0?

    Reply
    • Andrea

      will* ..sorry

  21. Alex

    Thanks!

    Reply
  22. zalman

    Any idea whether this would work with divi 3.0?

    Reply
  23. Rajeshkannan MJ

    Can you please let me know why it is not working for Divi 3 ? I think the admin check you are doing the function makes not to work in frontend builder?

    Reply
  24. Marco

    Someone tried in Extra theme? What changes?

    Reply
  25. Matt Adams

    I’ve had a few challenges with some custom modules and 3.0.1 and their appearance on the new front end builder. I’d be really curious to hear if you’ve had an success adapting custom modules to appear on the front end builder interface.

    Reply
  26. Gabriel

    Not working correctly on Divi 3 Visual Builder. How adding module in Divi 3 with support Visual Builder?

    Reply
    • Jorge L Saud

      add_action(‘et_builder_framework_loaded’,’DiviLoadGallery’);
      function DiviLoadGallery(){
      include(“divi-slick-gallery.php”);
      }
      class ET_Divi_Slick_Gallery extends ET_Builder_Module{

      }

    • Rui

      Hi were you able to add the modules to Visual Builder?
      Thanks!

  27. Flo Nelson

    Awesome! I need to do this and so happy to have this guide already written up!

    Reply
  28. Sergio

    Just in case someone is wondering, the snippet does not work with the latest version of Extra (I was getting an infinite loop). However, after sever hours of trying, I decided to copy the content of includes/builder/module/Blog.php from the parent theme, following the instructions in this article. Everything worked great :).

    Reply
  29. Hristo

    It works in Divi 3 but for some reason when using it it inserts new containers and if I set ID to the module it sets it two times. I made some modifications to the portfolio plugin and here is what I get:

    Does anybody has any idea why is this happening?

    Reply
    • Hristo

      I forgot to mention that it does this even if not modifying the code at all, just registering the new plugin. Thank you!

  30. Seth

    I copied this exactly, except I swapped the blog module for the accordion module. The new module is not appearing in builder. Any ideas? Thanks!

    Reply
  31. Travis

    I’m working on trying to create a custom project slider based on the project slider. Using the guidance from this post, I’m seeing my new slider module in the list of modules. However, when I click on it to apply the settings, it seems to be pulling the default slider options. The label changes in my custom file are showing. Thoughts?

    Reply
  32. Jack

    This tutorial is great! Now I cane some extra styles to Divi modules!

    Reply

Submit a Comment

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


Receive notifications about our new blog posts.


Don’t leave before entering to win an iPad Pro! It’s Free!

You may be too early for our Black Friday specials, but you can still enter our competition! Leave your details below and you could win an iPad Pro!

Great you've entered our giveaway! Only one step remaining! Please check your email and confirm it.