<?php
/**
 * Plugin Name: OrokuBlocks for Elementor
 * Plugin URI: https://orokublocks.com/
 * Description: Incredible widgets for WordPress and Elementor.
 * Version: 1.0.0
 * Requires at least: 6.9
 * Requires PHP: 7.4
 * Author: OrokuBlocks
 * Author URI: https://orokublocks.com/
 * License: GPLv3
 * License URI: http://www.gnu.org/licenses/gpl-3.0.html
 * Text Domain: orokublocks
 * Domain Path: /languages
 */

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

if ( ! function_exists( 'orokublocks_fs' ) ) {
  // Create a helper function for easy SDK access.
  function orokublocks_fs() {
      global $orokublocks_fs;

      if ( ! isset( $orokublocks_fs ) ) {
          // Include Freemius SDK.
          require_once dirname( __FILE__ ) . '/vendor/freemius/start.php';

          $orokublocks_fs = fs_dynamic_init( array(
              'id'                  => '29122',
              'slug'                => 'orokublocks',
              'premium_slug'        => 'orokublocks-pro',
              'type'                => 'plugin',
              'public_key'          => 'pk_83d4319abdb797adb9be54503d806',
              'is_premium'          => false,
              'premium_suffix'      => '(Pro)',
              'has_premium_version' => true,
              'has_addons'          => false,
              'has_paid_plans'      => true,
              'is_org_compliant'    => false,
              'menu'                => array(
                'slug'           => 'orokublocks',
                'override_exact' => true,
                'support'        => false,
                'contact'        => false,
                'parent'         => array(
                  'slug' => 'orokublocks',
                )
              )
          ) );
      }

      return $orokublocks_fs;
  }

  // Init Freemius.
  orokublocks_fs();
  // Signal that SDK was initiated.
  do_action( 'orokublocks_fs_loaded' );

  function orokublocks_fs_settings_url() {
    return admin_url( 'admin.php?page=orokublocks#tab=general' );
  }

  orokublocks_fs()->add_filter('connect_url', 'orokublocks_fs_settings_url');
  orokublocks_fs()->add_filter('after_skip_url', 'orokublocks_fs_settings_url');
  orokublocks_fs()->add_filter('after_connect_url', 'orokublocks_fs_settings_url');
  orokublocks_fs()->add_filter('after_pending_connect_url', 'orokublocks_fs_settings_url');

  function orokublocks_fs_custom_icon() {
    return dirname( __FILE__ ) . '/admin/assets/image/logo.png';
  }

  orokublocks_fs()->add_filter( 'plugin_icon', 'orokublocks_fs_custom_icon' );
}

if ( function_exists( 'orokublocks_fs' ) ) {
  if ( orokublocks_fs()->is_registered() ) {
    add_action( 'orokublocks_admin_menu_after_submenu_items', function () {
    ?>
      <li class="oroku-menu__item">
        <a class="oroku-menu__link" href="<?php echo esc_url( admin_url( 'admin.php?page=orokublocks-account' ) ); ?>">
          <?php esc_html_e( 'Account', 'orokublocks' ); ?>
        </a>
      </li>
    <?php
    });
  }
}

