This commit is contained in:
2026-04-26 23:47:49 +02:00
parent 1b95f03d1e
commit b073e009d8
5288 changed files with 1112699 additions and 55536 deletions

View File

@@ -0,0 +1,78 @@
<?php
/**
* Customizer Builder
*
*
* @since 6.0
*/
namespace Smashballoon\Customizer;
if (!\defined('ABSPATH')) {
exit;
}
/** @internal */
class Builder_Customizer
{
/**
* Controls Classes Array
*
*
* @since 6.0
* @access private
*
* @var array
*/
public static $controls_classes = array();
public function register()
{
$this->register_controls();
}
/**
* Get controls list.
*
* Getting controls list
*
* @since 6.0
* @access public
*
* @return array
*/
public static function get_controls_list()
{
return array('actionbutton', 'checkbox', 'checkboxsection', 'datepicker', 'colorpicker', 'number', 'select', 'switcher', 'text', 'textarea', 'toggle', 'toggleset', 'heading', 'separator', 'customview', 'coloroverride', 'togglebutton', 'hidden', 'imagechooser', 'checkboxlist');
}
/**
* Register Controls
*
* Including Control
*
* @since 6.0
* @access public
*
*/
public static function register_controls()
{
foreach (self::get_controls_list() as $control) {
$controlClassName = 'SB_' . \ucfirst($control) . '_Control';
$cls_name = __NAMESPACE__ . '\\Controls\\' . $controlClassName;
self::$controls_classes[$control] = new $cls_name();
}
}
/**
* Print Controls Vue JS Tempalte
*
* Including Control
*
* @since 6.0
* @access public
*
*/
public static function get_controls_templates($editingType)
{
$controls_list = self::get_controls_list();
foreach ($controls_list as $control) {
self::$controls_classes[$control]->print_control_wrapper($editingType);
}
}
}

View File

@@ -0,0 +1,8 @@
<?php
namespace Smashballoon\Customizer\Cache;
/** @internal */
abstract class FeedCache implements \Smashballoon\Customizer\Cache\FeedCacheInterface
{
}

View File

