/
www
/
wwwroot
/
alo88.autos
/
wp-content
/
plugins
/
wp-content-crawler
/
app
/
PostDetail
/
Upload File
HOME
<?php /** * Created by PhpStorm. * User: turgutsaricam * Date: 30/11/2018 * Time: 08:33 */ namespace WPCCrawler\PostDetail; use Exception; use WPCCrawler\Exceptions\DuplicatePostException; use WPCCrawler\Exceptions\StopSavingException; use WPCCrawler\Factory; use WPCCrawler\Objects\Crawling\Bot\PostBot; use WPCCrawler\Objects\Crawling\Data\PostData; use WPCCrawler\Objects\Crawling\Data\PostSaverData; use WPCCrawler\Objects\Crawling\Preparers\TransformablePreparer; use WPCCrawler\Objects\Informing\Informer; use WPCCrawler\Objects\Settings\Enums\SettingKey; use WPCCrawler\Objects\Settings\SettingsImpl; use WPCCrawler\Objects\ShortCode\ShortCodeApplier; use WPCCrawler\Objects\Transformation\Base\AbstractTransformationService; use WPCCrawler\Objects\Transformation\Base\AbstractTransformer; use WPCCrawler\Objects\Transformation\Interfaces\Transformable; use WPCCrawler\PostDetail\Base\BasePostDetailData; use WPCCrawler\PostDetail\Base\BasePostDetailDeleter; use WPCCrawler\PostDetail\Base\BasePostDetailFactory; use WPCCrawler\PostDetail\Base\BasePostDetailPreparer; use WPCCrawler\PostDetail\Base\BasePostDetailSaver; use WPCCrawler\PostDetail\Base\BasePostDetailService; use WPCCrawler\PostDetail\Base\BasePostDetailSettings; use WPCCrawler\PostDetail\Base\BasePostDetailTester; use WPCCrawler\Utils; /** * This class is a mediator between the client and the post detail package. * * @package WPCCrawler\PostDetail * @since 1.8.0 */ class PostDetailsService { /** @var PostDetailsService */ private static $instance = null; /** * Get the instance. * * @return PostDetailsService * @since 1.8.0 */ public static function getInstance() { if (static::$instance === null) static::$instance = new PostDetailsService(); return static::$instance; } /** This is a singleton */ protected function __construct() { } /** * Registers custom post detail factories * @since 1.8.0 */ public function registerCustomFactories(): void { // Register custom factories. /** @var string[] $factoryNames */ $factoryNames = []; /** * Register a custom post detail factory. * * @param string[] $factoryNames Already registered factory names * @return string[] An array of names of classes that extend {@link BasePostDetailFactory}. These classes will * be registered to the plugin. * @since 1.8.0 */ $factoryNames = apply_filters('wpcc/post/detail/register-factory', $factoryNames); // Register custom factories. $this->registerFactoryByName($factoryNames); } /** * Register a post detail factory so that it can be used in the plugin when necessary. * * @param string|string[] $factoryClass Name(s) of a class that extends {@link BasePostDetailFactory} * @since 1.8.0 */ public function registerFactoryByName($factoryClass): void { BasePostDetailFactory::registerFactoryByName($factoryClass); } /** * @param string $factoryClass See {@link BasePostDetailFactory::getFactoryInstance()} * @param PostBot|null $postBot See {@link BasePostDetailFactory::getFactoryInstance()} * @return BasePostDetailFactory|mixed * @throws Exception * @uses BasePostDetailFactory::getFactoryInstance() * @since 1.9.0 */ public function getFactoryInstance($factoryClass, $postBot = null) { return BasePostDetailFactory::getFactoryInstance($factoryClass, $postBot); } /** * Adds settings meta keys to the given array * * @param array $allKeys Already-existing settings meta keys. * @return array Settings meta keys with the meta keys retrieved from the post details added * @since 1.8.0 */ public function addAllSettingsMetaKeys($allKeys = []) { return $this->addMetaKeys(function($settings) { /** @var BasePostDetailSettings $settings */ return $settings->getAllMetaKeys(); }, $allKeys); } /** * Adds default values for settings meta keys to the given array * * @param array $allKeys Already-existing defaults. See {@link PostService::$metaKeyDefaults} * @return array The given defaults with defaults retrieved from post details are added * @since 1.8.0 */ public function addAllSettingsMetaKeyDefaults($allKeys = []) { return $this->addMetaKeys(function($settings) { /** @var BasePostDetailSettings $settings */ return $settings->getMetaKeyDefaults(); }, $allKeys); } /** * Adds single settings meta keys to the given array. * * @param array $singleKeys Already-existing single settings meta keys. * @return array Settings meta keys with the meta keys retrieved from the post details added * @since 1.8.0 */ public function addAllSingleSettingsMetaKeys($singleKeys = []) { return $this->addMetaKeys(function($settings) { /** @var BasePostDetailSettings $settings */ return $settings->getSingleMetaKeys(); }, $singleKeys); } /** * @param SettingsImpl|array|null $postSettings * @return array A key-value pair. The keys are meta keys of the settings. The values are arrays storing the * configuration for the options box for that setting. * @since 1.8.0 */ public function getOptionsBoxConfigs($postSettings) { // Get a valid instance from the given value $postSettings = $this->getPostSettingsImplInstance($postSettings); $allConfigs = []; $this->walkRegisteredFactories(function($factory) use (&$postSettings, &$allConfigs) { /** @var BasePostDetailFactory $factory */ // Check availability for post if (!$factory->isAvailableForPost($postSettings)) return; // Get the service $service = $factory->getService(); if (!$service || !is_a($service, BasePostDetailService::class)) return; // Get the configs $configs = $service->getOptionsBoxConfigs(); if (!$configs) return; $allConfigs = array_merge($allConfigs, $configs); }); return $allConfigs ?: []; } /** * Prepares the registered post details. * * @param null|PostBot $postBot * @since 1.8.0 */ public function preparePostDetails($postBot): void { // Get a valid instance from the given value $postSettings = $this->getPostSettingsImplInstanceFromPostBot($postBot); // Prepare the registered post details $this->walkRegisteredFactories(function($factory) use (&$postSettings) { /** @var BasePostDetailFactory $factory */ // Check availability for post if (!$factory->isAvailableForPost($postSettings)) return; // Get the preparer $preparer = $factory->getDetailPreparer(); if (!$preparer || !is_a($preparer, BasePostDetailPreparer::class)) return; // Prepare $preparer->prepare(); // Add the detail data to the post data so that it can be reached later $preparer->getBot()->getPostData()->addDetailData($preparer->getDetailData()); }, $postBot); } /** * Prepare templates of the registered post details. * * @param PostBot $postBot * @param ShortCodeApplier $applierRegular Short code applier that uses the default brackets * @param ShortCodeApplier $applierFile Short code applier that uses the file-name-specific brackets * @param array $frForMedia Find-replace config that can be used replace media file URLs with local * URLs. * @since 1.8.0 */ public function prepareTemplates($postBot, ShortCodeApplier $applierRegular, ShortCodeApplier $applierFile, $frForMedia): void { // Get a valid instance from the given value $postSettings = $this->getPostSettingsImplInstanceFromPostBot($postBot); // Prepare templates defined in Options Boxes of other post details implementations $this->walkRegisteredFactories(function($factory) use (&$postSettings, $applierRegular, $applierFile, &$frForMedia) { /** @var BasePostDetailFactory $factory */ // Check availability for post if (!$factory->isAvailableForPost($postSettings)) return; // Get the service $service = $factory->getService(); if (!$service || !is_a($service, BasePostDetailService::class)) return; // Apply short codes $service->applyShortCodes($factory->getDetailData(), $applierRegular, $applierFile, $frForMedia); }, $postBot); } /** * Prepare all interactable fields of the data, which should be an instance of {@link Transformable}, of all post * details. * * @param PostBot $postBot * @param callable $cbPrepare See {@link TransformablePreparer::__construct()} * @since 1.9.0 */ public function prepareDetailData($postBot, callable $cbPrepare): void { // Get a valid instance from the given value $postSettings = $this->getPostSettingsImplInstanceFromPostBot($postBot); // Prepare templates defined in Options Boxes of other post details implementations $this->walkRegisteredFactories(function($factory) use (&$postSettings, &$cbPrepare) { /** @var BasePostDetailFactory $factory */ // Check availability for post if (!$factory->isAvailableForPost($postSettings)) return; // Get the data $data = $factory->getDetailData(); if (!is_a($data, Transformable::class)) return; /** @var Transformable $data */ // Prepare the data $preparer = new TransformablePreparer($data, array_keys($data->getInteractableFields()->toAssociativeArray()), $cbPrepare); $preparer->prepare(); }, $postBot); } /** * Save registered post details. * * @param PostBot $postBot * @param PostSaverData $saverData * @throws DuplicatePostException|StopSavingException * @since 1.8.0 */ public function save($postBot, $saverData): void { // Get a valid instance from the given value $postSettings = $this->getPostSettingsImplInstanceFromPostBot($postBot); // Get duplicate check options $duplicateCheckOptions = null; $duplicateCheckSettingValues = $postBot->getSetting(SettingKey::DUPLICATE_CHECK_TYPES); // The values are stored under 0 key. So, make sure 0 key exists. if($duplicateCheckSettingValues && isset($duplicateCheckSettingValues[0])) { $duplicateCheckOptions = $duplicateCheckSettingValues[0]; } /** @var null|DuplicatePostException $duplicateException */ $duplicateException = null; $this->walkRegisteredFactories(function($factory) use (&$duplicateException, &$postSettings, &$saverData, &$duplicateCheckOptions) { /** @var BasePostDetailFactory $factory */ // If there is a duplicate post found, stop. if ($duplicateException) return; // Check availability for post if (!$factory->isAvailableForPost($postSettings)) return; // Get the saver $saver = $factory->getDetailSaver($saverData); if (!$saver || !is_a($saver, BasePostDetailSaver::class)) return; try { // Save $saver->save($factory->getDuplicateChecker(), $duplicateCheckOptions); } catch (DuplicatePostException $e) { $duplicateException = $e; } }, $postBot); // If there is a duplicate post exception, throw it here. if ($duplicateException) throw $duplicateException; } /** * Get settings views of the registered post details, combined. * * @param SettingsImpl|array|null $postSettings * @param array $viewVars Variables that will be injected to the settings views of the details * @return string Rendered settings views as a single string. * @since 1.8.0 */ public function getSettingsViews($postSettings, $viewVars = []) { // Get a valid instance from the given value $postSettings = $this->getPostSettingsImplInstance($postSettings); // Add post detail settings if there are any $postDetailSettingsViews = ''; $this->walkRegisteredFactories(function($factory) use (&$postDetailSettingsViews, &$viewVars, &$postSettings) { /** @var BasePostDetailFactory $factory */ // Check availability for post if (!$factory->isAvailableForPost($postSettings)) return; // Get the detail settings. Get a fresh one because the settings instance was created before in // addMetaKeys() method with a null post settings. Here, we provide a non-null post settings. Hence, // a new instance of detail settings must be created so that it can use the post settings. If a new instance // is not created, there would not be any value in providing a non-null post settings here. So, a fresh one. $settings = $factory->getSettings($postSettings, true); if (!$settings || !is_a($settings, BasePostDetailSettings::class)) return; // Get the settings view $detailView = $settings->getSettingsView(); if (!$detailView) return; // Append to other views $postDetailSettingsViews .= $detailView->with($viewVars)->render(); }); return $postDetailSettingsViews; } /** * Adds site settings assets of each available detail factory * * @param SettingsImpl|array|null $postSettings * @since 1.8.0 */ public function addSiteSettingsAssets($postSettings): void { // Get a valid instance from the given value $postSettings = $this->getPostSettingsImplInstance($postSettings); $this->walkRegisteredFactories(function($factory) use (&$postSettings) { /** @var BasePostDetailFactory $factory */ // Check availability if (!$factory->isAvailableForPost($postSettings)) return; $service = $factory->getService(); if (!$service || !is_a($service, BasePostDetailService::class)) return; $service->addSiteSettingsAssets(); }); } /** * Get test views of the registered post details, combined. * * @param PostBot $postBot * @param PostData $postData * @param array $viewVars * @return string Rendered test views as a single string. * @since 1.8.0 */ public function getTestViews(PostBot $postBot, $postData, $viewVars = []) { // Get a valid instance from the given value $postSettings = $this->getPostSettingsImplInstanceFromPostBot($postBot); // Add views defined for the custom post details $postDetailViews = ''; $this->walkRegisteredFactories(function($factory) use (&$postData, &$postSettings, &$viewVars, &$postDetailViews) { /** @var BasePostDetailFactory $factory */ // Check availability if (!$factory->isAvailableForPost($postSettings)) return; $detailTester = $factory->getTester(); if (!$detailTester || !is_a($detailTester, BasePostDetailTester::class)) return; $detailView = $detailTester->getTesterView(); if (!$detailView) return; // Inject required variables to the view, render, and combine with other views $postDetailViews .= $detailView->with($viewVars)->with([ 'detailData' => $factory->getDetailData(), 'postData' => $postData, ])->render(); }, $postBot); return $postDetailViews; } /** * Adds site tester assets of each available detail factory * * @since 1.8.0 */ public function addSiteTesterAssets(): void { $this->walkRegisteredFactories(function($factory) { /** @var BasePostDetailFactory $factory */ $service = $factory->getService(); if (!$service || !is_a($service, BasePostDetailService::class)) return; $service->addSiteTesterAssets(); }); } /** * Get duplicate check options from the factories. * * @param SettingsImpl $postSettings * @return array|null An array having "values" and "defaults" keys, each having an array. If there is no option, * returns null. * @since 1.8.0 */ public function getDuplicateOptions(SettingsImpl $postSettings) { $allOptions = [ "values" => [], "defaults" => [], ]; $this->walkRegisteredFactories(function($factory) use (&$postSettings, &$allOptions) { /** @var BasePostDetailFactory $factory */ // Check availability if (!$factory->isAvailableForPost($postSettings)) return; // Get the duplicate checker $duplicateChecker = $factory->getDuplicateChecker(); if (!$duplicateChecker) return; // Get the options $options = $duplicateChecker->getOptions(); if (!$options) return; // Check for validity: // 1. Values must exist // 2. If defaults exist, they must have the same number of items as the values // 3. If defaults exist, the values and the defaults have to have the same keys. if (!isset($options["values"]) || (isset($options["defaults"]) && sizeof($options["values"]) !== sizeof($options["defaults"])) || (isset($options["defaults"]) && array_keys($options["values"]) !== array_keys($options["defaults"])) ) { return; } // Get the values $values = $options['values']; // Get the defaults and prepare if they do not exist $defaults = Utils::array_get($options, 'defaults'); if (!$defaults) { $defaults = []; foreach($values as $k => $v) { $defaults[$k] = 0; } } // Add the values and defaults to all options $allOptions["values"] = array_merge($allOptions["values"], $values); $allOptions["defaults"] = array_merge($allOptions["defaults"], $defaults); }); // If the values array is not empty, return the results. Otherwise, return null. return !empty($allOptions["values"]) ? $allOptions : null; } /** * Calls the deleters of the registered factories. * * @since 1.8.0 * @param SettingsImpl $postSettings * @param PostSaverData|null $saverData */ public function delete(SettingsImpl $postSettings, $saverData): void { $this->walkRegisteredFactories(function($factory) use (&$postSettings, $saverData) { /** @var BasePostDetailFactory $factory */ // Check availability if (!$factory->isAvailableForPost($postSettings)) return; // Get the deleter $deleter = $factory->getDeleter(); if (!$deleter || !is_a($deleter, BasePostDetailDeleter::class)) return; // Check if the factory has any data $detailData = $factory->getDetailData(); if (!is_a($detailData, BasePostDetailData::class)) return; // Delete $deleter->delete($postSettings, $detailData, $saverData); }); } /** * Get category taxonomies defined by the post details * * @param null|SettingsImpl $postSettings * @return array See {@link BasePostDetailService::getCategoryTaxonomies()} * @since 1.8.0 */ public function getCategoryTaxonomies($postSettings) { $allTaxonomies = []; $this->walkRegisteredFactories(function($factory) use (&$postSettings, &$allTaxonomies) { /** @var BasePostDetailFactory $factory */ // Check availability if ($postSettings && !$factory->isAvailableForPost($postSettings)) return; // Get the service $service = $factory->getService(); if (!$service || !is_a($service, BasePostDetailService::class)) return; // Get the category taxonomies $taxonomies = $service->getCategoryTaxonomies(); if (!$taxonomies) return; // Collect them $allTaxonomies = array_merge($allTaxonomies, $taxonomies); }); return $allTaxonomies; } /** * @param PostBot $postBot * @param AbstractTransformer $transformer * @since 1.8.0 */ public function transform($postBot, $transformer): void { // Get a valid instance from the given value $postSettings = $this->getPostSettingsImplInstanceFromPostBot($postBot); $this->walkRegisteredFactories(function($factory) use (&$postSettings, &$transformer) { /** @var BasePostDetailFactory $factory */ // Check availability if (!$factory->isAvailableForPost($postSettings)) return; // Get the data $data = $factory->getDetailData(); if (!is_a($data, Transformable::class)) return; // Try to transform try { /** @var Transformable $data */ $transformer->setTransformable($data, $factory->getIdentifier()); $transformer->transform(); } catch (Exception $e) { // Inform the user about the error Informer::addError(sprintf( _wpcc('Transformation error for %1$s. Message: %2$s - Class: %3$s'), get_class($data), $e->getMessage(), get_class($transformer) ))->setException($e)->addAsLog(); } }, $postBot); } /** * Get transformable fields of registered post details * * @param SettingsImpl|array|null $postSettings * @return array A key-value pair where keys are post details' names, and the values are associative arrays. Value * arrays are structured such that the keys are transformable field dot notation keys and the values are * descriptions for them. * @since 1.9.0 */ public function getTransformableFields($postSettings) { // Get a valid instance from the given value $postSettings = $this->getPostSettingsImplInstance($postSettings); $result = []; $this->walkRegisteredFactories(function($factory) use (&$postSettings, &$result) { /** @var BasePostDetailFactory $factory */ // Check availability for post if (!$factory->isAvailableForPost($postSettings)) return; // Create a data object instance for the factory $data = $factory->getDetailData(); // Make sure it is transformable if (!is_a($data, Transformable::class)) return; /** @var Transformable $data */ $transformableFields = $data->getTransformableFields()->toAssociativeArray(); if (!$transformableFields) return; // Get the name and the identifier for this factory $name = $factory->getName(); $identifier = $factory->getIdentifier(); if (!$name || !$identifier) return; // Add the options $result[$name] = AbstractTransformationService::prepareTransformableFieldForSelect($transformableFields, $identifier); }); return $result; } /** * Get factories that have {@link Transformable} data * * @param SettingsImpl|array|null $postSettings * @return BasePostDetailFactory[] Array of factories that have {@link Transformable} data * @since 1.11.0 */ public function getTransformableFactories($postSettings) { // Get a valid instance from the given value $postSettings = $this->getPostSettingsImplInstance($postSettings); $result = []; $this->walkRegisteredFactories(function($factory) use (&$postSettings, &$result) { /** @var BasePostDetailFactory $factory */ // Check availability for post if (!$factory->isAvailableForPost($postSettings)) return; // Create a data object instance for the factory $data = $factory->getDetailData(); // Make sure it is transformable if (!is_a($data, Transformable::class)) return; /** @var Transformable $data */ $result[] = $factory; }); return $result; } /** * Invalidate all post detail factory instances * * @since 1.8.0 */ public function invalidateFactoryInstances(): void { BasePostDetailFactory::invalidateInstances(); } /* * PRIVATE METHODS */ /** * @param callable $callbackGetMetaKeys Provide the meta keys to be merged using the detail settings. Returns a * string array. For example: function(BasePostDetailSettings $settings) { * return $settings->getSingleMetaKeys() } * @param array $metaKeys Already-existing meta keys to which the meta keys retrieved from the * settings will be added. * @return array $metaKeys with the meta keys retrieved from the settings added * @since 1.8.0 */ private function addMetaKeys($callbackGetMetaKeys, $metaKeys = []) { $this->walkRegisteredFactories(function($factory) use (&$callbackGetMetaKeys, &$metaKeys) { /** @var BasePostDetailFactory $factory */ $settings = $factory->getSettings(null); if (!$settings || !is_a($settings, BasePostDetailSettings::class)) return; $detailMetaKeys = $callbackGetMetaKeys($settings); if (!$detailMetaKeys) return; $metaKeys = array_merge($metaKeys, $detailMetaKeys); }); return $metaKeys; } /** * Walks registered and available factories and calls the given callback. * * @param callable|null $callback A callback that will be called for each registered detail factory if it is available. * It takes only one parameter $factory, which is a BasePostDetailFactory, and returns * nothing. E.g. function($factory) {} * @param null|PostBot $postBot See {@link BasePostDetailFactory::getRegisteredFactoryInstances()} * @since 1.8.0 */ private function walkRegisteredFactories($callback, $postBot = null): void { if (!$callback) return; foreach(BasePostDetailFactory::getRegisteredFactoryInstances($postBot) as $factory) { if (!$factory->isAvailable()) continue; $callback($factory); } } /** * Get a post settings instance using a post bot * * @param PostBot|null $postBot * @since 1.8.0 * @return SettingsImpl */ private function getPostSettingsImplInstanceFromPostBot($postBot): SettingsImpl { if ($postBot) return $postBot->getSettingsImpl(); return $this->getPostSettingsImplInstance(null); } /** * @param SettingsImpl|array|null $postSettings * @param bool $prepare True if the settings should be prepared. Otherwise, false. * @return SettingsImpl * @since 1.8.0 */ private function getPostSettingsImplInstance($postSettings, $prepare = true): SettingsImpl { // If this is an instance, use it directly. if (is_object($postSettings) && is_a($postSettings, SettingsImpl::class)) { return $postSettings; } // Otherwise, make sure it is an array. if (!$postSettings || !is_array($postSettings)) { $postSettings = []; } // Create an instance return new SettingsImpl($postSettings, Factory::postService()->getSingleMetaKeys(), $prepare); } }