update
This commit is contained in:
@@ -0,0 +1,361 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SB_Analytics plugin integration
|
||||
* Class to impelement filters to return
|
||||
* data needed in the SB_Analytics plugin
|
||||
*/
|
||||
|
||||
namespace SmashBalloon\YouTubeFeed\Services\Integrations\Analytics;
|
||||
|
||||
use Smashballoon\Customizer\Feed_Builder;
|
||||
use SmashBalloon\YouTubeFeed\SBY_Feed;
|
||||
use SmashBalloon\YouTubeFeed\Pro\SBY_Feed_Pro;
|
||||
use SmashBalloon\YouTubeFeed\SBY_Settings;
|
||||
use SmashBalloon\YouTubeFeed\Pro\SBY_Settings_Pro;
|
||||
use SmashBalloon\YouTubeFeed\SBY_Parse;
|
||||
|
||||
class SB_Analytics
|
||||
{
|
||||
/**
|
||||
* Summary of current_plugin
|
||||
* @var string
|
||||
*/
|
||||
private static $current_plugin = 'youtube';
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since x.x.x
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->load();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indicate if current integration is allowed to load.
|
||||
*
|
||||
* @since x.x.x
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function allow_load()
|
||||
{
|
||||
//return defined( 'SB_ANALYTICS_NAMESPACE' ); -- Need to ask regarding this condition.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load an integration.
|
||||
*
|
||||
* @since x.x.x
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
if ($this->allow_load()) {
|
||||
$this->hooks();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hooks.
|
||||
*
|
||||
* @since x.x.x
|
||||
*/
|
||||
public function hooks()
|
||||
{
|
||||
|
||||
//Filter Top Posts
|
||||
add_filter(
|
||||
'sb_analytics_filter_top_posts',
|
||||
[$this, 'filter_top_posts'],
|
||||
10,
|
||||
3
|
||||
);
|
||||
|
||||
//Filter Profile Details
|
||||
add_filter(
|
||||
'sb_analytics_filter_profile_details',
|
||||
[$this, 'filter_profile_details'],
|
||||
10,
|
||||
3
|
||||
);
|
||||
|
||||
//Filter Feed Lists
|
||||
add_filter(
|
||||
'sb_analytics_filter_feed_list',
|
||||
[$this, 'filter_feed_list'],
|
||||
10,
|
||||
3
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Summary of filter_top_posts
|
||||
*
|
||||
* @param array $posts
|
||||
* @param array $post_ids
|
||||
* @param string $plugin_slug
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function filter_top_posts($posts, $post_ids, $plugin_slug)
|
||||
{
|
||||
if ($plugin_slug !== self::$current_plugin) {
|
||||
return $posts;
|
||||
}
|
||||
|
||||
if (empty($post_ids)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return self::get_posts_by_ids($post_ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters and modifies profile details based on the current plugin.
|
||||
*
|
||||
* @param array $profile_details
|
||||
* @param int $feed_id
|
||||
* @param string $plugin_slug
|
||||
*
|
||||
* @since x.x.x
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function filter_profile_details($profile_details, $feed_id, $plugin_slug)
|
||||
{
|
||||
|
||||
if ($plugin_slug !== self::$current_plugin) {
|
||||
return $profile_details;
|
||||
}
|
||||
|
||||
$info = self::get_feed_source_info($feed_id);
|
||||
|
||||
if (empty($info)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Summary of filter_feed_list
|
||||
*
|
||||
* @param array $feeds
|
||||
* @param string $plugin_slug
|
||||
*
|
||||
* @since x.x.x
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function filter_feed_list($feeds, $plugin_slug)
|
||||
{
|
||||
if ($plugin_slug !== self::$current_plugin) {
|
||||
return $feeds;
|
||||
}
|
||||
|
||||
return $this->get_all_feeds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get posts by id.
|
||||
*
|
||||
* @param array $post_ids Posts id.
|
||||
*
|
||||
* @since x.x.x
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_posts_by_ids($post_ids)
|
||||
{
|
||||
if (empty($post_ids)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$records = [];
|
||||
|
||||
$feeds = Feed_Builder::instance()->get_feed_list();
|
||||
|
||||
foreach ($feeds as $feed) {
|
||||
|
||||
$feed_id = $feed['id'];
|
||||
|
||||
if (empty($feed_id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$atts = ['feed' => $feed_id];
|
||||
$database_settings = sby_get_database_settings();
|
||||
|
||||
$youtube_feed_settings =
|
||||
sby_is_pro()
|
||||
? new SBY_Settings_Pro($atts, $database_settings, false)
|
||||
: new SBY_Settings($atts, $database_settings, false);
|
||||
|
||||
$youtube_feed_settings->set_feed_type_and_terms();
|
||||
$youtube_feed_settings->set_transient_name();
|
||||
$transient_name = $youtube_feed_settings->get_transient_name();
|
||||
|
||||
$youtube_feed =
|
||||
sby_is_pro()
|
||||
? new SBY_Feed_Pro($transient_name)
|
||||
: new SBY_Feed($transient_name);
|
||||
$youtube_feed->set_post_data_from_cache();
|
||||
|
||||
$posts = $youtube_feed->get_post_data();
|
||||
|
||||
foreach ($posts as $post) {
|
||||
|
||||
$youtube_id = '';
|
||||
|
||||
if (!empty($post['snippet']['liveBroadcastContent'])) {
|
||||
$youtube_id = !empty($post['id']) ? $post['id'] : '';
|
||||
} else {
|
||||
$youtube_id = !empty($post['snippet']['resourceId']['videoId']) ? $post['snippet']['resourceId']['videoId'] : '';
|
||||
}
|
||||
|
||||
if (in_array($youtube_id, $post_ids, true)) {
|
||||
|
||||
$records[$youtube_id] = [
|
||||
'plugin' => [
|
||||
'slug' => self::$current_plugin,
|
||||
],
|
||||
'feed_id' => $feed_id,
|
||||
'feed_post_id' => $youtube_id,
|
||||
'text' => !empty($post['snippet']['title']) ? $post['snippet']['title'] : '',
|
||||
'imageSrc' => !empty($post['snippet']['thumbnails']['medium']) ? $post['snippet']['thumbnails']['medium']['url'] : '',
|
||||
'updated_time_ago' => !empty($post['snippet']['publishedAt']) ? sprintf('%s ago', human_time_diff(strtotime($post['snippet']['publishedAt']), time())) : '',
|
||||
'profile' => [
|
||||
'label' => !empty($post['snippet']['channelTitle']) ? $post['snippet']['channelTitle'] : '',
|
||||
'url' => '',
|
||||
'id' => !empty($post['snippet']['channelId']) ? $post['snippet']['channelId'] : '',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $records;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all feeds by using the classes from the YouTube plugin.
|
||||
*
|
||||
* @since x.x.x
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_all_feeds()
|
||||
{
|
||||
$records = [];
|
||||
|
||||
$feeds = Feed_Builder::instance()->get_feed_list();
|
||||
|
||||
if (empty($feeds)) {
|
||||
return $records;
|
||||
}
|
||||
|
||||
foreach ($feeds as $feed) {
|
||||
$feed_id = $feed['id'];
|
||||
|
||||
$records[] = [
|
||||
'value' => [
|
||||
'feed_id' => !empty($feed_id) ? $feed_id : '',
|
||||
|
||||
],
|
||||
'label' => ! empty($feed['feed_title']) ? $feed['feed_title'] : $feed['feed_name'],
|
||||
];
|
||||
}
|
||||
|
||||
return $records;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves source information for a specific feed.
|
||||
*
|
||||
* @param int $feed_id
|
||||
*
|
||||
* @since x.x.x
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_feed_source_info($feed_id)
|
||||
{
|
||||
|
||||
if (empty($feed_id)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$header_data = self::get_header_data($feed_id);
|
||||
|
||||
if (empty($header_data)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$channel_title = SBY_Parse::get_channel_title( $header_data );
|
||||
$avatar = SBY_Parse::get_avatar( $header_data, [] );
|
||||
$source_id = null;
|
||||
|
||||
if ( ! empty( $header_data['items'][0]['id'] ) ) {
|
||||
$source_id = $header_data['items'][0]['id'];
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $source_id,
|
||||
'pluginSlug' => self::$current_plugin,
|
||||
'profile' => [
|
||||
'label' => !empty($channel_title) ? $channel_title : '',
|
||||
'imageSrc' => !empty($avatar) ? $avatar : '',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get header data.
|
||||
*
|
||||
* @param int $feed_id.
|
||||
*
|
||||
* @since x.x.x
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_header_data($feed_id)
|
||||
{
|
||||
$atts = ['feed' => $feed_id];
|
||||
$database_settings = sby_get_database_settings();
|
||||
$youtube_feed_settings = sby_is_pro() ? new SBY_Settings_Pro($atts, $database_settings) : new SBY_Settings($atts, $database_settings);
|
||||
|
||||
if (empty($database_settings['connected_accounts']) && empty($database_settings['api_key'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$youtube_feed_settings->set_feed_type_and_terms();
|
||||
$youtube_feed_settings->set_transient_name();
|
||||
$transient_name = $youtube_feed_settings->get_transient_name();
|
||||
|
||||
$youtube_feed = sby_is_pro()
|
||||
? new SBY_Feed_Pro($transient_name)
|
||||
: new SBY_Feed($transient_name);
|
||||
|
||||
$youtube_feed->set_header_data_from_cache();
|
||||
$header_data = $youtube_feed->get_header_data();
|
||||
|
||||
if ($header_data) {
|
||||
return $header_data;
|
||||
}
|
||||
|
||||
$youtube_feed->set_remote_header_data(
|
||||
$youtube_feed_settings,
|
||||
$youtube_feed_settings->get_feed_type_and_terms(),
|
||||
$youtube_feed_settings->get_connected_accounts_in_feed()
|
||||
);
|
||||
|
||||
$header_data = $youtube_feed->get_header_data();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
|
||||
namespace SmashBalloon\YouTubeFeed\Services\Integrations\Divi;
|
||||
|
||||
use SmashBalloon\YouTubeFeed\Services\Integrations\SBY_Integration;
|
||||
use SmashBalloon\YouTubeFeed\Helpers\Util;
|
||||
|
||||
/**
|
||||
* Class Divi Handler.
|
||||
*
|
||||
* @since 2.3
|
||||
*/
|
||||
class SBY_Divi_Handler
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 2.3
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->load();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indicate if current integration is allowed to load.
|
||||
*
|
||||
* @since 2.3
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function allow_load()
|
||||
{
|
||||
if (function_exists('et_divi_builder_init_plugin')) {
|
||||
return true;
|
||||
}
|
||||
$allow_themes = [ 'Divi' ];
|
||||
$theme_name = get_template();
|
||||
|
||||
return in_array($theme_name, $allow_themes, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load an integration.
|
||||
*
|
||||
* @since 2.3
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
if ($this->allow_load()) {
|
||||
$this->hooks();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks.
|
||||
*
|
||||
* @since 2.3
|
||||
*/
|
||||
public function hooks()
|
||||
{
|
||||
add_action('et_builder_ready', [ $this, 'register_module' ]);
|
||||
|
||||
if (wp_doing_ajax()) {
|
||||
add_action('wp_ajax_sb_youtubefeed_divi_preview', [ $this, 'preview' ]);
|
||||
}
|
||||
|
||||
if ($this->is_divi_builder()) {
|
||||
add_action('wp_enqueue_scripts', [ $this, 'builder_scripts' ]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load scripts.
|
||||
*
|
||||
* @since 2.3
|
||||
*/
|
||||
public function builder_scripts()
|
||||
{
|
||||
|
||||
$css_free_file_name = 'sb-youtube-free.min.css';
|
||||
$css_pro_file_name = 'sb-youtube.min.css';
|
||||
$css_file_name = sby_is_pro() ? $css_pro_file_name : $css_free_file_name;
|
||||
|
||||
wp_enqueue_style('sby_styles', trailingslashit(SBY_PLUGIN_URL) . 'css/' . $css_file_name, array(), SBYVER);
|
||||
|
||||
$data = array(
|
||||
'isAdmin' => is_admin(),
|
||||
'adminAjaxUrl' => admin_url('admin-ajax.php'),
|
||||
'placeholder' => trailingslashit(SBY_PLUGIN_URL) . 'img/placeholder.png',
|
||||
'placeholderNarrow' => trailingslashit(SBY_PLUGIN_URL) . 'img/placeholder-narrow.png',
|
||||
'lightboxPlaceholder' => trailingslashit(SBY_PLUGIN_URL) . 'img/lightbox-placeholder.png',
|
||||
'lightboxPlaceholderNarrow' => trailingslashit(SBY_PLUGIN_URL) . 'img/lightbox-placeholder-narrow.png',
|
||||
'autoplay' => false,
|
||||
'semiEagerload' => false,
|
||||
'eagerload' => false,
|
||||
'nonce' => wp_create_nonce('sby_nonce'),
|
||||
'isPro' => sby_is_pro(),
|
||||
'resized_url' => Util::sby_get_resized_uploads_url(),
|
||||
'isCustomizer' => false
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'sbyscripts',
|
||||
SBY_PLUGIN_URL . 'js/sb-youtube.min.js',
|
||||
array('jquery'),
|
||||
SBYVER,
|
||||
true
|
||||
);
|
||||
wp_localize_script('sbyscripts', 'sbyOptions', $data);
|
||||
|
||||
wp_enqueue_script(
|
||||
'sbyoutube-divi',
|
||||
// The unminified version is not supported by the browser.
|
||||
SBY_PLUGIN_URL . 'js/divi-handler.min.js',
|
||||
['react', 'react-dom', 'jquery'],
|
||||
SBYVER,
|
||||
true
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'sby-divi-handler',
|
||||
// The unminified version is not supported by the browser.
|
||||
SBY_PLUGIN_URL . 'js/divi-preview-handler.js',
|
||||
['jquery'],
|
||||
SBYVER,
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'sbyoutube-divi',
|
||||
'sb_divi_builder',
|
||||
[
|
||||
'ajax_handler' => admin_url('admin-ajax.php'),
|
||||
'nonce' => wp_create_nonce('sby-admin'),
|
||||
'feed_splash' => htmlspecialchars(SBY_Integration::get_widget_cta('button'), ENT_QUOTES | ENT_HTML5)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register module.
|
||||
*
|
||||
* @since 2.3
|
||||
*/
|
||||
public function register_module()
|
||||
{
|
||||
if (!class_exists('ET_Builder_Module')) {
|
||||
return;
|
||||
}
|
||||
|
||||
new SB_YouTube_Feed();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ajax handler for the Feed preview.
|
||||
*
|
||||
* @since 2.3
|
||||
*/
|
||||
public function preview()
|
||||
{
|
||||
check_ajax_referer('sby-admin', 'nonce');
|
||||
|
||||
$cap = current_user_can('manage_youtube_feed_options') ? 'manage_youtube_feed_options' : 'manage_options';
|
||||
$cap = apply_filters('sby_settings_pages_capability', $cap);
|
||||
if (! current_user_can($cap)) {
|
||||
wp_send_json_error(); // This auto-dies.
|
||||
}
|
||||
|
||||
$feed_id = absint(filter_input(INPUT_POST, 'feed_id', FILTER_SANITIZE_NUMBER_INT));
|
||||
|
||||
wp_send_json_success(
|
||||
do_shortcode(
|
||||
sprintf(
|
||||
'[youtube-feed feed="%1$s"]',
|
||||
absint($feed_id)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a current page is opened in the Divi Builder.
|
||||
*
|
||||
* @since 2.3
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_divi_builder()
|
||||
{
|
||||
return !empty($_GET['et_fb']); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace SmashBalloon\YouTubeFeed\Services\Integrations\Divi;
|
||||
|
||||
use ET_Builder_Module;
|
||||
use SmashBalloon\YouTubeFeed\Builder\SBY_Db;
|
||||
|
||||
class SB_YouTube_Feed extends ET_Builder_Module {
|
||||
/**
|
||||
* Module slug.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $slug = 'sb_youtube_feed';
|
||||
|
||||
/**
|
||||
* VB support.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $vb_support = 'on';
|
||||
|
||||
|
||||
/**
|
||||
* Init module.
|
||||
*
|
||||
* @since 2.3
|
||||
*/
|
||||
public function init() {
|
||||
$this->name = esc_html__('YouTube Feed', 'feeds-for-youtube');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of settings.
|
||||
*
|
||||
* @since 2.3
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_fields() {
|
||||
$feeds_divi = array();
|
||||
$feeds_list = SBY_Db::elementor_feeds_query();
|
||||
|
||||
if ( ! empty( $feeds_list ) ) {
|
||||
$feeds_divi[ 'sby-0' ] = esc_html__('Select Youtube Feed', 'feeds-for-youtube');
|
||||
foreach ( $feeds_list as $key => $feed ) {
|
||||
$feeds_divi[ 'sby-' . $key ] = $feed;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'feed_id' => [
|
||||
'label' => esc_html__('Feed', 'feeds-for-youtube'),
|
||||
'type' => 'select',
|
||||
'option_category' => 'basic_option',
|
||||
'toggle_slug' => 'main_content',
|
||||
'options' => $feeds_divi,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable advanced fields configuration.
|
||||
*
|
||||
* @since 2.3
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_advanced_fields_config() {
|
||||
return [
|
||||
'link_options' => false,
|
||||
'text' => false,
|
||||
'background' => false,
|
||||
'borders' => false,
|
||||
'box_shadow' => false,
|
||||
'button' => false,
|
||||
'filters' => false,
|
||||
'fonts' => false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Render module on the frontend.
|
||||
*
|
||||
* @since 2.3
|
||||
*
|
||||
* @param array $attrs List of unprocessed attributes.
|
||||
* @param string $content Content being processed.
|
||||
* @param string $render_slug Slug of module that is used for rendering output.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render($attrs, $content = null, $render_slug = '') {
|
||||
if (empty($this->props['feed_id'])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$feed_id = str_replace('sby-', '', $this->props['feed_id']);
|
||||
|
||||
return do_shortcode(
|
||||
sprintf(
|
||||
'[youtube-feed feed="%1$s"]',
|
||||
absint($feed_id)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace SmashBalloon\YouTubeFeed\Services\Integrations\Elementor;
|
||||
|
||||
use \Elementor\Widget_Base;
|
||||
use \Elementor\Controls_Manager;
|
||||
|
||||
if (!defined('ABSPATH'))
|
||||
exit; // Exit if accessed directly
|
||||
|
||||
class CFF_Elementor_Widget extends Widget_Base {
|
||||
|
||||
public function get_name() {
|
||||
return 'cff-widget';
|
||||
}
|
||||
public function get_title() {
|
||||
return esc_html__('Facebook Feed', 'feeds-for-youtube');
|
||||
}
|
||||
public function get_icon() {
|
||||
return 'sb-elem-icon sb-elem-inactive sb-elem-facebook';
|
||||
}
|
||||
public function get_categories() {
|
||||
return array('smash-balloon');
|
||||
}
|
||||
public function get_script_depends() {
|
||||
return [
|
||||
'sby-elementor-handler'
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace SmashBalloon\YouTubeFeed\Services\Integrations\Elementor;
|
||||
|
||||
use \Elementor\Widget_Base;
|
||||
use \Elementor\Controls_Manager;
|
||||
|
||||
if (!defined('ABSPATH'))
|
||||
exit; // Exit if accessed directly
|
||||
|
||||
class CTF_Elementor_Widget extends Widget_Base {
|
||||
|
||||
public function get_name() {
|
||||
return 'ctf-widget';
|
||||
}
|
||||
public function get_title() {
|
||||
return esc_html__('Twitter Feed', 'feeds-for-youtube');
|
||||
}
|
||||
public function get_icon() {
|
||||
return 'sb-elem-icon sb-elem-inactive sb-elem-twitter';
|
||||
}
|
||||
public function get_categories() {
|
||||
return array('smash-balloon');
|
||||
}
|
||||
public function get_script_depends() {
|
||||
return [
|
||||
'sby-elementor-handler'
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace SmashBalloon\YouTubeFeed\Services\Integrations\Elementor;
|
||||
|
||||
use \Elementor\Widget_Base;
|
||||
use \Elementor\Controls_Manager;
|
||||
|
||||
if (!defined('ABSPATH'))
|
||||
exit; // Exit if accessed directly
|
||||
|
||||
class SBI_Elementor_Widget extends Widget_Base {
|
||||
|
||||
public function get_name() {
|
||||
return 'sbi-widget';
|
||||
}
|
||||
public function get_title() {
|
||||
return esc_html__('Instagram Feed', 'feeds-for-youtube');
|
||||
}
|
||||
public function get_icon() {
|
||||
return 'sb-elem-icon sb-elem-inactive sb-elem-instagram';
|
||||
}
|
||||
public function get_categories() {
|
||||
return array('smash-balloon');
|
||||
}
|
||||
public function get_script_depends() {
|
||||
return [
|
||||
'sby-elementor-handler'
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace SmashBalloon\YouTubeFeed\Services\Integrations\Elementor;
|
||||
|
||||
use \Elementor\Widget_Base;
|
||||
use \Elementor\Controls_Manager;
|
||||
|
||||
if (!defined('ABSPATH'))
|
||||
exit; // Exit if accessed directly
|
||||
|
||||
class SBR_Elementor_Widget extends Widget_Base {
|
||||
|
||||
public function get_name() {
|
||||
return 'sbr-widget';
|
||||
}
|
||||
public function get_title() {
|
||||
return esc_html__('Reviews Feed', 'feeds-for-youtube');
|
||||
}
|
||||
public function get_icon() {
|
||||
return 'sb-elem-icon sb-elem-inactive sb-elem-reviews';
|
||||
}
|
||||
public function get_categories() {
|
||||
return array('smash-balloon');
|
||||
}
|
||||
public function get_script_depends() {
|
||||
return [
|
||||
'sby-elementor-handler'
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace SmashBalloon\YouTubeFeed\Services\Integrations\Elementor;
|
||||
|
||||
use \Elementor\Widget_Base;
|
||||
use \Elementor\Controls_Manager;
|
||||
|
||||
if (!defined('ABSPATH'))
|
||||
exit; // Exit if accessed directly
|
||||
|
||||
class SBTT_Elementor_Widget extends Widget_Base {
|
||||
|
||||
public function get_name() {
|
||||
return 'sbtt-widget';
|
||||
}
|
||||
public function get_title() {
|
||||
return esc_html__('TikTok Feed', 'feeds-for-youtube');
|
||||
}
|
||||
public function get_icon() {
|
||||
return 'sb-elem-icon sb-elem-inactive sb-elem-tiktok';
|
||||
}
|
||||
public function get_categories() {
|
||||
return array('smash-balloon');
|
||||
}
|
||||
public function get_script_depends() {
|
||||
return [
|
||||
'sby-elementor-handler'
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
namespace SmashBalloon\YouTubeFeed\Services\Integrations\Elementor;
|
||||
|
||||
use SmashBalloon\YouTubeFeed\Services\Integrations\Elementor\SBY_Elementor_Control;
|
||||
use SmashBalloon\YouTubeFeed\Services\Integrations\Elementor\SBY_Elementor_Widget;
|
||||
use SmashBalloon\YouTubeFeed\Helpers\Util;
|
||||
|
||||
class SBY_Elementor_Base
|
||||
{
|
||||
const VERSION = SBYVER;
|
||||
const MINIMUM_ELEMENTOR_VERSION = '3.6.0';
|
||||
const MINIMUM_PHP_VERSION = '5.6';
|
||||
const NAME_SPACE = 'SmashBalloon.YouTubeFeed.Services.Integrations.Elementor.';
|
||||
private static $instance;
|
||||
|
||||
public static function register()
|
||||
{
|
||||
if (!isset(self::$instance) && !self::$instance instanceof SBY_Elementor_Base) {
|
||||
self::$instance = new SBY_Elementor_Base();
|
||||
self::$instance->apply_hooks();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
private function apply_hooks()
|
||||
{
|
||||
add_action('elementor/frontend/after_register_scripts', [$this, 'register_frontend_scripts']);
|
||||
add_action('elementor/frontend/after_register_styles', [$this, 'register_frontend_styles'], 10);
|
||||
add_action('elementor/frontend/after_enqueue_styles', [$this, 'enqueue_frontend_styles'], 10);
|
||||
add_action('elementor/controls/register', [$this, 'register_controls']);
|
||||
add_action('elementor/widgets/register', [$this,'register_widgets']);
|
||||
add_action('elementor/elements/categories_registered', [$this, 'add_smashballon_categories']);
|
||||
}
|
||||
|
||||
public function register_controls($controls_manager)
|
||||
{
|
||||
$controls_manager->register(new SBY_Elementor_Control());
|
||||
}
|
||||
|
||||
public function register_widgets($widgets_manager)
|
||||
{
|
||||
$widgets_manager->register(new SBY_Elementor_Widget());
|
||||
|
||||
$installed_plugins = sby_get_installed_plugin_info();
|
||||
unset($installed_plugins['youtube']);
|
||||
|
||||
foreach($installed_plugins as $plugin) {
|
||||
if (!$plugin['installed']){
|
||||
$plugin_class = str_replace('.','\\', self::NAME_SPACE) . $plugin['class'];
|
||||
$widgets_manager->register(new $plugin_class());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function register_frontend_scripts(){
|
||||
|
||||
$css_free_file_name = 'sb-youtube-free.min.css';
|
||||
$css_pro_file_name = 'sb-youtube.min.css';
|
||||
$css_file_name = sby_is_pro() ? $css_pro_file_name : $css_free_file_name;
|
||||
|
||||
wp_enqueue_style(
|
||||
'sby_styles',
|
||||
trailingslashit(SBY_PLUGIN_URL) . 'css/' . $css_file_name,
|
||||
array(),
|
||||
SBYVER
|
||||
);
|
||||
|
||||
$data = array(
|
||||
'isAdmin' => is_admin(),
|
||||
'adminAjaxUrl' => admin_url('admin-ajax.php' ),
|
||||
'placeholder' => trailingslashit(SBY_PLUGIN_URL) . 'img/placeholder.png',
|
||||
'placeholderNarrow' => trailingslashit(SBY_PLUGIN_URL) . 'img/placeholder-narrow.png',
|
||||
'lightboxPlaceholder' => trailingslashit(SBY_PLUGIN_URL) . 'img/lightbox-placeholder.png',
|
||||
'lightboxPlaceholderNarrow' => trailingslashit(SBY_PLUGIN_URL) . 'img/lightbox-placeholder-narrow.png',
|
||||
'autoplay' => false,
|
||||
'semiEagerload' => false,
|
||||
'eagerload' => false,
|
||||
'nonce' => wp_create_nonce('sby_nonce'),
|
||||
'isPro' => sby_is_pro(),
|
||||
'resized_url' => Util::sby_get_resized_uploads_url(),
|
||||
'isCustomizer' => false
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'sbyscripts',
|
||||
SBY_PLUGIN_URL . 'js/sb-youtube.min.js',
|
||||
array('jquery'),
|
||||
SBYVER,
|
||||
true
|
||||
);
|
||||
wp_localize_script( 'sbyscripts', 'sbyOptions', $data );
|
||||
|
||||
$data_handler = array(
|
||||
'smashPlugins' => sby_get_installed_plugin_info(),
|
||||
'nonce' => wp_create_nonce('sby-admin'),
|
||||
'ajax_handler' => admin_url('admin-ajax.php'),
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'sby-elementor-handler',
|
||||
SBY_PLUGIN_URL . 'js/elementor-handler.js' ,
|
||||
array('jquery'),
|
||||
SBYVER,
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script('sby-elementor-handler', 'sbHandler', $data_handler);
|
||||
|
||||
wp_register_script(
|
||||
'sby-elementor-preview',
|
||||
SBY_PLUGIN_URL . 'js/elementor-preview.js' ,
|
||||
array('jquery'),
|
||||
SBYVER,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
public function register_frontend_styles()
|
||||
{
|
||||
$css_free_file_name = 'sb-youtube-free.min.css';
|
||||
$css_pro_file_name = 'sb-youtube.min.css';
|
||||
$css_file_name = sby_is_pro() ? $css_pro_file_name : $css_free_file_name;
|
||||
|
||||
wp_register_style(
|
||||
'sby-styles',
|
||||
SBY_PLUGIN_URL . 'css/' . $css_file_name,
|
||||
array(),
|
||||
SBYVER
|
||||
);
|
||||
}
|
||||
|
||||
public function enqueue_frontend_styles()
|
||||
{
|
||||
wp_enqueue_style('sby-styles');
|
||||
}
|
||||
|
||||
public function add_smashballon_categories($elements_manager)
|
||||
{
|
||||
$elements_manager->add_category(
|
||||
'smash-balloon',
|
||||
[
|
||||
'title' => esc_html__('Smash Balloon', 'feeds-for-youtube'),
|
||||
'icon' => 'fa fa-plug',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace SmashBalloon\YouTubeFeed\Services\Integrations\Elementor;
|
||||
|
||||
use \Elementor\Base_Data_Control;
|
||||
use \Elementor\Controls_Manager;
|
||||
|
||||
if (!defined('ABSPATH'))
|
||||
exit; // Exit if accessed directly
|
||||
|
||||
class SBY_Elementor_Control extends Base_Data_Control {
|
||||
|
||||
public function get_type(){
|
||||
return 'sby_feed_control';
|
||||
}
|
||||
|
||||
public function enqueue(){
|
||||
wp_enqueue_style(
|
||||
'sby-elementor-style',
|
||||
SBY_PLUGIN_URL . 'css/sb-elementor.css' ,
|
||||
null,
|
||||
SBYVER
|
||||
);
|
||||
}
|
||||
|
||||
protected function get_default_settings(){
|
||||
return [
|
||||
'label_block' => false
|
||||
];
|
||||
}
|
||||
|
||||
public function content_template() {
|
||||
$control_uid = $this->get_control_uid();
|
||||
?>
|
||||
<div class="elementor-control-field">
|
||||
<# if ( data.label ) {#>
|
||||
<label for="<?php echo $control_uid; ?>" class="elementor-control-title">{{{ data.label }}}</label>
|
||||
<# } #>
|
||||
<div class="elementor-control-input-wrapper elementor-control-unit-5">
|
||||
<select id="<?php echo $control_uid; ?>" data-setting="{{ data.name }}" onchange="jQuery(this).parents('.elementor-control-field').find('.link-sby-builder').attr('href', '<?php echo admin_url( 'admin.php?page=sby-feed-builder' ) ?>&feed_id='+jQuery(this).val())">
|
||||
<#
|
||||
var printOptions = function( options ) {
|
||||
_.each( options, function( option_title, option_value ) { #>
|
||||
<option value="{{ option_value }}">{{{ option_title }}}</option>
|
||||
<# } );
|
||||
};
|
||||
|
||||
if ( data.groups ) {
|
||||
for ( var groupIndex in data.groups ) {
|
||||
var groupArgs = data.groups[ groupIndex ];
|
||||
if ( groupArgs.options ) { #>
|
||||
<optgroup label="{{ groupArgs.label }}">
|
||||
<# printOptions( groupArgs.options ) #>
|
||||
</optgroup>
|
||||
<# } else if ( _.isString( groupArgs ) ) { #>
|
||||
<option value="{{ groupIndex }}">{{{ groupArgs }}}</option>
|
||||
<# }
|
||||
}
|
||||
} else {
|
||||
printOptions( data.options );
|
||||
}
|
||||
#>
|
||||
</select>
|
||||
<div style="font-weight: 700; color:#a73061; margin-top: 10px;">
|
||||
<# if( data.controlValue != undefined && data.controlValue != '' ) { #>
|
||||
<a class="link-sby-builder" href="<?php echo admin_url( 'admin.php?page=sby-feed-builder' ) ?>&feed_id={{data.controlValue}}" target="_blank"><?php echo __('Edit this Feed', 'feeds-for-youtube'); ?></a>
|
||||
<span style="color:#aaa; display: inline-block; margin: 0 5px;">|</span>
|
||||
<# } #>
|
||||
<a href="<?php echo admin_url( 'admin.php?page=sby-feed-builder' ) ?>" target="_blank"><?php echo __('Create New Feed', 'feeds-for-youtube'); ?></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<# if ( data.description ) { #>
|
||||
<div class="elementor-control-field-description">{{{ data.description }}}</div>
|
||||
<# } #>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace SmashBalloon\YouTubeFeed\Services\Integrations\Elementor;
|
||||
|
||||
use \Elementor\Widget_Base;
|
||||
use \Elementor\Controls_Manager;
|
||||
use SmashBalloon\YouTubeFeed\Builder\SBY_Db;
|
||||
use SmashBalloon\YouTubeFeed\Services\Integrations\SBY_Integration;
|
||||
|
||||
if (!defined('ABSPATH'))
|
||||
exit; // Exit if accessed directly
|
||||
|
||||
class SBY_Elementor_Widget extends Widget_Base {
|
||||
|
||||
public function get_name() {
|
||||
return 'sby-widget';
|
||||
}
|
||||
public function get_title() {
|
||||
return esc_html__('YouTube Feed', 'feeds-for-youtube');
|
||||
}
|
||||
public function get_icon() {
|
||||
return 'sb-elem-icon sb-elem-youtube';
|
||||
}
|
||||
public function get_categories() {
|
||||
return array('smash-balloon');
|
||||
}
|
||||
public function get_script_depends() {
|
||||
return [
|
||||
'sbyscripts',
|
||||
'sby-elementor-preview'
|
||||
];
|
||||
}
|
||||
|
||||
protected function register_controls() {
|
||||
/********************************************
|
||||
CONTENT SECTION
|
||||
********************************************/
|
||||
$this->start_controls_section(
|
||||
'section_content',
|
||||
[
|
||||
'label' => esc_html__('YouTube Feed Settings', 'feeds-for-youtube'),
|
||||
]
|
||||
);
|
||||
$this->add_control(
|
||||
'feed_id',
|
||||
[
|
||||
'label' => esc_html__('Select a Feed', 'feeds-for-youtube'),
|
||||
'type' => 'sby_feed_control',
|
||||
'label_block' => true,
|
||||
'dynamic' => ['active' => true],
|
||||
'options' => SBY_Db::elementor_feeds_query(),
|
||||
]
|
||||
);
|
||||
$this->end_controls_section();
|
||||
}
|
||||
|
||||
protected function render() {
|
||||
$settings = $this->get_settings_for_display();
|
||||
if (isset($settings['feed_id']) && !empty($settings['feed_id'])) {
|
||||
$feed_id = (int) $settings['feed_id'];
|
||||
$output = do_shortcode( shortcode_unautop( '[youtube-feed feed='. $feed_id .']' ) );
|
||||
} else {
|
||||
$output = is_admin() ? SBY_Integration::get_widget_cta() : '';
|
||||
}
|
||||
echo apply_filters('sby_output', $output, $settings);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
namespace SmashBalloon\YouTubeFeed\Services\Integrations;
|
||||
|
||||
use SmashBalloon\YouTubeFeed\Builder\SBY_Db;
|
||||
|
||||
if (!defined('ABSPATH'))
|
||||
exit; // Exit if accessed directly
|
||||
|
||||
/**
|
||||
* SBY_Integration Class
|
||||
* Common funcions for Elementor/Divi/Gutenberg
|
||||
*
|
||||
* @since 2.3
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
|
||||
class SBY_Integration {
|
||||
|
||||
|
||||
/**
|
||||
* Generate link
|
||||
*
|
||||
* @since 2.3
|
||||
*
|
||||
* @return HTML
|
||||
*/
|
||||
|
||||
public static function linkGenerator($link, $link_text) {
|
||||
|
||||
ob_start(); ?>
|
||||
<a href="<?php echo esc_url( $link ) ?>" class="sby-feed-block-link">
|
||||
<?php echo esc_html($link_text); ?>
|
||||
</a>
|
||||
<?php
|
||||
$html = ob_get_contents();
|
||||
ob_get_clean();
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Widget/Module/Block Info
|
||||
*
|
||||
* @since 2.3
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_widget_info()
|
||||
{
|
||||
return [
|
||||
'plugin' => 'youtube',
|
||||
'cta_header' => esc_html__('Get started with your first feed from your YouTube Channel', 'feeds-for-youtube'),
|
||||
'cta_header2' => esc_html__('Select a YouTube feed to embed', 'feeds-for-youtube'),
|
||||
'cta_description_free' => esc_html__('You can display feeds for your YouTube channel, playlist, live streams and more using the ', 'feeds-for-youtube') . self::linkGenerator('https://smashballoon.com/youtube-feed/?utm_campaign=' . sby_utm_campaign() . '&utm_source=elementor&utm_medium=widget&utm_content=proversion', 'Pro version'),
|
||||
'cta_description_pro' => esc_html__('You can also add Instagram, Facebook, and Twitter posts into your feed using our ', 'feeds-for-youtube') . self::linkGenerator('https://smashballoon.com/social-wall/?utm_campaign=' . sby_utm_campaign() . '&utm_source=elementor&utm_medium=widget&utm_content=socialwall', 'Social Wall plugin'),
|
||||
'plugins' => sby_get_installed_plugin_info()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Widget CTA
|
||||
*
|
||||
* @since 2.3
|
||||
*
|
||||
* @return HTML
|
||||
*/
|
||||
public static function get_widget_cta( $type = 'dropdown' )
|
||||
{
|
||||
$widget_cta_html = '';
|
||||
$feeds_list = SBY_Db::elementor_feeds_query();
|
||||
ob_start();
|
||||
self::get_widget_cta_html( $feeds_list, $type );
|
||||
$widget_cta_html .= ob_get_contents();
|
||||
ob_get_clean();
|
||||
return $widget_cta_html;
|
||||
}
|
||||
|
||||
public static function get_widget_cta_html( $feeds_list, $type )
|
||||
{
|
||||
$info = self::get_widget_info();
|
||||
|
||||
$feeds_exist = is_array( $feeds_list ) && sizeof( $feeds_list ) > 0;
|
||||
?>
|
||||
<div class="sby-feed-block-cta">
|
||||
<div class="sby-feed-block-cta-img-ctn">
|
||||
<div class="sby-feed-block-cta-img">
|
||||
<span><?php echo $info['plugins'][$info['plugin']]['icon']; ?></span>
|
||||
<svg class="sby-feed-block-cta-logo" width="31" height="39" viewBox="0 0 31 39" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1.62525 18.4447C1.62525 26.7883 6.60827 33.9305 13.3915 35.171L12.9954 36.4252L12.5937 37.6973L13.923 37.5843L18.4105 37.2026L20.0997 37.0589L19.0261 35.7468L18.4015 34.9834C24.7525 33.3286 29.3269 26.4321 29.3269 18.4447C29.3269 9.29016 23.2952 1.53113 15.4774 1.53113C7.65975 1.53113 1.62525 9.2899 1.62525 18.4447Z" fill="#FE544F" stroke="white" stroke-width="1.78661"/><path fill-rule="evenodd" clip-rule="evenodd" d="M18.5669 8.05676L19.1904 14.4905L25.6512 14.6761L20.9776 19.0216L24.6689 22.3606L18.4503 23.1916L16.5651 29.4104L13.7026 23.8415L7.92284 26.4899L10.1462 20.5199L4.50931 17.6767L10.5435 15.7361L8.8784 9.79176L14.5871 13.0464L18.5669 8.05676Z" fill="white"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="sby-feed-block-cta-heading"><?php echo $feeds_exist ? $info['cta_header2'] : $info['cta_header'] ?></h3>
|
||||
|
||||
<?php if($feeds_exist): ?>
|
||||
<div class="sby-feed-block-cta-selector">
|
||||
<?php if( $type == 'dropdown' ): ?>
|
||||
<select class="sby-feed-block-cta-feedselector">
|
||||
<option><?php echo __('Select', 'feeds-for-youtube') . ' ' . ucfirst($info['plugin']) . ' '. __('Feed', 'feeds-for-youtube')?> </option>
|
||||
<?php foreach ($feeds_list as $feed_id => $feed_name): ?>
|
||||
<option value="<?php echo $feed_id ?>"><?php echo $feed_name ?></option>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
<?php elseif( $type == 'button' ): ?>
|
||||
<a href="<?php echo esc_url( admin_url( 'admin.php?page=sby-feed-builder' ) ) ?>" rel="noopener noreferrer" class="sby-feed-block-cta-btn">
|
||||
<?php echo esc_html__('Create YouTube Feed', 'feeds-for-youtube'); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php if( $type == 'dropdown' ): ?>
|
||||
<span class="sby-feed-block-create-with">
|
||||
<?php
|
||||
echo esc_html__('Or create a Feed for', 'feeds-for-youtube');
|
||||
unset( $info['plugins'][$info['plugin']] );
|
||||
foreach ($info['plugins'] as $name => $plugin):
|
||||
$dashboard_permalink = !empty($plugin['dashboard_permalink']) ? $plugin['dashboard_permalink'] : '';
|
||||
$installed = !empty($plugin['installed']) ? $plugin['installed'] : '';
|
||||
$activated = !empty($plugin['activated']) ? $plugin['activated'] : '';
|
||||
$website_link = !empty($plugin['website_link']) ? $plugin['website_link'] : '';
|
||||
$link = $installed && $activated ? $dashboard_permalink : $website_link;
|
||||
?>
|
||||
<a href="<?php echo esc_attr($link); ?>" target="_blank" class="sby-feed-block-link"><?php echo $name ?></a>
|
||||
<?php endforeach ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<a href="<?php echo esc_url( admin_url( 'admin.php?page=sby-feed-builder' ) ) ?>" class="sby-feed-block-cta-btn"><?php echo esc_html__('Create', 'feeds-for-youtube') . ' ' . ucfirst($info['plugin']) . ' ' . esc_html__('Feed', 'feeds-for-youtube') ?></a>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="sby-feed-block-cta-desc">
|
||||
<strong><?php echo esc_html__('Did you Know?', 'feeds-for-youtube') ?></strong>
|
||||
<span>
|
||||
<?php echo \sby_is_pro() ? $info['cta_description_pro'] : $info['cta_description_free']; ?>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user