elegantthemes / Divi-Beta

8 stars 0 forks source link

Adding Toggles & Fields to existing modules and modifying module output #1

Open dtcblyth opened 1 year ago

dtcblyth commented 1 year ago

Description: I have two D4 extensions which use the et_builder_get_parent_modules(), et_builder_get_child_modules(), and et_pb_all_fields_unprocessed_{$slug}() filters to add custom Toggles and Fields to built-in and third-party Divi modules. These additional field settings are then processed using the et_module_shortcode_output() filter to modify module output on the front-end.

Request: In order for me to continue supporting my products it will be important for me to be able to replicate this functionality in D5. I imagine that there are other third-party extensions which rely on these features also. It may be too early to ask, but any documentation or assistance that ET can provide for replicating this functionality in D5 would be greatly appreciated.

Conclusion: I’m more than happy to provide sample code and/or access to my extensions upon request. Thanks!

etstaging commented 1 year ago

QA Checklist

Reporter

Developer

Peer Review

Quality Assurance

Design Review

Dev Review

dtcblyth commented 1 year ago

Bonus Request:

The current implementation of et_pb_all_fields_unprocessed_{$slug} requires us to manually loop through all possible parent and child module slugs, setting up a filter for each. Although this is kind of laborious, it is at least possible to do for built-in modules because we know what they are. However, for third-party modules we have no way to know which third-party modules a customer will use.

Currently, we get around this by providing the user with an admin option (ie. textarea field) which allows them to manually enter slugs for additional third-party modules they wish to register with our extension.

It would be great if the D5 implementation could automatically process all registered and internal and third-party modules.

ChAsadUrRehman commented 4 months ago

@dtcblyth did you find any solution?

dtcblyth commented 4 months ago

No... as far as I know this has not yet been implemented in Divi 5.

dtcblyth commented 3 months ago

Please find below a minimal code example of how I use the et_builder_get_parent_modules(), et_builder_get_child_modules(), et_pb_all_fields_unprocessed_{$slug}(), and et_module_shortcode_output() filter in my extensions. I am using this structure in four of my D4 extensions, and without this functionality these extensions cannot be replicated in D5.

Note: This code was originally partly based on this D4 code example: https://github.com/elegantthemes/create-divi-extension/issues/462

Note: It may be possible to replicate the et_module_shortcode_output() filter functionality using the Wordpress render_block() filter.


<?php

class My_Class {

  /**
   * Module slugs.
   * 
   * @var  array
   */
  private static $included_modules = array();

  /**
   * Module toggle flags.
   *
   * @var  boolean
   */
  private static $parent_toggles_added = false;
  private static $child_toggles_added  = false;

  /**
   * Constructor.
   *
   * @return  void
   */
  public function __construct() {

    // Prepare module slugs.
    self::prepare_included_modules();

    // Add toggles to modules.
    add_filter('et_builder_get_parent_modules', array(__CLASS__, 'add_parent_module_toggle'));
    add_filter('et_builder_get_child_modules',  array(__CLASS__, 'add_child_module_toggle'));

    // Add fields to modules.
    foreach (self::$included_modules as $slug) {
      add_filter("et_pb_all_fields_unprocessed_{$slug}", array(__CLASS__, 'add_module_fields'));
    }

    // Filter shortcode output.
    add_filter('et_module_shortcode_output', array(__CLASS__, 'shortcode_output'), 10, 3);
  }