if ( ! class_exists( 'OrokuBlocks' ) ) {
   final class OrokuBlocks {
    /**
     * @var string Current version
     */
    public $version = '';

    /**
     * @var string Plugin name
     */
    public $name = '';
    
    /**
     * @var string Pathname of the current file
     * 
     * @access private
     */
    private $file = '';

    /**
     * @var string Plugin folder path
     */
    public $path = '';

    /**
     * @var string Plugin folder url
     */
    public $url = '';

    /**
     * @var array Supported plugin integrations
     * 
     * @access private
     */
    private $plugins = [];

    /**
     * @var array Widget categories by id
     */
    public $categories = [];

    /**
     * @var array Widget tags by id
     */
    public $tags = [];

    /**
     * @var array Widgets by id
     */
    public $widgets = [];

    /**
     * @var OrokuBlocks_Admin Singleton instance
     */
    public $admin = null;

    /**
     * @var OrokuBlocks_Settings Singleton instance
     */
    public $settings = null;

    /**
     * Prevent more than one instance from being created
     * 
     * @since 1.0.0
     * 
     * @access private
     */
    private function __construct() {}

    /**
     * Prevent cloning
     * 
     * @since 1.0.0
     * 
     * @access private
     */
    private function __clone() {}

    /**
     * Returns the OrokuBlocks class instance
     * 
     * @since 1.0.0
     * 
     * @return OrokuBlocks $instance  OrokuBlocks class instance
     */
    public static function getInstance() {
      static $instance = null;

      if ( $instance === null ) {
        $instance = new OrokuBlocks();

        // setup
        $instance->initialize();
        $instance->includeFiles();
        $instance->addActions();
      }

      return $instance;
    }

    /**
     * Initialize property values
     * 
     * @since 1.0.0
     * 
     * @access private
     */
    private function initialize() {
      $this->version = '1.0.0';
      $this->name = 'OrokuBlocks';
      $this->file = __FILE__;
      $this->path = plugin_dir_path( $this->file );
      $this->url = plugin_dir_url( $this->file );
      $this->plugins = [
        'gamipress',
        'sportspress',
        'woocommerce'
      ];
    }

    /**
     * Include files
     * 
     * @since 1.0.0
     * 
     * @access private
     */
    private function includeFiles() {
      /**
       * Load router.
       * 
       * @since 1.0.0
       */
      require_once $this->path . '/includes/classes/class-orokublocks-router.php';

      /**
       * Load settings functionality.
       * 
       * @since 1.0.0
       */
      require_once $this->path . '/admin/includes/classes/class-orokublocks-settings.php';

      /**
       * Load admin related functionality
       * only when on the WordPress admin.
       */
      if ( is_admin() ) {
        /**
         * Load admin functionality.
         * 
         * @since 1.0.0
         */
        require_once $this->path . '/admin/includes/classes/class-orokublocks-admin.php';
      }

      /**
       * Load WordPress integrations.
       * 
       * @since 1.0.0
       */
      require_once $this->path . '/includes/classes/class-orokublocks-blog.php';

      /**
       * Load plugin integrations.
       * 
       * @since 1.0.0
       */
      foreach ( $this->plugins as $plugin ) {
        // load the plugin integration file only if the plugin is active
        if ( $this->pluginIsActive( $plugin ) ) {
          require_once $this->path . '/includes/classes/plugins/class-orokublocks-' . $plugin . '.php';
        }
      }

      if ( $this->pluginIsActive( 'elementor' ) ) {
        /**
         * Load widgets functionality.
         * 
         * @since 1.0.0
         */
        require_once $this->path . '/includes/classes/class-orokublocks-widget.php';
      }
    }

    /**
     * Setup variables
     * 
     * @since 1.0.0
     */
    public function setup() {
      $this->categories = [
        'general' => [
          'id'    => 'general',
          'name'  => __( 'General', 'orokublocks' ),
          'theme' => 'secondary'
        ],
        'gamipress' => [
          'id'      => 'gamipress',
          'name'  => __( 'GamiPress', 'orokublocks' ),
          'plugin'  => 'gamipress'
        ],
        'sportspress' => [
          'id'      => 'sportspress',
          'name'  => __( 'SportsPress', 'orokublocks' ),
          'plugin'  => 'sportspress'
        ],
        'woocommerce' => [
          'id'      => 'woocommerce',
          'name'  => __( 'WooCommerce', 'orokublocks' ),
          'plugin'  => 'woocommerce'
        ]
      ];

      $this->tags = [
        'freemium'  => [
          'id'    => 'freemium',
          'name'  => 'Freemium',
          'theme' => 'positive'
        ],
        'pro'       => [
          'id'    => 'pro',
          'name'  => 'Pro',
          'theme' => 'highlight'
        ]
      ];

      $this->widgets = [
        /**
         * General
         */
        'scoring_rating'              => [
          'id'          => 'scoring_rating',
          'name'        => __( 'Scoring Rating', 'orokublocks' ),
          'categories'  => [ 'general' ],
          'tags'        => [ 'freemium' ],
          'urls'        => [
            'documentation' => 'https://orokublocks.com/documentation/scoring-rating',
            'demo'          => 'https://orokublocks.com/widgets/scoring-rating'
          ],
          'versions'  => [
            'v1'  => [
              'pro' => false
            ],
            'v2'  => [
              'pro' => false
            ],
            'v3'  => [
              'pro' => true
            ],
            'v4'  => [
              'pro' => true
            ]
          ]
        ],
        'item_review'                 => [
          'id'          => 'item_review',
          'name'        => __( 'Item Review', 'orokublocks' ),
          'categories'  => [ 'general' ],
          'tags'        => [ 'freemium' ],
          'urls'        => [
            'documentation' => 'https://orokublocks.com/documentation/item-review',
            'demo'          => 'https://orokublocks.com/widgets/item-review'
          ],
          'versions'  => [
            'v1'  => [
              'pro' => false
            ],
            'v2'  => [
              'pro' => false
            ],
            'v3'  => [
              'pro' => true
            ],
            'v4'  => [
              'pro' => true
            ],
            'v5'  => [
              'pro' => true
            ]
          ]
        ],
        'affiliate_referral'          => [
          'id'          => 'affiliate_referral',
          'name'        => __( 'Affiliate and Referral', 'orokublocks' ),
          'categories'  => [ 'general' ],
          'tags'        => [ 'freemium' ],
          'urls'        => [
            'documentation' => 'https://orokublocks.com/documentation/affiliate-referral',
            'demo'          => 'https://orokublocks.com/widgets/affiliate-referral'
          ],
          'versions'  => [
            'v1'  => [
              'pro' => false
            ],
            'v2'  => [
              'pro' => false
            ],
            'v3'  => [
              'pro' => true
            ],
            'v4'  => [
              'pro' => true
            ]
          ]
        ],
        'promo_discount'              => [
          'id'          => 'promo_discount',
          'name'        => __( 'Promo and Discount', 'orokublocks' ),
          'categories'  => [ 'general' ],
          'tags'        => [ 'freemium' ],
          'urls'        => [
            'documentation' => 'https://orokublocks.com/documentation/promo-discount',
            'demo'          => 'https://orokublocks.com/widgets/promo-discount'
          ],
          'versions'  => [
            'v1'  => [
              'pro' => false
            ],
            'v2'  => [
              'pro' => false
            ],
            'v3'  => [
              'pro' => true
            ],
            'v4'  => [
              'pro' => true
            ]
          ]
        ],
        'testimonial_recommendation'  => [
          'id'          => 'testimonial_recommendation',
          'name'        => __( 'Testimonial and Recommendation', 'orokublocks' ),
          'categories'  => [ 'general' ],
          'tags'        => [ 'freemium' ],
          'urls'        => [
            'documentation' => 'https://orokublocks.com/documentation/testimonial-recommendation',
            'demo'          => 'https://orokublocks.com/widgets/testimonial-recommendation'
          ],
          'versions'  => [
            'v1'  => [
              'pro' => false
            ],
            'v2'  => [
              'pro' => false
            ],
            'v3'  => [
              'pro' => true
            ],
            'v4'  => [
              'pro' => true
            ]
          ]
        ],
        /**
         * GamiPress
         */
        'gamipress_achievement' => [
          'id'          => 'gamipress_achievement',
          'name'        => __( 'Achievement', 'orokublocks' ),
          'plugin'      => 'gamipress',
          'categories'  => [ 'gamipress' ],
          'tags'        => [ 'freemium' ],
          'urls'        => [
            'documentation' => 'https://orokublocks.com/documentation/achievement',
            'demo'          => 'https://orokublocks.com/widgets/achievement'
          ],
          'versions'  => [
            'v1'  => [
              'pro' => false
            ],
            'v2'  => [
              'pro' => true
            ],
            'v3'  => [
              'pro' => true
            ],
            'v4'  => [
              'pro' => true
            ]
          ]
        ],
        'gamipress_rank'        => [
          'id'          => 'gamipress_rank',
          'name'        => __( 'Rank', 'orokublocks' ),
          'plugin'      => 'gamipress',
          'categories'  => [ 'gamipress' ],
          'tags'        => [ 'pro' ],
          'urls'        => [
            'documentation' => 'https://orokublocks.com/documentation/rank',
            'demo'          => 'https://orokublocks.com/widgets/rank'
          ],
          'versions'  => [
            'v1'  => [
              'pro' => true
            ],
            'v2'  => [
              'pro' => true
            ],
            'v3'  => [
              'pro' => true
            ],
            'v4'  => [
              'pro' => true
            ]
          ]
        ],
        'gamipress_point'       => [
          'id'          => 'gamipress_point',
          'name'        => __( 'Point', 'orokublocks' ),
          'plugin'      => 'gamipress',
          'categories'  => [ 'gamipress' ],
          'tags'        => [ 'pro' ],
          'urls'        => [
            'documentation' => 'https://orokublocks.com/documentation/point',
            'demo'          => 'https://orokublocks.com/widgets/point'
          ],
          'versions'  => [
            'v1'  => [
              'pro' => true
            ],
            'v2'  => [
              'pro' => true
            ],
            'v3'  => [
              'pro' => true
            ],
            'v4'  => [
              'pro' => true
            ]
          ]
        ],
        /**
         * SportsPress
         */
        'sportspress_player_card' => [
          'id'          => 'sportspress_player_card',
          'name'        => __( 'Player Card', 'orokublocks' ),
          'plugin'      => 'sportspress',
          'categories'  => [ 'sportspress' ],
          'tags'        => [ 'freemium' ],
          'urls'        => [
            'documentation' => 'https://orokublocks.com/documentation/player-card',
            'demo'          => 'https://orokublocks.com/widgets/player-card'
          ],
          'versions'  => [
            'v1'  => [
              'pro' => false
            ],
            'v2'  => [
              'pro' => true
            ],
            'v3'  => [
              'pro' => true
            ],
            'v4'  => [
              'pro' => true
            ]
          ]
        ],
        /**
         * WooCommerce
         */
        'woocommerce_product' => [
          'id'          => 'woocommerce_product',
          'name'        => __( 'Product', 'orokublocks' ),
          'plugin'      => 'woocommerce',
          'categories'  => [ 'woocommerce' ],
          'tags'        => [ 'freemium' ],
          'urls'        => [
            'documentation' => 'https://orokublocks.com/documentation/product-card',
            'demo'          => 'https://orokublocks.com/widgets/product-card'
          ],
          'versions'  => [
            'v1'  => [
              'pro' => false
            ],
            'v2'  => [
              'pro' => true
            ],
            'v3'  => [
              'pro' => true
            ],
            'v4'  => [
              'pro' => true
            ]
          ]
        ]
      ];

      /**
       * Setup settings
       * 
       * @since 1.0.0
       */
      $this->settings = OrokuBlocks_Settings::getInstance();

      /**
       * Setup admin related functionality
       * only when on the WordPress admin
       * 
       * @since 1.0.0
       */
      if ( is_admin() ) {
        $this->admin = OrokuBlocks_Admin::getInstance();
      }

      if ( $this->pluginIsActive( 'elementor' ) ) {
        /**
         * Load widget files and instantiate them
         * 
         * @since 1.0.0
         */
        foreach ( $this->widgets as $widget_id => $widget ) {
          // if the widget requires a plugin and it's not active, do not load its file nor instantiate it
          if ( array_key_exists( 'plugin', $widget ) && ( ! $this->pluginIsActive( $widget['plugin'] ) ) ) {
            continue;
          }

          // Load widget file
          $widget_path_id = OrokuBlocks_Router::widgetPathIdGet( $widget_id );

          require_once OrokuBlocks_Router::widgetPathGet( $widget_id ) . '/class-orokublocks-' . $widget_path_id . '.php';

          // Instantiate widget
          $widget_classname = OrokuBlocks_Router::widgetClassNameGet( $widget_id );
          
          if ( class_exists( $widget_classname ) ) {
            $this->widgets[$widget_id]['instance'] = $widget_classname::getInstance();
          }
        }
      }
    }

    /**
     * Register actions
     * 
     * @since 1.0.0
     * 
     * @access private
     */
    private function addActions() {
      register_activation_hook( $this->file, array( $this, 'onActivation' ) );
      register_deactivation_hook( $this->file, array( $this, 'onDeactivation' ) );

      add_action( 'wp_enqueue_scripts', array( $this, 'enqueueStyles' ) );
      add_action( 'wp_enqueue_scripts', array( $this, 'enqueueScripts' ) );

      add_action( 'init', array( $this, 'setup' ) );

      if ( $this->pluginIsActive( 'elementor' ) ) {
        // Register Elementor categories if the plugin is active
        add_action( 'elementor/elements/categories_registered', array( $this, 'registerElementorCategories' ) );
        // Register Elementor editor styles if the plugin is active
        add_action( 'elementor/editor/after_enqueue_styles', array( $this, 'registerElementorEditorStyles' ) );
      }
    }

    /**
     * Run on plugin activation. Used to check for dependencies
     * 
     * @since 1.0.0
     */
    public function onActivation() {
      // if the orokublocks main plugin is missing, do not activate
      if ( ! $this->pluginIsActive( 'elementor' ) ) {
        deactivate_plugins( plugin_basename( $this->file ) );

        wp_die(
          sprintf(
            /* translators: 1: opening anchor tag, 2: closing anchor tag. */
            esc_html__( 'The %1$sElementor%2$s plugin is required. Please install and activate it first.', 'orokublocks' ),
            '<a href="https://wordpress.org/plugins/elementor/" target="_blank">',
            '</a>'
          ),
          esc_html__( 'Activation Error', 'orokublocks' ),
          [ 'back_link' => true ]
        );
      }
    }

    /**
     * Run on plugin deactivation. Used to check for dependencies
     * 
     * @since 1.0.0
     */
    public function onDeactivation() {
      // if the orokublocks pro is still active, do not deactivate
      if ( $this->isProVersion() ) {
        activate_plugins( plugin_basename( $this->file ) );

        wp_die(
          sprintf(
            /* translators: 1: opening strong tag, 2: closing strong tag. */
            esc_html__( 'The %1$sOrokuBlocks Pro%2$s plugin is active. Please deactivate it first.', 'orokublocks' ),
            '<strong>',
            '</strong>'
          ),
          esc_html__( 'Deactivation Error', 'orokublocks' ),
          [ 'back_link' => true ]
        );
      }
    }

    /**
     * Enqueue styles and fonts
     * 
     * @since 1.0.0
     */
    public function enqueueStyles() {
      // Fonts
      wp_enqueue_style( 'orokublocks-fonts', 'https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,100..900&display=swap' );

      // Main styles
      wp_enqueue_style( 'orokublocks-styles', $this->url . 'assets/css/style.css', [], '1.0.0' );
    }

    /**
     * Enqueue scripts
     * 
     * @since 1.0.0
     */
    public function enqueueScripts() {
      // Main scripts
      wp_enqueue_script( 'orokublocks-script', $this->url . 'assets/js/main.js', [], '1.0.0', true );
    }

    /**
     * Returns true if using pro version, false otherwise
     * 
     * @since 1.0.0
     * 
     * @return bool     True if pro version, false otherwise
     */
    public function isProVersion() {
      return class_exists( 'OrokuBlocks_Pro' );
    }

    /**
     * Returns true if using pro version and it is valid, false otherwise
     * 
     * @since 1.0.0
     * 
     * @return bool     True if pro version and it is valid, false otherwise
     */
    public function isProVersionValid() {
      return $this->isProVersion() && OrokuBlocks_Pro_License::isValid();
    }

    /**
     * Return social links data
     * 
     * @since 1.0.0
     * 
     * @return array $social_links        Social links data.
     */
    public function socialLinksGet() {
      $social_links = [
        'x'       => [
          'name'  => 'X / Twitter',
          'url'   => 'https://x.com/OrokuBlocks'
        ],
        'youtube' => [
          'name'  => 'YouTube',
          'url'   => 'https://www.youtube.com/@OrokuBlocks'
        ]
      ];

      return $social_links;
    }

    /**
     * Returns unlock pro url
     * 
     * @since 1.0.0
     * 
     * @return string $unlock_pro_url      Url of the unlock pro page.
     */
    public function getUnlockProUrl() {
      $unlock_pro_url = 'https://orokublocks.com/pricing';

      return $unlock_pro_url;
    }

    /**
     * Returns freemius pricing page url.
     * 
     * @since 1.0.0
     * 
     * @return string $pricing_page_url      Url of the pricing page.
     */
    public function getFreemiusPricingUrl() {
      $pricing_page_url = admin_url( 'admin.php?page=orokublocks-pricing' );

      return $pricing_page_url;
    }

    /**
     * Returns freemius account page url.
     * 
     * @since 1.0.0
     * 
     * @return string $account_page_url      Url of the account page.
     */
    public function getFreemiusAccountUrl() {
      $account_page_url = admin_url( 'admin.php?page=orokublocks-account' );

      return $account_page_url;
    }

    /**
     * Returns true if the provided widget id belongs to a pro widget, false otherwise
     * 
     * @since 1.0.0
     * 
     * @param string $widget_id           Id of the widget to validate.
     * @param string $widget_version      Widget version. Optional.
     * @return bool  $widget_is_pro       True if pro widget, false otherwise
     */
    public function isWidgetVersionPro( $widget_id, $widget_version = 'v1' ) {
      $widget_is_pro = false;

      if ( $this->widgetExists( $widget_id ) ) {
        $widget_is_pro = $this->widgets[$widget_id]['versions'][$widget_version]['pro'];
      }

      return $widget_is_pro;
    }

    /**
     * Return true if the provided widget id has at least one free version
     * 
     * @since 1.0.0
     * 
     * @param string $widget_id                     Id of the widget to validate.
     * @return bool  $widget_has_free_version       True if widget has at least one free version, false otherwise
     */
    public function widgetHasFreeVersion( $widget_id ) {
      $widget_has_free_version = false;

      if ( $this->widgetExists( $widget_id ) ) {
        foreach ( $this->widgets[$widget_id]['versions'] as $widget_version => $widget ) {
          if ( ! $widget['pro'] ) {
            return true;
          }
        }
      }

      return $widget_has_free_version;
    }

    /**
     * Return widget version ids
     * 
     * @since 1.0.0
     * 
     * @param string  $widget_id            Id of the widget.
     * @param bool    $is_pro_version       True to return pro widget versions, false otherwise.
     * @return array  $widget_versions      Widget pro versions. 
     */
    private function getWidgetVersions( $widget_id, $is_pro_version ) {
      $versions = [];

      if ( $this->widgetExists( $widget_id ) ) {
        foreach ( $this->widgets[$widget_id]['versions'] as $widget_version => $widget ) {
          if ( $widget['pro'] === $is_pro_version ) {
            $versions[] = $widget_version;
          }
        }
      }

      return $versions;
    }

    /**
     * Return widget free version ids
     * 
     * @since 1.0.0
     * 
     * @param string $widget_id           Id of the widget.
     * @return array $free_versions       Widget pro versions. 
     */
    public function getWidgetFreeVersions( $widget_id ) {
      $free_versions = $this->getWidgetVersions( $widget_id, false );

      return $free_versions;
    }

    /**
     * Return widget pro version ids
     * 
     * @since 1.0.0
     * 
     * @param string $widget_id           Id of the widget.
     * @return array $pro_versions        Widget pro versions. 
     */
    public function getWidgetProVersions( $widget_id ) {
      $pro_versions = $this->getWidgetVersions( $widget_id, true );

      return $pro_versions;
    }

    /**
     * Returns true if the provided id corresponds to an existing widget
     * 
     * @since 1.0.0
     * 
     * @param string  $widget_id          Id of the widget to validate.
     * @return bool   $widget_is_valid    True if widget exists, false otherwise.
     */
    public function widgetExists( $widget_id ) {
      $widget_is_valid = array_key_exists( $widget_id, $this->widgets );

      return $widget_is_valid;
    }

    /**
     * Returns true if the provided id corresponds to an enabled widget
     * 
     * @since 1.0.0
     * 
     * @param string  $widget_id            Id of the widget to validate.
     * @return bool   $widget_is_enabled    True if widget is enabled, false otherwise.
     */
    public function widgetIsEnabled( $widget_id ) {
      $enabled_widgets = $this->settings->getSettings()['widget_enabled'];

      $widget_is_enabled = array_key_exists( $widget_id, $enabled_widgets ) && $enabled_widgets[$widget_id];

      return $widget_is_enabled;
    }

    /**
     * Require a template file by name
     * 
     * @since 1.0.0
     * 
     * @param string  $template_path    Path of the template to require.
     * @param array   $args             Arguments to pass to the template. Optional.
     */
    public function requireTemplate( $template_path, $args = [] ) {
      require $template_path . '.php';
    }

    /**
     * Include a template file by name
     * 
     * @since 1.0.0
     * 
     * @param string  $template_path    Path of the template to include.
     * @param array   $args             Arguments to pass to the template. Optional.
     */
    public function includeTemplate( $template_path, $args = [] ) {
      $file_path = $template_path . '.php';

      if ( is_file( $this->path . $file_path ) ) {
        include $file_path;
      }
    }

    /**
     * Include a widget by id and version
     * 
     * @since 1.0.0
     * 
     * @param string  $widget_id          Id of the widget template to include.
     * @param string  $widget_version     Widget version. Optional.
     * @param array   $args               Arguments to pass to the template. Optional.
     */
    public function includeWidget( $widget_id, $widget_version = 'v1', $args = [] ) {
      if ( $this->widgetExists( $widget_id ) ) {
        $widget_path_id = OrokuBlocks_Router::widgetPathIdGet( $widget_id );
        $widget_path = 'widgets/' . $widget_path_id . '/template/' . $widget_path_id;

        if ( $widget_version !== '' ) {
          $widget_path .= '-' . $widget_version;
        }

        if ( $this->isWidgetVersionPro( $widget_id, $widget_version ) ) {
          $this->isProVersion() && orokublocks_pro()->includeTemplate( $widget_path, $args );
        } else {
          $this->includeTemplate( $widget_path, $args );
        }
      }
    }

    /**
     * Return true if the plugin that corresponds to the provided plugin name is active
     * 
     * @since 1.0.0
     * 
     * @return bool       True if plugin is active, false if plugin is not active or provided plugin name is invalid.
     */
    public function pluginIsActive( $plugin_name = '' ) {
      $plugin_path = $plugin_name . '/' . $plugin_name . '.php';

      return is_plugin_active( $plugin_path );
    }

    /**
     * Returns incremental id for provided component
     * 
     * @since 1.0.0
     * 
     * @param string $component_id        Id of the component to generate an id for.
     * @return int                        Generated id for provided component.
     */
    public static function generateId( $component_id ) {
      static $component_ids = [];

      if ( ! array_key_exists( $component_id, $component_ids ) ) {
        $component_ids[$component_id] = 0;
      }

      return ++$component_ids[$component_id];
    }

    /**
     * Registers widget categories for Elementor
     * 
     * @since 1.0.0
     * 
     * @param object $elements_manager      Elementor elements manager instance.
     */
    public function registerElementorCategories( $elements_manager ) {
      $elements_manager->add_category(
        'orokublocks',
        [
          'title' => 'OrokuBlocks'
        ]
      );
    }

    /**
     * Enqueue styles for the Elementor editor
     * 
     * @since 1.0.0
     */
    public function registerElementorEditorStyles() {
      wp_enqueue_style( 'orokublocks-elementor-editor-font-styles', $this->url . 'admin/assets/css/font.css', [], '1.0.0' );
      wp_enqueue_style( 'orokublocks-elementor-editor-styles', $this->url . 'admin/assets/css/elementor-editor.css', [], '1.0.0' );
    }
  }

  /**
   * Returns the class instance
   * 
   * @since 1.0.0
   * 
   * @return OrokuBlocks  Class instance
   */
  function orokublocks() {
    return OrokuBlocks::getInstance();
  }

  // initialize singleton
  orokublocks();
}