toolsData = $toolsData; $this->cardsDataStorage = $cardsDataStorage; $this->viewsDataStorage = $viewsDataStorage; $this->plugin = $plugin; $this->acfViewData = $acfViewData; $this->acfCardData = $acfCardData; $this->values = []; $this->exportData = []; $this->isImportSuccessfull = false; $this->importResultMessage = ''; } protected function isMySource($postId): bool { $screen = get_current_screen(); return !!$screen && 'acf_views_page_acf-views-tools' === $screen->id && 'options' === $postId; } protected function getPosts(string $postType, array $slugs): array { $queryArgs = [ 'fields' => 'ids', 'post_type' => $postType, 'post_status' => 'publish', 'posts_per_page' => -1, ]; if ($slugs) { $queryArgs['post_name__in'] = $slugs; } $query = new WP_Query($queryArgs); // already only IDs return $query->get_posts(); } protected function export(): void { $isViewsInExport = $this->toolsData->isExportAllViews || $this->toolsData->exportViews; $isCardsInExport = $this->toolsData->isExportAllCards || $this->toolsData->exportCards; $viewIds = $isViewsInExport ? $this->getPosts(ViewsCpt::NAME, $this->toolsData->exportViews) : []; $cardIds = $isCardsInExport ? $this->getPosts(CardsCpt::NAME, $this->toolsData->exportCards) : []; foreach ($viewIds as $viewId) { $acfViewData = $this->viewsDataStorage->get($viewId); $viewUniqueId = $acfViewData->getUniqueId(); $this->exportData[$viewUniqueId] = array_merge($acfViewData->getFieldValues(), [ '_post_title' => get_the_title($viewId), '_post_excerpt' => get_post_field('post_excerpt', $viewId), '_post_content_filtered' => get_post_field('post_content_filtered', $viewId), ]); } foreach ($cardIds as $cardId) { $acfCardData = $this->cardsDataStorage->get($cardId); $cardUniqueId = $acfCardData->getUniqueId(); $this->exportData[$cardUniqueId] = array_merge($acfCardData->getFieldValues(), [ '_post_title' => get_the_title($cardId), '_post_excerpt' => get_post_field('post_excerpt', $cardId), '_post_content_filtered' => get_post_field('post_content_filtered', $cardId), ]); } } protected function getExistingItems(array $jsonData): array { $items = []; $query = new WP_Query([ 'post_type' => [ViewsCpt::NAME, CardsCpt::NAME], 'post_name__in' => array_keys($jsonData), 'posts_per_page' => -1, 'post_status' => ['publish', 'draft', 'trash',], ]); $posts = $query->get_posts(); foreach ($posts as $post) { $items[$post->post_name] = $post->ID; } return $items; } protected function getImportResultMessage( array $successViewIds, array $successCardIds, array $failViewUniqueIds, array $failCardUniqueIds ): string { $viewsInfo = []; $cardsInfo = []; $importResultMessage = ''; foreach ($successViewIds as $successViewId) { $viewsInfo[] = sprintf( '%s', (string)get_edit_post_link($successViewId), get_the_title($successViewId) ); } foreach ($successCardIds as $successCardId) { $cardsInfo[] = sprintf( '%s', get_edit_post_link($successCardId), get_the_title($successCardId) ); } if (!$failViewUniqueIds && !$failCardUniqueIds) { $this->isImportSuccessfull = true; $importResultMessage .= sprintf( __('Successfully imported %d Views and %d Cards.', 'acf-views'), count($successViewIds), count($successCardIds) ); $importResultMessage .= '
'; } else { $importResultMessage .= sprintf( __('Something went wrong. Imported %d from %d Views and %d from %d Cards.', 'acf-views'), count($successViewIds), count($successViewIds) + count($failViewUniqueIds), count($successCardIds), count($successCardIds) + count($failCardUniqueIds) ); $importResultMessage .= '
'; } if ($viewsInfo) { $viewsLabel = __('Imported Views', 'acf-views'); $importResultMessage .= sprintf( '%s: %s.', $viewsLabel, implode(', ', $viewsInfo) ); $importResultMessage .= '
'; } if ($cardsInfo) { $cardsLabel = __('Imported Cards', 'acf-views'); $importResultMessage .= sprintf( '%s: %s.', $cardsLabel, implode(', ', $cardsInfo) ); $importResultMessage .= '
'; } if ($failViewUniqueIds) { $viewsLabel = __('Wrong Views', 'acf-views'); $importResultMessage .= sprintf( '%s: %s.', $viewsLabel, implode(', ', $failViewUniqueIds) ); $importResultMessage .= '
'; } if ($failCardUniqueIds) { $cardsLabel = __('Wrong Cards', 'acf-views'); $importResultMessage .= sprintf( '%s: %s.', $cardsLabel, implode(', ', $failCardUniqueIds) ); $importResultMessage .= '
'; } return $importResultMessage; } protected function importOrUpdateItems(array $jsonData): void { $successViewIds = []; $successCardIds = []; $failViewUniqueIds = []; $failCardUniqueIds = []; $existingItems = $this->getExistingItems($jsonData); foreach ($jsonData as $uniqueId => $details) { $postType = false !== strpos($uniqueId, 'view_') ? ViewsCpt::NAME : CardsCpt::NAME; $isExistingItem = key_exists($uniqueId, $existingItems); $postTitle = $details['_post_title'] ?? ''; $postExcerpt = $details['_post_excerpt'] ?? ''; $postContentFiltered = $details['_post_content_filtered'] ?? ''; if (isset($details['_post_title'])) { unset($details['_post_title']); } if (isset($details['_post_excerpt'])) { unset($details['_post_excerpt']); } if (isset($details['_post_content_filtered'])) { unset($details['_post_content_filtered']); } $postId = !$isExistingItem ? wp_insert_post([ 'post_type' => $postType, 'post_status' => 'publish', 'post_name' => $uniqueId, 'post_title' => $postTitle, 'post_excerpt' => $postExcerpt, 'post_content_filtered' => $postContentFiltered, ]) : $existingItems[$uniqueId]; if (is_wp_error($postId)) { if (ViewsCpt::NAME === $postType) { $failViewUniqueIds[] = $uniqueId; } else { $failCardUniqueIds[] = $uniqueId; } continue; } $postFields = []; if ($isExistingItem) { $postFields = array_merge($postFields, [ 'post_title' => $postTitle, 'post_excerpt' => $postExcerpt, 'post_content_filtered' => $postContentFiltered, ]); } $cptData = ViewsCpt::NAME === $postType ? $this->acfViewData->getDeepClone() : $this->acfCardData->getDeepClone(); // pass through loading, instead of the DB query to update the post_content // it some kind of verification process, because only the right fields will be picked up from json and saved $cptData->load($postId, '', $details); $cptData->saveToPostContent($postFields); // there is no sense to call the 'performSaveActions' method // just clean the cache (if somebody uses Redis or something like that) if ($isExistingItem) { clean_post_cache($postId); } if (ViewsCpt::NAME === $postType) { $successViewIds[] = $postId; } else { $successCardIds[] = $postId; } } $resultMessage = []; $resultMessage[] = implode(',', $successViewIds); $resultMessage[] = implode(',', $successCardIds); $resultMessage[] = implode(',', $failViewUniqueIds); $resultMessage[] = implode(',', $failCardUniqueIds); $this->importResultMessage = implode(';', $resultMessage); } protected function import(): void { $pathToFile = (string)get_attached_file($this->toolsData->importFile); if (!$pathToFile || !file_exists($pathToFile)) { $this->importResultMessage = __('Import file not found.', 'acf-views'); return; } $fileContent = file_get_contents($pathToFile); // remove the prefix, that was added to avoid WP Media library JSON detection $fileContent = str_replace('ACF Views:', '', $fileContent); $jsonData = json_decode($fileContent, true); if (JSON_ERROR_NONE !== json_last_error()) { $this->importResultMessage = __('Import file is not a valid JSON.', 'acf-views'); return; } $this->importOrUpdateItems($jsonData); wp_delete_attachment($this->toolsData->importFile, true); $url = $this->plugin->getAdminUrl(self::SLUG) . sprintf( '&message=1&type=import&isSuccess=%s&resultMessage=%s', $this->isImportSuccessfull, $this->importResultMessage ); wp_redirect($url); exit; } public function maybeEchoExportFile($postId): void { if (!$this->isMySource($postId) || !$this->exportData) { return; } $ids = array_keys($this->exportData); $viewIds = array_filter($ids, function ($id) { return false !== strpos($id, 'view_'); }); $countOfViewIds = count($viewIds); $cardIds = array_filter($ids, function ($id) { return false !== strpos($id, 'card_'); }); $countOfCardIds = count($cardIds); $redirectUrl = $this->plugin->getAdminUrl(self::SLUG) . sprintf('&message=1&type=export&_views=%s&_cards=%s', $countOfViewIds, $countOfCardIds); ?> getImportResultMessage( $successViewIds, $successCardIds, $failViewUniqueIds, $failCardUniqueId ); } acf_options_page()->add_page([ 'slug' => self::SLUG, 'page_title' => __('Tools', 'acf-views'), 'menu_title' => __('Tools', 'acf-views'), 'parent_slug' => sprintf('edit.php?post_type=%s', ViewsCpt::NAME), 'position' => 3, 'update_button' => __('Process', 'acf-views'), 'updated_message' => $updatedMessage, ]); } public function maybeCatchValues($postId) { if (!$this->isMySource($postId)) { return; } add_filter('acf/pre_update_value', function ($isUpdated, $value, $postId, array $field): bool { // extra check, as probably it's about another post if (!$this->isMySource($postId)) { return $isUpdated; } $fieldName = $field['name'] ?? ''; $this->values[$fieldName] = $value; // avoid saving to the postmeta return true; }, 10, 4); } public function maybeProcess($postId): void { if (!$this->isMySource($postId) || !$this->values) { return; } $this->toolsData->load(false, '', $this->values); $isExport = $this->toolsData->isExportAllViews || $this->toolsData->isExportAllCards || $this->toolsData->exportViews || $this->toolsData->exportCards; $isImport = !!$this->toolsData->importFile; if ($isExport) { $this->export(); } if ($isImport) { $this->import(); } } public function setHooks(bool $isAdmin): void { if (!$isAdmin) { return; } // init, not acf/init, as the method uses 'get_edit_post_link' which will be available only since this hook // (because we sign up the CPTs in this hook) add_action('init', [$this, 'addPage',]); add_action('acf/save_post', [$this, 'maybeCatchValues',]); // priority 20, as it's after the ACF's save_post hook add_action('acf/save_post', [$this, 'maybeProcess',], 20); // priority 30, after the process action add_action('acf/save_post', [$this, 'maybeEchoExportFile',], 30); } }