Extend Elementor like a pro: Creating a New Widget

Creating a New Widget Featured Image

Elementor is packed with various elements or widgets. Widgets allow us to build websites and display appropriate content. But sometimes we need specific functionality for our website. And to do this, we need to create a new widget or element.

What is a widget?

A widget or element is a section in the Graphical User Interface (GUI) that lets us display the information we want to showcase to users.

The Elementor widget is a combination of PHP, JavaScript, HTML and CSS codes that generate a custom output for us. We suggest visiting this link before reading this article.

Elementor Widgets

Each widget has custom controls like input, fields, buttons and more. With these controls, we can set our custom settings and see a live preview in the editor and render the final HTML in frontend.

Widget Structure

In order to create a new widget, we must extend the Widget_Base abstract class. This class has several methods that we first need to override.

<?php
class Elementor_Test_Widget extends \Elementor\Widget_Base {

    public function get_name() {}

    public function get_title() {}

    public function get_icon() {}

    public function get_categories() {}

    protected function _register_controls() {}

    protected function render() {}

    protected function _content_template() {}
}

Widget Name

With the get_name() method, we can set the name of a widget and return the name of the widget.

public function get_name() {
    return 'Widget Name';
}

Widget Title

With the get_title() method, we can set the label of the widget.

public function get_title() {
    return __( 'Widget Title', 'Plugin Name' );
}

Widget Icon

The get_icon() method set the icon for our widget that displayed in the panel.

public function get_icon() {
    return 'eicon-gallery-grid';
}

Widget Categories

The get_categories() method set the category that our widget must be placed on the panel’s categories. In default, there are several categories including the following:

  • Basic
  • Pro-elements
  • General
  • Woocommerce-elements
  • WordPress
  • And so on.
public function get_categories() {
    return [ 'basic' ];
}

We can also create custom categories with the elementor/elements/categories_registered action.

<?php
function create_custom_categories( $elements_manager ) {

    $elements_manager->add_category(
        'custom-category',
        [
         'title' => __( 'Custom Category', 'plugin-name' ),
         'icon' => 'fa fa-plug',
        ]
    );
}
add_action( 'elementor/elements/categories_registered', 'create_custom_categories' );

Widget Controls

With the _register_controls() method, we can set essential sections and controls for the widget.

There are several controls that we can use for widget settings, such as the following:

  1. UI Controls
    • Button
    • Divider
    • Heading
    • Raw Html
  2. Data Controls
    • Text
    • Select
    • Choose
    • Gallery
    • Slider
    • Typography
    • And so on.
  3. Multiple Controls
    • Url
    • Media
    • Image Dimensions
  4. Unit Controls
    • Slider
    • Dimensions
  5. Group Controls
    • Typography
    • Text Shadow
    • Box Shadow
    • Border
    • Background

For more information on Elementor controls, you can check out this link.

Widget Template

The render() method allows creating a frontend HTML code with the PHP.

The _content_template() method generates a live preview for the widget in the editor by using the Backbone JavaScript library.

How to add controls to our widget

In default, there are three tabs in the Elementor panel, which are the Content Tab, Style Tab, and Advanced Tab. To add controls to our widget, we should first create a section and assign it to one of the tabs. Then we can add our favorite controls to the section. As mentioned above, we must put our code in the _register_controls() method.

Creating a Section

To create a section, we need to set the following essential parameters:

  • Section Name
  • Section Settings(label ,tab)
$this->start_controls_section(
     'section_name',//set unique name

          //set setting of section
        [
         'label' => __( 'Section Label', 'plugin-name' ),
         'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
        ]
 );

  //THE CONTROLS COME HERE

$this->end_controls_section();

We must set the following parameters for each control as seen below:

  • Control Name
  • Control Setting(label,type)

Adding a normal control

Use the following control to add a normal control.

$this->add_control(
    'control_name',//set unique name for control

           //set essential settings
         [
        'label' => __( 'Control Label', 'plugin-name' ),

            //CONTROL_TYPE can be TEXT, SLIDER, ...
        'type' => \Elementor\Controls_Manager::CONTROL_TYPE,
         ]
    );

Adding a responsive control

This control type includes different settings value for desktop, tablet, and mobile devices.

$this->add_responsive_control(
     'control_name',
       [
        'label' => __( 'Control Name', 'plugin-name' ),
           //CONTROL_TYPE can be TEXT, SLIDER, …

         type' => \Elementor\Controls_Manager::CONTROL_TYPE,
           //SET FAVORITE RANGE LIKE PX, %, EM

         'range' => [
                 'px' => [
                        'min' => 0,
                        'max' => 100,
                       ],
                ],
            // SET FAVORITE DEVICES
          'devices' => [ 'desktop', 'tablet', 'mobile' ],
        'desktop_default' => [
                        'size' => 30,
                        'unit' => 'px',
                         ],
          'tablet_default' =>  [
                        'size' => 20,
                        'unit' => 'px',
                         ],
          'mobile_default' =>  [
                        'size' => 10,
                        'unit' => 'px',
                         ],
                
                ],
            ]
        );

Adding a group control

This control groups several normal controls in one control such as typography.

$this->add_group_control(
//Group_Control_Class can be       Group_Control_Typography,Group_Control_Border or other group controls

  Group_Control_Class::get_type(),
  [
   'name' => 'control_name',
   'label' => __( 'control Label', 'plugin-name' ),
  ]
);

Adding control tabs

Create a tab area to add various controls.

//start tabs area container
$this->start_controls_tabs();

  // create first tab area
  $this->start_controls_tab();

    $this->add_control();

      $this->end_controls_tab();

     // create second tab area
      $this->start_controls_tab();

    $this->add_control();

      $this->end_controls_tab();

$this->end_controls_tabs();
//end tabs area container

Adding control popovers

Create a popover window will allow you to display various controls.

$this->add_control(
   'control_name',
    [
     'label' => __( 'Control Label', 'plugin-name' ),
     'type' => \Elementor\Controls_Manager::POPOVER_TOGGLE,
     'label_off' => __( 'Default', 'plugin-name' ),
     'label_on' => __( 'Custom', 'plugin-name' ),
     'return_value' => 'yes',
    ]
  );

    $this->start_popover();

    $this->add_control();

    $this->add_control();

    $this->end_popover();

How to Display or Hide Sections and Controls in Widgets

At times, you might want to hide some sections or controls in your widget, but using if / else in the code is a hassle. Fortunately, now you can do this conveniently with the condition in the settings for sections or controls.

$this->start_controls_section(
  'my_section',
   [
    'label' => __( 'My Section', 'plugin-name' ),
    'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
    'condition' => [
                       'Name' => 'Value',
                      ]
      //name must be unique name of one control
      //Value must be the return value of control
   ]
);

Let’s say that we have one control named Choose_ Layout with two values first and second. After adding the code found below code to any section or control, our section or control will be displayed if the return value of the Choose_ Layout control is equal to first. And it will be hidden if the return value of Choose_ Layout control is equal to the second.

'condition' => [
                'Choose_ Layout' => 'first',
               ]

How to Change Widget Output Style Automatically from the Control Settings

One of Elementor’s amazing features is the ability to easily change the style of widget output with CSS code and selectors. For example, if we want to change the height of images in output, first we must assign a class attribute to our HTML tag. Then use this class with the selector in settings of control.

<img class="test-img">

$this->add_control(
          'test_height',
            [
             'label' => __( 'Height', 'Spicy-extension' ),
             'type' => \Elementor\Controls_Manager::SLIDER,
             'separator' => 'after',
             'show_label' => true,
             'selectors' => [
                '{{WRAPPER}} .test-img' => 'height: {{SIZE}}{{UNIT}};',
              ],
            ]
        );

How to Get Widget Settings

To create the final Html code in the render() method and live preview in the _content_template() we’ll need input the parameter from the widget controls like height, width, font-size and more. In Elementor, the input parameter is called Settings. We can arrange settings in render() using this method.

$widget_settings= $this->get_settings_for_display();
$widget_settings['control_name'];

For the _content_template() method the Elementor store setting in the settings variable.

settings.control_name

One important note that we must consider is the settings are saved in a multidimensional array. We can get an inner setting with the following code.

$widget_settings['control_name ']['option'];

settings.control_name.option 

Creating a Sample Widget

Below, we have illustrated how to build a gallery widget step-by-step so you can better understand how to cover the topics mentioned above and create an Elementor widget.

Our gallery widget is registered in the Spicy plugin and the root directory of the gallery widget is:

-Css
-gallery.css
-index.php
-widgets
-gallery.php
-index.php
-spicy.php
-index.php

Index.php file is an empty file that prevents a malicious hacker from directly accessing the plugin.
Spicy.php file is the main plugin file that we register our widget with.
Gallery.css file is a CSS file that sets the default style of a gallery widget.
Gallery.php is the main file that defines the functionality of a gallery widget.

Download the Source Code here.

Our gallery widget is based on the grid and has two layouts, which are classic and pro. Both layouts have the following controls:

  • Skin
  • Column gap
  • Row gap
  • Columns
  • Hover Animation
  • Box Shadow

The classic layout has a gallery control, and the image section contains the below controls:

  • Height
  • Image size behavior
  • Border type
  • Border radius

Also, the pro layout has an image repeater control with several sections such as the following:

  • Item section contains Border Type, Width, Color, and Border Radius controls.
  • Image section contains Height and Image Size behavior controls.
  • Text section contains Color, Typography, Alignment, and Spacing controls.
  • Avatar section contains Height, Width, Alignment, and Spacing controls.
  • Textarea section contains Color, Typography, Alignment, and Spacing controls.

Extending the Widget_Base Abstract Class

The main class of the gallery widget is similar to the code found below.