  /**
   * Prepares an array of included module slugs.
   *
   * @return  void
   */
  private static function prepare_included_modules() {

    // Parent modules.
    $slugs = 'et_pb_section, et_pb_row, et_pb_row_inner, et_pb_accordion, et_pb_audio, et_pb_counters, et_pb_blog, et_pb_blurb, et_pb_button, et_pb_circle_counter, et_pb_code, et_pb_comments, et_pb_contact_form, et_pb_countdown_timer, et_pb_cta, et_pb_divider, et_pb_filterable_portfolio, et_pb_fullwidth_code, et_pb_fullwidth_header, et_pb_fullwidth_image, et_pb_fullwidth_map, et_pb_fullwidth_menu, et_pb_fullwidth_portfolio, et_pb_fullwidth_post_content, et_pb_fullwidth_post_slider, et_pb_fullwidth_post_title, et_pb_fullwidth_slider, et_pb_gallery, et_pb_icon, et_pb_image, et_pb_login, et_pb_map, et_pb_menu, et_pb_number_counter, et_pb_portfolio, et_pb_post_content, et_pb_post_slider, et_pb_post_title, et_pb_post_nav, et_pb_pricing_tables, et_pb_search, et_pb_sidebar, et_pb_signup, et_pb_slider, et_pb_social_media_follow, et_pb_tabs, et_pb_team_member, et_pb_testimonial, et_pb_text, et_pb_toggle, et_pb_video, et_pb_video_slider,';

    // Child modules.
    $slugs .= 'et_pb_column, et_pb_column_inner, et_pb_accordion_item, et_pb_counter, et_pb_contact_field, et_pb_signup_custom_field, et_pb_map_pin, et_pb_pricing_table, et_pb_slide, et_pb_social_media_follow_network, et_pb_tab, et_pb_video_slider_item,';

    // Woocommerce modules.
    $slugs .= 'et_pb_wc_additional_info, et_pb_wc_add_to_cart, et_pb_wc_breadcrumb, et_pb_wc_cart_notice, et_pb_wc_cart_products, et_pb_wc_cart_totals, et_pb_wc_checkout_additional_info, et_pb_wc_checkout_billing, et_pb_wc_checkout_order_details, et_pb_wc_checkout_payment_info, et_pb_wc_checkout_shipping, et_pb_wc_cross_sells, et_pb_wc_description, et_pb_wc_gallery, et_pb_wc_images, et_pb_wc_meta, et_pb_wc_price, et_pb_wc_rating, et_pb_wc_related_products, et_pb_wc_reviews, et_pb_shop, et_pb_wc_stock, et_pb_wc_tabs, et_pb_wc_title, et_pb_wc_upsells,';

    // Return.
    self::$included_modules = $slugs;
  }

  /**
   * Adds toggle to parent-modules.
   *
   * @param   array  $modules  All parent modules.
   *
   * @return  array
   */
  public static function add_parent_module_toggle($modules) {
    if (self::$parent_toggles_added) return $modules;
    if (empty($modules)) return $modules;
    self::$parent_toggles_added = true;
    return self::add_module_settings($modules);
  }

  /**
   * Adds toggle to child-modules.
   *
   * @param   array  $modules  All child modules.
   *
   * @return  array
   */
  public static function add_child_module_toggle($modules) {
    if (self::$child_toggles_added) return $modules;
    if (empty($modules)) return $modules;
    self::$child_toggles_added = true;
    return self::add_module_settings($modules);
  }

  /**
   * Adds toggle to parent-modules and child-modules.
   * See: https://github.com/elegantthemes/create-divi-extension/issues/462
   *
   * @param   array  $modules  All parent or child modules.
   *
   * @return array
   */
  private static function add_module_settings($modules) {

    // Each module.
    foreach ($modules as $slug => $module) {

      // Bail.
      if (!in_array($slug, self::$included_modules)) continue;
      if (!isset($module->settings_modal_toggles)) continue;

      // Add toggle.
      $toggles = $module->settings_modal_toggles;
      if (isset($toggles['custom_css']) && !empty($toggles['custom_css']['toggles'])) {

        // Content tab.
        $toggles['custom_css']['toggles']['my_toggle'] = array(
          'title'    => 'My Toggle',
          'priority' => 95,
        );

        // Add toggle.
        $module->settings_modal_toggles = $toggles;
      }
    }

    // Return.
    return $modules;
  }

  /**
   * Adds fields to each module.
   *
   * @param   array  $unprocessed_fields  The current module’s fields.
   *
   * @return  array
   */
  public static function add_module_fields($unprocessed_fields) {

    // Field.
    $fields['my_field'] =   array(
      'label'           =>  esc_html__('My Field', 'my-extension'),
      'description'     =>  esc_html__('My description.', 'my-extension'),
      'option_category' => 'configuration',
      'tab_slug'        => 'custom_css',
      'toggle_slug'     => 'my_toggle',
      'type'            => 'yes_no_button',
      'default'         => 'off',
      'options'         =>  array(
        'on'            =>  esc_html__('On', 'my-extension'),
        'off'           =>  esc_html__('Off', 'my-extension'),
      ),
    );

    // More fields here...

    // Return.
    return array_merge($unprocessed_fields, $fields);
  }

  /**
   * Filters shortcode output.
   *
   * @param   string  $output       The current module’s html output.
   * @param   string  $render_slug  The current module’s unique slug.
   * @param   object  $module       The current module’s class instance.
   * 
   * @return  string
   */
  public static function shortcode_output($output, $render_slug, $module) {

    if (in_array($render_slug, self::$included_modules)) {

      // Do something...

    }

    // Return.
    return $output;
  }
}