Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding more hooks & filters to settings and widget html output. #291

Closed
dtbaker opened this issue Jul 20, 2016 · 13 comments
Closed

Adding more hooks & filters to settings and widget html output. #291

dtbaker opened this issue Jul 20, 2016 · 13 comments
Assignees
Labels
type/developer-api Indicates when a topic is related to the Developer API and Documentation.

Comments

@dtbaker
Copy link
Contributor

dtbaker commented Jul 20, 2016

Hey guys,

Great plugin! I'd love to use this in our next ThemeForest theme instead of Visual Composer.

A few more do_action() and apply_filters() through the code would be wonderful!

I need some extra hooks and filters on the generated HTML output (e.g. adding some more markup to the default image-carousel HTML output or adding some more "controls" in the _register_controls function).

If you're happy to accept pull requests I'd be happy to make a couple of suggestions that would make it easier for us to integrate more tightly with our themes.

Cheers,
Dave

@dtbaker
Copy link
Contributor Author

dtbaker commented Jul 20, 2016

For now I'm just doing this to load my own custom "Elementor Widget" which is a copy of your image-carousel widget.

I had to register my own fake control because the frontend elementor clears all the registered page scripts. So unable to use normal wp_enqueue stuff.

<?php

if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly


if(!class_exists('dtbaker_elementor')) {
    class dtbaker_elementor {

        const DTBAKER_CONTROL = 'dtbaker';

        private static $instance = null;

        public static function get_instance() {
            if ( ! self::$instance ) {
                self::$instance = new self; }

            return self::$instance;
        }

        public function init() {
            add_action( 'elementor/widgets/widgets_registered', array( $this, 'widgets_registered' ) );
            add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_scripts' ], 100 );
            add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_styles' ] );
            add_action( 'init', [ $this, 'register_controls' ] );
        }

        public function register_controls(){
            if(defined('ELEMENTOR_PATH') && class_exists('Elementor\Widget_Base')) {
                $widget_file = 'plugins/elementor/controls/dtbaker.php';
                $template_file = locate_template($widget_file);
                if ( $template_file && is_readable( $template_file ) ) {
                    require_once $template_file;
                    Elementor\Plugin::instance()->controls_manager->register_control( self::DTBAKER_CONTROL, 'Control_dtbaker' );


                }
            }
        }

        public function widgets_registered() {

            if(defined('ELEMENTOR_PATH') && class_exists('Elementor\Widget_Base')){
                // get our own widgets up and running:
                // copied from widgets-manager.php

                $widget_file = 'plugins/elementor/widgets/image-slider.php';
                $template_file = locate_template($widget_file);
                if ( $template_file && is_readable( $template_file ) ) {
                    require_once $template_file;
                    Elementor\Plugin::instance()->widgets_manager->register_widget( 'Elementor\Widget_Image_Slider' );

                }
            }
        }

        public function enqueue_scripts(){
            // this is called from our control.php file as well.
            $widget_script_file = 'plugins/elementor/widgets/image-slider.js';
            wp_register_script(
                'elementor-image-slider',
                ( file_exists( STYLESHEETPATH . '/' . $widget_script_file ) ? get_stylesheet_directory_uri() : get_template_directory_uri() ) . '/' . $widget_script_file,
                [
                ],
                Elementor\Plugin::instance()->get_version()
            );
            wp_enqueue_script( 'elementor-image-slider' );

        }
        public function enqueue_styles(){

        }
    }

    dtbaker_elementor::get_instance()->init();
}

and my control dtbaker.php file:

<?php

if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly

class Control_dtbaker extends Elementor\Control_Base {

    public function get_type() {
        return 'dtbaker';
    }

    public function enqueue(){
        dtbaker_elementor::get_instance()->enqueue_scripts();

    }

    public function content_template() {
        ?>
        <div class="elementor-control-field">
            <label class="elementor-control-title"><%= data.label %></label>
            <div class="elementor-control-input-wrapper">
                <select data-setting="<%= data.name %>">
                    <% _.each( data.options, function( option_title, option_value ) { %>
                    <option value="<%= option_value %>"><%= option_title %></option>
                    <% } ); %>
                </select>
            </div>
        </div>
        <% if ( data.description ) { %>
        <div class="elementor-control-description"><%= data.description %></div>
        <% } %>
        <?php
    }
}

@arielk arielk added the request/enhancement Indicates an Enhancement Request for an existing feature. label Aug 1, 2016
@KingYes
Copy link
Member

KingYes commented Aug 1, 2016

Hi @dtbaker
We are working about Developer API to extend the Elementor.

If you have any idea please share us.. We need some good references to learn what is the best way to do this.

By the way, I don't have any idea how you can change the HTML markup by your theme. I have two ways to do this, but I don't very like this:

  1. Copy all the widget class to your theme and unregister our widget and register your own.
  2. Each output widgets need to add a lots actions/filters to extend them.

Do you have more ideas for this?

@dtbaker
Copy link
Contributor Author

dtbaker commented Aug 2, 2016

