toolsData = $toolsData; $this->cache = $cache; $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(AcfViews::NAME, $this->toolsData->exportViews) : []; $cardIds = $isCardsInExport ? $this->getPosts(AcfCards::NAME, $this->toolsData->exportCards) : []; foreach ($viewIds as $viewId) { $acfViewData = $this->cache->getAcfViewData($viewId); $viewUniqueId = $acfViewData->getUniqueId(); $this->exportData[$viewUniqueId] = array_merge($acfViewData->getFieldValues(), [ '_post_title' => get_the_title($viewId), ]); } foreach ($cardIds as $cardId) { $acfCardData = $this->cache->getAcfCardData($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), ]); } } protected function getExistingItems(array $jsonData): array { $items = []; $query = new WP_Query([ 'post_type' => [AcfViews::NAME, AcfCards::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_') ? AcfViews::NAME : AcfCards::NAME; $isExistingItem = key_exists($uniqueId, $existingItems); $postTitle = $details['_post_title'] ?? ''; $postExcerpt = $details['_post_excerpt'] ?? ''; if (isset($details['_post_title'])) { unset($details['_post_title']); } if (isset($details['_post_excerpt'])) { unset($details['_post_excerpt']); } $postId = !$isExistingItem ? wp_insert_post([ 'post_type' => $postType, 'post_status' => 'publish', 'post_name' => $uniqueId, 'post_title' => $postTitle, 'post_excerpt' => $postExcerpt, ]) : $existingItems[$uniqueId]; if (is_wp_error($postId)) { if (AcfViews::NAME === $postType) { $failViewUniqueIds[] = $uniqueId; } else { $failCardUniqueIds[] = $uniqueId; } continue; } $postFields = []; if ($isExistingItem) { $postFields = array_merge($postFields, [ 'post_title' => $postTitle, ]); } $cptData = AcfViews::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 (AcfViews::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', AcfViews::NAME), 'position' => 2, 'update_button' => __('Process', 'acf-views'), 'updated_message' => $updatedMessage, ]); } public function showSubmitNote(array $page): void { if ($page['slug'] !== self::SLUG) { return; } printf( '

%s

', __( "Note: Related ACF Groups won't be included, you must handle them separately.", 'acf-views' ) ); // hide the collapse icon for the postbox (submit button) echo ''; } 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(): void { // 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/options_page/submitbox_major_actions', [$this, 'showSubmitNote',]); 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); } }