@@ -0,0 +1,8 @@
<?php
namespace Smashballoon\Customizer\Cache;
/** @internal */
interface FeedCacheInterface
{
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Smashballoon\Customizer;
/** @internal */
class Config
{
public $plugin_slug = 'sbc';
public $statuses_option = 'sbc_statuses';
public function isPro()
{
return \false;
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Smashballoon\Customizer;
/** @internal */
class Container
{
public static $container;
/**
* @return \DI\Container
*/
public static function getInstance()
{
if (self::$container === null) {
self::$container = (new \SmashBalloon\YoutubeFeed\Vendor\DI\ContainerBuilder())->build();
}
return self::$container;
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* Customizer Builder
* Action Button Control
*
* @since 6.0
*/
namespace Smashballoon\Customizer\Controls;
if (!\defined('ABSPATH')) {
exit;
}
/** @internal */
class SB_Actionbutton_Control extends \Smashballoon\Customizer\Controls\SB_Controls_Base
{
/**
* Get control type.
*
* Getting the Control Type
*
* @since 6.0
* @access public
*
* @return string
*/
public function get_type()
{
return 'actionbutton';
}
/**
* Output Control
*
*
* @since 6.0
* @access public
*/
public function get_control_output($controlEditingTypeModel)
{
?>
<button class="sb-control-action-button sb-btn sbc-fb-fs sb-btn-grey">
<div v-if="control.buttonIcon" v-html="svgIcons[control.buttonIcon]"></div>
<span class="sb-small-p sb-bold sb-dark-text">{{control.label}}</span>
</button>
<?php
}
}

View File

@@ -0,0 +1,52 @@
<?php
/**
* Customizer Builder
* CheckBox Control
*
* @since 6.0
*/
namespace Smashballoon\Customizer\Controls;
if (!\defined('ABSPATH')) {
exit;
}
/** @internal */
class SB_Checkbox_Control extends \Smashballoon\Customizer\Controls\SB_Controls_Base
{
/**
* Get control type.
*
* Getting the Control Type
*
* @since 6.0
* @access public
*
* @return string
*/
public function get_type()
{
return 'checkbox';
}
/**
* Output Control
*
*
* @since 6.0
* @access public
*
*/
public function get_control_output($controlEditingTypeModel)
{
?>
<div class="sb-control-checkbox-ctn sbc-fb-fs" @click.prevent.default="(control.custom != undefined && control.custom == 'feedtype') ? changeCheckboxSectionValue('type', control.value, 'feedFlyPreview') : changeSwitcherSettingValue(control.id, control.options.enabled, control.options.disabled, control.ajaxAction != undefined ? control.ajaxAction : false)" :class="control.cssClass">
<div class="sb-control-checkbox" :data-active="(control.custom != undefined && control.custom == 'feedtype') ? <?php
echo $controlEditingTypeModel;
?>['type'].includes(control.value) : <?php
echo $controlEditingTypeModel;
?>[control.id] == control.options.enabled"></div>
<div class="sb-control-label">{{control.label}}</div>
</div>
<?php
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* Customizer Builder
* CheckBox List Control
*
* @since 6.0
*/
namespace Smashballoon\Customizer\Controls;
if (!\defined('ABSPATH')) {
exit;
}
/** @internal */
class SB_Checkboxlist_Control extends \Smashballoon\Customizer\Controls\SB_Controls_Base
{
/**
* Get control type.
*
* Getting the Control Type
*
* @since 6.0
* @access public
*
* @return string
*/
public function get_type()
{
return 'checkboxlist';
}
/**
* Output Control
*
*
* @since 6.0
* @access public
*/
public function get_control_output($controlEditingTypeModel)
{
?>
<div class="sb-control-checkbox-ctn sbc-fb-fs" v-for="option in control.options" @click.prevent.default="changeCheckboxListValue(control.id, option.value)">
<div class="sb-control-checkbox" :data-active="<?php
echo $controlEditingTypeModel;
?>[control.id].includes(option.value)"></div>
<div class="sb-control-label sb-small-p sb-dark-text" v-html="option.label"></div>
</div>
<?php
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Customizer Builder
* CheckBox Section Control
*
* @since 6.0
*/
namespace Smashballoon\Customizer\Controls;
if (!\defined('ABSPATH')) {
exit;
}
/** @internal */
class SB_Checkboxsection_Control extends \Smashballoon\Customizer\Controls\SB_Controls_Base
{
/**
* Get control type.
*
* Getting the Control Type
*
* @since 6.0
* @access public
*
* @return string
*/
public function get_type()
{
return 'checkboxsection';
}
/**
* Output Control
*
*
* @since 6.0
* @access public
*
* @return HTML
*/
public function get_control_output($controlEditingTypeModel)
{
?>
<div class="sb-control-checkboxsection-header" v-if="control.header">
<div class="sb-control-checkboxsection-name">
<div v-html="svgIcons['preview']"></div>
<strong class="">{{genericText.name}}</strong>
</div>
<strong>{{genericText.edit}}</strong>
</div>
<div class="sb-control-checkbox-ctn sbc-fb-fs" @click.prevent.default="control.disabled ? null : control.section ? switchNestedSection(control.section.id, control.section) : null" :data-disabled="control.checkExtensionPopup != undefined ? false : control.disabled" :data-default-cursor="!control.section">
<div class="sb-control-checkbox-hover sb-tr-2"></div>
<div class="sb-control-checkbox" @click.stop.prevent.default="control.disabled ? activateAPIForm() : changeCheckboxSectionValue(control.id, control.value)" :data-active="checkboxSectionValueExists(control.id, control.value)"></div>
<div class="sbc-fb-fs" :data-active="checkboxSectionValueExists(control.id, control.value)">
<strong class="sb-control-label">
<span v-html="svgIcons[control.icon]" v-if="control.icon"></span>
{{control.label}}
</strong>
</div>
<svg v-if="control.section" class="sb-control-checkboxsection-btn" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M96 480c-8.188 0-16.38-3.125-22.62-9.375c-12.5-12.5-12.5-32.75 0-45.25L242.8 256L73.38 86.63c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0l192 192c12.5 12.5 12.5 32.75 0 45.25l-192 192C112.4 476.9 104.2 480 96 480z"/></svg>
</div>
<?php
}
}

View File

@@ -0,0 +1,54 @@
<?php
/**
* Customizer Builder
* Color Override Field Control
*
* @since 6.0
*/
namespace Smashballoon\Customizer\Controls;
if (!\defined('ABSPATH')) {
exit;
}
/** @internal */
class SB_Coloroverride_Control extends \Smashballoon\Customizer\Controls\SB_Controls_Base
{
/**
* Get control type.
*
* Getting the Control Type
*
* @since 6.0
* @access public
*
* @return string
*/
public function get_type()
{
return 'coloroverride';
}
/**
* Output Control
*
*
* @since 6.0
* @access public
*/
public function get_control_output($controlEditingTypeModel)
{
?>
<div class="sb-control-input-ctn sbc-fb-fs sb-control-coloroverride-ctn">
<div class="sb-control-coloroverride-content">
<div class="sb-control-coloroverride-txt" v-html="<?php
echo $controlEditingTypeModel;
?>[control.id]"></div>
<div class="sb-control-coloroverride-swatch" :style="'background:'+<?php
echo $controlEditingTypeModel;
?>[control.id]"></div>
</div>
<div class="sb-control-colorpicker-btn" @click.prevent.default="resetColorOverride(control.id)">{{genericText.reset}}</div>
</div>
<?php
}
}

View File

@@ -0,0 +1,74 @@
<?php
/**
* Customizer Builder
* Color Picker Field Control
*
* @since 4.0
*/
namespace Smashballoon\Customizer\Controls;
if (!\defined('ABSPATH')) {
exit;
}
/** @internal */
class SB_Colorpicker_Control extends \Smashballoon\Customizer\Controls\SB_Controls_Base
{
/**
* Get control type.
*
* Getting the Control Type
*
* @since 4.0
* @access public
*
* @return string
*/
public function get_type()
{
return 'colorpicker';
}
/**
* Output Control
*
*
* @since 4.0
* @access public
*
* @return HTML
*/
public function get_control_output($controlEditingTypeModel)
{
?>
<div class="sb-control-input-ctn sbc-fb-fs sb-control-colorpicker-ctn" :data-picker-style="control.pickerType ? control.pickerType : 'default'" @click.stop="showColorPickerPospup(control.id)" v-on-clickaway="hideColorPickerPospup">
<!--<sbc-colorpicker :color="<?php
echo $controlEditingTypeModel;
?>[control.id]" v-on:change="changeSettingValue(control.id,...arguments)" :control-id="control.id"></sbc-colorpicker>-->
<input class="sb-control-input" placeholder="Select" type="text" v-model="<?php
echo $controlEditingTypeModel;
?>[control.id]">
<div class="sb-control-colorpicker-swatch" :style="'background:'+<?php
echo $controlEditingTypeModel;
?>[control.id]+';'"></div>
<div class="sb-control-colorpicker-popup" v-show="customizerScreens.activeColorPicker == control.id">
<sketch-picker
@input="updateColorValue(control.id)"
v-model="<?php
echo $controlEditingTypeModel;
?>[control.id]"
:value="<?php
echo $controlEditingTypeModel;
?>[control.id]"
:preset-colors="['#fff','#000','#e92b2b','#ffc104','#31e92b','#2b4ee9','#a72be9','#e92b82']"
></sketch-picker>
<button class="sb-control-action-button sb-colorpicker-reset-btn sb-btn sbc-fb-fs sb-btn-grey" @click.prevent.default="resetColor(control.id)">
<div v-html="svgIcons['update']"></div>
<span>{{genericText.reset}}</span>
</button>
</div>
<div class="sb-control-colorpicker-btn" v-if="control.pickerType == 'reset'">{{genericText.reset}}</div>
</div>
<?php
}
}

View File

@@ -0,0 +1,126 @@
<?php
/**
* Customizer Builder Control Base
*
* @since 6.0
*/
namespace Smashballoon\Customizer\Controls;
if (!\defined('ABSPATH')) {
exit;
}
/** @internal */
abstract class SB_Controls_Base
{
/**
* Get control type.
*
* Getting the Control Type
*
* @since 6.0
* @access public
*
* @return string
*/
public function get_type()
{
return '';
}
/**
* Get control info.
*
* Getting the Control information []
*
* @since 6.0
* @access public
*
* @return array
*/
public function get_info()
{
return array('id' => '', 'type' => '', 'modelname' => '', 'layout' => 'full', 'reverse' => 'false', 'default' => '', 'seperator' => 'none', 'heading' => '', 'description' => '', 'tooltip' => '');
}
/**
* Control Output
*
*
* @since 6.0
* @access public
*/
public function get_control_output($controlEditingTypeModel)
{
}
/**
* Getting Editing Control Type
*
*
* @since 1.0.0
* @access public
*
* @return String
*/
public function get_control_edit_type($editingType)
{
switch ($editingType) {
case 'settings':
return 'customizerFeedData.settings';
break;
}
}
/**
* Get Control HTML.
*
*
* @since 6.0
* @access public
*
* @return HTML
*/
public function print_control_wrapper($editingType)
{
$control_type = $this->get_type();
$controlEditingTypeModel = $this->get_control_edit_type($editingType);
?>
<div class="sb-control-elem-ctn sbc-fb-fs" v-if="control.type == '<?php
echo $control_type;
?>'"
v-show="isControlShown(control)"
:class="control.class"
:data-child="control.child ? 'true' : false"
:data-separator="control.separator != undefined ? control.separator : 'none'"
:data-type="control.type" :data-layout="control.layout == undefined ? 'block' : 'half'"
:data-reverse="control.reverse != undefined ? 'true' : 'false'" :data-stacked="control.stacked ? 'true' : 'false'"
:data-heading="control.strongHeading != undefined && control.strongHeading != 'true' ? '' : 'strong'"
:data-disabled="control.disabledInput != undefined ? isControlShown(control) : false"
:data-switcher-top="control.switcherTop != undefined ? 'true' : false"
>
<div class="sb-control-elem-overlay"
v-show="shouldShowOverlay(control)"
@click.prevent.default="control.checkExtensionPopup != false && !checkExtensionActive(control.checkExtensionPopup) ? viewsActive.extensionsPopupElement = control.checkExtensionPopup : false"
:class="control.checkExtensionPopup != undefined && !checkExtensionActive(control.checkExtensionPopup) ? 'sb-cursor-pointer' : ''"
>
</div>
<div class="sb-control-elem-label" v-if="(control.heading == undefined && control.description == undefined) ? false : true && control.type != 'customview'" :class="control.class">
<div class="sb-control-elem-label-title sbc-fb-fs">
<div v-if="control.icon != undefined" class="sb-control-elem-icon" v-html="svgIcons[control.icon]"></div>
<div class="sb-control-elem-heading sb-small-p sb-dark-text" :data-underline="control.underline" :class="control.enableViewAction != undefined && control.enableViewAction != false ? 'sb-cursor-pointer' : ''" v-html="control.heading" @click.prevent.default="control.enableViewAction != undefined && control.enableViewAction != false ? switchNestedSection(control.enableViewAction, null ) : false"></div>
<div class="sb-control-elem-tltp" v-if="control.tooltip != undefined" @mouseover.prevent.default="toggleElementTooltip(control.tooltip, 'show', control.tooltipAlign ? control.tooltipAlign : 'center' )" @mouseleave.prevent.default="toggleElementTooltip('', 'hide')">
<div class="sb-control-elem-tltp-icon" v-html="svgIcons['info']"></div>
</div>
</div>
<div class="sb-control-elem-description" v-if="control.descriptionPosition != 'bottom'" v-html="control.description"></div>
</div>
<div class="sb-control-elem-output">
<?php
$this->get_control_output($controlEditingTypeModel);
?>
<div class="sb-control-elem-description" v-if="control.descriptionPosition != undefined && control.descriptionPosition == 'bottom'" v-html="control.description"></div>
</div>
</div>
<?php
}
}

View File

@@ -0,0 +1,199 @@
<?php
/**
* Customizer Builder
* Custom View
* This control will used for custom HTMlL controls like (source, feed type...)
* @since 6.0
*/
namespace Smashballoon\Customizer\Controls;
if (!\defined('ABSPATH')) {
exit;
}
/** @internal */
class SB_Customview_Control extends \Smashballoon\Customizer\Controls\SB_Controls_Base
{
/**
* Get control type.
*
* Getting the Control Type
*
* @since 6.0
* @access public
*
* @return string
*/
public function get_type()
{
return 'customview';
}
/**
* Output Control
*
*
* @since 6.0
* @access public
*/
public function get_control_output($controlEditingTypeModel)
{
$this->get_control_feedtype_output($controlEditingTypeModel);
$this->get_control_feedtemplate_output($controlEditingTypeModel);
}
/**
* Feed Templates Output Control
*
*
* @since 4.0
* @access public
*
* @return HTML
*/
public function get_control_feedtype_output($controlEditingTypeModel)
{
?>
<div :class="['sb-control-feedtype-ctn sb-control-feedtemplate-ctn', 'sbc-feedtemplate-' + customizerScreens.printedTemplate.type]" v-if="control.viewId == 'feedtype'">
<div class="sbc-feedtemplate-el" v-if="customizerFeedTypePrint()" @click.prevent.default="activateView('feedtypesPopup')">
<div class="sbc-feedtemplate-el-img sbc-fs" v-html="svgIcons[customizerScreens.printedTemplate.icon]"></div>
<div class="sbc-feedtemplate-el-info sbc-fs">
<strong class="sbc-fs" v-html="customizerScreens.printedTemplate.title"></strong>
</div>
</div>
<button class="sb-control-action-button sb-btn sbc-fs sb-btn-grey" @click.prevent.default="activateView('feedtypesPopup')">
<div v-html="svgIcons['edit']"></div>
<span>{{genericText.change}}</span>
</button>
</div>
<!-- For Channel feed type -->
<div class="sbc-control-feedtype-source sbc-fs" v-if="control.viewId == 'feedtype' && customizerFeedData.settings.type == 'channel'">
<div class="sbc-feedtype-label-wrap">
<strong>{{genericText.channelOrUsername}}</strong>
<div class="sb-control-elem-tltp" @mouseover.prevent.default="toggleElementTooltip(tooltipContent[customizerFeedData.settings.type], 'show', 'left' )" @mouseleave.prevent.default="toggleElementTooltip('', 'hide')">
<div class="sb-control-elem-tltp-icon" v-html="svgIcons['info']"></div>
</div>
</div>
<div class="sb-control-feedtype-source-input cff-fb-fs">
<input class="sb-control-input" type="text" v-model="customizerFeedData.settings.channel">
<button class="sb-control-action-button sbc-btn sbc-btn-default" @click.prevent.default="customizerControlAjaxAction('feedHandleFlyPreview')">
<span>{{genericText.update}}</span>
</button>
</div>
</div>
<!-- For Playlist feed type -->
<div class="sbc-control-feedtype-source sbc-fs" v-if="control.viewId == 'feedtype' && customizerFeedData.settings.type == 'playlist'">
<div class="sbc-feedtype-label-wrap">
<strong>{{genericText.playlistId}}</strong>
<div class="sb-control-elem-tltp" @mouseover.prevent.default="toggleElementTooltip(tooltipContent[customizerFeedData.settings.type], 'show', 'left' )" @mouseleave.prevent.default="toggleElementTooltip('', 'hide')">
<div class="sb-control-elem-tltp-icon" v-html="svgIcons['info']"></div>
</div>
</div>
<div class="sb-control-feedtype-source-input cff-fb-fs">
<input class="sb-control-input" type="text" v-model="customizerFeedData.settings.playlist">
<button class="sb-control-action-button sbc-btn sbc-btn-default" @click.prevent.default="customizerControlAjaxAction('feedFlyPreview')">
<span>{{genericText.update}}</span>
</button>
</div>
</div>
<!-- For Favorites feed type -->
<div class="sbc-control-feedtype-source sbc-fs" v-if="control.viewId == 'feedtype' && customizerFeedData.settings.type == 'favorites'">
<div class="sbc-feedtype-label-wrap">
<strong>{{genericText.channelOrUsername}}</strong>
<div class="sb-control-elem-tltp" @mouseover.prevent.default="toggleElementTooltip(tooltipContent[customizerFeedData.settings.type], 'show', 'left' )" @mouseleave.prevent.default="toggleElementTooltip('', 'hide')">
<div class="sb-control-elem-tltp-icon" v-html="svgIcons['info']"></div>
</div>
</div>
<div class="sb-control-feedtype-source-input cff-fb-fs">
<input class="sb-control-input" type="text" v-model="customizerFeedData.settings.favorites">
<button class="sb-control-action-button sbc-btn sbc-btn-default" @click.prevent.default="customizerControlAjaxAction('feedHandleFlyPreview')">
<span>{{genericText.update}}</span>
</button>
</div>
</div>
<!-- For Search feed type -->
<div class="sbc-control-feedtype-source sbc-fs" v-if="control.viewId == 'feedtype' && customizerFeedData.settings.type == 'search'">
<div class="sbc-feedtype-label-wrap">
<strong>{{genericText.searchTerm}}</strong>
<div class="sb-control-elem-tltp" @mouseover.prevent.default="toggleElementTooltip(tooltipContent[customizerFeedData.settings.type], 'show', 'left' )" @mouseleave.prevent.default="toggleElementTooltip('', 'hide')">
<div class="sb-control-elem-tltp-icon" v-html="svgIcons['info']"></div>
</div>
</div>
<div class="sb-control-feedtype-source-input cff-fb-fs">
<input class="sb-control-input" type="text" v-model="customizerFeedData.settings.search">
<button class="sb-control-action-button sbc-btn sbc-btn-default" @click.prevent.default="customizerControlAjaxAction('feedFlyPreview')">
<span>{{genericText.update}}</span>
</button>
</div>
</div>
<!-- For Livestream feed type -->
<div class="sbc-control-feedtype-source sbc-fs" v-if="control.viewId == 'feedtype' && customizerFeedData.settings.type == 'live'">
<div class="sbc-feedtype-label-wrap">
<strong>{{genericText.channelOrUsername}}</strong>
<div class="sb-control-elem-tltp" @mouseover.prevent.default="toggleElementTooltip(tooltipContent[customizerFeedData.settings.type], 'show', 'left' )" @mouseleave.prevent.default="toggleElementTooltip('', 'hide')">
<div class="sb-control-elem-tltp-icon" v-html="svgIcons['info']"></div>
</div>
</div>
<div class="sb-control-feedtype-source-input cff-fb-fs">
<input class="sb-control-input" type="text" v-model="customizerFeedData.settings.live">
<button class="sb-control-action-button sbc-btn sbc-btn-default" @click.prevent.default="customizerControlAjaxAction('feedHandleFlyPreview')">
<span>{{genericText.update}}</span>
</button>
</div>
</div>
<!-- For Single Videos feed type -->
<div class="sbc-control-feedtype-source sbc-fs" v-if="control.viewId == 'feedtype' && customizerFeedData.settings.type == 'single'">
<div class="sbc-feedtype-label-wrap">
<strong>{{genericText.singleVideosId}}</strong>
<div class="sb-control-elem-tltp" @mouseover.prevent.default="toggleElementTooltip(tooltipContent[customizerFeedData.settings.type], 'show', 'left' )" @mouseleave.prevent.default="toggleElementTooltip('', 'hide')">
<div class="sb-control-elem-tltp-icon" v-html="svgIcons['info']"></div>
</div>
</div>
<div class="sb-control-feedtype-source-input cff-fb-fs">
<input class="sb-control-input" type="text" v-model="customizerFeedData.settings.single">
<button class="sb-control-action-button sbc-btn sbc-btn-default" @click.prevent.default="customizerControlAjaxAction('feedFlyPreview')">
<span>{{genericText.update}}</span>
</button>
</div>
</div>
<?php
}
/**
* Feed Templates Output Control
*
*
* @since 4.0
* @access public
*
* @return HTML
*/
public function get_control_feedtemplate_output($controlEditingTypeModel)
{
?>
<div :class="['sb-control-feedtype-ctn sb-control-feedtemplate-ctn', 'sbc-feedtemplate-' + customizerScreens.printedTemplate.type]" v-if="control.viewId == 'feedtemplate'">
<div class="sbc-feedtemplate-el" v-if="customizerFeedTemplatePrint()" @click.prevent.default="activateView('feedtemplatesPopup')">
<div class="sbc-feedtemplate-el-img sbc-fs" v-html="svgIcons[customizerScreens.printedTemplate.icon]"></div>
<div class="sbc-feedtemplate-el-info sbc-fs">
<strong class="sbc-fs" v-html="customizerScreens.printedTemplate.title"></strong>
</div>
</div>
<button class="sb-control-action-button sb-btn sbc-fs sb-btn-grey" @click.prevent.default="activateView('feedtemplatesPopup')">
<div v-html="svgIcons['edit']"></div>
<span>{{genericText.change}}</span>
</button>
</div>
<!-- For Feed type -->
<div class="sbc-customview-alert sbc-fs" v-if="control.viewId == 'feedtemplate'">
<span>
<span v-html="svgIcons.info" class="sbc-alert-icon"></span>
<span v-html="genericText.feedTemplateAlert"></span>
</span>
</div>
<?php
}
}

View File

@@ -0,0 +1,48 @@
<?php
/**
* Customizer Builder
* Date Picker Field Control
*
* @since 6.0
*/
namespace Smashballoon\Customizer\Controls;
if (!\defined('ABSPATH')) {
exit;
}
/** @internal */
class SB_Datepicker_Control extends \Smashballoon\Customizer\Controls\SB_Controls_Base
{
/**
* Get control type.
*
* Getting the Control Type
*
* @since 6.0
* @access public
*
* @return string
*/
public function get_type()
{
return 'datepicker';
}
/**
* Output Control
*
*
* @since 6.0
* @access public
*/
public function get_control_output($controlEditingTypeModel)
{
?>
<div class="sb-control-input-ctn sbc-fb-fs">
<input type="date" class="sb-control-input sbc-fb-fs" v-model="<?php
echo $controlEditingTypeModel;
?>[control.id]" @change.prevent.default="changeSettingValue(control.id, false,false, control.ajaxAction ? control.ajaxAction : false)" :placeholder="control.placeholder ? control.placeholder : ''">
</div>
<?php
}
}

View File

@@ -0,0 +1,41 @@
<?php
/**
* Customizer Builder
* Heading Text Control
*
* @since 6.0
*/
namespace Smashballoon\Customizer\Controls;
if (!\defined('ABSPATH')) {
exit;
}
/** @internal */
class SB_Heading_Control extends \Smashballoon\Customizer\Controls\SB_Controls_Base
{
/**
* Get control type.
*
* Getting the Control Type
*
* @since 6.0
* @access public
*
* @return string
*/
public function get_type()
{
return 'heading';
}
/**
* Output Control
*
*
* @since 6.0
* @access public
*/
public function get_control_output($controlEditingTypeModel)
{
}
}

View File

@@ -0,0 +1,48 @@
<?php
/**
* Customizer Builder
* Hidden Field Control
*
* @since 6.0
*/
namespace Smashballoon\Customizer\Controls;
if (!\defined('ABSPATH')) {
exit;
}
/** @internal */
class SB_Hidden_Control extends \Smashballoon\Customizer\Controls\SB_Controls_Base
{
/**
* Get control type.
*
* Getting the Control Type
*
* @since 6.0
* @access public
*
* @return string
*/
public function get_type()
{
return 'hidden';
}
/**
* Output Control
*
*
* @since 6.0
* @access public
*/
public function get_control_output($controlEditingTypeModel)
{
?>
<div class="sb-control-input-ctn sbc-fb-fs">
<input type="hidden" v-model="<?php
echo $controlEditingTypeModel;
?>[control.id]">
</div>
<?php
}
}

View File

@@ -0,0 +1,66 @@
<?php
/**
* Customizer Builder
* Image Chooser Field Control
*
* @since 6.0
*/
namespace Smashballoon\Customizer\Controls;
if (!\defined('ABSPATH')) {
exit;
}
/** @internal */
class SB_Imagechooser_Control extends \Smashballoon\Customizer\Controls\SB_Controls_Base
{
/**
* Get control type.
*
* Getting the Control Type
*
* @since 6.0
* @access public
*
* @return string
*/
public function get_type()
{
return 'imagechooser';
}
/**
* Output Control
*
*
* @since 6.0
* @access public
*/
public function get_control_output($controlEditingTypeModel)
{
?>
<div class="sb-control-imagechooser-ctn sbc-fb-fs">
<div class="sbc-fb-fs">
<input type="text" class="sb-control-imagechooser-input sbc-fb-fs" :class="checkNotEmpty(<?php
echo $controlEditingTypeModel;
?>[control.id]) ? 'sb-control-imagechooser-padding' : ''" v-model="<?php
echo $controlEditingTypeModel;
?>[control.id]" :placeholder="control.placeholder ? control.placeholder : <?php
echo $controlEditingTypeModel;
?>[control.id]" disabled>
<div class="sb-control-imagechooser-clear sbc-fb-tltp-parent" v-if="checkNotEmpty(<?php
echo $controlEditingTypeModel;
?>[control.id])">
<div class="sb-control-imagechooser-clear-icon" @click.prevent.default="changeSettingValue(control.id, '')"></div>
<div class="sbc-fb-tltp-elem"><span>{{genericText.clear.replace(/ /g,"&nbsp;")}}</span></div>
</div>
</div>
<div class="sb-control-imagechooser-btn" @click.prevent.default="imageChooser( control.id )">
<div v-html="svgIcons['imageChooser']"></div>
<span v-html="checkNotEmpty(<?php
echo $controlEditingTypeModel;
?>[control.id]) ? genericText.change : genericText.addImage.replace(/ /g,'&nbsp;')"></span>
</div>
</div>
<?php
}
}

View File

@@ -0,0 +1,53 @@
<?php
/**
* Customizer Builder
* Number Field Control
*
* @since 6.0
*/
namespace Smashballoon\Customizer\Controls;
if (!\defined('ABSPATH')) {
exit;
}
/** @internal */
class SB_Number_Control extends \Smashballoon\Customizer\Controls\SB_Controls_Base
{
/**
* Get control type.
*
* Getting the Control Type
*
* @since 6.0
* @access public
*
* @return string
*/
public function get_type()
{
return 'number';
}
/**
* Output Control
*
*
* @since 6.0
* @access public
*/
public function get_control_output($controlEditingTypeModel)
{
?>
<div class="sb-control-input-ctn sbc-fb-fs" :data-contains-suffix="control.fieldSuffix !== undefined ? 'true' : 'false'">
<div class="sb-control-input-info" :class="control.fieldPrefixAction != undefined ? 'sb-cursor-pointer' : ''" v-if="control.fieldPrefix" @click.prevent.default="control.fieldPrefixAction != undefined ? fieldCustomClickAction(control.fieldPrefixAction) : false">{{control.fieldPrefix.replace(/ /g,"&nbsp;")}}</div>
<input type="number" class="sb-control-input sbc-fb-fs" :placeholder="control.placeholder ? control.placeholder : ''" :step="control.step ? control.step : 1" :max="control.max ? control.max : 1000" :min="control.min ? control.min : 0" v-model="<?php
echo $controlEditingTypeModel;
?>[control.id]" @change.prevent.default="changeSettingValue(control.id,false,false, control.ajaxAction ? control.ajaxAction : false)">
<div class="sb-control-input-info" :class="control.fieldSuffixAction != undefined ? 'sb-cursor-pointer' : ''" v-if="control.fieldSuffix" @click.prevent.default="control.fieldSuffixAction != undefined ? fieldCustomClickAction(control.fieldSuffixAction) : false">
<div class="sb-control-btn-icon" v-if="control.buttonIcon" v-html="svgIcons[control.buttonIcon]"></div>
{{control.fieldSuffix.replace(/ /g,"&nbsp;")}}
</div>
</div>
<?php
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* Customizer Builder
* Select Field Control
*
* @since 6.0
*/
namespace Smashballoon\Customizer\Controls;
if (!\defined('ABSPATH')) {
exit;
}
/** @internal */
class SB_Select_Control extends \Smashballoon\Customizer\Controls\SB_Controls_Base
{
/**
* Get control type.
*
* Getting the Control Type
*
* @since 6.0
* @access public
*
* @return string
*/
public function get_type()
{
return 'select';
}
/**
* Output Control
*
*
* @since 6.0
* @access public
*/
public function get_control_output($controlEditingTypeModel)
{
?>
<div class="sb-control-input-ctn sbc-fb-fs">
<select class="sb-control-input sbc-fb-fs" v-model="<?php
echo $controlEditingTypeModel;
?>[control.id]" @change.prevent.default="changeSettingValue(control.id,false,false, control.ajaxAction ? control.ajaxAction : false)">
<option v-for="(opName, opValue) in control.options" :value="opValue">{{opName}}</option>
</select>
</div>
<?php
}
}

View File

@@ -0,0 +1,44 @@
<?php
/**
* Customizer Builder
* Separator Control
*
* @since 6.0
*/
namespace Smashballoon\Customizer\Controls;
if (!\defined('ABSPATH')) {
exit;
}
/** @internal */
class SB_Separator_Control extends \Smashballoon\Customizer\Controls\SB_Controls_Base
{
/**
* Get control type.
*
* Getting the Control Type
*
* @since 6.0
* @access public
*
* @return string
*/
public function get_type()
{
return 'separator';
}
/**
* Output Control
*
*
* @since 6.0
* @access public
*/
public function get_control_output($controlEditingTypeModel)
{
?>
<div class="sb-control-elem-separator sbc-fb-fs" :style="'margin-top:'+ (control.top ? control.top : 0) +'px;margin-bottom:'+ (control.bottom ? control.bottom : 0) +'px;'"></div>
<?php
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* Customizer Builder
* Switcher Field Control
*
* @since 6.0
*/
namespace Smashballoon\Customizer\Controls;
if (!\defined('ABSPATH')) {
exit;
}
/** @internal */
class SB_Switcher_Control extends \Smashballoon\Customizer\Controls\SB_Controls_Base
{
/**
* Get control type.
*
* Getting the Control Type
*
* @since 6.0
* @access public
*
* @return string
*/
public function get_type()
{
return 'switcher';
}
/**
* Output Control
*
*
* @since 6.0
* @access public
*/
public function get_control_output($controlEditingTypeModel)
{
?>
<div class="sb-control-switcher-ctn" :data-active="<?php
echo $controlEditingTypeModel;
?>[control.id] == control.options.enabled" @click.prevent.default="changeSwitcherSettingValue(control.id, control.options.enabled, control.options.disabled, control.ajaxAction ? control.ajaxAction : false)">
<div class="sb-control-switcher sb-tr-2"></div>
<div class="sb-control-label" v-if="control.label" :data-title="control.labelStrong ? 'true' : false">{{control.label}}</div>
</div>
<?php
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* Customizer Builder
* Text Field Control
*
* @since 6.0
*/
namespace Smashballoon\Customizer\Controls;
if (!\defined('ABSPATH')) {
exit;
}
/** @internal */
class SB_Text_Control extends \Smashballoon\Customizer\Controls\SB_Controls_Base
{
/**
* Get control type.
*
* Getting the Control Type
*
* @since 6.0
* @access public
*
* @return string
*/
public function get_type()
{
return 'text';
}
/**
* Output Control
*
*
* @since 6.0
* @access public
*/
public function get_control_output($controlEditingTypeModel)
{
?>
<div class="sb-control-input-ctn sbc-fb-fs">
<div class="sb-control-input-info" v-if="control.fieldPrefix">{{control.fieldPrefix.replace(/ /g,"&nbsp;")}}</div>
<input type="text" class="sb-control-input sbc-fb-fs" v-model="<?php
echo $controlEditingTypeModel;
?>[control.id]" @change.prevent.default="changeSettingValue(control.id, false,false, control.ajaxAction ? control.ajaxAction : false)" :placeholder="control.placeholder ? control.placeholder : ''">
<div class="sb-control-input-info" v-if="control.fieldSuffix">{{control.fieldSuffix.replace(/ /g,"&nbsp;")}}</div>
</div>
<?php
}
}

View File

@@ -0,0 +1,48 @@
<?php
/**
* Customizer Builder
* TextArea Field Control
*
* @since 6.0
*/
namespace Smashballoon\Customizer\Controls;
if (!\defined('ABSPATH')) {
exit;
}
/** @internal */
class SB_Textarea_Control extends \Smashballoon\Customizer\Controls\SB_Controls_Base
{
/**
* Get control type.
*
* Getting the Control Type
*
* @since 6.0
* @access public
*
* @return string
*/
public function get_type()
{
return 'textarea';
}
/**
* Output Control
*
*
* @since 6.0
* @access public
*/
public function get_control_output($controlEditingTypeModel)
{
?>
<div class="sb-control-textarea-ctn sbc-fb-fs">
<textarea class="sb-control-input-textrea sbc-fb-fs" v-model="<?php
echo $controlEditingTypeModel;
?>[control.id]" :placeholder="control.placeholder ? control.placeholder : ''" @focusout.prevent.default="changeSettingValue(false,false,false, control.ajaxAction ? control.ajaxAction : false)"></textarea>
</div>
<?php
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* Customizer Builder
* Toggle Control
*
* @since 6.0
*/
namespace Smashballoon\Customizer\Controls;
if (!\defined('ABSPATH')) {
exit;
}
/** @internal */
class SB_Toggle_Control extends \Smashballoon\Customizer\Controls\SB_Controls_Base
{
/**
* Get control type.
*
* Getting the Control Type
*
* @since 6.0
* @access public
*
* @return string
*/
public function get_type()
{
return 'toggle';
}
/**
* Output Control
*
*
* @since 6.0
* @access public
*/
public function get_control_output($controlEditingTypeModel)
{
?>
<div class="sb-control-toggle-ctn sbc-fb-fs">
<div class="sb-control-toggle-elm sbc-fb-fs sb-tr-2" data-active="true">
<div class="sb-control-toggle-deco sb-tr-1"></div>
<div class="sb-control-toggle-icon" v-if="control.toggle.icon" v-html="svgIcons[control.toggle.icon]"></div>
<div class="sb-control-label">{{control.toggle.label}}</div>
</div>
</div>
<?php
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* Customizer Builder
* Toggle Buttons
*
* @since 6.0
*/
namespace Smashballoon\Customizer\Controls;
if (!\defined('ABSPATH')) {
exit;
}
/** @internal */
class SB_Togglebutton_Control extends \Smashballoon\Customizer\Controls\SB_Controls_Base
{
/**
* Get control type.
*
* Getting the Control Type
*
* @since 6.0
* @access public
*
* @return string
*/
public function get_type()
{
return 'togglebutton';
}
/**
* Output Control
*
*
* @since 6.0
* @access public
*/
public function get_control_output($controlEditingTypeModel)
{
?>
<div class="sb-control-togglebutton-ctn sbc-fb-fs">
<div class="sb-control-togglebutton-elm sbc-fb-fs sb-tr-1" v-for="toggle in control.options" :data-active="<?php
echo $controlEditingTypeModel;
?>[control.id] == toggle.value" v-show="toggle.condition != undefined ? checkControlCondition(toggle.condition) : true" @click.prevent.default="changeSettingValue(control.id,toggle.value, true)" >
{{toggle.label}}
</div>
</div>
<?php
}
}

View File

@@ -0,0 +1,64 @@
<?php
/**
* Customizer Builder
* Toggle Set Control
*
* @since 6.0
*/
namespace Smashballoon\Customizer\Controls;
if (!\defined('ABSPATH')) {
exit;
}
/** @internal */
class SB_Toggleset_Control extends \Smashballoon\Customizer\Controls\SB_Controls_Base
{
/**
* Get control type.
*
* Getting the Control Type
*
* @since 6.0
* @access public
*
* @return string
*/
public function get_type()
{
return 'toggleset';
}
/**
* Output Control
*
*
* @since 6.0
* @access public
*/
public function get_control_output($controlEditingTypeModel)
{
?>
<div class="sb-control-toggle-set-ctn sbc-fb-fs">
<div
class="sb-control-toggle-elm sbc-fb-fs sb-tr-2"
v-for="toggle in control.options"
:data-active="<?php
echo $controlEditingTypeModel;
?>[control.id] == toggle.value"
@click.prevent.default="changeSettingValue(control.id,toggle.value, toggle.checkExtension != undefined ? checkExtensionActive(toggle.checkExtension) : true, control.ajaxAction != undefined ? control.ajaxAction : false)"
v-show="toggle.condition != undefined ? checkControlCondition(toggle.condition) : true"
:data-disabled="toggle.checkExtension != undefined && (sbyIsPro && sbyLicenseNoticeActive || !sbyIsPro) ? !checkExtensionActive(toggle.checkExtension) : false"
>
<div
class="sb-control-toggle-extension-cover"
v-show="shouldShowTogglesetCover(toggle)"
@click.prevent.default="togglesetExtPopup(toggle)"
></div>
<div class="sb-control-toggle-deco sb-tr-1"></div>
<div class="sb-control-toggle-icon" v-if="toggle.icon" v-html="svgIcons[toggle.icon]"></div>
<div class="sb-control-label">{{toggle.label}}</div>
</div>
</div>
<?php
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Smashballoon\Customizer;
use Smashballoon\Stubs\Services\ServiceProvider;
/** @internal */
class Customizer_Service extends ServiceProvider
{
/**
* @var Builder_Customizer
*/
private $builder_customizer;
public function __construct(\Smashballoon\Customizer\Builder_Customizer $builder_customizer)
{
$this->builder_customizer = $builder_customizer;
}
public function register()
{
include_once __DIR__ . '/../bootstrap.php';
$this->builder_customizer->register();
}
}

View File

@@ -0,0 +1,450 @@
<?php
/**
* YouTube Feed Database
*
* @since 6.0
*/
namespace Smashballoon\Customizer;
/** @internal */
class DB
{
const RESULTS_PER_PAGE = 20;
protected $sources_table = 'sb_sources';
protected $feeds_table = 'sb_feeds';
public function __construct()
{
global $wpdb;
$this->sources_table = \apply_filters('sb_customizer_sources_table', $wpdb->prefix . $this->sources_table);
$this->feeds_table = \apply_filters('sb_customizer_feeds_table', $wpdb->prefix . $this->feeds_table);
}
/**
* Set Feeds Table
*
* @since 2.0
*/
public function set_feeds_table($name)
{
if (!$name) {
return;
}
$this->feeds_table = $name;
}
/**
* Query the sbi_sources table
*
* @param array $args
*
* @return array|bool
*
* @since 6.0
*/
public function source_query($args = array())
{
global $wpdb;
$sources_table_name = $wpdb->prefix . $this->sources_table;
$feeds_table_name = $wpdb->prefix . $this->feeds_table;
if ($wpdb->get_var("show tables like '{$sources_table_name}'") !== $sources_table_name) {
return [];
}
$page = 0;
if (isset($args['page'])) {
$page = (int) $args['page'] - 1;
unset($args['page']);
}
$offset = \max(0, $page * self::RESULTS_PER_PAGE);
if (empty($args)) {
$limit = (int) self::RESULTS_PER_PAGE;
$sql = "SELECT s.id, s.account_id, s.account_type, s.privilege, s.access_token, s.username, s.info, s.error, s.expires, count(f.id) as used_in\n\t\t\t\tFROM {$sources_table_name} s\n\t\t\t\tLEFT JOIN {$feeds_table_name} f ON f.settings LIKE CONCAT('%', s.account_id, '%')\n\t\t\t\tGROUP BY s.id, s.account_id\n\t\t\t\tLIMIT {$limit}\n\t\t\t\tOFFSET {$offset};\n\t\t\t\t";
$results = $wpdb->get_results($sql, \ARRAY_A);
if (empty($results)) {
return array();
}
$i = 0;
foreach ($results as $result) {
if ((int) $result['used_in'] > 0) {
$account_id = \sanitize_key($result['account_id']);
$sql = "SELECT *\n\t\t\t\t\t\tFROM {$feeds_table_name}\n\t\t\t\t\t\tWHERE settings LIKE CONCAT('%', {$account_id}, '%')\n\t\t\t\t\t\tGROUP BY id\n\t\t\t\t\t\tLIMIT 100;\n\t\t\t\t\t\t";
$results[$i]['instances'] = $wpdb->get_results($sql, \ARRAY_A);
}
$i++;
}
return $results;
}
if (!empty($args['expiring'])) {
$sql = $wpdb->prepare("\n\t\t\tSELECT * FROM {$sources_table_name}\n\t\t\tWHERE account_type = 'personal'\n\t\t\tAND expires < %s\n\t\t\tAND last_updated < %s\n\t\t\tORDER BY expires ASC\n\t\t\tLIMIT 5;\n\t\t ", \gmdate('Y-m-d H:i:s', \time() + SBI_REFRESH_THRESHOLD_OFFSET), \gmdate('Y-m-d H:i:s', \time() - SBI_MINIMUM_INTERVAL));
return $wpdb->get_results($sql, \ARRAY_A);
}
if (!empty($args['username'])) {
return $wpdb->get_results($wpdb->prepare("\n\t\t\tSELECT * FROM {$sources_table_name}\n\t\t\tWHERE username = %s;\n\t\t ", $args['username']), \ARRAY_A);
}
if (isset($args['access_token']) && !isset($args['id'])) {
return $wpdb->get_results($wpdb->prepare("\n\t\t\tSELECT * FROM {$sources_table_name}\n\t\t\tWHERE access_token = %s;\n\t\t ", $args['access_token']), \ARRAY_A);
}
if (!isset($args['id'])) {
return \false;
}
if (\is_array($args['id'])) {
$id_array = array();
foreach ($args['id'] as $id) {
$id_array[] = \esc_sql($id);
}
} elseif (\strpos($args['id'], ',') !== \false) {
$id_array = \explode(',', \str_replace(' ', '', \esc_sql($args['id'])));
}
if (isset($id_array)) {
$id_string = "'" . \implode("' , '", \array_map('esc_sql', $id_array)) . "'";
}
if (!empty($args['all_business'])) {
$id_string = empty($id_string) ? '0' : $id_string;
$sql = "\n\t\t\tSELECT * FROM {$sources_table_name}\n\t\t\tWHERE account_id IN ({$id_string})\n\t\t\tOR account_type = 'business'\n\t\t ";
return $wpdb->get_results($sql, \ARRAY_A);
}
$privilege = '';
if (!empty($privilege)) {
if (isset($id_string)) {
$sql = $wpdb->prepare("\n\t\t\tSELECT * FROM {$sources_table_name}\n\t\t\tWHERE account_id IN ({$id_string})\n\t\t\tAND privilege = %s;\n\t\t ", $privilege);
} else {
$sql = $wpdb->prepare("\n\t\t\tSELECT * FROM {$sources_table_name}\n\t\t\tWHERE account_id = %s\n\t\t\tAND privilege = %s;\n\t\t ", $args['id'], $privilege);
}
} else {
if (isset($id_string)) {
$sql = "\n\t\t\t\tSELECT * FROM {$sources_table_name}\n\t\t\t\tWHERE account_id IN ({$id_string});\n\t\t\t\t";
} else {
$sql = $wpdb->prepare("\n\t\t\t\tSELECT * FROM {$sources_table_name}\n\t\t\t\tWHERE account_id = %s;\n\t\t\t ", $args['id']);
}
}
return $wpdb->get_results($sql, \ARRAY_A);
}
/**
* Update a source (connected account)
*
* @param array $to_update
* @param array $where_data
*
* @return false|int
*
* @since 6.0
*/
public function source_update($to_update, $where_data)
{
global $wpdb;
$sources_table_name = $this->sources_table;
$encryption = new \SmashBalloon\YoutubeFeed\Vendor\SB_YouTube_Data_Encryption();
$data = array();
$where = array();
$format = array();
$where_format = array();
if (isset($to_update['type'])) {
$data['account_type'] = $to_update['type'];
$format[] = '%s';
}
if (isset($to_update['privilege'])) {
$data['privilege'] = $to_update['privilege'];
$format[] = '%s';
}
if (isset($to_update['id'])) {
$where['account_id'] = $to_update['id'];
$where_format[] = '%s';
}
if (isset($to_update['access_token'])) {
$data['access_token'] = $encryption->maybe_encrypt($to_update['access_token']);
$format[] = '%s';
}
if (isset($to_update['username'])) {
$data['username'] = $to_update['username'];
$format[] = '%s';
}
if (isset($to_update['info'])) {
$data['info'] = $encryption->maybe_encrypt($to_update['info']);
$format[] = '%s';
}
if (isset($to_update['error'])) {
$data['error'] = $to_update['error'];
$format[] = '%s';
}
if (isset($to_update['expires'])) {
$data['expires'] = $to_update['expires'];
$format[] = '%s';
}
if (isset($to_update['last_updated'])) {
$data['last_updated'] = $to_update['last_updated'];
$format[] = '%s';
}
if (isset($to_update['author'])) {
$data['author'] = $to_update['author'];
$format[] = '%d';
}
if (isset($where_data['type'])) {
$where['account_type'] = $where_data['type'];
$where_format[] = '%s';
}
if (isset($where_data['privilege'])) {
$where['privilege'] = $where_data['privilege'];
$where_format[] = '%s';
}
if (isset($where_data['author'])) {
$where['author'] = $where_data['author'];
$where_format[] = '%d';
}
if (isset($where_data['id'])) {
$where['account_id'] = $where_data['id'];
$where_format[] = '%s';
}
if (isset($where_data['record_id'])) {
$where['id'] = $where_data['record_id'];
$where_format[] = '%d';
}
$affected = $wpdb->update($sources_table_name, $data, $where, $format, $where_format);
return $affected;
}
/**
* New source (connected account) data is added to the
* sbi_sources table and the new insert ID is returned
*
* @param array $to_insert
*
* @return false|int
*
* @since 6.0
*/
public function source_insert($to_insert)
{
global $wpdb;
$sources_table_name = $this->sources_table;
$encryption = new \SmashBalloon\YoutubeFeed\Vendor\SB_Instagram_Data_Encryption();
$data = array();
$format = array();
if (isset($to_insert['id'])) {
$data['account_id'] = $to_insert['id'];
$format[] = '%s';
}
if (isset($to_insert['type'])) {
$data['account_type'] = $to_insert['type'];
$format[] = '%s';
} else {
$data['account_type'] = 'page';
$format[] = '%s';
}
if (isset($to_insert['privilege'])) {
$data['privilege'] = $to_insert['privilege'];
$format[] = '%s';
}
if (isset($to_insert['access_token'])) {
$data['access_token'] = $encryption->maybe_encrypt($to_insert['access_token']);
$format[] = '%s';
}
if (isset($to_insert['username'])) {
$data['username'] = $to_insert['username'];
$format[] = '%s';
}
if (isset($to_insert['info'])) {
$data['info'] = $encryption->maybe_encrypt($to_insert['info']);
$format[] = '%s';
}
if (isset($to_insert['error'])) {
$data['error'] = $to_insert['error'];
$format[] = '%s';
}
if (isset($to_insert['expires'])) {
$data['expires'] = $to_insert['expires'];
$format[] = '%s';
} else {
$data['expires'] = '2100-12-30 00:00:00';
$format[] = '%s';
}
$data['last_updated'] = \gmdate('Y-m-d H:i:s');
$format[] = '%s';
if (isset($to_insert['author'])) {
$data['author'] = $to_insert['author'];
$format[] = '%d';
} else {
$data['author'] = \get_current_user_id();
$format[] = '%d';
}
return $wpdb->insert($sources_table_name, $data, $format);
}
/**
* Count the sby_feeds table
*
* @return int
*
* @since 6.0
*/
public function feeds_count()
{
global $wpdb;
$feeds_table_name = $this->feeds_table;
$results = $wpdb->get_results("SELECT COUNT(*) AS num_entries FROM {$feeds_table_name}", \ARRAY_A);
return isset($results[0]['num_entries']) ? (int) $results[0]['num_entries'] : 0;
}
/**
* Query the sby_feeds table
*
* @param array $args
*
* @return array|bool
*
* @since 6.0
*/
public function feeds_query($args = array())
{
global $wpdb;
$feeds_table_name = $this->feeds_table;
$page = 0;
if (isset($args['page'])) {
$page = (int) $args['page'] - 1;
unset($args['page']);
}
$offset = \max(0, $page * self::RESULTS_PER_PAGE);
if (isset($args['id'])) {
$sql = $wpdb->prepare("\n\t\t\tSELECT * FROM {$feeds_table_name}\n\t\t\tWHERE id = %d;\n\t\t ", $args['id']);
} else {
$sql = $wpdb->prepare("\n\t\t\tSELECT * FROM {$feeds_table_name}\n\t\t\tLIMIT %d\n\t\t\tOFFSET %d;", self::RESULTS_PER_PAGE, $offset);
}
return $wpdb->get_results($sql, \ARRAY_A);
}
/**
* Update feed data in the sbi_feed table
*
* @param array $to_update
* @param array $where_data
*
* @return false|int
*
* @since 6.0
*/
public function feeds_update($to_update, $where_data)
{
global $wpdb;
$feeds_table_name = $this->feeds_table;
$data = array();
$where = array();
$format = array();
foreach ($to_update as $single_insert) {
if ($single_insert['key']) {
$data[$single_insert['key']] = $single_insert['values'][0];
$format[] = '%s';
}
}
if (isset($where_data['id'])) {
$where['id'] = $where_data['id'];
$where_format = array('%d');
} elseif (isset($where_data['feed_name'])) {
$where['feed_name'] = $where_data['feed_name'];
$where_format = array('%s');
} else {
return \false;
}
$data['last_modified'] = \gmdate('Y-m-d H:i:s');
$format[] = '%s';
$affected = $wpdb->update($feeds_table_name, $data, $where, $format, $where_format);
return $affected;
}
/**
* New feed data is added to the sby_feeds table and
* the new insert ID is returned
*
* @param array $to_insert
*
* @return false|int
*
* @since 6.0
*/
public function feeds_insert($to_insert)
{
global $wpdb;
$feeds_table_name = $this->feeds_table;
$data = array();
$format = array();
foreach ($to_insert as $single_insert) {
if ($single_insert['key']) {
$data[$single_insert['key']] = $single_insert['values'][0];
$format[] = '%s';
}
}
$data['last_modified'] = \gmdate('Y-m-d H:i:s');
$format[] = '%s';
$data['author'] = \get_current_user_id();
$format[] = '%d';
$wpdb->insert($feeds_table_name, $data, $format);
return $wpdb->insert_id;
}
/**
* Creates all database tables used in the new admin area in
* the 6.0 update.
*
* TODO: Add error reporting
*
* @since 6.0
*/
public function create_tables($include_charset_collate = \true, $skip_sources = \false)
{
if (!\function_exists('dbDelta')) {
require_once \ABSPATH . '/wp-admin/includes/upgrade.php';
}
global $wpdb;
$max_index_length = 191;
$charset_collate = '';
if ($include_charset_collate && \method_exists($wpdb, 'get_charset_collate')) {
// get_charset_collate introduced in WP 3.5
$charset_collate = $wpdb->get_charset_collate();
}
$feeds_table_name = $this->feeds_table;
if ($wpdb->get_var("show tables like '{$feeds_table_name}'") !== $feeds_table_name) {
$sql = "\n\t\t\tCREATE TABLE {$feeds_table_name} (\n\t\t\t id bigint(20) unsigned NOT NULL auto_increment,\n\t\t\t feed_name text NOT NULL default '',\n\t\t\t feed_title text NOT NULL default '',\n\t\t\t settings longtext NOT NULL default '',\n\t\t\t author bigint(20) unsigned NOT NULL default '1',\n\t\t\t status varchar(255) NOT NULL default '',\n\t\t\t last_modified datetime NOT NULL,\n\t\t\t PRIMARY KEY (id),\n\t\t\t KEY author (author)\n\t\t\t) {$charset_collate};\n\t\t\t";
$wpdb->query($sql);
}
$error = $wpdb->last_error;
$query = $wpdb->last_query;
$had_error = \false;
if ($wpdb->get_var("show tables like '{$feeds_table_name}'") !== $feeds_table_name) {
$had_error = \true;
}
if (!$had_error) {
}
$feed_caches_table_name = $wpdb->prefix . 'sby_feed_caches';
if ($wpdb->get_var("show tables like '{$feed_caches_table_name}'") !== $feed_caches_table_name) {
$sql = '
CREATE TABLE ' . $feed_caches_table_name . " (\n\t\t\t\tid bigint(20) unsigned NOT NULL auto_increment,\n\t\t\t\tfeed_id varchar(255) NOT NULL default '',\n cache_key varchar(255) NOT NULL default '',\n cache_value longtext NOT NULL default '',\n cron_update varchar(20) NOT NULL default 'yes',\n last_updated datetime NOT NULL,\n PRIMARY KEY (id),\n KEY feed_id (feed_id({$max_index_length}))\n ) {$charset_collate};";
$wpdb->query($sql);
}
$error = $wpdb->last_error;
$query = $wpdb->last_query;
$had_error = \false;
if ($wpdb->get_var("show tables like '{$feed_caches_table_name}'") !== $feed_caches_table_name) {
$had_error = \true;
}
if (!$had_error) {
}
$sources_table_name = $wpdb->prefix . 'sby_sources';
if ($skip_sources === \false && $wpdb->get_var("show tables like '{$sources_table_name}'") !== $sources_table_name) {
$sql = '
CREATE TABLE ' . $sources_table_name . " (\n\t\t\t\tid bigint(20) unsigned NOT NULL auto_increment,\n\t\t\t\taccount_id varchar(255) NOT NULL default '',\n account_type varchar(255) NOT NULL default '',\n privilege varchar(255) NOT NULL default '',\n access_token varchar(1000) NOT NULL default '',\n username varchar(255) NOT NULL default '',\n info text NOT NULL default '',\n error text NOT NULL default '',\n expires datetime NOT NULL,\n last_updated datetime NOT NULL,\n author bigint(20) unsigned NOT NULL default '1',\n PRIMARY KEY (id),\n KEY account_type (account_type({$max_index_length})),\n KEY author (author)\n ) {$charset_collate};";
$wpdb->query($sql);
}
$error = $wpdb->last_error;
$query = $wpdb->last_query;
$had_error = \false;
if ($wpdb->get_var("show tables like '{$sources_table_name}'") !== $sources_table_name) {
$had_error = \true;
}
if (!$had_error) {
}
}
public function reset_tables()
{
global $wpdb;
$feeds_table_name = $this->feeds_table;
$wpdb->query("DROP TABLE IF EXISTS {$feeds_table_name}");
$feed_caches_table_name = $wpdb->prefix . 'sbi_feed_caches';
$wpdb->query("DROP TABLE IF EXISTS {$feed_caches_table_name}");
$sources_table_name = $this->sources_table;
$wpdb->query("DROP TABLE IF EXISTS {$sources_table_name}");
}
public function get_results_per_page()
{
return self::RESULTS_PER_PAGE;
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,118 @@
<?php
namespace Smashballoon\Customizer;
/** @internal */
class Feed_Locator
{
/**
* @var Config
*/
private $config;
private $table;
public function __construct(\Smashballoon\Customizer\Config $config)
{
$this->config = $config;
$this->table = $this->config->plugin_slug . '_feed_locator';
}
public function legacy_feed_locator_query($args)
{
global $wpdb;
$feed_locator_table_name = $wpdb->prefix . $this->table;
$group_by = '';
if (isset($args['group_by'])) {
$group_by = 'GROUP BY ' . \esc_sql($args['group_by']);
}
$location_string = 'content';
$by_feed_id = '';
$by_shortcode_atts = '';
if (isset($args['html_location'])) {
$locations = \array_map('esc_sql', $args['html_location']);
$location_string = \implode("', '", $locations);
}
if (isset($args['feed_id'])) {
$by_feed_id = \sprintf("AND feed_id = '%s'", $args['feed_id']);
}
if (isset($args['shortcode_atts'])) {
$by_feed_id = \sprintf("AND shortcode_atts = '%s'", $args['shortcode_atts']);
}
$page = 0;
if (isset($args['page'])) {
$page = (int) $args['page'] - 1;
unset($args['page']);
}
$offset = \max(0, $page * \Smashballoon\Customizer\DB::RESULTS_PER_PAGE);
$limit = \Smashballoon\Customizer\DB::RESULTS_PER_PAGE;
$results = $wpdb->get_results("\r\n\t\t\tSELECT *\r\n\t\t\tFROM {$feed_locator_table_name}\r\n\t\t\tWHERE feed_id NOT LIKE '*%'\r\n\t\t \tAND html_location IN ( '{$location_string}' )\r\n\t\t \t{$by_feed_id}\r\n\t\t \t{$group_by}\r\n\t\t \tLIMIT {$limit}\r\n\t\t\tOFFSET {$offset};", \ARRAY_A);
return $results;
}
public function count($args)
{
global $wpdb;
$feed_locator_table_name = $wpdb->prefix . $this->table;
if (isset($args['shortcode_atts'])) {
$results = $wpdb->get_results($wpdb->prepare("\r\n\t\t\tSELECT COUNT(*) AS num_entries\r\n FROM {$feed_locator_table_name}\r\n WHERE shortcode_atts = %s\r\n ", $args['shortcode_atts']), \ARRAY_A);
} else {
$results = $wpdb->get_results($wpdb->prepare("\r\n\t\t\tSELECT COUNT(*) AS num_entries\r\n FROM {$feed_locator_table_name}\r\n WHERE feed_id = %s\r\n ", $args['feed_id']), \ARRAY_A);
}
if (isset($results[0]['num_entries'])) {
return (int) $results[0]['num_entries'];
}
return 0;
}
public function feed_locator_query($args)
{
global $wpdb;
$feed_locator_table_name = $wpdb->prefix . $this->table;
$group_by = '';
if (isset($args['group_by'])) {
$group_by = 'GROUP BY ' . \esc_sql($args['group_by']);
}
$location_string = 'content';
if (isset($args['html_location'])) {
$locations = \array_map('esc_sql', $args['html_location']);
$location_string = \implode("', '", $locations);
}
$page = 0;
if (isset($args['page'])) {
$page = (int) $args['page'] - 1;
unset($args['page']);
}
$offset = \max(0, $page * \Smashballoon\Customizer\DB::RESULTS_PER_PAGE);
if (isset($args['shortcode_atts'])) {
$results = $wpdb->get_results($wpdb->prepare("\r\n\t\t\tSELECT *\r\n\t\t\tFROM {$feed_locator_table_name}\r\n\t\t\tWHERE shortcode_atts = %s\r\n\t\t \tAND html_location IN ( '{$location_string}' )\r\n\t\t \t{$group_by}\r\n\t\t \tLIMIT %d\r\n\t\t\tOFFSET %d;", $args['shortcode_atts'], \Smashballoon\Customizer\DB::RESULTS_PER_PAGE, $offset), \ARRAY_A);
} else {
$results = $wpdb->get_results($wpdb->prepare("\r\n\t\t\tSELECT *\r\n\t\t\tFROM {$feed_locator_table_name}\r\n\t\t\tWHERE feed_id = %s\r\n\t\t \tAND html_location IN ( '{$location_string}' )\r\n\t\t \t{$group_by}\r\n\t\t \tLIMIT %d\r\n\t\t\tOFFSET %d;", $args['feed_id'], \Smashballoon\Customizer\DB::RESULTS_PER_PAGE, $offset), \ARRAY_A);
}
return $results;
}
/**
* A custom table stores locations
*/
public function create_table()
{
global $wpdb;
$feed_locator_table_name = $wpdb->prefix . $this->table;
if ($wpdb->get_var("show tables like '{$feed_locator_table_name}'") != $feed_locator_table_name) {
$sql = "CREATE TABLE " . $feed_locator_table_name . " (\r\n\t\t\t\tid BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,\r\n feed_id VARCHAR(50) DEFAULT '' NOT NULL,\r\n post_id BIGINT(20) UNSIGNED NOT NULL,\r\n html_location VARCHAR(50) DEFAULT 'unknown' NOT NULL,\r\n shortcode_atts LONGTEXT NOT NULL,\r\n last_update DATETIME\r\n );";
$wpdb->query($sql);
}
$error = $wpdb->last_error;
$query = $wpdb->last_query;
$had_error = \false;
if ($wpdb->get_var("show tables like '{$feed_locator_table_name}'") != $feed_locator_table_name) {
$had_error = \true;
}
if (!$had_error) {
$wpdb->query("ALTER TABLE {$feed_locator_table_name} ADD INDEX feed_id (feed_id)");
$wpdb->query("ALTER TABLE {$feed_locator_table_name} ADD INDEX post_id (post_id)");
}
}
public function update_legacy_to_builder($args)
{
global $wpdb;
$data = array('feed_id' => '*' . $args['new_feed_id'], 'shortcode_atts' => '{"feed":"' . $args['new_feed_id'] . '"}');
$affected = $wpdb->query($wpdb->prepare("UPDATE {$this->table}\r\n \t\t\t\tSET feed_id = %s, shortcode_atts = %s", $data['feed_id'], $data['shortcode_atts']));
return $affected;
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Smashballoon\Customizer;
/** @internal */
class Feed_Processor
{
public function process($settings)
{
return $settings;
}
}

View File

@@ -0,0 +1,607 @@
<?php
/**
* YouTube Feed Database
*
* @since 6.0
*/
namespace Smashballoon\Customizer;
/** @internal */
class Feed_Saver
{
/**
* @var int
*
* @since 6.0
*/
private $insert_id;
/**
* @var array
*
* @since 6.0
*/
private $data;
/**
* @var array
*
* @since 6.0
*/
private $sanitized_and_sorted_data;
/**
* @var array
*
* @since 6.0
*/
private $feed_db_data;
/**
* @var string
*
* @since 6.0
*/
private $feed_name;
/**
* @var bool
*
* @since 6.0
*/
private $is_legacy;
private $db;
/**
* @var ProxyProvider
*/
private $proxy_provider;
/**
* SBY_Feed_Saver constructor.
*
* @since 2.0
*/
public function __construct(\Smashballoon\Customizer\DB $db, \Smashballoon\Customizer\ProxyProvider $proxy_provider)
{
$this->db = $db;
$this->proxy_provider = $proxy_provider;
}
public function set_feed_id($insert_id)
{
if ($insert_id === 'legacy') {
$this->is_legacy = \true;
$this->insert_id = 0;
} else {
$this->is_legacy = \false;
$this->insert_id = $insert_id;
}
}
/**
* Feed insert ID if it exists
*
* @return bool|int
*
* @since 6.0
*/
public function get_feed_id()
{
if ($this->is_legacy) {
return 'legacy';
}
if (!empty($this->insert_id)) {
return $this->insert_id;
} else {
return \false;
}
}
/**
* @param array $data
*
* @since 6.0
*/
public function set_data($data)
{
$this->data = $data;
}
/**
* @param string $feed_name
*
* @since 6.0
*/
public function set_feed_name($feed_name)
{
$this->feed_name = $feed_name;
}
/**
*
* @return array
*
* @since 6.0
*/
public function get_feed_db_data()
{
return $this->feed_db_data;
}
/**
* Adds a new feed if there is no associated feed
* found. Otherwise updates the exiting feed.
*
* @return false|int
*
* @since 6.0
*/
public function update_or_insert()
{
$this->sanitize_and_sort_data();
if ($this->exists_in_database()) {
return $this->update();
} else {
return $this->insert();
}
}
/**
* Whether or not a feed exists with the
* associated insert ID
*
* @return bool
*
* @since 6.0
*/
public function exists_in_database()
{
if ($this->is_legacy) {
return \true;
}
if ($this->insert_id === \false) {
return \false;
}
$args = array('id' => $this->insert_id);
$results = $this->db->feeds_query($args);
return isset($results[0]);
}
/**
* Inserts a new feed from sanitized and sorted data.
* Some data is saved in the sbi_feeds table and some is
* saved in the sbi_feed_settings table.
*
* @return false|int
*
* @since 6.0
*/
public function insert()
{
if ($this->is_legacy) {
return $this->update();
}
if (!isset($this->sanitized_and_sorted_data)) {
return \false;
}
$settings_array = self::format_settings($this->sanitized_and_sorted_data['feed_settings']);
$this->sanitized_and_sorted_data['feeds'][] = array('key' => 'settings', 'values' => array(\json_encode($settings_array)));
if (!empty($this->feed_name)) {
$this->sanitized_and_sorted_data['feeds'][] = array('key' => 'feed_name', 'values' => array($this->feed_name));
}
$this->sanitized_and_sorted_data['feeds'][] = array('key' => 'status', 'values' => array('publish'));
$insert_id = $this->db->feeds_insert($this->sanitized_and_sorted_data['feeds']);
if ($insert_id) {
$this->insert_id = $insert_id;
return $insert_id;
}
return \false;
}
/**
* Updates an existing feed and related settings from
* sanitized and sorted data.
*
* @return false|int
*
* @since 6.0
*/
public function update()
{
if (!isset($this->sanitized_and_sorted_data)) {
return \false;
}
$args = array('id' => $this->insert_id);
$settings_array = self::format_settings($this->sanitized_and_sorted_data['feed_settings']);
if ($this->is_legacy) {
$to_save_json = \json_encode($settings_array);
\update_option('sbi_legacy_feed_settings', $to_save_json, \false);
return \true;
}
$this->sanitized_and_sorted_data['feeds'][] = array('key' => 'settings', 'values' => array(\json_encode($settings_array)));
$this->sanitized_and_sorted_data['feeds'][] = array('key' => 'feed_name', 'values' => array(\sanitize_text_field($this->feed_name)));
$success = $this->db->feeds_update($this->sanitized_and_sorted_data['feeds'], $args);
return $success;
}
/**
* Converts settings that have been sanitized into an associative array
* that can be saved as JSON in the database
*
* @param $raw_settings
*
* @return array
*
* @since 6.0
*/
public static function format_settings($raw_settings)
{
$settings_array = array();
foreach ($raw_settings as $single_setting) {
if (\count($single_setting['values']) > 1) {
$settings_array[$single_setting['key']] = $single_setting['values'];
} else {
$settings_array[$single_setting['key']] = isset($single_setting['values'][0]) ? $single_setting['values'][0] : '';
}
}
return $settings_array;
}
/**
* Retrieves and organizes feed setting data for easy use in
* the builder
*
* @return array|bool
*
* @since 6.0
*/
public function get_feed_settings()
{
if ($this->is_legacy) {
$feed_settings = $this->proxy_provider->get_settings_class();
$feed_settings->set_feed_type_and_terms();
$feed_settings->set_transient_name();
$return = $feed_settings->get_legacy_feed_settings();
$this->feed_db_data = array('id' => 'legacy', 'feed_name' => \__('Legacy Feeds', 'feeds-for-youtube'), 'feed_title' => \__('Legacy Feeds', 'feeds-for-youtube'), 'status' => 'publish', 'last_modified' => \date('Y-m-d H:i:s'));
} elseif (empty($this->insert_id)) {
return \false;
} else {
$args = array('id' => $this->insert_id);
$settings_db_data = $this->db->feeds_query($args);
if (\false === $settings_db_data || \sizeof($settings_db_data) === 0) {
return \false;
}
$this->feed_db_data = array('id' => $settings_db_data[0]['id'], 'feed_name' => $settings_db_data[0]['feed_name'], 'feed_title' => $settings_db_data[0]['feed_title'], 'status' => $settings_db_data[0]['status'], 'last_modified' => $settings_db_data[0]['last_modified']);
$return = \json_decode($settings_db_data[0]['settings'], \true);
$return['feed_name'] = $settings_db_data[0]['feed_name'];
}
$return = \wp_parse_args($return, $this->proxy_provider->get_db_settings());
if (empty($return['id'])) {
return $return;
}
if (!\is_array($return['id'])) {
$return['id'] = \explode(',', \str_replace(' ', '', $return['id']));
}
if (!\is_array($return['tagged'])) {
$return['tagged'] = \explode(',', \str_replace(' ', '', $return['tagged']));
}
if (!\is_array($return['hashtag'])) {
$return['hashtag'] = \explode(',', \str_replace(' ', '', $return['hashtag']));
}
$args = array('id' => $return['id']);
$source_query = $this->db->source_query($args);
$return['sources'] = array();
if (!empty($source_query)) {
foreach ($source_query as $source) {
$user_id = $source['account_id'];
$return['sources'][$user_id] = self::get_processed_source_data($source);
}
} else {
$found_sources = array();
foreach ($return['id'] as $id_or_slug) {
$maybe_source_from_connected = \Smashballoon\Customizer\Source::maybe_one_off_connected_account_update($id_or_slug);
if ($maybe_source_from_connected) {
$found_sources[] = $maybe_source_from_connected;
}
}
if (!empty($found_sources)) {
foreach ($found_sources as $source) {
$user_id = $source['account_id'];
$return['sources'][$user_id] = self::get_processed_source_data($source);
}
} else {
$source_query = $this->db->source_query($args);
if (isset($source_query[0])) {
$source = $source_query[0];
$user_id = $source['account_id'];
$return['sources'][$user_id] = self::get_processed_source_data($source);
}
}
}
return $return;
}
public static function get_processed_source_data($source)
{
$encryption = new \SmashBalloon\YoutubeFeed\Vendor\SB_Instagram_Data_Encryption();
$user_id = $source['account_id'];
$info = !empty($source['info']) ? \json_decode($encryption->decrypt($source['info']), \true) : array();
$cdn_avatar_url = \SmashBalloon\YoutubeFeed\Vendor\SB_Instagram_Parse_Pro::get_avatar_url($info);
$processed = array('record_id' => \stripslashes($source['id']), 'user_id' => $user_id, 'type' => \stripslashes($source['account_type']), 'privilege' => \stripslashes($source['privilege']), 'access_token' => \stripslashes($encryption->decrypt($source['access_token'])), 'username' => \stripslashes($source['username']), 'name' => \stripslashes($source['username']), 'info' => \stripslashes($encryption->decrypt($source['info'])), 'error' => \stripslashes($source['error']), 'expires' => \stripslashes($source['expires']), 'profile_picture' => $cdn_avatar_url, 'local_avatar_url' => \SmashBalloon\YoutubeFeed\Vendor\SB_Instagram_Connected_Account::maybe_local_avatar($source['username'], $cdn_avatar_url));
return $processed;
}
/**
* Retrieves and organizes feed setting data for easy use in
* the builder
* It will NOT get the settings from the DB, but from the Customizer builder
* To be used for updating feed preview on the fly
*
* @return array|bool
*
* @since 6.0
*/
public function get_feed_settings_preview($settings_db_data)
{
if (\false === $settings_db_data || \sizeof($settings_db_data) === 0) {
return \false;
}
$return = $settings_db_data;
$return = \wp_parse_args($return, self::settings_defaults());
if (empty($return['sources'])) {
return $return;
}
$sources = array();
foreach ($return['sources'] as $single_source) {
\array_push($sources, $single_source['account_id']);
}
$args = array('id' => $sources);
$source_query = $this->db->source_query($args);
$return['sources'] = array();
if (!empty($source_query)) {
foreach ($source_query as $source) {
$user_id = $source['account_id'];
$return['sources'][$user_id] = self::get_processed_source_data($source);
}
}
return $return;
}
/**
* Default settings, $return_array equalling false will return
* the settings in the general way that the "SBI_Shortcode" class,
* "sbi_get_processed_options" method does
*
* @param bool $return_array
*
* @return array
*
* @since 6.0
*/
public static function settings_defaults($return_array = \true)
{
$defaults = array(
'connected_accounts' => array(),
'type' => 'channel',
'channel' => '',
'num' => 9,
'nummobile' => 9,
'minnum' => 9,
'widthresp' => \true,
'class' => '',
'height' => '',
'heightunit' => '%',
'disablemobile' => \false,
'itemspacing' => 5,
'itemspacingunit' => 'px',
'background' => '',
'headercolor' => '',
'subscribecolor' => '',
'subscribetextcolor' => '',
'buttoncolor' => '',
'buttonhovercolor' => '',
'buttontextcolor' => '',
'layout' => 'grid',
'playvideo' => 'automatically',
'sortby' => 'none',
'imageres' => 'auto',
'showheader' => \true,
'showdescription' => \true,
'showbutton' => \true,
'headersize' => 'small',
'headeroutside' => \false,
'showsubscribe' => \true,
'buttontext' => \__('Load More...', 'feeds-for-youtube'),
'subscribetext' => \__('Subscribe', 'feeds-for-youtube'),
'caching_type' => 'page',
'cache_time' => 1,
'cache_time_unit' => 'hours',
'backup_cache_enabled' => \true,
'resizeprocess' => 'background',
'disable_resize' => \true,
'storage_process' => 'background',
'favor_local' => \false,
'disable_js_image_loading' => \false,
'ajax_post_load' => \false,
'ajaxtheme' => \false,
'enqueue_css_in_shortcode' => \false,
'font_method' => 'svg',
'customtemplates' => \false,
'gallerycols' => 3,
'gallerycolsmobile' => 2,
'gridcols' => 3,
'gridcolsmobile' => 2,
'playerratio' => '9:16',
'eagerload' => \false,
'custom_css' => '',
'custom_js' => '',
'gdpr' => 'auto',
'disablecdn' => \false,
'allowcookies' => \false,
// pro only
'usecustomsearch' => \false,
'headerchannel' => '',
'customsearch' => '',
'showpast' => \true,
'showlikes' => \true,
'carouselcols' => 3,
'carouselcolsmobile' => 2,
'carouselarrows' => \true,
'carouselpag' => \true,
'carouselautoplay' => \false,
'infoposition' => 'below',
'include' => array('title', 'icon', 'user', 'views', 'date', 'countdown'),
'hoverinclude' => array('description', 'stats'),
'descriptionlength' => 150,
'userelative' => \true,
'dateformat' => '0',
'customdate' => '',
'showsubscribers' => \true,
'subscriberstext' => \__('subscribers', 'feeds-for-youtube'),
'viewstext' => \__('views', 'feeds-for-youtube'),
'agotext' => \__('ago', 'feeds-for-youtube'),
'beforedatetext' => \__('Streaming live', 'feeds-for-youtube'),
'beforestreamtimetext' => \__('Streaming live in', 'feeds-for-youtube'),
'minutetext' => \__('minute', 'feeds-for-youtube'),
'minutestext' => \__('minutes', 'feeds-for-youtube'),
'hourstext' => \__('hours', 'feeds-for-youtube'),
'thousandstext' => \__('K', 'feeds-for-youtube'),
'millionstext' => \__('M', 'feeds-for-youtube'),
'watchnowtext' => \__('Watch Now', 'feeds-for-youtube'),
'cta' => 'related',
'colorpalette' => 'inherit',
'linktext' => \__('Learn More', 'feeds-for-youtube'),
'linkurl' => '',
'linkopentype' => 'same',
'linkcolor' => '',
'linktextcolor' => '',
'custombgcolor1' => '',
'customtextcolor1' => '',
'customtextcolor2' => '',
'customlinkcolor1' => '',
'custombuttoncolor1' => '',
'custombuttoncolor2' => '',
);
$defaults = self::filter_defaults($defaults);
// some settings are comma separated and not arrays when the feed is created
if ($return_array) {
$settings_with_multiples = array('sources');
foreach ($settings_with_multiples as $multiple_key) {
if (isset($defaults[$multiple_key])) {
$defaults[$multiple_key] = \explode(',', $defaults[$multiple_key]);
}
}
}
return $defaults;
}
/**
* Provides backwards compatibility for extensions
*
* @param array $defaults
*
* @return array
*
* @since 6.0
*/
public static function filter_defaults($defaults)
{
return $defaults;
}
/**
* Used for taking raw post data related to settings
* an sanitizing it and sorting it to easily use in
* the database tables
*
* @since 6.0
*/
private function sanitize_and_sort_data()
{
$data = $this->data;
$sanitized_and_sorted = array('feeds' => array(), 'feed_settings' => array());
foreach ($data as $key => $value) {
$data_type = $this->get_data_type($key);
$sanitized_values = array();
if (\is_array($value)) {
foreach ($value as $item) {
$type = $this->is_boolean($item) ? 'boolean' : $data_type['sanitization'];
$sanitized_values[] = $this->sanitize($type, $item);
}
} else {
$type = $this->is_boolean($value) ? 'boolean' : $data_type['sanitization'];
$sanitized_values[] = $this->sanitize($type, $value);
}
$single_sanitized = array('key' => $key, 'values' => $sanitized_values);
$sanitized_and_sorted[$data_type['table']][] = $single_sanitized;
}
$this->sanitized_and_sorted_data = $sanitized_and_sorted;
}
/**
* Determines what table and sanitization should be used
* when handling feed setting data.
*
* TODO: Add settings that need something other than sanitize_text_field
*
* @param string $key
*
* @return array
*
* @since 2.0
*/
private function get_data_type($key)
{
switch ($key) {
case 'feed_name':
case 'status':
case 'feed_title':
$return = array('table' => 'feeds', 'sanitization' => 'sanitize_text_field');
break;
case 'author':
$return = array('table' => 'feeds', 'sanitization' => 'int');
break;
default:
$return = array('table' => 'feed_settings', 'sanitization' => 'sanitize_text_field');
break;
}
return $return;
}
/**
* Check if boolean
* for a value
*
* @param string $type
* @param int|string $value
*
* @return int|string
*
* @since 2.0
*/
private function is_boolean($value)
{
return $value === 'true' || $value === 'false' || \is_bool($value);
}
private function cast_boolean($value)
{
return $value === 'true' || $value === \true || $value === 'on';
}
/**
* Uses the appropriate sanitization function and returns the result
* for a value
*
* @param string $type
* @param int|string $value
*
* @return int|string
*
* @since 2.0
*/
private function sanitize($type, $value)
{
switch ($type) {
case 'int':
$return = (int) $value;
break;
case 'boolean':
$return = $this->cast_boolean($value);
break;
default:
$return = \sanitize_text_field($value);
break;
}
return $return;
}
/**
* Returns an associate array of all existing sources along with their data
*
* @param int $page
*
* @return array
*
* @since 6.0
*/
public function get_source_list($page = 1)
{
$args['page'] = $page;
return $this->db->source_query($args);
}
}

View File

@@ -0,0 +1,151 @@
<?php
/**
* YouTube Feed Saver Manager
*
* @since 2.0
*/
namespace Smashballoon\Customizer;
/** @internal */
class Feed_Saver_Manager
{
private $saver;
private $preview_provider;
private $feed_processor;
public function __construct(\Smashballoon\Customizer\Feed_Saver $saver, \Smashballoon\Customizer\PreviewProvider $preview_provider, \Smashballoon\Customizer\Feed_Processor $feed_processor)
{
$this->saver = $saver;
$this->preview_provider = $preview_provider;
$this->feed_processor = $feed_processor;
}
/**
* AJAX hooks for various feed data related functionality
*
* @since 2.0
*/
public function hooks()
{
\add_action('wp_ajax_sbc_feed_saver_manager_fly_preview', array('Smashballoon\\Customizer\\Feed_Saver_Manager', 'feed_customizer_fly_preview'));
}
/**
* Used To check if it's customizer Screens
* Returns Feed info or false!
*
* @param bool $include_comments
*
* @return array|bool
*
* @since 2.0
*/
public function maybe_feed_customizer_data($include_comments = \false)
{
if (isset($_GET['feed_id'])) {
$feed_id = \sanitize_key($_GET['feed_id']);
$this->saver->set_feed_id($feed_id);
$settings = $this->saver->get_feed_settings();
$settings = $this->process_videos_elements_settings($settings);
$feed_db_data = $this->saver->get_feed_db_data();
if ($settings !== \false) {
$return = array('feed_info' => $feed_db_data, 'headerData' => $feed_db_data, 'settings' => $settings, 'posts' => array());
return $this->feed_processor->process($return);
}
}
return \false;
}
/**
* Process videos elements settings
*
* @since 2.0
*
* @return array $settings
*/
public function process_videos_elements_settings($settings)
{
$api_key_activated = \Smashballoon\Customizer\Feed_Builder::check_api_key_status();
// if all video elements are disabled then make it as an empty array
if (empty($settings['include'])) {
$settings['include'] = array();
}
if (empty($settings['hoverinclude'])) {
$settings['hoverinclude'] = array();
}
// if there are only one video element shown as string
// convert it to an array
if (!\is_array($settings['include'])) {
$settings['include'] = array($settings['include']);
}
if (!\is_array($settings['hoverinclude'])) {
$settings['hoverinclude'] = array($settings['hoverinclude']);
}
// If api is not added then remove stats and views from settings if were added before
if (!$api_key_activated) {
$hover_settings = $settings['hoverinclude'];
if ($key = \array_search('view', $hover_settings) !== \false) {
unset($hover_settings[$key]);
}
if ($key = \array_search('stats', $hover_settings) !== \false) {
unset($hover_settings[$key]);
}
$settings['hoverinclude'] = $hover_settings;
}
return $settings;
}
/**
* Used to retrieve Feed Posts for preview screen
* Returns Feed info or false!
*
* @since 2.0
*/
public function feed_customizer_fly_preview()
{
\check_ajax_referer('sby-admin', 'nonce');
if (!sby_current_user_can('manage_youtube_feed_options')) {
\wp_send_json_error();
}
if (isset($_POST['feedID']) && isset($_POST['previewSettings'])) {
$feed_id = $_POST['feedID'];
$preview_settings = $_POST['previewSettings'];
$feed_name = $_POST['feedName'];
$feed_cache = new \Smashballoon\Customizer\SBY_Cache($feed_id);
$feed_cache->clear('all');
$feed_cache->clear('posts');
$this->saver->set_feed_id($feed_id);
$this->saver->set_feed_name($feed_name);
$this->saver->set_data($preview_settings);
$atts = \Smashballoon\Customizer\Feed_Builder::add_customizer_att(array('feed' => $feed_id, 'customizer' => \true));
$return['feed_html'] = $this->preview_provider->render($atts);
echo $return['feed_html'];
}
\wp_die();
}
public function get_source_list($page = 1)
{
return $this->saver->get_source_list($page);
}
/**
* Use a JSON string to import a feed with settings and sources. The return
* is whether or not the import was successful
*
* @param string $json
*
* @return array
*/
public function import_feed($json, $name = null)
{
$settings_data = \json_decode($json, \true);
$return = [];
$this->saver->set_data($settings_data);
if (!empty($name)) {
$this->saver->set_feed_name($name);
}
$this->saver->set_feed_id(\false);
if ($this->saver->update_or_insert()) {
$return = array('success' => \true, 'feed_id' => $this->saver->get_feed_id(), 'message' => \__('Feed imported successfully.', 'feeds-for-youtube'));
return $return;
} else {
$return['message'] = \__('Could not import feed. Please try again', 'feeds-for-youtube');
}
return $return;
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Smashballoon\Customizer;
/** @internal */
interface PreviewProvider
{
public function render($attr, $settings);
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Smashballoon\Customizer;
/** @internal */
abstract class ProxyProvider
{
public function get_settings_class()
{
}
public function get_db_settings()
{
}
}

View File

@@ -0,0 +1,650 @@
<?php
/**
* YouTube Feed Source
*
* @since 6.0
*
* Current Connected Account Data Example
*
*/
namespace Smashballoon\Customizer;
use function SmashBalloon\YoutubeFeed\Vendor\DI\value;
/** @internal */
class Source
{
const BATCH_SIZE = 10;
/**
* @var Config
*/
private $config;
public function __construct(\Smashballoon\Customizer\Config $config)
{
$this->config = $config;
}
/**
* AJAX hooks for various feed data related functionality
*
* @since 6.0
*/
public static function hooks()
{
\add_action('wp_ajax_sbi_source_builder_update', array(self::class, 'builder_update'));
\add_action('wp_ajax_sbi_source_builder_update_multiple', array(self::class, 'builder_update_multiple'));
\add_action('wp_ajax_sbi_source_get_page', array(self::class, 'get_page'));
\add_action('wp_ajax_sbi_source_get_featured_post_preview', array(self::class, 'get_featured_post_preview'));
\add_action('wp_ajax_sbi_source_get_playlist_post_preview', array(self::class, 'get_playlist_post_preview'));
\add_action('admin_init', array(self::class, 'batch_process_legacy_source_queue'));
}
/**
* Used in an AJAX call to update sources based on selections or
* input from a user. Makes an API request to add additiona info
* about the connected source.
*
* @since 6.0
*/
public static function builder_update()
{
if (!\check_ajax_referer('sbi_admin_nonce', 'nonce', \false) && !\check_ajax_referer('sby-admin', 'nonce', \false)) {
\wp_send_json_error();
}
if (!sbi_current_user_can('manage_instagram_feed_options')) {
\wp_send_json_error();
}
$source_data = array('access_token' => \sanitize_text_field($_POST['access_token']), 'id' => \sanitize_text_field($_POST['id']), 'type' => \sanitize_text_field($_POST['type']), 'username' => isset($_POST['username']) ? \sanitize_text_field($_POST['username']) : '');
$return = sbi_connect_new_account($source_data['access_token'], $source_data['id']);
if (empty($return['error'])) {
\wp_send_json_success(\Smashballoon\Customizer\SBI_Feed_Builder::get_source_list());
}
\wp_send_json_error(array('message' => $return['error']));
}
/**
* Add our update a source from raw API data.
*
* @param $source_data
*
* @return string
*/
public static function process_connecting_source_data($source_data)
{
$connected_account = array('id' => $source_data['id'], 'user_id' => $source_data['id'], 'type' => $source_data['type'], 'account_type' => $source_data['type'], 'username' => $source_data['username'], 'access_token' => $source_data['access_token'], 'privilege' => $source_data['privilege']);
$single_source = self::update_single_source($connected_account, \false);
if (!empty($single_source['error'])) {
$message = !empty($single_source['error']['error']['message']) ? \esc_html($single_source['error']['error']['message']) : '';
$code = !empty($single_source['error']['error']['code']) ? \esc_html($single_source['error']['error']['code']) : '';
if (isset($single_source['error']['response']) && \is_wp_error($single_source['error']['response'])) {
$response = $single_source['error']['response'];
$message = \sprintf(\__('Error connecting to %s.', 'feeds-for-youtube'), $single_source['error']['url']);
if (isset($response) && isset($response->errors)) {
foreach ($response->errors as $key => $item) {
$code = $key;
$message .= $item[0];
}
}
}
$message .= ' <a href="https://smashballoon.com/instagram-feed/docs/errors/" target="_blank" rel="noopener">' . \__('Directions on how to resolve this issue', 'feeds-for-youtube') . '</a>';
$return_html = '<div class="sb-alerts-wrap"><div class="sb-alert">';
$return_html .= '<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">';
$return_html .= '<path d="M8.99935 0.666504C4.39935 0.666504 0.666016 4.39984 0.666016 8.99984C0.666016 13.5998 4.39935 17.3332 8.99935 17.3332C13.5993 17.3332 17.3327 13.5998 17.3327 8.99984C17.3327 4.39984 13.5993 0.666504 8.99935 0.666504ZM9.83268 13.1665H8.16602V11.4998H9.83268V13.1665ZM9.83268 9.83317H8.16602V4.83317H9.83268V9.83317Z" fill="#995C00"/>';
$return_html .= '</svg>';
$return_html .= '<span><strong>' . \sprintf(\__('Connection Error: %s ', 'feeds-for-youtube'), $code) . '</strong></span><br>' . $message . '</div></div>';
$return = array('success' => \false, 'message' => $return_html);
return \json_encode($return);
} else {
global $sb_instagram_posts_manager;
$sb_instagram_posts_manager->remove_error('connection');
}
$manager = new \SmashBalloon\YoutubeFeed\Vendor\SB_Instagram_Data_Manager();
$manager->update_last_used();
return \json_encode(\Smashballoon\Customizer\SBI_Feed_Builder::get_source_list());
}
/**
* Used in an AJAX call to update Multiple sources based on selections or
* input from a user. Makes an API request to add additiona info
* about the connected source.
*
* @since 6.0
*/
public static function builder_update_multiple()
{
if (!\check_ajax_referer('sbi_admin_nonce', 'nonce', \false) && !\check_ajax_referer('sby-admin', 'nonce', \false)) {
\wp_send_json_error();
}
if (!sbi_current_user_can('manage_instagram_feed_options')) {
\wp_send_json_error();
}
if (isset($_POST['sourcesList']) && !empty($_POST['sourcesList']) && \is_array($_POST['sourcesList'])) {
foreach ($_POST['sourcesList'] as $single_source) {
$source_data = array('access_token' => \sanitize_text_field($single_source['access_token']), 'id' => \sanitize_text_field($single_source['id']), 'type' => \sanitize_text_field($single_source['type']), 'username' => isset($single_source['username']) ? \sanitize_text_field($single_source['username']) : '');
if ($single_source['type'] === 'business') {
$source_data['privilege'] = 'tagged';
}
if (!empty($single_source['name'])) {
$source_data['name'] = \sanitize_text_field($single_source['name']);
}
self::process_connecting_source_data($source_data);
}
}
echo \json_encode(\Smashballoon\Customizer\SBI_Feed_Builder::get_source_list());
\wp_die();
}
/**
* Get a list of sources with a limit and offset like a page
*
* @since 6.0
*/
public static function get_page()
{
if (!\check_ajax_referer('sbi_admin_nonce', 'nonce', \false) && !\check_ajax_referer('sby-admin', 'nonce', \false)) {
\wp_send_json_error();
}
if (!sbi_current_user_can('manage_instagram_feed_options')) {
\wp_send_json_error();
}
$args = array('page' => $_POST['page']);
$source_data = \Smashballoon\Customizer\SBI_Db::source_query($args);
echo \json_encode($source_data);
\wp_die();
}
/**
* Connection URLs are based on the website connecting accounts so that is
* configured here and returned
*
* @param bool $is_settings
*
* @return array
*
* @since 6.0
*/
public function get_connection_urls($is_settings = \false)
{
$urls = array();
$admin_url_state = $is_settings ? \admin_url(\sprintf('admin.php?page=%s-settings', $this->config->plugin_slug)) : \admin_url(\sprintf('admin.php?page=%s-feed-builder', $this->config->plugin_slug));
//If the admin_url isn't returned correctly then use a fallback
if ($admin_url_state === \sprintf('/wp-admin/admin.php?page=%s-feed-builder', $this->config->plugin_slug) || $admin_url_state === \sprintf('/wp-admin/admin.php?page=%s-feed-builder&tab=configuration', $this->config->plugin_slug)) {
$admin_url_state = "http://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";
}
#$urls['personal'] ='https://connect.smashballoon.com/auth/ig/?wordpress_user=&v=pro&vn=' . SBYVER . '&state=';
#$urls['business'] = 'https://connect.smashballoon.com/auth/ig/?wordpress_user=&v=pro&vn=' . SBYVER . '&state=';
$urls['personal'] = 'https://connect.smashballoon.com/auth/ig/?wordpress_user=&v=pro&vn=' . SBYVER;
$urls['business'] = 'https://connect.smashballoon.com/auth/ig/?wordpress_user=&v=pro&vn=' . SBYVER;
$urls['stateURL'] = $admin_url_state;
return $urls;
}
/**
* Used as a listener for the account connection process. If
* data is returned from the account connection processed it's used
* to generate the list of possible sources to chose from.
*
* @return array|bool
*
* @since 6.0
*/
public static function maybe_source_connection_data()
{
if (isset($_GET['sbi_access_token']) && isset($_GET['sbi_graph_api'])) {
$return = self::retrieve_available_business_accounts();
return $return;
} elseif (isset($_GET['sbi_access_token']) && isset($_GET['sbi_account_type'])) {
$return = self::retrieve_available_personal_accounts();
return $return;
}
return \false;
}
/**
* Uses the Instagram Basic Display API to get available personal
* accounts
*
* @return array|bool
*
* @since 6.0
*/
public static function retrieve_available_personal_accounts()
{
$encryption = new \SmashBalloon\YoutubeFeed\Vendor\SB_Instagram_Data_Encryption();
$return = array('type' => 'personal', 'unconnectedAccounts' => array(), 'matchingExistingAccounts' => array(), 'didQuickUpdate' => \false);
$access_token = \sanitize_text_field($_GET['sbi_access_token']);
if (empty($access_token)) {
return array();
}
$user_id = \sanitize_text_field($_GET['sbi_id']);
$user_name = \sanitize_text_field($_GET['sbi_username']);
$expires_in = (int) $_GET['sbi_expires_in'];
$expires_timestamp = \time() + $expires_in;
$source_data = array('access_token' => $access_token, 'id' => $user_id, 'user_id' => $user_id, 'type' => 'basic', 'username' => $user_name, 'privilege' => '', 'expires' => \date('Y-m-d H:i:s', $expires_timestamp));
$connection = new \SmashBalloon\YoutubeFeed\Vendor\SB_Instagram_API_Connect($source_data, 'header', array());
$connection->connect();
$header_details = '{}';
$source_data['error'] = '';
if (!$connection->is_wp_error() && !$connection->is_instagram_error()) {
$header_details_array = $connection->get_data();
$header_details_array = self::merge_account_details($header_details_array, $source_data);
$source_data['username'] = $header_details_array['username'];
$header_details = \json_encode($header_details_array);
} else {
$source_data['error'] = $connection;
if ($connection->is_wp_error()) {
$page_error = $connection->get_wp_error();
if (!empty($page_error) && isset($page_error['response']->errors)) {
$error_message = '';
foreach ($page_error['response']->errors as $key => $item) {
$error_message .= $key . ': ' . $item[0] . ' ';
}
return array('error' => array('code' => 'HTTP Request', 'message' => $error_message, 'details' => $error_message));
}
} else {
$error = $connection->get_data();
return array('error' => array('code' => $error['error']['code'], 'message' => $error['error']['message'], 'details' => $error['error']['message']));
}
}
$source_data['info'] = $header_details;
$return['unconnectedAccounts'][] = $source_data;
$args = array('id' => $user_id);
$results = \Smashballoon\Customizer\SBI_Db::source_query($args);
$already_connected_as_business_account = isset($results[0]) && $results[0]['account_type'] === 'business';
$matches_existing_personal = isset($results[0]) && $results[0]['account_type'] === 'personal';
if ($already_connected_as_business_account) {
$return['matchingExistingAccounts'] = $results[0];
$instagram_account_data = \json_decode($encryption->decrypt($results[0]['info']), \true);
$return['matchingExistingAccounts']['avatar'] = isset($instagram_account_data['profile_picture_url']) ? $instagram_account_data['profile_picture_url'] : \false;
$return['notice'] = \__('The Instagram account you are logged into is already connected as a "business" account. Remove the business account if you\'d like to connect as a basic account instead (not recommended).', 'feeds-for-youtube');
} elseif ($matches_existing_personal) {
$return['matchingExistingAccounts'] = $results[0];
self::update_or_insert($source_data);
$return['notice'] = '';
$return['didQuickUpdate'] = \true;
} else {
self::update_or_insert($source_data);
$return['didQuickUpdate'] = \true;
}
return $return;
}
/**
* Uses the Facebook API to retrieve a list of business accounts
*
* @return array|bool
*
* @since 6.0
*/
public static function retrieve_available_business_accounts()
{
$return = array('type' => 'business', 'unconnectedAccounts' => array(), 'matchingExistingAccounts' => array(), 'didQuickUpdate' => \false);
$access_token = sbi_maybe_clean(\urldecode($_GET['sbi_access_token']));
if (empty($access_token)) {
return array();
}
$url = 'https://graph.facebook.com/me/accounts?fields=instagram_business_account,access_token&limit=500&access_token=' . $access_token;
$args = array('timeout' => 60);
$result = \wp_remote_get($url, $args);
$pages_data = '{}';
if (!\is_wp_error($result)) {
$pages_data = $result['body'];
} else {
$page_error = $result;
}
if (isset($page_error) && isset($page_error->errors)) {
$error_message = '';
foreach ($page_error->errors as $key => $item) {
$error_message .= $key . ': ' . $item[0] . ' ';
}
return array('error' => array('code' => 'HTTP Request', 'message' => \__('Your server could not complete a remote request to Facebook\'s API. Your host may be blocking access or there may be a problem with your server.', 'feeds-for-youtube'), 'details' => $error_message));
}
$pages_data_arr = \json_decode($pages_data, \true);
if (empty($pages_data_arr['data'])) {
return array('error' => array('code' => 'No Accounts Found', 'message' => \__('Couldn\'t find Business Profile', 'feeds-for-youtube'), 'details' => \sprintf(\__('Uh oh. It looks like this Facebook account is not currently connected to an Instagram Business profile. Please check that you are logged into the %1$sFacebook account%2$s in this browser which is associated with your Instagram Business Profile.', 'feeds-for-youtube'), '<a href="https://www.facebook.com/" target="_blank" rel="noopener noreferrer">', '</a>')));
}
$user_url = 'https://graph.facebook.com/me?fields=name,id,picture&access_token=' . $access_token;
$args = array('timeout' => 60);
$result = \wp_remote_get($user_url, $args);
if (!\is_wp_error($result)) {
$user_data = $result['body'];
$user_data_arr = \json_decode($user_data, \true);
$return['user'] = $user_data_arr;
}
$return['numFound'] = \count($pages_data_arr['data']);
foreach ($pages_data_arr['data'] as $page_data) {
if (isset($page_data['instagram_business_account'])) {
$instagram_business_id = $page_data['instagram_business_account']['id'];
$page_access_token = isset($page_data['access_token']) ? $page_data['access_token'] : '';
$instagram_account_url = 'https://graph.facebook.com/' . $instagram_business_id . '?fields=name,username,profile_picture_url&access_token=' . $access_token;
$args = array('timeout' => 60);
$result = \wp_remote_get($instagram_account_url, $args);
if (!\is_wp_error($result)) {
$instagram_account_info = $result['body'];
$instagram_account_data = \json_decode($instagram_account_info, \true);
$instagram_biz_img = isset($instagram_account_data['profile_picture_url']) ? $instagram_account_data['profile_picture_url'] : \false;
$source_data = array('access_token' => $access_token, 'id' => $instagram_business_id, 'user_id' => $instagram_business_id, 'type' => 'business', 'username' => $instagram_account_data['username'], 'avatar' => $instagram_biz_img, 'privilege' => 'tagged');
$source_data['info'] = \json_encode($instagram_account_data);
$return['unconnectedAccounts'][] = $source_data;
$args = array('id' => $instagram_business_id);
$results = \Smashballoon\Customizer\SBI_Db::source_query($args);
$already_connected_as_business_account = isset($results[0]) && $results[0]['account_type'] === 'business';
$matches_existing_personal = isset($results[0]) && $results[0]['account_type'] === 'personal';
if ($already_connected_as_business_account) {
self::update_or_insert($source_data);
} elseif ($matches_existing_personal && $return['numFound'] === 1) {
$return['didQuickUpdate'] = \true;
self::update_or_insert($source_data);
}
} else {
$page_error = $result;
}
}
}
if (empty($return['unconnectedAccounts'])) {
return array('error' => array('code' => 'No Accounts Found', 'message' => \__('Couldn\'t find Business Profile', 'feeds-for-youtube'), 'details' => \sprintf(\__('Uh oh. It looks like this Facebook account is not currently connected to an Instagram Business profile. Please check that you are logged into the %1$sFacebook account%2$s in this browser which is associated with your Instagram Business Profile. If you are, in fact, logged-in to the correct account please make sure you have Instagram accounts connected with your Facebook account by following %3$sthis FAQ%4$s', 'feeds-for-youtube'), '<a href="https://www.facebook.com/" target="_blank" rel="noopener noreferrer">', '</a>', '<a href="https://smashballoon.com/reconnecting-an-instagram-business-profile/" target="_blank" rel="noopener noreferrer">', '</a>')));
}
return $return;
}
/**
* Used to update or insert connected accounts (sources)
*
* @param array $source_data
*
* @return bool
*
* @since 6.0
*/
public static function update_or_insert($source_data)
{
if (!isset($source_data['id'])) {
return \false;
}
if (isset($source_data['info'])) {
// data from an API request related to the source is saved as a JSON string
if (\is_object($source_data['info']) || \is_array($source_data['info'])) {
$source_data['info'] = \json_encode($source_data['info']);
}
}
if (self::exists_in_database($source_data)) {
$source_data['last_updated'] = \date('Y-m-d H:i:s');
self::update($source_data, \false);
} else {
if (!isset($source_data['access_token'])) {
return \false;
}
self::insert($source_data);
}
return \true;
}
/**
* Whether or not the source exists in the database
*
* @param array $args
*
* @return bool
*
* @since 6.0
*/
public static function exists_in_database($args)
{
$results = \Smashballoon\Customizer\SBI_Db::source_query($args);
return isset($results[0]);
}
/**
* Add a new source as a row in the sbi_sources table
*
* @param array $source_data
*
* @return false|int
*
* @since 6.0
*/
public static function insert($source_data)
{
if (isset($source_data['name'])) {
$source_data['username'] = $source_data['name'];
}
$data = $source_data;
return \Smashballoon\Customizer\SBI_Db::source_insert($data);
}
/**
* Update info in rows that match the source data
*
* @param array $source_data
*
* @return false|int
*
* @since 6.0
*/
public static function update($source_data, $where_privilige = \true)
{
$where = array('id' => $source_data['id']);
unset($source_data['id']);
if ($where_privilige && isset($source_data['privilege'])) {
$where['privilege'] = $source_data['privilege'];
}
// usernames are more common in the other plugins so
// that is the name of the column that is used as the
// page or group "name" data
if (isset($source_data['name'])) {
$source_data['username'] = $source_data['name'];
}
$data = $source_data;
return \Smashballoon\Customizer\SBI_Db::source_update($data, $where);
}
/**
* Creates a queue of connected accounts that need to be added to
* the sources table
*
* @since 6.0
*/
public static function set_legacy_source_queue()
{
$sbi_statuses_option = \get_option('sbi_statuses', array());
$options = \get_option('sb_instagram_settings', array());
$connected_accounts = isset($options['connected_accounts']) ? $options['connected_accounts'] : array();
$sbi_statuses_option['legacy_source_queue'] = \array_chunk(\array_keys($connected_accounts), self::BATCH_SIZE);
\update_option('sbi_statuses', $sbi_statuses_option);
return $sbi_statuses_option['legacy_source_queue'];
}
/**
* Whether or not there are still sources in the queue and
* this isn't disabled
*
* @return bool
*
* @since 6.0
*/
public static function should_do_source_updates()
{
$sbi_statuses_option = \get_option('sbi_statuses', array());
$should_do_source_updates = isset($sbi_statuses_option['legacy_source_queue']) ? !empty($sbi_statuses_option['legacy_source_queue']) : \false;
return \apply_filters('should_do_source_updates', $should_do_source_updates);
}
/**
* Processes one set of connected accounts
*
* @since 6.0
*/
public static function batch_process_legacy_source_queue()
{
if (!self::should_do_source_updates()) {
return;
}
$sbi_statuses_option = \get_option('sbi_statuses', array());
$batch = \array_shift($sbi_statuses_option['legacy_source_queue']);
\update_option('sbi_statuses', $sbi_statuses_option);
// updated early just in case there is a fatal error
if (empty($batch)) {
return;
}
$options = \get_option('sb_instagram_settings', array());
$connected_accounts = isset($options['connected_accounts']) ? $options['connected_accounts'] : array();
foreach ($batch as $account_key) {
$connected_account = isset($connected_accounts[$account_key]) ? $connected_accounts[$account_key] : \false;
if ($connected_account) {
self::update_single_source($connected_account);
}
}
return $sbi_statuses_option['legacy_source_queue'];
}
/**
* Transfer data from a connected account to the sources table
* after it's been validated with an API call
*
* @param array $connected_account
* @param bool $connect_if_error
*
* @return array
*
* @since 6.0
*/
public static function update_single_source($connected_account, $connect_if_error = \true)
{
$account_type = isset($connected_account['account_type']) ? $connected_account['account_type'] : 'business';
$connection = new \SmashBalloon\YoutubeFeed\Vendor\SB_Instagram_API_Connect($connected_account, 'header', array());
$connection->connect();
if (isset($connected_account['privilege']) && $connected_account['privilege'] === 'tagged') {
$connected_account['use_tagged'] = \true;
}
$source_data = array('access_token' => $connected_account['access_token'], 'id' => $connected_account['user_id'], 'type' => $account_type, 'username' => $connected_account['username'], 'privilege' => !empty($connected_account['use_tagged']) ? 'tagged' : '');
if (!empty($connected_account['expires_timestamp'])) {
$source_data['expires'] = \date('Y-m-d H:i:s', $connected_account['expires_timestamp']);
}
if ($connected_account['local_avatar']) {
\SmashBalloon\YoutubeFeed\Vendor\SB_Instagram_Connected_Account::update_local_avatar_status($connected_account['username'], \true);
}
$header_details = '{}';
$source_data['error'] = '';
if (!$connection->is_wp_error() && !$connection->is_instagram_error()) {
$header_details_array = $connection->get_data();
$header_details_array = self::merge_account_details($header_details_array, $connected_account);
$cdn_avatar_url = \SmashBalloon\YoutubeFeed\Vendor\SB_Instagram_Parse::get_avatar($header_details_array, array(), \true);
if (!empty($cdn_avatar_url)) {
$created = \SmashBalloon\YoutubeFeed\Vendor\SB_Instagram_Connected_Account::create_local_avatar($header_details_array['username'], $cdn_avatar_url);
\SmashBalloon\YoutubeFeed\Vendor\SB_Instagram_Connected_Account::update_local_avatar_status($header_details_array['username'], $created);
}
$source_data['username'] = $header_details_array['username'];
$header_details = \json_encode($header_details_array);
} else {
$source_data['error'] = $connection;
if ($connection->is_wp_error()) {
$source_data['error'] = $connection->get_wp_error();
} else {
$source_data['error'] = $connection->get_data();
}
}
$source_data['info'] = $header_details;
if (!empty($connected_account['private'])) {
$source_data['info']['private'] = $connected_account['private'];
}
if (empty($source_data['error']) || $connect_if_error) {
self::update_or_insert($source_data);
}
$source_data['record_id'] = 0;
$source_data['account_id'] = $connected_account['user_id'];
$source_data['account_type'] = $account_type;
return $source_data;
}
/**
* Creates a source from the access token and
* source ID saved in 3.x settings
*
* @since 6.0
*/
public static function update_source_from_legacy_settings()
{
// not needed
}
public static function merge_account_details($header_details_array, $connected_account)
{
$header_details_array['local_avatar'] = !empty($connected_account['local_avatar']);
$header_details_array['name'] = !empty($connected_account['name']) ? $connected_account['name'] : '{}';
$header_details_array['page_access_token'] = !empty($connected_account['page_access_token']) ? $connected_account['page_access_token'] : '';
return $header_details_array;
}
/**
* If the plugin is still updating legacy sources this function
* can be used to udpate a single source if needed before
* the update is done.
*
* @param string $slug_or_id
*
* @return array|bool
*/
public static function maybe_one_off_connected_account_update($slug_or_id)
{
if (!self::should_do_source_updates()) {
return \false;
}
$connected_accounts = (array) \json_decode(\stripcslashes(\get_option('sbi_connected_accounts')), \true);
$connected_account = isset($connected_accounts[$slug_or_id]) ? $connected_accounts[$slug_or_id] : \false;
if ($connected_account) {
return self::update_single_source($connected_account);
}
return \false;
}
/**
* Clears the "error" column in the sbi_sources table for a specific
* account
*
* @param string $account_id
*
* @return bool
*
* @since 6.0
*/
public static function clear_error($account_id)
{
$source_data = array('id' => $account_id, 'error' => '');
return self::update_or_insert($source_data);
}
/**
* Adds an error to the error table by account ID
*
* @param string $account_id
* @param string|object|array $error
*
* @return bool
*
* @since 6.0
*/
public static function add_error($account_id, $error)
{
$source_data = array('id' => $account_id, 'error' => \is_string($error) ? $error : \json_encode($error));
return self::update_or_insert($source_data);
}
/**
* Uses query results from the sbi_sources table to convert them
* into connected account data and return them as a connected account
* array as would be used in versions 5.x and below
*
* @param array $source_data
*
* @return array
*
* @since 6.0
*/
public static function convert_sources_to_connected_accounts($source_data)
{
$encryption = new \SmashBalloon\YoutubeFeed\Vendor\SB_Instagram_Data_Encryption();
$connected_accounts = array();
foreach ($source_data as $source_datum) {
$info = !empty($source_datum['info']) ? \json_decode($encryption->decrypt($source_datum['info']), \true) : array();
$settings = array('gdpr' => 'no');
$avatar = \SmashBalloon\YoutubeFeed\Vendor\SB_Instagram_Parse::get_avatar($info, $settings, \true);
$connected_account = array('id' => $source_datum['account_id'], 'user_id' => $source_datum['account_id'], 'type' => $source_datum['account_type'], 'account_type' => $source_datum['account_type'], 'username' => $source_datum['username'], 'access_token' => sbi_maybe_clean($source_datum['access_token']), 'privilege' => $source_datum['privilege'], 'expires_timestamp' => \strtotime($source_datum['expires']), 'is_valid' => empty($source_datum['error']), 'profile_picture' => $avatar, 'last_checked' => isset($source_datum['last_updated']) ? \strtotime($source_datum['last_updated']) : \time());
if (!empty($info['private'])) {
$connected_account['private'] = $info['private'];
}
$connected_account['local_avatar_url'] = \SmashBalloon\YoutubeFeed\Vendor\SB_Instagram_Connected_Account::maybe_local_avatar($source_datum['username'], $avatar);
$connected_accounts[$source_datum['account_id']] = $connected_account;
}
return $connected_accounts;
}
/**
* Returns a batch of accounts that have expiring access tokens
*
* @return array|bool
*
* @since 6.0
*/
public static function get_expiring()
{
$args = array('expiring' => \true);
$results = \Smashballoon\Customizer\SBI_Db::source_query($args);
return $results;
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace Smashballoon\Customizer\Tabs;
use Smashballoon\Stubs\Traits\Singleton;
/** @internal */
class Manager
{
use Singleton;
/**
* @var Tab[]
*/
private $tabs = [];
/**
* @param Tab $tab
*
* @return void
*/
public function register_tab(\Smashballoon\Customizer\Tabs\Tab $tab)
{
$this->tabs[] = $tab;
}
/**
* @return array
*/
public function get_tabs()
{
$tabs = [];
foreach ($this->tabs as $tab) {
$section_id = $tab->get_id();
$tabs[$section_id] = ['id' => $section_id, 'heading' => $tab->get_heading(), 'sections' => $tab->get_sections()];
}
return $tabs;
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Smashballoon\Customizer\Tabs;
/** @internal */
abstract class Tab
{
protected $id;
protected $heading;
public function get_sections()
{
return [];
}
public function get_id()
{
return $this->id;
}
public function get_heading()
{
return $this->heading;
}
}

View File

@@ -0,0 +1,106 @@
<?php
/**
* YouTube License Tier
*
* @since 2.1
*/
namespace Smashballoon\Customizer;
use Smashballoon\Framework\Packages\License_Tier\License_Tier;
/** @internal */
class YouTube_License_Tier extends License_Tier
{
/**
* This gets the license key
*/
public $license_key_option_name = 'sby_license_key';
/**
* This gets the license status
*/
public $license_status_option_name = 'sby_license_status';
/**
* This gets the license data
*/
public $license_data_option_name = 'sby_license_data';
/**
* Item IDs
*/
public $item_id_basic = 1722787;
public $item_id_plus = 1722791;
public $item_id_elite = 1722793;
public $item_id_all_access_elite = 1724078;
/**
* Legacy item IDs
*/
public $item_id_personal = 762236;
// Item id for the personal tier.
public $item_id_business = 762320;
// Item id for the business tier.
public $item_id_developer = 762322;
// Item id for the developer tier.
public $item_id_all_access = 789157;
/**
* Tier names
*/
public $license_tier_free_name = 'free';
// Basic tier name.
public $license_tier_basic_name = 'basic';
// Basic tier name.
public $license_tier_plus_name = 'plus';
// Plus tier name.
public $license_tier_elite_name = 'elite';
// Elite tier name.
/**
* Legacy tier names
*/
public $license_tier_personal_name = 'personal';
// Personal tier name.
public $license_tier_business_name = 'business';
// Business tier name.
public $license_tier_developer_name = 'developer';
// Developer tier name.
public $edd_item_name = SBY_PLUGIN_EDD_NAME;
public function __construct()
{
parent::__construct();
}
/**
* This defines the features list of the plugin
*
* @return void
*/
public function features_list()
{
$features_list = ['free' => ['channel_feeds'], 'basic' => ['channel_feeds', 'favorites_feeds', 'carousel_feeds', 'combine_feeds', 'performance_optimization', 'downtime_prevention_system', 'gbpr_compliant', 'playlist_feeds', 'single_feeds'], 'plus' => ['call_to_actions', 'search_feeds', 'feeds_templates', 'convert_videos_to_cpt'], 'elite' => ['live_feeds', 'video_filtering', 'feed_themes']];
$this->plugin_features = $features_list;
}
/**
* This defines features for legacy tiers
*
* @return void
*/
public function legacy_features_list()
{
$legacy_features = ['personal' => [
// List of features for personal tier.
'channel_feeds',
'favorites_feeds',
'playlist_feeds',
'carousel_feeds',
'combine_feeds',
'performance_optimization',
'downtime_prevention_system',
'gbpr_compliant',
'call_to_actions',
'search_feeds',
'single_feeds',
'feeds_templates',
'convert_videos_to_cpt',
'live_feeds',
'video_filtering',
'feed_themes',
], 'business' => [], 'developer' => []];
$this->legacy_features = $legacy_features;
}
}