Here is an example of using the three filters added in #361

This adds two new dropdowns to the "Image Carousel" widget and modifies the rendered HTML based on these dropdown options.

$existing_image_carousel = Elementor\Plugin::instance()->widgets_manager->get_widget('image-carousel');
$existing_image_carousel->add_control(
    'dtbwp_border_style',
    [
        'label' => __( 'Border Style', 'elementor' ),
        'type' => 'select', //Controls_Manager::SELECT,
        'default' => 'default',
        'section' => 'section_image_carousel',
        'options' => array(
            'default' => 'Default',
            'boxed' => 'Line Box',
        ),
    ]
);
$existing_image_carousel->add_control(
    'dtbwp_layout_type',
    [
        'label' => __( 'Layout Type', 'elementor' ),
        'type' => 'select',
        'default' => 'default',
        'section' => 'section_image_carousel',
        'options' => array(
            'default' => 'Default',
            'slider' => 'Slider with Text',
        ),
    ]
);
// modify output of existing widget.
add_action('elementor/widgets/render_content/before', function($widget, $instance = []){
    if(!empty($instance['dtbwp_border_style'])) {
        $widget->add_render_attribute( 'widget-container', 'class', [
            esc_attr('dtbwp-border-style-' . $instance['dtbwp_border_style'])
        ] );
    }
    if(!empty($instance['dtbwp_layout_type'])) {
        $widget->add_render_attribute( 'widget-container', 'class', [
            esc_attr('dtbwp-layout-type-' . $instance['dtbwp_layout_type'])
        ] );
    }
}, 10, 2);
add_filter('elementor/widgets/image_carousel/image_html', function($image_html, $instance = []){
    //print_r($instance);
    if(!empty($instance['dtbwp_layout_type']) && $instance['dtbwp_layout_type'] == 'slider') {
        $slider_text = '<h2>Post Title</h2>';
        $slider_text .= '<p>Post text post text post text post text post text post text post text post text post text </p>';
        $slider_text .= '<a href="adf">Test Button</a>';
        $image_html = '<div class="dtbaker-image-slider-text">' . $slider_text . '</div><div class="dtbaker-image-slider-image"><div class="dtbaker-photo-frame"><div>' . $image_html . '</div></div></div>';
    }
    return $image_html;
}, 10, 2);

Here's a quick video of how I used these filters to apply new wrapper CSS classes and modify the default HTML layout with great success:

recorded

@owiekindisch
Copy link

I would recommend to create a factory class for all Widgets and the factory class have generic hooks. So for example:

<?php
    abstract class Widget_Factory_Class {
        public function output() {
            $widget = $this->get_class_name();
            $widget_id = $this->get_widget_id();
            $html = call_user_func( 'html', $widget );
            $html = apply_filter( 'elementor_output_'.$widget_id, $html, $this );
            $html = apply_filter( 'elementor_output_filter', $html, $this );

            return $html;
        }
    }
?>

@dtbaker
Copy link
Contributor Author

dtbaker commented Aug 2, 2016

@owiekindisch this can be added to the existing base/factory class for all widgets: base.php

@dtbaker
Copy link
Contributor Author

dtbaker commented Aug 3, 2016

Adding a custom button class with theme filters:

recorded

@daviedR
Copy link
Contributor

daviedR commented Aug 18, 2016

Any update on this? The hooks is very important for theme developers.

@arielk arielk added type/developer-api Indicates when a topic is related to the Developer API and Documentation. and removed request/enhancement Indicates an Enhancement Request for an existing feature. labels Sep 5, 2016
@agungsijawir
Copy link

@KingYes

A suggestion, since Elementor package comes with Namespace, how about tutorial / guide to autoload class?

@doup
Copy link

doup commented Sep 17, 2016

I'm looking at the code and looks like via \Elementor\Plugin::instance()->... all the inner stuff can be accessed, I hope that a POPO (Plain Old PHP Object) approach is also available for extending Elementor.

I mean, for me it seems strange given that there is already an OO interface having to deal with the plugin via hooks; obviously both approaches are not exclusive but I think that it makes more sense that the main way of interacting with the plugin to be the POPO API.

Please look at Carbon Fields, I find it to be a good reference for a developer friendly plugin.

@KingYes
Copy link
Member

KingYes commented Apr 30, 2017

We published our Developer API so please continue there..

@KingYes KingYes closed this as completed Apr 30, 2017
@GreenRidingHood
Copy link

@dtbaker "Adding a custom button class with theme filters:" is there any documentation about this, this is exactly what I wanna do..

@notrealdev
Copy link

Hi, how i can modify widget output???

@brittaweller
Copy link

"We published our Developer API so please continue there.."
Where do we find any information for this in there?!

(And could you please increase the output of results?! - I repeatedly freak out when I just want to have a look at all the available Classes, Methods, Hooks etc and it shows me only 20 per page with 20+ pages to go. Why would you do that with only text content?)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type/developer-api Indicates when a topic is related to the Developer API and Documentation.
Projects
None yet
Development

No branches or pull requests

10 participants