gallery.php
<?php
class Spicy_Gallery_Widget extends \Elementor\Widget_Base {
public function get_name() {
   return 'Gallery';
   }
public function get_title() {
    return __( 'Gallery', 'Spicy-extension' );
    }
public function get_icon() {
    return 'eicon-gallery-grid';
    }
public function get_categories() {
    return [ 'basic' ];
    }
protected function _register_controls() {

    //  start Content tab
        $this->start_controls_section(
            'content_section',
            [
             'label' => __( 'Content', 'Spicy-extension' ),
             'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
            ]
        );
        //add skin control
        $this->register_controls_skin();

        //add repeater for pro skin
        $this->register_controls_pro_repeater();

        //add gallery control
        $this->register_controls_Classic_gallery();

        //add column-gap slider
        $this->register_controls_column_gap();

        //add row-gap slider
        $this->register_controls_row_gap();


       
     
 //add columns number list
        $this->register_controls_column_number();

        $this->end_controls_section();
        // end content tab

        //start Style tab
        $this->register_controls_classic_style_image();

        //create pro item section
        $this->register_controls_pro_item();

      //create pro style section-image
        $this->register_controls_pro_style_image();

      //create pro style section-text
        $this->register_controls_pro_style_text();

      //create pro style section-avatar
        $this->register_controls_pro_style_avatar();

      //create pro style section-textarea
        $this->register_controls_pro_style_textarea();

      //Box Shadow
        $this->register_controls_pro_style_boxshadow();

        //hover animation
        $this->register_controls_pro_style_hoveranimation();
      //end style tab
    }

protected function render() {
        $settings = $this->get_settings_for_display();
        ?>
        <div class="spicy-gallery" width="100%">
        <?php
   
        if($settings['spicy_skin']=='classic'){
            $this->classic_render($settings);
        }else{
            $this->pro_render($settings);
  
                 }
        ?>
        </div>
        
        <?php
    }   

protected function _content_template() {
        ?>
        <div class="spicy-gallery">
        <#
         if(settings.spicy_skin=='classic'){
         _.each( settings.spicy_images, function( image ) {
        #>
        <div class="spicy-item elementor-animation-{{settings.spicyhover}}">
         <img  class="spicy-img" id="{{image.id}}" src="{{image.url}}"/>
        </div>
        <# }); } else{
         _.each( settings.spicy_image_list, function( image ) {
        #>
        <div class="spicy-item elementor-animation-{{settings.spicyhover}}">    
          <div class="spicy-pro-container">
            <img class="spicy-pro-img" src="{{image.spicy_pro_image.url}}" >
          </div>
          <p class="spicy-pro-text">{{image.spicy_pro_text}}</p>
          <div class="spicy-avatar-wrapper">
           <img class="spicy-avatar" src="{{image.spicy_pro_avatar.url}}" alt="Avatar">
          </div>
          <p class="spicy-pro-description">{{image.spicy_pro_description}}</p>
         </div>
        <# }); }#>
        </div>
     <?php
    }
?>

Creating Controls for a Gallery Widget

In the above code, you’ll see several functions in _register_controls() and render() functions.In fact, these methods assign sections and controls for the gallery widget. We can create these functions only for grouping codes and simplicity.

Common Controls

1. Skin control: This control’s type is Select with two options set as classic and pro. With this control, we have the ability to change the layout of the gallery widget.

protected function register_controls_skin(){
        $this->add_control(
               'spicy_skin',
               [
                'label' => __( 'Skin', 'spicy' ),
                'type' => \Elementor\Controls_Manager::SELECT,
                'options' => [
                'classic' => __( 'Classic', 'Spicy-extension' ),
                'pro' => __( 'Pro', 'spicy' ),
               ],
                'default' => 'classic',   
            ]
       );
}

2. Column Gap: This type of control is Slider, which allows us to change the space between columns of the gallery widget.

protected function register_controls_column_gap(){
        $this->add_responsive_control(
               'spicy_column_gap',
               [
                'label' => __( 'Column Gap', 'Spicy-extension' ),
                'type' => \Elementor\Controls_Manager::SLIDER,
                'size_units' => [ '%','em', 'px' ],
                'range' => [
                    'px' => [
                        'min' => 0,
                        'max' => 50,
                        'step' => 1,
                    ],
                    '%' => [
                        'min' => 0,
                        'max' => 10,
                        'step' => 0.1,
                    ],
                    'em' => [
                        'min' => 0,
                        'max' => 10,
                        'step' => 0.1,
                    ],
                ],
                'default' => [
                    'unit' => '%',
                    'size' => 0.5,
                ],
                'show_label' => true,
                'selectors' => [
                    '{{WRAPPER}} .spicy-gallery' => 'grid-column-gap: {{SIZE}}{{UNIT}};',
                ],
            ]
        );
    }

3. Row Gap: This type of control is Slider, which we can use to change the space between rows of the gallery widget.

protected function register_controls_row_gap(){
        $this->add_responsive_control(
               'spicy_row_gap',
               [
                'label' => __( 'row Gap', 'Spicy-extension' ),
                'type' => \Elementor\Controls_Manager::SLIDER,
                'size_units' => [ 'px','em','%' ],
                'range' => [
                    'px' => [
                        'min' => 0,
                        'max' => 50,
                        'step' => 1,
                    ],
                    '%' => [
                        'min' => 0,
                        'max' => 10,
                        'step' => 0.1,
                    ],
                    'em' => [
                        'min' => 0,
                        'max' => 10,
                        'step' => 0.1,
                    ],
                ],
                'default' => [
                    'unit' => 'px',
                    'size' => 5,
                ],
                'show_label' => true,
                'selectors' => [
                  '{{WRAPPER}} .spicy-gallery' => ' grid-row-gap: {{SIZE}}{{UNIT}};',
                ],
            ]
        );
    }

4. Columns: The type of control is Select, which lets us alter the number of columns of the gallery widget.

protected function register_controls_column_number(){
        $this->add_responsive_control(
               'spicy_columns',
               [
                'label' => __( 'Columns', 'Spicy-extension' ),
                'type' => \Elementor\Controls_Manager::SELECT,
                'default' => 3,
                'options' => ['1'=>1,'1'=>1,'2'=>2,'3'=>3,'4'=>4,'5'=>5,'6'=>6,'7'=>7,'8'=>8,'9'=>9,'10'=>10],
                'devices' => [ 'desktop', 'tablet', 'mobile' ],
                'desktop_default' => [
                    'size' => '',
                ],
                'tablet_default' => [
                    'size' => '',
                ],
                'mobile_default' => [
                    'size' => '',
                ],
                'selectors' => [
                  '{{WRAPPER}} .spicy-gallery' => 'grid-template-columns: repeat({{SIZE}},1fr);',
                ],
            ]
            
       );
    }

5. Box Shadow: This section is used to add a box shadow effect to gallery items.

protected function register_controls_style_boxshadow(){
        $this->start_controls_section(
               'spicy_box_shadow',
               [
                'label' => __( 'Box Shadow', 'Spicy-extension' ),
                'tab' => \Elementor\Controls_Manager::TAB_STYLE,
               ]
        );
        $this->add_group_control(
               Elementor\Group_Control_Box_Shadow::get_type(),
               [
                'name' => 'box_shadow',
                'label' => __( 'Box Shadow', 'Spicy-extension' ),
                'selector' => '{{WRAPPER}} .spicy-item',
               ]
        );
        $this->end_controls_section();
 }

6. Hover Animation: This section adds an animation effect to the gallery item.

protected function register_controls_style_hoveranimation(){
        $this->start_controls_section(
               'spicy_hover',
               [
                'label' => __( 'Hover Animation', 'Spicy-extension' ),
               'tab' => \Elementor\Controls_Manager::TAB_STYLE,
             ]
        );
        $this->add_control(
               'spicyhover',
               [
                'label' => __( 'Hover Animation', 'Spicy-extension' ),
                'type' => \Elementor\Controls_Manager::HOVER_ANIMATION,
                'default'=>'',
               ]
            
        );
        $this->end_controls_section();
    }

Classic Layout

The following controls or sections are only displayed in the classic layout.

1. Gallery Control: used to select multiple images for the gallery and displayed in the Content Tab.

protected function register_controls_Classic_gallery(){
        $this->add_control(
               'spicy_images',
              [
                'label' => __( 'Add Images', 'Spicy-extension' ),
                'type' => \Elementor\Controls_Manager::GALLERY,
                'separator' => 'default',
                'show_label' => false,
                'condition' => [
                                'spicy_skin' => 'classic',
                               ],
                'dynamic' => [
                              'active' => true,
                             ]
              ]
        );
 }

2. Image sections: This is displayed in the Style Tab to change the style of the image.

protected function register_controls_classic_style_image(){
        $this->start_controls_section(
               'style_section',
               [
                'label' => __( 'Image', 'Spicy-extension' ),
                'tab' => \Elementor\Controls_Manager::TAB_STYLE,
                'condition'=>[
                              'spicy_skin' => 'classic'
                             ]
               ]
        );
        //classic height image control
        $this->add_responsive_control(
               'spicy_height',
               [
                'label' => __( 'Height', 'Spicy-extension' ),
                'type' => \Elementor\Controls_Manager::SLIDER,
                'separator' => 'after',
                'size_units' => [ 'px','vw'],
                'range' => [
                            'px' => [
                                     'min' => 0,
                                     'max' => 500,
                                     'step' => 1,
                                    ],
                            'vw' => [
                                     'min' => 0,
                                     'max' => 100,
                                     'step' => 1,
                                    ],
                           ],
                'default' => [
                              'unit' => 'px',
                              'size' => 150,
                             ],
                'show_label' => true,
                'selectors' => [
                    '{{WRAPPER}} .spicy-img' => 'height: {{SIZE}}{{UNIT}};',
                ],
            ]
        );

        //image fitness classic
        $this->add_responsive_control(
               'spicy_image_fitness',
               [
                'label' => __( 'Image Size Behavior', 'Spicy-extension' ),
                'type' => \Elementor\Controls_Manager::SELECT,
                'options' => ['cover'=> __( 'Cover', 'Spicy-extension' ),'fill'=>__( 'Fit', 'Spicy-extension' ),'contain'=>__( 'Contain', 'Spicy-extension' ),'scale-down'=>__( 'Scale Down', 'Spicy-extension' ),'none'=>__( 'None', 'Spicy-extension' )],
                'devices' => [ 'desktop', 'tablet', 'mobile' ],
                'desktop_default' => [
                                      'val' => '',
                                     ],
                'tablet_default' => [
                                      'val' => '',
                                    ],
                'mobile_default' => [
                                     'val' => '',
                                    ],
                'default' => 'cover',
                'selectors' => [
                    '{{WRAPPER}} .spicy-img' => 'object-fit: {{val}};',
                 ],
               ]
            
            );
        //border classic    
        $this->add_group_control(
               Elementor\Group_Control_Border::get_type(),
               [
                'name' => 'spicy_border',
                'label' => __( 'Border', 'Spicy-extension' ),
                'selector' => '{{WRAPPER}} .spicy-img',
               ]
        );
        //border radius classic
        $this->add_responsive_control(
               'spicy_image_border_radius',
               [
                'label' => __( 'Border Radius', 'Spicy-extension' ),
                'type' => Elementor\Controls_Manager::DIMENSIONS,
                'size_units' => [ 'px', '%' ],
                'selectors' => [
                    '{{WRAPPER}} .spicy-img' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
                ],
            ]
        );
        $this->end_controls_section();
    }

Pro Layout

The following controls or sections are only displayed in the pro layout.

1. Repeater Section: This section has four controls, which are Image, Text, Avatar, and Textarea. It’s used for adding images individually and is displayed in the Content Tab.

protected function register_controls_pro_repeater(){
        $repeater = new \Elementor\Repeater();
        //add pro image control
        $repeater->add_control(
                   'spicy_pro_image',
                   [
                    'label' => __( 'Choose Image', 'Spicy-extension' ),
                    'type' => \Elementor\Controls_Manager::MEDIA,
                    'dynamic' => [
                                  'active' => true,
                                 ],
                    'default' => [
                     'url' => \Elementor\Utils::get_placeholder_image_src(),
                    ],
                  ]
        );
        //add pro text control
        $repeater->add_control(
                   'spicy_pro_text',
               [
                'label' => __( 'Text', 'Spicy-extension' ),
                'type' => \Elementor\Controls_Manager::TEXT,
                'label_block' => true,
                'placeholder' => __( 'Your Text', 'Spicy-extension' ),
                'default' => __( '', 'Spicy-extension' ),
                'dynamic' => [
                              'active' => true,
                             ],
               ]
        );
        //add avatar image control
        $repeater->add_control(
            'spicy_pro_avatar',
            [
                'label' => __( 'Choose Avatar', 'Spicy-extension' ),
                'type' => \Elementor\Controls_Manager::MEDIA,
                'dynamic' => [
                    'active' => true,
                ],
                'default' => [
                    'url' => \Elementor\Utils::get_placeholder_image_src(),
                ],
            ]
     );
        //pro textarea control
        $repeater->add_control(
               'spicy_pro_description',
               [
                'label' => __( 'Description', 'Spicy-extension' ),
                'type' => \Elementor\Controls_Manager::TEXTAREA,
                'rows' => 5,
                'default' => __( '', 'Spicy-extension' ),
                'placeholder' => __( 'Type your description here', 'spicy' ),
               ]
        );

        //add repeater control
        $this->add_control(
               'spicy_image_list',
               [
                'label' => __('Images','Spicy-extension'),
                'type' => \Elementor\Controls_Manager::REPEATER,
                'label_block' => true,
                'separator' => 'default',
                'fields' => $repeater->get_controls(),
                'title_field' => '{{{spicy_pro_text }}}',
                'condition' => [
                                'spicy_skin' => 'pro',
                            ],
             ]
        );
    }

2. Item Section: This is displayed in the Style Tab to change the border style of the gallery item.

protected function register_controls_pro_item(){
     $this->start_controls_section(
               'spicy_pro_item',
               [
                'label' => __( 'Item', 'Spicy-extension' ),
                'tab' => \Elementor\Controls_Manager::TAB_STYLE,
                'condition'=>[
                              'spicy_skin' => 'pro'
                             ]
              ]
        );
        // pro item border
        $this->add_group_control(
               \Elementor\Group_Control_Border::get_type(),
               [
                'name' => 'spicy_item_border',
                'label' => __( 'Border', 'Spicy-extension' ),
                'selector' => '{{WRAPPER}} .spicy-item',
               ]
        );  
        //pro item border radius
        $this->add_responsive_control(
               'spicy_pro_item_border_radius',
               [
                'label' => __( 'Border Radius', 'Spicy-extension' ),
                'type' => Elementor\Controls_Manager::DIMENSIONS,
                'size_units' => [ 'px', '%' ],
                'selectors' => [
                    '{{WRAPPER}} .spicy-item' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
                ],
               ]
        );
        $this->end_controls_section();
    }

3. Image Section: This is displayed in the Style Tab to change the image size.

protected function register_controls_pro_style_image(){
        $this->start_controls_section(
               'spicy_pro_style_image',
               [
                'label' => __( 'Image', 'Spicy-extension' ),
                'tab' => \Elementor\Controls_Manager::TAB_STYLE,
                'condition'=>[
                              'spicy_skin' => 'pro'
                             ]
               ]
        );
        //pro image size
        $this->add_responsive_control(
               'spicy_pro_image_height',
               [
                'label' => __( 'height', 'Spicy-extension' ),
                'type' => \Elementor\Controls_Manager::SLIDER,
                'size_units' => [ 'px','em','vh' ],
                'range' => [
                            'px' => [
                                     'min' => 0,
                                     'max' => 800,
                                     'step' => 1,
                                    ],
                            'vh' => [
                                     'min' => 0,
                                     'max' => 100,
                                     'step' => 1,
                                    ],
                            'em' => [
                                     'min' => 0,
                                     'max' => 10,
                                     'step' => 0.1,
                                    ],
                          ],
                'default' => [
                              'unit' => 'px',
                              'size' => 150,
                             ],
                'show_label' => true,
                'selectors' => [
                  '{{WRAPPER}} .spicy-pro-img' => ' height: {{SIZE}}{{UNIT}};',
                ],
            ]
        );
        //pro image fitness
        $this->add_responsive_control(
            'spicy_pro_image_fitness',
            [
                'label' => __( 'Image Size Behavior', 'Spicy-extension' ),
                'type' => \Elementor\Controls_Manager::SELECT,
                'options' => ['cover'=> __( 'Cover', 'Spicy-extension' ),'fill'=>__( 'Fill', 'Spicy-extension' ),'contain'=>__( 'Contain', 'Spicy-extension' ),'scale-down'=>__( 'Scale Down', 'Spicy-extension' ),'none'=>__( 'None', 'Spicy-extension' )],
                'devices' => [ 'desktop', 'tablet', 'mobile' ],
                'desktop_default' => [
                                      'val' => '',
                                     ],
                'tablet_default' => [
                                     'val' => '',
                                    ],
                'mobile_default' => [
                                     'val' => '',
                                    ],
                'default' => 'cover',
                'selectors' => [
                  '{{WRAPPER}} .spicy-pro-img' => 'object-fit: {{val}};',
                ],
            ]
            
      );
        $this->end_controls_section();

4. Text Section: This is displayed in the Style Tab to change the style of the text item.

protected function register_controls_pro_style_text(){
        $this->start_controls_section(
                'spicy_pro_style_text',
                [
                 'label' => __( 'Text', 'Spicy-extension' ),
                 'tab' => \Elementor\Controls_Manager::TAB_STYLE,
                 'condition'=>[
                    'spicy_skin' => 'pro'
                ]
            ]
        );

        //pro text color control
        $this->add_control(
               'spicy_pro_text_color',
               [
                'label' => __( 'Color', 'Spicy-extension' ),
                'type' => \Elementor\Controls_Manager::COLOR,
                'default' => '',
                'selectors' => [
                  '{{WRAPPER}} .spicy-pro-text' => 'color: {{VALUE}};',
                 ],
                'scheme' => [
                             'type' => \Elementor\Scheme_Color::get_type(),
                             'value' => \Elementor\Scheme_Color::COLOR_1,
                            ],
               ]
        );

        //pro text typography
        $this->add_group_control(
                \Elementor\Group_Control_Typography::get_type(),
                [
                 'name' => 'spicy_pro_text_typography',
                 'selector' => '{{WRAPPER}} .spicy-pro-text',
                 'scheme' => \Elementor\Scheme_Typography::TYPOGRAPHY_1,
                ]
        );

        //pro text alignment
        $this->add_responsive_control(
               'spicy_pro_text-_align',
               [
                'label' => __( 'Alignment', 'Spicy-extension' ),
                'type' => \Elementor\Controls_Manager::CHOOSE,
                'options' => [
                              'left' => [
                                          'title' => __( 'Left','Spicy-extension'),
                                          'icon' => 'fa fa-align-left',
                                        ],
                              'center' => [
                                            'title' => __( 'Center', 'Spicy-extension' ),
                                            'icon' => 'fa fa-align-center',
                                           ],
                              'right' => [
                                          'title' => __( 'Right', 'Spicy-extension' ),
                                          'icon' => 'fa fa-align-right',
                                         ],
                              'justify' => [
                                            'title' => __( 'Justified', 'Spicy-extension' ),
                                            'icon' => 'fa fa-align-justify',
                                           ],
                             ],
                'default' =>'',
                'selectors' => [
                  '{{WRAPPER}} .spicy-pro-text' => 'text-align: {{VALUE}};',
                ],
            ]
        );
        //pro text spacing control
        $this->add_responsive_control(
               'spicy_text_margin',
               [
                'label' => __( 'Spacing', 'Spicy-extension' ),
                'type' => \Elementor\Controls_Manager::DIMENSIONS,
                'size_units' => [ 'px', '%', 'em' ],
                'selectors' => [
                  '{{WRAPPER}} .spicy-pro-text' => 'margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
                ],
              ]
        );

        $this->end_controls_section();
    } 

5. Avatar Section: This is displayed in the Style Tab to change the style of the avatar.

protected function register_controls_pro_style_avatar(){
        $this->start_controls_section(
            'spicy_pro_style_avatar',
            [
                'label' => __( 'Avatar', 'Spicy-extension' ),
                'tab' => \Elementor\Controls_Manager::TAB_STYLE,
                'condition'=>[
                    'spicy_skin' => 'pro'
                ]
            ]
        );
        //pro avatar height
        $this->add_responsive_control(
            'spicy_avatar_height',
             [
                'label' => __( 'height', 'Spicy-extension' ),
                'type' => \Elementor\Controls_Manager::SLIDER,
                'size_units' => [ 'px','em','vh' ],
                'range' => [
                    'px' => [
                        'min' => 0,
                        'max' => 100,
                        'step' => 1,
                    ],
                    'vh' => [
                        'min' => 0,
                        'max' => 100,
                        'step' => 1,
                    ],
                    'em' => [
                        'min' => 0,
                        'max' => 10,
                        'step' => 0.1,
                    ],
                ],
                'default' => [
                    'unit' => 'px',
                    'size' => 50,
                ],
                'show_label' => true,
                'selectors' => [
                    '{{WRAPPER}} .spicy-avatar' => ' height: {{SIZE}}{{UNIT}};',
                ],
             ]
        );
        //pro avatar width
        $this->add_responsive_control(
            'spicy_avatar_width',
            [
                'label' => __( 'Width', 'Spicy-extension' ),
                'type' => \Elementor\Controls_Manager::SLIDER,
                'size_units' => [ 'px','em','vh' ],
                'range' => [
                    'px' => [
                        'min' => 0,
                        'max' => 100,
                        'step' => 1,
                    ],
                    'vh' => [
                        'min' => 0,
                        'max' => 100,
                        'step' => 1,
                    ],
                    'em' => [
                        'min' => 0,
                        'max' => 10,
                        'step' => 0.1,
                    ],
                ],
                'default' => [
                    'unit' => 'px',
                    'size' => 50,
                ],
                'show_label' => true,
                'selectors' => [
                    '{{WRAPPER}} .spicy-avatar' => ' width: {{SIZE}}{{UNIT}};',
                ],
            ]
        );
        //pro avatar align control
        $this->add_responsive_control(
            'spicy_avatar_align',
            [
                'label' => __( 'Alignment', 'Spicy-extension' ),
                'type' => \Elementor\Controls_Manager::CHOOSE,
                'options' => [
                    'left' => [
                        'title' => __( 'Left', 'Spicy-extension' ),
                        'icon' => 'eicon-h-align-left',
                    ],
                    'center' => [
                        'title' => __( 'Center', 'Spicy-extension' ),
                        'icon' => 'eicon-h-align-center',
                    ],
                    'right' => [
                        'title' => __( 'Right', 'Spicy-extension' ),
                        'icon' => 'eicon-h-align-right',
                    ],
                ],
                'selectors' => [
                    '{{WRAPPER}} .spicy-avatar-wrapper' => ' text-align: {{VALUE}};',
                ],
            ]
        );
        //pro avatar spacing control
        $this->add_responsive_control(
            'spicy_avatar_spacing',
            [
                'label' => __( 'Spacing', 'Spicy-extension' ),
                'type' => \Elementor\Controls_Manager::DIMENSIONS,
                'size_units' => [ 'px', '%', 'em' ],
                'selectors' => [
                    '{{WRAPPER}} .spicy-avatar' => 'margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
                ],
            ]
        );
        //pro border avatar
        $this->add_group_control(
            \Elementor\Group_Control_Border::get_type(),
            [
                'name' => 'spicy_avatar_border',
                'label' => __( 'Border', 'Spicy-extension' ),
                'selector' => '{{WRAPPER}} .spicy-avatar',
            ]
        );
        //pro border radius
        $this->add_responsive_control(
            'spicy_pro_border_radius',
            [
                'label' => __( 'Border Radius', 'Spicy-extension' ),
                'type' => Elementor\Controls_Manager::DIMENSIONS,
                'size_units' => [ 'px', '%' ],
                'selectors' => [
                    '{{WRAPPER}} .spicy-avatar' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
                ],
            ]
        );
        $this->end_controls_section();
    }

5. Textarea Section: This is displayed in the Style Tab to change the style of Textarea.

protected function register_controls_pro_style_textarea(){
        $this->start_controls_section(
               'spicy_pro_style_textarea',
               [
                'label' => __( 'Textarea', 'Spicy-extension' ),
                'tab' => \Elementor\Controls_Manager::TAB_STYLE,
                'condition'=>[
                  'spicy_skin' => 'pro'
                ]
              ]
        );
        //pro textarea color control
        $this->add_control(
               'spicy_pro_textarea-color',
               [
                'label' => __( 'Color', 'Spicy-extension' ),
                'type' => \Elementor\Controls_Manager::COLOR,
                'default' => '',
                'selectors' => [
                  '{{WRAPPER}} .spicy-pro-description' => 'color: {{VALUE}};',
                ],
                'scheme' => [
                  'type' => \Elementor\Scheme_Color::get_type(),
                  'value' => \Elementor\Scheme_Color::COLOR_1,
                ],
               ]
        );
        //pro textarea typography control
        $this->add_group_control(
                \Elementor\Group_Control_Typography::get_type(),
                [
                 'name' => 'spicy_pro_textarea_typography',
                 'selector' => '{{WRAPPER}} .spicy-pro-description',
                 'scheme' => \Elementor\Scheme_Typography::TYPOGRAPHY_1,
             ]
        );
        //pro textarea align control
        $this->add_responsive_control(
               'spicy_pro_textarea-align',
                [
                 'label' => __( 'Alignment', 'Spicy-extension' ),
                 'type' => \Elementor\Controls_Manager::CHOOSE,
                 'options' => [
                               'left' => [
                                          'title' => __( 'Left','Spicy-extension' ),
                                          'icon' => 'fa fa-align-left',
                                         ],
                               'center' =>[
                                           'title'=>__('Center','Spicy-extension' ),
                                           'icon' => 'fa fa-align-center',
                                          ],
                               'right' =>[
                                          'title' => __( 'Right','Spicy-extension'),
                                          'icon' => 'fa fa-align-right',
                                         ],
                               'justify'=>[
                                           'title' => __( 'Justified','Spicy-extension'),
                                           'icon' => 'fa fa-align-justify',
                                          ],
                              ],
                'default' =>'',
                'selectors' => [
                  '{{WRAPPER}} .spicy-pro-description' => 'text-align: {{VALUE}};',
                ],
            ]
        );
        //pro textarea spacing control
        $this->add_responsive_control(
               'spicy_textarea_spacing',
               [
                'label' => __( 'Spacing', 'Spicy-extension' ),
                'type' => \Elementor\Controls_Manager::DIMENSIONS,
                'size_units' => [ 'px', '%', 'em' ],
                'selectors' => [
                  '{{WRAPPER}} .spicy-pro-description' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
                ],
            ]
        );
        $this->end_controls_section();
    } 

Create Final Html Output

To render our gallery output in the frontend, we must create the render function as the following:

protected function render() {
        $settings = $this->get_settings_for_display();
        ?>
        <div class="spicy-gallery" width="100%">
        <?php
   
        if($settings['spicy_skin']=='classic'){
            $this->classic_render($settings);
        }
         else{
          $this->pro_render($settings);
         }
        ?>
        </div>        
        <?php
    }
protected function classic_render($settings){
        if($settings['spicy_images']){
            foreach ( $settings['spicy_images'] as $image ) {
            echo '<div class="spicy-item elementor-animation-'. $settings['spicyhover'] .' ">';
               echo '<img id="'. $image['id'].'" class="spicy-img" src="' . $image['url'] . '">';
               echo '</div>';
            }
      }  
    }
protected function pro_render($settings){
        if($settings['spicy_image_list']){
            foreach ( $settings['spicy_image_list'] as $image ) {
                    echo '<div class="spicy-item elementor-animation-'. $settings['spicyhover'] .' ">';
                    echo '<div class="spicy-pro-container">';   
                        echo '<img class="spicy-pro-img" src="'.$image['spicy_pro_image']['url'].'" >';     
                    echo '</div>';  
                    echo '<p class="spicy-pro-text">'.$image['spicy_pro_text'].'</p>';
                    echo'<div class="spicy-avatar-wrapper">';
                        echo '<img class="spicy-avatar" src="'.$image['spicy_pro_avatar']['url'].'" alt="Avatar">';
                    echo'</div>';
                    echo '<p class="spicy-pro-description">'.$image['spicy_pro_description'].'</p>';
                    
                    
                echo '</div>';
            }
        }
    }

Create Live Preview in Editor

To generate a live preview of the gallery output, we must create the _content_template() method by using Javascript:

protected function _content_template() {
       ?>
        <div class="spicy-gallery">
        <#
         if(settings.spicy_skin=='classic'){
         _.each( settings.spicy_images, function( image ) {
        #>
        <div class="spicy-item elementor-animation-{{settings.spicyhover}}">
         <img  class="spicy-img" id="{{image.id}}" src="{{image.url}}"/>
        </div>
        <# }); } else{
            _.each( settings.spicy_image_list, function( image ) {
        #>
            <div class="spicy-item elementor-animation-{{settings.spicyhover}}">    
             <div class="spicy-pro-container">
                <img class="spicy-pro-img" src="{{image.spicy_pro_image.url}}" >
           </div>
              <p class="spicy-pro-text">{{image.spicy_pro_text}}</p>
                <div class="spicy-avatar-wrapper">
                 <img class="spicy-avatar" src="{{image.spicy_pro_avatar.url}}" alt="Avatar">
                </div>
                <p class="spicy-pro-description">{{image.spicy_pro_description}}</p>
            </div>
        <# }); }#>
        </div>
      <?php
    }

How to Register CSS Style to Widget

After creating a gallery widget, we need to register essential CSS style to our widget. To do this, we need to first create a CSS file, then register it in Spicy.php.

Gallery.css
.spicy-gallery{
 display:grid;
 grid-template-rows: auto;
 }
.spicy-gallery .spicy-img{
 width: 100%;
 object-fit: cover;
 margin: 0 auto;
 }

.spicy-gallery .spicy-pro-container{
 width:100%; 
 }
.spicy-gallery .spicy-pro-text{
 text-align:center;
 margin-top:-70px;
 margin-bottom: 10px;
 }
.spicy-gallery .spicy-pro-description{
 width:100%;
 text-align: center;
 padding-left: 10px;
 padding-right: 10px;
        
 }
.spicy-gallery .spicy-pro-img{
 width: 100%;
 }
.spicy-gallery .spicy-avatar{
 display:inline-block;
 margin-left:10px;
 margin-bottom: 20px;
 border-radius: 12px;
 }
.spicy-gallery .spicy-avatar.wrapper{
 display: block;
 }

In order to register our CSS file, we need to add below code to Spicy.php

// Register Widget Styles
add_action( 'elementor/frontend/after_enqueue_styles', [ $this, 'widget_styles' ] );
public function widget_styles() {
 wp_enqueue_style( 'spicyPluginStylesheet', plugins_url( '/css/gallery.css', __FILE__ ) );
}

Classic layout output

Creating a New Widget Classic Layout Output

Pro layout output

Creating a New Widget Pro Layout Output

Conclusion

In this blog post, we described how to extend Elementor like a pro in detail and explained the structure of an Elementor widget. We also illustrated how to create a section and control, how to display/hide a section or control, how to change the widget style from controls, how to get settings and how to render the final output in frontend and Elementor editor.

Finally, we walked you through the steps of developing a gallery widget. Feel free to share your ideas and experiences in the comments section below.

Creating A Brief For Your WordPress Developer: 8 Things To Remember

Creating a brief for your WordPress developer Featured Image

This is a guest post contributed to Artbees Themes blog by Kristin Savage.

Writing a brief for your WordPress developer is something you must never overlook or discard as unnecessary. If you want to get a high-quality end product, you must be ready to put in some effort to phrase what you want this product to be. Here are the things to keep in mind when creating a brief for your WordPress developer.

Why Is It Important?

A brief for your WordPress developer is important for four main reasons:

  • It provides key details about what you want the developer to do and what you want the result to be.
  • It sets the tone of your project and gives a general idea of what it is that you have in mind.
  • It sets the timeframes and deadlines for the project describing when and what should be completed.
  • It specifies the requirements and additional features you might decide to add later on.

If your brief achieves all of these, then your WordPress developer will definitely know what you want from them and what they must do to get the results you need. Now, let’s get into the step-by-step guide.

1. General Project Information

The first section of your brief contains general project information. It functions as a kind of introduction to your document that aims to provide some of the key data about the client and the project.

First, include your personal details such as your full name, contact information, the company or organization you are representing, and so on. Anything that you consider necessary can be added. If you are not the client, then include the details of the person who you are working for and who will be the client.

Second, include the basic details about the project such as the project’s name, timeframes, and so on. Don’t go into much detail about any particular piece of information in this section as these will be discussed later on in your brief.

2. Goals & Estimates

The next section of your brief must contain the goals and estimates for your project. Think of what you want to achieve with this particular endeavor and then also include what your personal goals are if you are in charge of it.

Photo by Isaac Smith on Unsplash

In addition to that, talk about the problems that the project will solve. These can be both big and small, but make sure to include at least one problem. Also, include estimates for your project and what you think it will achieve that is not connected to solving problems.

If you haven’t set your goals yet, it’s a good idea to use a goal tracking app such as Aha! to do this and keep your aims in front of you at all times as well as check progress.

3. Target Audience

Creating a Brief for your WordPress Developer Target Audience
Photo by Austin Distel on Unsplash

The third section of your brief must contain the details about your target audience. If you haven’t researched this yet, then make sure to do it as soon as possible as this is a very important component of every successful project.

Describe such characteristics of your audience as its gender, age, occupation, location, interests, habits, and so on. Any relevant information will only be useful rather than a burden, so don’t be afraid to add something on top of that.

However, keep in mind that your audience may vary a lot and you may need to construct several customer personas. As long as you do this, you will be heading in the right direction with your project.

4. Detailed Project Description

This is the biggest section of your brief which goes into detail about every aspect of your project. Look back at your first section and elaborate on the points you made in it. You must be as precise and clear as possible. Otherwise, your developer won’t have a concrete picture of what you want and need.

If you don’t want to write your brief yourself, you can hire a professional from an online writing service such as Studicus to do this for you. Alternatively, you can ask only for this biggest part to be written for you and work on everything else yourself. If you decide to write your brief entirely on your own, make sure to check its grammar, spelling, and punctuation with the help of Grammarly.

5. Project Requirements

Right after the detailed project description, outline the project requirements. These include both functional and secondary requirements. Any tools, as well as resources, can also be added here in case you require those. Functional requirements are the ones that the project absolutely needs in order to be what you want it to be.

These include different features you want to be added and such. Secondary requirements are the ones that are not necessary right away and can be considered if there is enough time or resources for them. They serve as an additional advantage that your project may have in the future.

6. Timeline & Deadlines

Creating a Brief for your WordPress Developer Timeline and Deadlines
Photo by Alvaro Reyes on Unsplash

This section must include a timeline with all of the deadlines for your project. Even though you included a start and end date in the very first section of your brief, you must elaborate on it here and outline other dates. Make sure that you write about deadlines and timeframes for each stage of development. This will guarantee that the progress is steady and you can check back on how your developer is doing every once in a while.

Alternatively, you may want to ask your developer to report back on certain dates. You can also outline another timeline that can be used in case the first one fails due to an unexpected turn of events. This will work as a kind of a Plan B, so you don’t necessarily have to include it into the brief and can simply keep it for yourself.

7. Budget & Expenses

Creating a Brief for your WordPress Developer Budget and Expenses
Screenshot taken from Mint

The budget and expenses are always a sensitive topic for both sides. This is why you must treat this section with caution and make sure that all of the data you include here is as accurate as possible. Think of all the expenses you will have. If there are any additional ones that can happen out of the blue, describe them too as you wouldn’t want any surprises. You can use different apps such as Mint to help you keep track of your budget and manage your bank accounts at the same time.

8. Measures of Success

Creating a Brief for your WordPress Developer Measures of Success
Photo by Carlos Muza on Unsplash

The last section should include your measures of success. To put it simply, these are the standards that you will be comparing the result in order to check the project’s success. You must make them as realistic as possible, but not too high or too low at the same time.

You could say that these are similar to the second section of your brief where you mentioned your estimates. But unlike that section, this one should have more exact and concrete standards that you will stick to more seriously.

Final Thoughts on Creating a Brief for your WordPress Developer

In conclusion, writing a brief for your WordPress developer is definitely not as difficult once you know what it must include. Proper communication with your developer will ensure that both of you are aware of each other’s thoughts and intentions, so don’t hesitate to contact them if there is something you’d like to specify.

Google Maps Alternatives: Good or Bad?

Google Maps Alternatives Featured Image

Many websites created by Jupiter users include a Google Map element. They’re particularly helpful for businesses who have a set location that they would like their visitors to know about and visit. But in using Google Maps, you might run into difficulties such as error messages as seen in the image below. In the next paragraph, we’ll explain in detail why you’ve received this message and how you can begin using alternatives to Google Maps!

The launch of Google Maps Platform

As of 11 June 2018, Google Maps changed its name to Google Maps Platform. With this change, any site that uses Google Maps is required to have a valid API key and a Google Cloud Platform billing account. Featuring Google Maps on your website is still possible, but you’ll have to make some modifications to how it is set up, including giving Google your credit card.

You can read more about the restructuring of Google Maps here.

Without an API key, sites with Google Maps will no longer be supported, and any Maps requests will now show only low-resolution maps watermarked with “for development purposes only” unless a credit card is associated with it.

If you are unwilling to create a billing account with Google, you can still find some Google Maps alternatives. We’ll list some of them.

Open Street Maps as a Google Maps Alternative

Tons of sites all around the world are working on OSM. The WP OSM Plugin is an open-source plugin with a free license and without any pro version or a business plan.

Features of the WP OSM Plugin include:

  • Add geolocation to a post or a page
  • Add GPX and KML files or just a single marker to a map
  • Add maps as shortcode or widget to a post/page
  • Fullscreen map, maps with markers linked to post/page with geolocation
  • OpenStreetMap, OpenSeaMap, Stamen maps, basemap, etc.
Google Maps Alternatives Features

You can read their FAQ and documentation on their official site.
Read the “Display a simple map” article to find out how to add a map to your WordPress site.

MapBox

Another alternative to Google Maps is MapBox. Mapbox is an online mapping service that allows users to customize maps – much more than Google Maps ever did. You can change backgrounds, road colors, and a lot more directly with a simple interface.

WP Mapbox GL JS Maps is the only plugin that enables you to make highly customized maps with 3D features, custom icons, zooming, custom filters, among many more options. It has a free and an inexpensive premium version, which adds custom markers, better control over directions, and more advanced control over marker and popup behavior.

To get started with MapBox, visit http://mapbox.com/ and click “Sign Up.” There are different pricing options you can select when signing up for MapBox. For now, go ahead and sign up for a free account, which comes with more than enough to get started. Read more here on how to use it.

The plugin provides you with a live preview while building the map, so you’ll always be able to see what the map will be before it’s published onto the main site. You can embed your map with a shortcode or publish it directly!

We’ll now show you how to use these alternatives to Google Maps on the Jupiter X site and describe the Open Street Map.

How to use Open Street Maps on Jupiter X

You’ll first need to install the OSM plugin in your WordPress dashboard via Plugins > Add New as described in this Jupiter X article.

Then follow the instructions from the OSM article:

1. Create a page or a post where you want to show a map and open it to edit.

2. Find the “WP OSM Plugin shortcode generator” section in the page editor and customize the map to your liking.

3. Below, you’ll see a map that you’ll need to adjust. Click on the place where you want to show a marker on the map. It will generate the map latitude and longitude based on where you placed the marker.

4. The last step is saving your marker and generating the shortcode.

Google Maps Alternatives Last Step

5. You’ll see a shortcode above the button.

6. Copy it to be able to add it to the page in the Elementor editor.

7. Once you’ve copied the shortcode, you can go and edit the page with Elementor.

8. Look for the “Shortcode” widget in the Elementor editor.

Google Maps Alternatives Elementor Editor

9. Drag it to the page and paste the OSM shortcode you copied in the previous step.

10. You’ll see the map as you styled it. Here is the result you’ll see on the frontend:

Google Maps Alternatives Result

Wrapping Up

Open Street Maps and MapBox are fantastic free plugins for placing detailed, interactive maps on your website. If you take the time to read the instructions for the plugins, you’ll find that they are a useful way of enhancing any post or page.

How to Create Custom Forms with Jupiter X Form Builder

Create Custom Forms Featured

Creating custom forms in WordPress has always been a hassle. Jupiter X has begun a new era in the form building experience. Now, you can create custom forms visually. Typically, site visitors come across forms such as signup, sign in, register, subscription, contact or any other custom forms.

Forms allow site visitors the ability to send information such as usernames, passwords, comments, emails, among other things to the server.

Forms contain fields such as dropdowns, checkboxes, text boxes, input, as well as submit and radio buttons. Nowadays forms play an integral role, particularly in digital marketing and social networks.

Form Builder in Depth

If you want to build a beautiful and secure form with Jupiter X, then you can use the Form Builder, which allows you to create custom forms in a few minutes with minimum effort.

Content Tab

This tab contains four sections such as Form Fields, Submit Button, Settings and Feedback Messages. Let’s go through each of them one by one.

Form Fields

In this section, we can create fields for our forms. Form Fields have two options:

  • Form: for setting the name of the form.
  • Items: each field in the form.
Create Custom Forms Screenshot 1
Create Custom Forms Screenshot 2

Furthermore, each item has its options like:

  • Type
  • Label
  • Placeholder
  • Required
  • Column Width
  • And more.

One of the important options of each field is the Type option, which defines the role of the field. The Jupiter X Form Builder has eleven types for fields:

  • Text: for creating the text input.
  • Email: for creating the email input.
  • Textarea: for creating the textarea input.
  • Tel: for creating only the phone number input.
  • Number: for creating only the number input.
  • Date: for creating the date picker.
  • Time: for creating the time picker.
  • Checkbox: for creating a checkbox element.
  • Radio: for creating a radio button element.
  • Acceptance: for creating an agreement for conditions or a term of use button.
  • reCAPTCHA: for creating the human testing Google service.
Create Custom Forms Screenshot 3

Submit Button

After clicking on the Submit Button, the information contained in the form is sent to the server. The Submit Button section has three options:

  • Text: for changing the bottom text.
  • Icon: for setting an icon for the Submit Button.
  • Column Width: for changing the Submit Button container.
Create Custom Forms Screenshot 4

Settings

The Settings section has three options:

  • Label: Show/Hide status for fields label.
  • Required Mark: Show/Hide * after fields label for required fields.
  • Add Action: Set the action in the form.
Create Custom Forms Screenshot 5

The Jupiter X Form Builder allows the user to choose from seven action fields. If you choose one of them, the options section of each action appears at the bottom of the setting section to where you can modify it.

1. Email: This action email form allows users to fill in their email address such as admin. We can set To, Email Subject, From Email, From Name, Reply-To, Cc, Bcc.

Create Custom Forms Screenshot 6
Create Custom Forms Screenshot 7

2. MailChimp: This is used for a subscription form. In order to use this, we must first have an API Key from MailChimp to manage our subscriber list. You can learn more about how to get a MailChimp API by clicking on this link.

In the MailChimp section, set the API Key, choose the Audience, and finally set the field mapping. In default, MailChimp has six fields including Email, Address, Birthday, First Name, Last Name, and Phone Number.

Create Custom Forms Screenshot 8
Create Custom Forms Screenshot 9

3. Redirect: With this, we can redirect our page to another URL.

Create Custom Forms Screenshot 10
Create Custom Forms Screenshot 11

4. Slack: this action allows us to send information from our form to Slack. In order to do this, we must first sign up for Slack, enable the incoming webhook, then set our information in the Slack setting section. For more info on how to do this, you can head over to this link.

Create Custom Forms Screenshot 12
Create Custom Forms Screenshot 13

5. HubSpot: One feature that Hubspot provides is allowing users to create a contact form and then to publish these forms on the internet. This allows us to directly submit our form data to the Hubspot form.

First, signup to HubSpot then go to Marketing > Forms and create a new form. After that, design a form and get a Portal ID and Form ID to enter in the HubSpot action settings in the form.

Create Custom Forms Screenshot 16
Create Custom Forms Screenshot 17

6. Download: This allows site visitors to download a file from your website.

Create Custom Forms Screenshot 18
Create Custom Forms Screenshot 19
Create Custom Forms Screenshot 20

7. Webhook: this enables us to integrate our form with Zapier webhooks. Zapier webhooks allows us to complete several tasks such as sending an email, connecting to Facebook, Twitter, and Slack, as well as accessing more than 1,500 apps.

To activate this action, go to Zapier, generate a zap, and copy the Webhook URL and paste it into the Webhook action setting. To find out more about how to do this, visit this link.

Feedback Messages

After a page visitor submits a form, they will be able to see a message on their screen. The Jupiter X Form Builder has four types of messages, including Successful Message, Error Message, Required Message and Subscriber Already Exists Message. In the Feedback Messages section, you can alter the messages tailored to fit your needs.

Create Custom Forms Screenshot 22

Style Tab

This tab contains six settings including General, Label, Field, Checkbox, Radio, Button. Let’s walk through each setting one by one.

General

In the general section, we have two options:

  • Column Spacing: this allows you to change the space between fields that are arranged horizontally.
  • Row Spacing: this enables you to adjust the space between fields that are arranged vertically.

Label

In the label section, we can set the below style for the label:

  • Color
  • Font family
  • Font size
  • Font weight
  • Transform
  • Style
  • Decoration
  • Line-height
  • Letter spacing
  • Space between the label and field.

Field

In the field section, we can set the below style in the normal and focused situation.

  • Background color
  • Border Type
  • Border Radius
  • Box Shadow

Also, we can set Color, Typography, and Padding of the placeholder and value.

Checkbox

In the section, we can set below style in the normal and checked situation.

  • Size
  • Color
  • Typography
  • Spacing between
  • Spacing
  • Background color
  • Border style
  • Box-shadow

Radio

In the section, we can set below style in the normal and checked situation.

  • Size
  • Color
  • Typography
  • Spacing between
  • Spacing
  • Background color
  • Border style
  • Box-shadow

Button

In this section, we can adjust the style for a button with the following settings:

  • Height
  • Color
  • Typography
  • Spacing
  • Alignment
  • Background Type
  • Icon color
  • Border style
  • Box-shadow

Creating a Custom Contact Form

Contact forms are a useful type of form commonly used on websites. Visitors to your page can fill out contact forms to send messages, ideas, questions or any other information to the site owner.

In creating this form, you have the option to include Email, Redirect, Slack and Webhook actions. At the end of this section, we will create a Contact Us form like in the image below:

1. Go to Elementor > Add New.

2. Create a Section that is named Contact Us.

Create Custom Forms Screenshot 24

3. Add a new section with one column and set the following:

  • This image as a background image.
  • In the Content tab, Content Width: 680px
  • In the Advanced tab, Padding top and Padding bottom: 100px

4. Add a Heading.

  • In the Content tab, set Title to Contact Us and Alignment to Center.
  • In the Style tab, set Color to #FFFFFF
  • In the Advanced tab, set Padding Bottom to 16px.

5. Add a Form element from Raven Elements, then set the name to contact.

6. Create a First Name Field.

Create Custom Forms Screenshot 25

7. Create a Last Name field.

Create Custom Forms Screenshot 26

8. Create an Email Field.

Create Custom Forms Screenshot 27

9. Create a Phone Number field.

Create Custom Forms Screenshot 28

10. Create a Message field.

Create Custom Forms Screenshot 29

11. In the Style tab, set:

  • Background Color to rgba(255,255,255,0.91)
  • Placeholder Color to #000000

Also, set the other settings as shown in the images below.

Create Custom Forms Screenshot 30
Create Custom Forms Screenshot 31

12. Go to Content tab > Submit Button section.

Create Custom Forms Screenshot 32

13. Go to Style Tab > Button Section. Set Background Color to #6073e0

Create Custom Forms Screenshot 33

14. Go to Content tab > Setting section. Set your preferred actions.

Create Custom Forms Screenshot 34

15. Change the settings like in the images below in the sections of each action.

Create Custom Forms Screenshot 35
Create Custom Forms Screenshot 36

 

In the end, when submitting the form, the data will be sent to [email protected], the page will be redirected to www.artbees.net, the form data will be sent to our Slack channel and a new email will be sent to the Gmail account that we set up in our zap in Zapier.

Creating a Subscription Form

Subscription forms are popular on websites as it enables the site owner to send news and deals to their subscriber list.

In this section, we’ll go through and explain the MailChimp, Download and Hubspot actions in detail. Also at the end, we’ll create the subscription form shown in the following image.

1. Go to Elementor > Add New. Create a new section and call it “subscribe.”

2. Add a New Section with two columns then set:

  • Background Image to this image.
  • In the Layout tab, set content width to 800.
  • In the Advanced tab, set Padding to 100px.

3. Edit the left column. In the Advanced tab, set:

  • Padding-top to 19 %.
  • Padding-Right to 30%.

4. Add a Heading element in the left column the set following settings.

  • Title: Subscribe to our Feeds
  • Alignment: center
  • Text color : #FFFFFF
  • Size: 26

5. Add a Spacer element in the left column.

6. Add a Social Icons element in the left column with the following settings.

  • Shape : Circle
  • Alignment : Center
  • In style tab: Primary Color to rgba(255,255,255,0.54), Secondary Color to rgba(0,0,0,0.68), Size to 17, Spacing to 18.

7. Edit the Right column:

  1. In the Style tab, set background color to rgba(255,255,255,0.8).
  2. In the Advanced tab, set the following:
  • Padding-top to 10%.
  • Padding-Right to 5%.
  • Padding-left to 5%.
  • Padding-bottom to 4%.

8. Edit the Right column:

Add a Form Element. In the Content tab set Form to Subscribe.

9. Create the First Name and Last Name fields like in the images below:

 

10. Create the Email and Phone fields as seen in the following images:

11. Create a Birthday field like the images below:

Create Custom Forms Screenshot 46

Important Note: In your MailChimp account, the type of birthday field must be Date.

12. Change the Submit button and Settings sections like in the image below:

Create Custom Forms Screenshot 49
Create Custom Forms Screenshot 49

13. In the MailChimp section, set our API Key and create five fields in Field Mapping with the following values:

Field Mapping

MailChimp FieldForm Field
Phone NumberPhone
BirthdayBirthday
First NameFirst Name
Last NameLast Name
EmailEmail

14. In the Download section, set your URL for download.

15. In the Hubspot section, set the settings as seen in the following image:

Field Mapping

HubspotForm Field
firstnameFirst Name
lastnameLast Name
emailEmail

16. Edit the Form > Style Tab, set the following:

  • Column Spacing: 15
  • Row Spacing: 25

Finally, after submitting a form the download will begin, the data in the form will be sent to Hubspot, and users will be subscribed to MailChimp.

Securing the Form

There’s no doubt that security is one of the most vital factors when it comes to forms. The Jupiter X Form Builder has been built on top of standard security practices.

Frontend Validation

Frontend validation (HTML5 validation) is used on the client-side to ensure that the data is correct before sending. The data entered into a form needs to be in the right format and certain fields need to be filled in order to effectively send the submitted form. For example, if we enter a number in the email field, an error occurs and the data is not sent.

Backend validation

Backend validation runs on the server after the data from the form is sent to the server. Backend is more important than frontend validation because hackers can disable frontend validation and send malicious data to the server.

reCAPTCHA

reCAPTCHA is a free Google service that protects websites from spam and abuse. With this service, we can ensure that the data is sent by a human, not a robot.

To add this to our form, we must go to reCAPTCHA and get the Site Key and Secret Key, and then insert them into Elementor > Setting > Raven.

Then, create a field with reCAPTCHA type to use it.

Logging the Sent Emails

Being able to log and view the sent emails in WordPress admin is very helpful both for development purposes and in tracking the emails.

There are some plugins that allow us to log the emails.

1. Email Log

Email Log is a WordPress plugin that allows you to easily log and view all emails sent from WordPress.

The features of Email Log include the following:

  • See all sent emails.
  • Filtered or sorted based on the date, email, subject, etc.
  • Delete selected emails or bulk emails.
  • Resend emails.
  • Forward emails.
  • Export email logs.

2. WP Mail Logging

WP Mail Logging logs each email sent by WordPress. This can be useful if you don’t want to lose such mail content. It can also be useful for debugging purposes while developing.

Features

  • A complete list of sent emails.
  • Error status from mail implementation is logged.
  • Zero-configuration – just install and enjoy.
  • Log rotation – decide which emails you want to keep.
  • DevOP: IP of server sent the mail.

Conclusion

In this article, we took a deep dive into how you can create custom forms with the Jupiter X Form Builder. This post provided step-by-step instructions on how to create two types of forms: a contact form and a subscription form. Through looking at both of these examples, we also covered the Email, Redirect, Slack, MailChimp and Download actions that are found in them.

We took this post a step further and explained the significance of security and addressed frontend and backend validations, as well as reCAPTCHA. This post further examined two plugins that will allow you to log the emails that have been sent to your server from your site’s forms.

Feel free to share your ideas and experiences with us in the comments!

How to Create a Custom Footer with the Jupiter X Footer Builder

Create a Custom Footer Featured Image

Almost all of the themes out there provide you a way to customize the footer for the website they belong to. But have you ever wondered if there is a way to break the mold and build a brand new design to create a custom footer? This was a question frequently asked by Artbees users – now it’s possible with the new Jupiter X theme!

In this article, we’re going to discover how we can customize our footer area with the Jupiter X theme via the default options and then see how we can create a completely new footer design from scratch. The tools we need for this tutorial include:

  • A WordPress website
  • Jupiter X Pro theme
  • Elementor Page Builder
  • Raven Plugin (Exclusively developed by Artbees to extend Elementor free functionality)

Let’s have a quick overview of how we can customize our website footer with the default options first since it’s an easy and fast method for having a beautiful footer. After that, we’ll show you how to create a completely new footer from scratch.

Customizing the Default Footer in Jupiter X

The first method will use the Jupiter X Customizer settings. As mentioned, it’s powerful enough to build a completely customized footer. However, when using this method, you have only one footer for your entire website, and in case you need a different footer for other pages, you’ll need to create a custom footer and assign it to those pages. We’ll get to that later. For now, let’s look at how we can access the footer customizer and apply some changes on the prebuilt footer.

From the WordPress left sidebar click on Jupiter X > Customize.

Create a Custom Footer Jupiter X Customizer
Access to Jupiter X Customizer

Then find Footer from the list and click on it.

Create a Custom Footer Dashboard
Customizer Dashboard Menu

These settings will allow you to customize everything in your footer including static/fixed behavior, showing/hiding the sub footer, making it full width, and showing/hiding some other elements on the footer like Copyright text and menu.

Create a Custom Footer Choose Footer
Choose Footer from the customizer left dashboard

The cool part is the Widget Area option. As soon as you enable it, you’ll see how many layouts of widgets you can choose for your footer. You can then populate the footer widget areas from WordPress Appearance > Widgets.

Create a custom footer widget area enable
Enabling Widget Area option unveils a set of footer layouts

As soon as you enable the Widget Area, you’ll be able to see the widget styling options in the second tab. It has a set of options including Widgets Title, Texts, Links, Thumbnail, Container, Divider, and Container Styles which allows you to customize every single corner of your widget. And you can do all of it live.

Create a custom footer widget styling options
Widget Styling options in Jupiter X Footer Customizer

It’s pretty neat, isn’t it? Now, let’s see what we can build as a custom footer.

Creating a Custom Footer in Jupiter X

This is the part where you’ll need the Elementor and Raven plugins. Elementor will give you the ability to build fantastic layouts, and the Raven plugin will let you create custom footers and headers on Elementor (free version) and also will give you 22 new elements to use. You can find more information about Raven plugin here.

The great thing about a custom footer is that you are no longer limited to the WordPress widgets. You can put anything you want, anywhere you want in your footer. Also, Jupiter X Pro offers a set of bundled plugins that extends the number of elements and effects (such as Jet Elements and Jet Tricks) that you can use in Elementor. You can utilize them as well.

To use a custom footer on your website, you must:

  • Create your custom footer in Elementor.
  • Assign it to all pages on your website or to a specific page.

We’ll walk you through this process step-by-step.

Creating a new Custom Footer in Elementor

1. Like every other WordPress modification, the first step will start from the backend Side Dashboard. From the menu on the left in, click on Saved Templates beneath Elementor.

Create a custom footer saved templates
Click on Saved Templates to see the old templates and add a new one

2. Next to the Saved Templates title, click on the Add New button.

Create a customer footer add a new template
Add a new template to Elementor

Note: If you are using Elementor Pro, you may see your old footer templates in the Theme Builder instead of Saved Templates.

3. For the template type select Footer, give your footer a name and click on the Create Template button.

Crate a custom footer choosing a template
Choosing a template type while adding a new template to Elementor

4. You will now be redirected to the Elementor page editor where you can choose a footer template from the library or create a custom footer from scratch using Elementor. Some of the pre-made footers are part of Elementor Pro and, in order to use them, you must first purchase Elementor Pro. You can also use the pre-made Jupiter X templates, which are free.

To see the Jupiter X templates, simply click on Jupiter X. You can use your preferred template by clicking on the Insert button.

Create a custom footer Jupiter X footer templates for Elementor
Jupiter X Footer Templates for Elementor

If you don’t want to use pre-made templates and plan to create your custom footer from scratch, simply click on the “x” icon. That will redirect you to the Elementor page editor.

Create a custom footer closing templates modal
Closing the templates modal

5. If you’re using a pre-made template, you can customize it or add new widgets. In order to customize a brand new footer (without using a pre-made template), first determine how many columns you need for your footer template. Then add your desired Elementor’s widgets to it, such as Site Logo. Since this is same as creating any other content on your page, in case you needed more information about how to add and modify content on your template, you can check some Elementor Tutorials and the Jupiter X knowledge base.

Create a custom footer building template from scratch
Building up the template from scratch

You can also add WordPress widgets to your footer template by going to the WORDPRESS category in the Elementor widgets panel.

Create a custom footer widgets in Elementor
WordPress widgets in Elementor

6. Publish the template after customizing it.

Create a custom footer save changes
Save and Publish the changes

Important Notes!
1. In case you are using Elementor Pro for a footer template with Display Conditions, some of the Jupiter X footer customizations may not work properly. To fix this:

Make sure the Display Conditions are not provided for your footer templates.

Create a custom footer elementor display conditions
Elementor Display Conditions

Simply save a template without conditions.

2. Make sure the Sticky option under Scrolling Effect is set to None in the Advanced settings of the Section container in your footer template.

Create a custom footer section scrolling
Section Scrolling effect option.

Assigning a Custom Footer to your Website

Now it’s time to assign our beloved footer template to the website.

1. From the menu on the left in WordPress, go to Jupiter X > Customize.

2. From the dashboard on the left in Customizer, click on Footer.

3. For the Footer Type, select Custom and assign your footer from Template.

Create a custom footer customizer
Choosing the footer template in the Footer Customizer

4. Click on Close and Publish the changes.

You’re done! Now, refresh your page and see how your new footer looks on your website. One thing to mention is that you can add as many footer templates as you want and assign them to different pages. Have a look at this tutorial to find more!.

Summary

Footers are like the summary of an entire website. Having a good summary that contains relevant links and content at the end of a page will send your viewers to the right place.

In this article, we learned how to get rid of the technical coding stuff when creating a footer and easily build customized or new footers using the Jupiter X theme. Now, you can channel your energy into creating amazing your content instead of worrying about how to build a footer because every tool you need is ready for you!

Is WordPress Secure Enough? A WordPress Security Review

WordPress Security Featured Image

Let’s consider for a moment a hot topic when it comes to the web: security. Poor security on WordPress websites could lead to secrets being exposed, reputations getting lost in the market and even a service getting shut down.

Yes, security is the most important thing on the web and, unfortunately, many users and even administrators don’t have enough know-how about this matter.

In this post, we’ll take a glance at WordPress security to become familiar with the most crucial things about securing a WordPress website.

As an immense open-source project, WordPress is the most well-known CMS (content management system). Based on statistics, WordPress powers 34% of websites on the internet and more than 60% of websites that use a known CMS use WordPress. This also makes it the most attractive platform for hackers as well. Indeed, if you find an important security hole in WordPress, you can affect 34% of the internet.

You may have heard about WordPress websites getting hacked, and the main question that comes to mind is: “Is WordPress secure enough?” In this post, we’ll address this question by reviewing some stats, layers of WordPress installation security and the performances of administrators. Read until the end to get a good idea of the state of WordPress security.

WordPress Security Statistics

WordPress Security Report Sucuri.Net
Image from sucuri.net

According to a report from Sucuri & GoDaddy, from a total of 25,466 infected sites in 2018, 90% of them used WordPress. The report reveals an increase in WordPress infections from 2017.

When looking at the most significant problems, you won’t find anything related to WordPress core security. Interestingly, common issues with WordPress security are not related to WordPress itself. What matters are the configuration, the usage and what administrators do.

We know that WordPress is an open-source project, meaning that everyone can see the codes and make some changes to them. On the one hand, this would seem to be an open-source CMS that could potentially cause more security problems because anyone can see the codes, discover a security bug and later use it to attack websites.

On the other hand, any individual can watch the status of a project’s security and report any possible security issue privately to the team. WordPress applies security patches immediately, and a new version becomes available to download as soon as possible. Each time a new version comes out, they mention how many security problems have been fixed in the changelog.

Based on the statistics, there is no common effect on websites due to WordPress core security problems. In other words, it works!

Layers of WordPress Security

We first need to wrap our heads about the fact that the security of a WordPress site is not only about WordPress itself. It depends on some other aspects such as themes and plugins, as well as some third-party tools like hosts and servers – and above all, site administrators.

WordPress Core Security

We briefly reviewed WordPress core security in the previous paragraph and mentioned that WordPress patches any security problems immediately. But, what will happen if you don’t upgrade your WordPress installation with the latest version? Then, you’re an excellent target for hackers.

WordPress Core Security

Detecting the version of WordPress is not hard to do. Even if you use plugins and codes to hide which version of WordPress your site is using, there’s no guarantee that hackers won’t be able to detect the version of your WordPress. And after that, there’s a list of security problems (which are fixed in the latest version) that you won’t have access to it unless you upgrade WordPress. With a list of potential security holes in your site, the chances of being hacked increase. Therefore, it’s better to constantly keep your WordPress site up to date.

Security of Themes & Plugins

Unknown Sources

Most WordPress sites get hacked because of their backdoors. So, if you download a theme or plugin from an unknown source or a site that provides a nulled version of premium themes, you are at risk. It’s all too easy to inject some codes into the package and provide a zip package to download. Unfortunately, some users are not aware of this and, for that, administrators are responsible.

Non-updated Themes or Plugins

Similar to WordPress core, you should keep plugins or themes up to date because they are even more vulnerable. Sometimes it takes time to patch a security problem. If you read the fascinating facts surrounding the Panama Papers hack on Wordfence’s site, you may never forget to update your themes and plugins with the latest version.

Host, Server and WordPress Installation Configuration

Web Hosting WordPress Security
Photo by Web Hosting on Unsplash

Sometimes, a website gets hacked in shared hosting. After hacking a website, the hacker may penetrate the host and then access other websites on that server.

Consequently, it’s crucial to host your website in a known and secure host provider. And it’s better to configure the hosts accurately. There are many factors to consider while configuring a server for a WordPress installation. As a quick checklist, you should be aware of the firewall, backup system, SSL and SFTP, automatic security checks, malicious activity detector, email security, and file permission. We’ll take a deep dive into these matters in a later blog post.

Moreover, it’s necessary to be aware of configuring the installation. Changing default database table prefixes, using a strong password and not using “admin” as the username can decrease the chances of your website getting hacked. Following these simple steps can help you to avoid any possible security problems on your website. We’ll also describe in detail WordPress configuration problems in another blog post.

Administration

As mentioned previously, admin is one of the biggest problems and the main reason why websites get hacked. As we can gather from the statistics, the most prevalent problem is website administrators and webmasters. Unfortunately, not enough attention has been paid to this matter.

The security of a website is highly dependent on the performance of webmasters. They can simply allow hackers in by setting an easy-to-guess password or username like “admin” and “12345678.” Sometimes, webmasters aren’t informed about security patches or small updates, and it can put the website at risk. An administrator should be cautious about user roles and permissions when a website is open for new user registration.

Many of these precautions are easy to take when using a security plugin. We’ll have a blog post about WordPress security plugins that will include a review of the most popular ones.

As a result, it’s easy to say that the WordPress core is highly secure. The community will take care of WordPress security, and if you want to use it for your new project, it’s better to take some time to learn more about common security problems, find a good host provider and correct configuration.

Artbees Takeaways from Translation Day 4 as First-Time Contributors

Artbees Hosted Istanbul Meetup on WordPress Translation Day 4

24 hours, 81 local events, 612 volunteers and contributors. Yes, this is the power of WordPress! On Translation Day 4 on May 11, the WordPress family came together in 35 countries around the world to celebrate the MakeWordPress polyglots team. Hundreds of volunteers dedicated their time and skills to translate all things WordPress and to make this online platform available to more people around the globe.

We at Artbees wanted to be part of this amazing polyglot crowd and event, so we decided to hold a meetup in Istanbul. It was the best chance for us to both meet with fellow WordPress lovers in Istanbul and to contribute what we could to help WordPress grow into as many languages as possible.

It took us such as long time to recover from all the excitement of Translation Day 4 in that we’re finally getting around to publishing this post! Everything went as planned, and on a cloudy Saturday morning, Niloufar and Maziar gathered at a cozy diner in Istanbul to help make the event a success.

After setting up the equipment, preparing snacks and welcoming two other attendees, we lined up to join the live session with Afsana Multani, WordPress enthusiast, speaker and contributor, and to speak with the WordPress community as first-time polyglots contributors.

https://twitter.com/artbees_design/status/1127211307655139329?s=20
https://twitter.com/niloomand/status/1127129544572510208?s=20

Other activities as part of WordPress Translation Day 4 included live online training, localization and internalization sessions as well as local and remote events. During the course of the day, polyglot contributors collaborated on a number of common goals such as increasing the number of PTEs and mentors, translating the 200 most popular themes and plugins and improving Rosetta’s Translate page.

We hit the ground running, and our local Istanbul meetup contributed translations to WordPress.org and WordPress Rosetta projects! Being among avid participants from 35 countries with the common goal of making WordPress more accessible in different languages felt beyond amazing!

https://twitter.com/niloomand/status/1127162966623903744?s=20
https://twitter.com/rouzbehfirouz/status/1127162174542090240?s=20

What is WordPress Translation Day

WordPress Translation Day – which takes place every year – is a 24-hour worldwide marathon that is committed to translating everything in the WordPress sphere, including core, themes, plugins, documentation and marketing assets into as many different languages as possible.

The day is entirely driven by contributors, who volunteer their competence, time, labor, and equipment to translate WordPress into their languages. Everyone from WordPress professionals to inexperienced users is invited to join in the event.

Previous WordPress Translation days had over 700 people globally participate both online and in person in local meetups to translate WordPress – but the event has grown in size throughout the years. Translation Day 4 had more local meet-ups than previous translation days with contributors working around the clock in Africa, Asia, Europe, North America, South America, and Oceania.

Why We Should Contribute to WordPress

The main aim of WordPress Translation Day is to help make WordPress understandable to as many people as possible around the world. This aligns with the WordPress culture of giving back to the community.

On a regular basis, contributors with varying levels of experience from developers to marketers, designers and translators lend their expertise and knowledge to the WordPress open source project, which in turn helps millions of people throughout the world to build websites and provide digital services.

Anyone who knows about any aspect of WordPress can contribute to creating, maintaining and growing the platform. Contributors are part of the larger global WordPress community and ultimately aim to enhance the platform and connect with other WordPress enthusiasts.

Why WordPress Needs to be Translated

The reach of WordPress is far and wide: it powers more than one-third of all websites – meaning that there’s a need to translate a massive amount of content into several different languages.

The number of languages that WordPress has been translated into has grown since the first Translation Day was held. In 2017, WordPress was translated into 178 languages. That number has now surpassed the 200th mark.

Presently, WordPress is accessible in 201 locales with 3,086 PTEs, 614 GTEs, and 32,585 contributors.

The more that WordPress is translated, the more that users throughout the world are empowered. Thanks to the hard work and commitment of the global polyglot community, more and more people are able to access the most popular WordPress plugins and themes that have been translated into their language.

How to Create a Custom Header with Jupiter X like on the Apple website

Custom Header Featured Image

With the Jupiter X theme, you can create a new custom header from scratch using Elementor. It allows you to use all of the Elementor elements to build a highly customized header, which is the first thing visitors will see when they enter your site.

Please note that the Raven plugin must be installed and activated among the bundled plugins before being able to use the Custom Header feature with the free edition of Elementor.

The difference between the old Header Builder in Jupiter 6 and the Custom Elementor Header Builder in Jupiter X

Simply put, the old header builder has limited options and styles, while the custom Elementor header has virtually unlimited customization due to its widgets. This means that you will be able to add any Elementor elements to the header, including buttons, images, headings, icons and much more.

The Header Builder in Jupiter 6 doesn’t have as many elements, meaning the design is simpler:

Custom Header Jupiter 6 Header Builder

If you’re looking for a more specific header design, Elementor will allow you to do just that:

Custom Header Elementor Header Design

What you can do with the Jupiter X Header Builder

With the power of the Elementor editor, you can design a custom header any way you like and be able to complete projects faster than ever before. You can also save a header template and reuse it for another project. With the Header Builder and the Raven plugin, things are going to be much simpler and faster.

Custom Header Template Library

You also have the option of choosing a header from a large selection of designer-made blocks, which are readily available in the Template Library. There, you’ll be able to find templates from Jupiter X and the Elementor Pro version. Customize it any way you like, and give it your own personal touch.

Note that to use the Elementor Pro templates, you’ll have to buy the Elementor Pro version as the Jupiter X theme only provides the free version of Elementor. However, there are several Jupiter X header templates for you to choose from, and the Pro version is not required.

Creating a custom header like on the Apple website

Creating a custom header can be done with ease and in no time at all as code editing is not required.

As an example, we’ll use a header from the Apple website:

Custom Header Apple Website Header

To create a similar header, make sure you have activated the Raven and Elementor plugins.

1. From the menu on the left side in WordPress, click on Templates and go to the Saved Templates menu

Custom Header First Step

2. Click on the Add New button next to the My Templates title.

Custom Header Second Step

3. For the template type, select Header, give your header a name and click on the Create Template button.

Custom Header Choose Template

4. Click on the + icon to add a new section and select the structure as a row without columns.

Custom Header Add New Section

5. Before adding a menu to the section, create it in Appearance > Menus. If you want to have the icon as the homepage instead of just a text as on the Apple site, you will also need to activate the Menu Icons plugin that is bundled in the theme. Then, in Appearance > Menus, create a menu, and select an icon for your homepage item:

Custom Header Create Menu

Select any icon you want. If you want to hide the Home text in the menu and only show an icon, you need to check the Hide Label option and set other settings. Then, click the Select button:

Custom Header Select Icon

6. Add other menu items from your pages or custom links to complete the menu.

7. When it’s saved, go back to your header template in Elementor editor and look for the Navigation Menu element from Raven (the blue one).

Custom Header Navigation Menu

8. Select your menu and enable the Menu Icons option in the Settings tab.

Custom Header Menu Icons

You’ll still see the Home label in the editor, but the icon will be shown on the frontend.

Custom Header Home Icon

9. You can then add a background color for the header. You’ll need to edit the Section settings, and set the background in the Style tab:

Custom Header Set Background Color

Also, style your Navigation Menu via the Style tab in the Navigation Menu settings.

Once you have made the needed changes to the header design, click Publish, and assign the custom header in Appearance > Customize > Header.

custom header result

That’s it! You’ll now be able to see your handcrafted header live on your site. You can easily create as many headers as you like, and spread them across the relevant pages with a single click.

Wrap Up

From now on, you don’t need to change your header.php file or hire a developer to customize the CSS header elements of your site. Jupiter X features make designing a header quick and straightforward. With the bundled plugins like Raven, Menu Icons and the main one Elementor, you’ll be able to create any custom design for your header.

Extend Elementor like a pro: Creating a new extension in Elementor

Creating a New Extension Featured Image

Elementor is a powerful drag and drop page builder that allows us to create pages conveniently. One of Elementor’s greatest features is extendibility. With this feature, we can build custom widgets/controls via creating a new extension in Elementor.

This extension implements object-oriented programming in which a main class and extra classes for smaller parts like custom Elementor Widgets or any other components are used.

Plugin Structure

The main plugin should have basic information about the extensions, to check basic requirements and to load the required files to activate the plugin functionality. In the following sections, we’ll take a deep dive into each part of the plugin.

Defining Variable

Variables are used to store information to be referenced and manipulated in a computer program.

In the main class, we must define three constants like:

  • VERSION: store the version of our plugin.
  • MINIMUM_ELEMENTOR_VERSION: store the essential version of Elementor.
  • MINIMUM_PHP_VERSION: store the PHP version used in the plugin.
const VERSION;
const MINIMUM_ELEMENTOR_VERSION;
const MINIMUM_PHP_VERSION;

Single Instance

The singleton pattern is used to restrict the instantiation of a class to a single object, which can be useful when only one object is required across the system.

When creating a new extension in Elementor, we can only have one instance of the main class. To do this, we use a singleton pattern. In this pattern, we must define a private static variable and public static function.

private static $_instance = null;

	public static function instance() {
            //check $_instance is null or not
		if ( is_null( self::$_instance ) ) {
			self::$_instance = new self();
		}
		return self::$_instance;

	}

Constructor

The Constructor or magic function is a special type of function that is automatically executed after a class is created or instantiated. Usually, the constructor starts with two underscore characters.

In the main class, the role of the constructor is to load localization functionality and initiate the plugin.

public function __construct() {

		add_action( 'init', [ $this, 'i18n' ] );
		add_action( 'plugins_loaded', [ $this, 'init' ] );

	}
public function i18n() {
		load_plugin_textdomain( 'our-plugin-name' );
	}
public function init() {
		// Plugin logic here...
	}

Check if Elementor is Installed

As the plugin extends Elementor functionality, you should first check whether Elementor is installed and activated before the main class loads.

If Elementor is activated, the main class will load. If it’s not activated, an admin notice will be displayed and the functionality won’t continue loading. This check must be done in the initialization stage of the main class.

public function init() {

		// Check if Elementor installed and activated
		if ( ! did_action( 'elementor/loaded' ) ) {
			add_action( 'admin_notices', [ $this, 'admin_notice_missing_main_plugin' ] );
			return;
		}

	}

	public function admin_notice_missing_main_plugin() {

		if ( isset( $_GET['activate'] ) ) unset( $_GET['activate'] );

		$message = sprintf(
			/* translators: 1: Our plugin name 2: Elementor */
			esc_html__( '"%1$s" requires "%2$s" to be installed and activated.', 'our-plugin-name' ),
			'<strong>' . esc_html__( 'Elementor our-plugin-name', 'our-plugin-name' ) . '</strong>',
			'<strong>' . esc_html__( 'Elementor', 'our-plugin-name' ) . '</strong>'
		);

		printf( '<div class="notice notice-warning is-dismissible"><p>%1$s</p></div>', $message );

	}

Check the Version of Elementor

After checking whether or not Elementor is installed, we must check the Elementor version for backward compatibility with older Elementor versions. If the defined minimum version of our plugin is not compatible with the installed version of Elementor, then the admin message will be displayed, and functionality will not be able to load.
This check is done in the initialisation stage of the main class.

const MINIMUM_ELEMENTOR_VERSION = '2.5.11';

	public function init() {

		// Check for required Elementor version
		if ( ! version_compare( ELEMENTOR_VERSION, self::MINIMUM_ELEMENTOR_VERSION, '>=' ) ) {
			add_action( 'admin_notices', [ $this, 'admin_notice_minimum_elementor_version' ] );
			return;
		}

	}

	public function admin_notice_minimum_elementor_version() {

		if ( isset( $_GET['activate'] ) ) unset( $_GET['activate'] );

		$message = sprintf(
			/* translators: 1: Our plugin name 2: Elementor 3: Required Elementor version */
			esc_html__( '"%1$s" requires "%2$s" version %3$s or greater.', 'our-plugin-name' ),
			'<strong>' . esc_html__( 'Elementor our-plugin-name', 'our-plugin-name' ) . '</strong>',
			'<strong>' . esc_html__( 'Elementor', 'our-plugin-name' ) . '</strong>',
			 self::MINIMUM_ELEMENTOR_VERSION
		);

		printf( '<div class="notice notice-warning is-dismissible"><p>%1$s</p></div>', $message );

	}

Check for the PHP Version

Finally, we must check our extension’s minimum PHP version, which . must be newer than the PHP version of the Elementor plugin. If there’s an older version, then the admin message will be displayed, and the functionality won’t load.

const MINIMUM_PHP_VERSION = '7.0';

	public function init() {

		// Check for required PHP version
		if ( version_compare( PHP_VERSION, self::MINIMUM_PHP_VERSION, '<' ) ) {
			add_action( 'admin_notices', [ $this, 'admin_notice_minimum_php_version' ] );
			return;
		}

	}

	public function admin_notice_minimum_php_version() {

		if ( isset( $_GET['activate'] ) ) unset( $_GET['activate'] );

		$message = sprintf(
			/* translators: 1: Our plugin name 2: PHP 3: Required PHP version */
			esc_html__( '"%1$s" requires "%2$s" version %3$s or greater.', 'our-plugin-name' ),
			'<strong>' . esc_html__( 'Elementor our-plugin-name', 'extension-name' ) . '</strong>',
			'<strong>' . esc_html__( 'PHP', 'our-plugin-name' ) . '</strong>',
			 self::MINIMUM_PHP_VERSION
		);

		printf( '<div class="notice notice-warning is-dismissible"><p>%1$s</p></div>', $message );

	}

Including Essential Files to Correctly Create a New Extension

After completing all checks, the extension must load essential files like widgets and controls in order for it to run correctly.

public function init() {

		// Add Plugin actions
		add_action( 'elementor/widgets/widgets_registered', [ $this, 'init_widgets' ] );
		add_action( 'elementor/controls/controls_registered', [ $this, 'init_controls' ] );
	}

	public function init_widgets() {

		// Include Widget files
		require_once( __DIR__ . '/widgets/our-plugin-name-widget.php' );

		// Register widget
		\Elementor\Plugin::instance()->widgets_manager->register_widget_type( new \Elementor_extension_Widget() );

	}

	public function init_controls() {

		// Include Control files
		require_once( __DIR__ . '/controls/our-plugin-name-control.php' );

		// Register control
		\Elementor\Plugin::$instance->controls_manager->register_control( 'control-type-', new \extension_Control() );

	}

Workflow Diagram

Create a New Extension - Workflow Diagram

In the end, if we want to create our extension named Spicy, we must have the following code in its entirety.

<?php
 /**
 * Plugin Name: Spicy Extension
 * Description: Custom Elementor extension.
 * Plugin URI:  https://spicy.test/
 * Version:     1.0.0
 * Author:      Spicy
 * Author URI:  https://spicy.test/
 * Text Domain: spicy-extension
 */

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

	/**
	 * Main Spicy Extension Class
	 *
	 * The main class that initiates and runs the plugin.
	 *
	 * @since 1.0.0
	 */
	final class Spicy_Extension {

		/**
		 * Plugin Version
		 *
		 * @since 1.0.0
		 *
		 * @var string The plugin version.
		 */
		const VERSION = '1.0.0';

		/**
		 * Minimum Elementor Version
		 *
		 * @since 1.0.0
		 *
		 * @var string Minimum Elementor version required to run the plugin.
		 */
		const MINIMUM_ELEMENTOR_VERSION = '2.5.11';

		/**
		 * Minimum PHP Version
		 *
		 * @since 1.0.0
		 *
		 * @var string Minimum PHP version required to run the plugin.
		*/
		const MINIMUM_PHP_VERSION = '6.0';

		/**
		 * Instance
		 *
		 * @since 1.0.0
		 *
		 * @access private
		 * @static
		 *
		 * @var Spicy_Extension The single instance of the class.
		 */
		private static $_instance = null;

		/**
		 * Instance
		 *
		 * Ensures only one instance of the class is loaded or can be loaded.
		 *
		 * @since 1.0.0
		 *
		 * @access public
		 * @static
		 *
		 * @return Spicy_Extension An instance of the class.
		 */
		public static function instance() {

			if ( is_null( self::$_instance ) ) {
				self::$_instance = new self();
			}

		 	return self::$_instance;
		}

		/**
		 * Constructor
		 *
		 * @since 1.0.0
		 *
		 * @access public
		 */
		
		public function __construct() {	
			add_action( 'init', [ $this, 'i18n' ] );
			add_action( 'plugins_loaded', [ $this, 'init' ] );
		}

		/**
		 * Load Textdomain
		 *
		 * Load plugin localization files.
		 *
		 * Fired by `init` action hook.
		 *
		 * @since 1.0.0
		 *
		 * @access public
		 */
		public function i18n(){}

		/**
		 * Initialize the plugin
		 *
		 * Load the plugin only after Elementor (and other plugins) are loaded.
		 * Checks for basic plugin requirements, if one check fail don't continue,
		 * if all check have passed load the files required to run the plugin.
		 *
		 * Fired by `plugins_loaded` action hook.
		 *
		 * @since 1.0.0
		 *
		 * @access public
		 */
		public function init() {

			// Check if Elementor installed and activated
			if ( ! did_action( 'elementor/loaded' ) ) {
				add_action( 'admin_notices', [ $this, 'admin_notice_missing_main_plugin' ] );
				return;
			}

			// Check for required Elementor version			
			if ( ! version_compare( ELEMENTOR_VERSION, self::MINIMUM_ELEMENTOR_VERSION, '>=' ) ) {
				add_action( 'admin_notices', [ $this, 'admin_notice_minimum_elementor_version' ] );
				return;
			}

			// Check for required PHP version
			if ( version_compare( PHP_VERSION, self::MINIMUM_PHP_VERSION, '<' ) ) {
				add_action( 'admin_notices', [ $this, 'admin_notice_minimum_php_version' ] );
				return;
			}

			// Add Plugin actions
			add_action( 'elementor/widgets/widgets_registered', [ $this, 'init_widgets' ] );
			add_action( 'elementor/controls/controls_registered', [ $this, 'init_controls' ] );
			
			// Register Widget Styles
			add_action( 'elementor/frontend/after_enqueue_styles', [ $this, 'widget_styles' ] );

		}

		public function widget_styles() {
			//For Example
			//wp_enqueue_style( 'spicyPluginStylesheet', plugins_url( '/css/gallery.css', __FILE__ ) );
		}

		/**
		 * Admin notice
		 *
		 * Warning when the site doesn't have Elementor installed or activated.
		 *
		 * @since 1.0.0
		 *
		 * @access public
		 */
		public function admin_notice_missing_main_plugin() {

			if ( isset( $_GET['activate'] ) ) unset( $_GET['activate'] );

			$message = sprintf(
				/* translators: 1: Plugin name 2: Elementor */
				esc_html__( '"%1$s" requires "%2$s" to be installed and activated.', 'Spicy-extension' ),
				'<strong>' . esc_html__( 'Elementor Spicy Extension', 'Spicy-extension' ) . '</strong>',
				'<strong>' . esc_html__( 'Elementor', 'Spicy-extension' ) . '</strong>'
			);		

			printf( '<div class="notice notice-warning is-dismissible"><p>%1$s</p></div>', $message );

		}

		/**
		 * Admin notice
		 *
		 * Warning when the site doesn't have a minimum required Elementor version.
		 *
		 * @since 1.0.0
		 *
		 * @access public
		 */
		public function admin_notice_minimum_elementor_version() {

			if ( isset( $_GET['activate'] ) ) unset( $_GET['activate'] );

			$message = sprintf(
				/* translators: 1: Plugin name 2: Elementor 3: Required Elementor version */
				esc_html__( '"%1$s" requires "%2$s" version %3$s or greater.', 'Spicy-extension' ),
				'<strong>' . esc_html__( 'Elementor Spicy Extension', 'Spicy-extension' ) . '</strong>',
				'<strong>' . esc_html__( 'Elementor', 'Spicy-extension' ) . '</strong>',
				self::MINIMUM_ELEMENTOR_VERSION
			);

			printf( '<div class="notice notice-warning is-dismissible"><p>%1$s</p></div>', $message );

		}

		/**
		 * Admin notice
		 *
		 * Warning when the site doesn't have a minimum required PHP version.
		 *
		 * @since 1.0.0
		 *
		 * @access public
		 */
		public function admin_notice_minimum_php_version() {		

			if ( isset( $_GET['activate'] ) ) unset( $_GET['activate'] );

			$message = sprintf(
				/* translators: 1: Plugin name 2: PHP 3: Required PHP version */
				esc_html__( '"%1$s" requires "%2$s" version %3$s or greater.', 'Spicy-extension' ),
				'<strong>' . esc_html__( 'Elementor Spicy Extension', 'Spicy-extension' ) . '</strong>',
				'<strong>' . esc_html__( 'PHP', 'Spicy-extension' ) . '</strong>',
				self::MINIMUM_PHP_VERSION
			);

			printf( '<div class="notice notice-warning is-dismissible"><p>%1$s</p></div>', $message );

		}

		/**
		 * Init Widgets
		 *
		 * Include widgets files and register them
		 *
		 * @since 1.0.0
		 *
		 * @access public
		 */
		public function init_widgets() {
			 // For Example
			// Include Widget files
			//require_once( __DIR__ . '/widgets/gallery.php' );

			// Register widget
			//\Elementor\Plugin::instance()->widgets_manager->register_widget_type( new \spicy_oEmbed_Widget() );
			//\Elementor\Plugin::instance()->widgets_manager->register_widget_type( new \spicy_gallery_Widget() );

		}

		/*
		 * Init Controls
		 *
		 * Include controls files and register them
		 *
		 * @since 1.0.0				
		 *
		 * @access public
		*/
		public function init_controls() {
			//For example
			
			//Include Control files
			//require_once( __DIR__ . '/controls/multi-unit.php' );

			// Register control
		    //\Elementor\Plugin::$instance->controls_manager->register_control( 'spicy-multi-unit-control', new spicy_multi_unit());

		}

    }

spicy_Extension::instance();

Also, you can download the entire code by clicking on this link.

Conclusion

In this article, we went into depth to illustrate the basic structure of the plugin that extends the functionality of Elementor. Also, we explained each essential part of the plugin code like Variable, Single Instance, Constructor, Checking steps and Include files. Finally, we included the entire code for the Spicy plugin.

We’d love to hear from you! Feel free to drop us a comment to share your experiences and ideas.

Artbees Takeaways from WordCamp Europe 2019

WordCamp Europe 2019 was the biggest and the best WordCamp I have ever attended. But that’s not what made it special. What WordCamp Europe 2019 successfully accomplished for the first time was holding a massive event without falling into the usual pitfalls of events this size.

In my first encounter with WordCamp Europe 2019 at the Estrel venue in Berlin on June 20, I recalled events such as Web Summit. It was a three-day event held in a huge venue packed with talks, workshops, booths, and activities with thousands of attendees. It was the largest WordPress event ever, with 3,260 tickets sold and 2,734 attendees.

The attendees came from 97 different countries, and 1,722 of them had never attended WordCamp Europe before. Events this size must be quite difficult to organize and might require months of planning, arrangements and hundreds of people involved.

How was it different from any other WordCamp?

Events on this massive of a scale might end up making them mediocre with poor organization, average talks and bad service for attendees. But this is exactly where WordCamp differed from events like WebSummit. WordCamp 2019 was an event much larger than its usual size and so wonderfully organized that its scale didn’t make it mediocre.

The other important way WordCamp sets itself far above other large-scale tech events is that it’s driven by the community, not profit. Look at an event like WebSummit or the TNW Conference, and you’ll notice an event designed to make money. At these events, celebrities are invited to a venue with a private jet to talk about your run-of-the-mill topics so more people in that crowd understand it. This is the problem in events that sell as many tickets as possible to earn more money and end up with a very disparate audience!

WordCamp Europe 2019 Estrel

An event like WordCamp is designed primarily to gather members of a community, rather than running a business to make money out of it. It’s very common to see someone who is a speaker at one WordCamp to be a volunteer at another. And yes! Volunteers aren’t volunteering for a reference letter here! WordCamp Europe 2019 didn’t just set a new standard for continental WordCamps but also set one for other tech events.

Jupiter X in WordCamp Europe 2019

Artbees attended WordCamp Europe 2019 on June 20-22 in Berlin. We had the pleasure of not just attending but also co-sponsoring the event this year. Jupiter X had one of the busiest booths in a small business hall in WCEU this year.

It was also our first WordCamp where we gave away a small but lovely prize. The Jupiter X AirPod Giveaway was so welcomed by the attendees at WordCamp this year that during the drawing session on the last day of the event, our booth was hardly able to host the massive crowd waiting for their name to be selected from the box as the winner!

Meeting with Jupiter X users, friends and partners

We had so many great guests stop by the Jupiter X booth for a visit, including some of our fantastic users, fellow authors in the Envato market, our friends in the industry and the WordPress community.

Meeting with the Envato Team & Envato Worldwide

We had various meetings with the Envato team during different WordCamp Europe 2019 events. On Envato Worldwide Day, which was one of the side events, we met with and greeted the great Envato team. Cameron Gough, James Giroux, Stephen Cronin and Aaron Rutley from the Envato team stopped by our booth during the first conference day for a nice chat and some fruitful discussions about the state of WordPress, the Envato market, trends and more.

It was also a pleasure having a special meeting with Cameron Gough – General Manager of Content at Envato – and to share some ideas and suggestions regarding ThemeForest as one of its authors. Cameron is not just a great human but also very determined to reflect the authors’ voices in Envato and to take some huge steps forward by addressing issues and possible improvements to the marketplace.

It’s also been a year in which Envato is hosting meetups of their own called Envato Worldwide in different cities around the world, including London, Amsterdam, Kyiv, Warsaw, and Saint Petersburg. As part of their worldwide tour, they held a meetup in Berlin a few days before WordCamp Europe 2019.

Envato Worldwide meetups are a great combination of keynote presentations, local author presentations, networking, Q&A sessions and a nice after party. As members of the Envato community, we were thrilled to meet and greet with some of the great authors in ThemeForest as well as the amazing members of the Envato team, discuss ideas, share experiences and learn from one another.

WordCamp Europe 2019 Contributor Day

There is no denying that one of the best parts of every WordCamp is the Contributor Day. At large WordCamps like WordCamp Europe, it’s even better! What you could see was a large hall filled with passionate community members who were helping to build or expand some aspect of WordPress.

500 contributors formed different groups such as core, review, marketing, support, polyglot, design, etc. and were led by 37 experienced lead contributors to different MakeWordPress projects. From Artbees, Gulhan joined and contributed to the “theme review” team. Rouzbeh contributed to the marketing team, and Nel joined the community team just like at WCEU 2018.

During Contributor Day at WordCamp Europe 2019, we also had an exciting encounter with the one and only Matt Mullenweg, WordPress co-founder and CEO.

WordCamp Europe 2019 Gulhan and Carolina
Gulhan with Carolina Nymark from the “theme review” team on Contributor Day
WordCamp Europe 2019 Contributor Day Matt
The Jupiter X team with Matt Mullenweg at WordCamp Europe 2019 on Contributor Day

The group photo taken at the end of Contributor Day may have been the biggest photo ever taken at a WordCamp. Check for yourself and see how many of Artbees members you can find in the crowd!

WordCamp Europe 2019 Group Photo
Photo courtesy of WordCamp Europe 2019

Talks

Unlike last year, we were running a booth at WCEU this year, so, unfortunately, we could not attend most of the talks. There were some great topics and speakers I shared in our “we’re heading Berlin” post on June 13. But one talk in particular that is most talked about at every WordCamp Europe or WordCamp US is Matt Mullenweg’s State of WordPress talks.

This year, he again went up on stage thanking and giving a round of applause to the organizing team and sponsors who made possible what he called the best WordCamp ever. He explained the latest developments of different WordPress projects, especially Project Gutenberg and its roadmap in the coming months. And, as usual, it concluded with a Q&A session.

Side events

There were several side events before and during WordCamp Europe 2019. We were invited to a side event or afterparty almost every day before WCEU and during the conference days.

Just like at WCEU 2018, we joined Elementor’s side event this year but with a brand new name called “Meet the Makers of Elementor.” This event was much more than an afterparty and included various sessions such as workshops and keynotes. I’m sure this will be a good beginning for them to make great community get-togethers in the future.

WordCamp Europe 2019 Elementor Makers

Just like WCEU 2018, the Freemius team held a lovely side event in Berlin to gather different kinds of WordPress community members. It was great to meet Vova at this WCEU and engage in some insightful discussions about their service Freemius, which is a platform for WordPress businesses to operate on.

WordCamp Europe 2019 Freemius

Afterparty

A WordCamp is never complete without its afterparty! Just like many other WordCamp Europe attendees, we were counting down the seconds for another great afterparty for WCEU 2019, and it definitely lived up to the hype! The afterparty this year was a 4-hour long chat, complete with music and dancing with a party theme I’ve always liked: the 80s!

WordCamp Europe 2019 Jupiter X Afterparty
The Jupiter X team at the WordCamp Europe 2019 afterparty!

Closing Talk

The WordCamp Europe 2019 closing talks were a holistic review of this year’s event with tons of interesting figures about its contributors, volunteers, sponsors, speakers, and attendees. Though there was a section this year celebrating those who helped make the event happen but couldn’t attend the event.

Like any other event, WordCamp Europe 2019 was not without issues. But the great thing about WordCamps is that you can see a noticeable betterment after each WordCamp from city to city. WordCamp 2019 in Berlin was definitely a better WordCamp than WordCamp Europe 2018 in Belgrade – and hopefully, WordCamp 2020 in Porto will be better as well!