*/ protected array $values; protected SettingsData $settingsData; protected Settings $settings; protected ViewsDataStorage $viewsDataStorage; protected CardsDataStorage $cardsDataStorage; protected string $savedMessage; protected GitRepository $gitRepository; public function __construct( SettingsData $settingsData, Settings $settings, ViewsDataStorage $viewsDataStorage, CardsDataStorage $cardsDataStorage, GitRepository $gitRepository ) { $this->values = []; $this->settingsData = $settingsData; $this->settings = $settings; $this->viewsDataStorage = $viewsDataStorage; $this->cardsDataStorage = $cardsDataStorage; $this->savedMessage = ''; $this->gitRepository = $gitRepository->getDeepClone(); } /** * @param mixed $postId */ protected function isMySource($postId): bool { $screen = get_current_screen(); return null !== $screen && 'acf_views_page_acf-views-settings' === $screen->id && 'options' === $postId; } protected function activateFsStorage(): void { if (false === mkdir($this->viewsDataStorage->getFileSystem()->getTargetBaseFolder(), 0755)) { $this->savedMessage = __( 'Fail to activate the file system storage. Check your FS permissions.', 'acf-views' ); return; } // set, as the folder was just created $this->viewsDataStorage->getFileSystem()->setBaseFolder(); $this->cardsDataStorage->getFileSystem()->setBaseFolder(); $this->viewsDataStorage->activateFileSystemStorage(); $this->cardsDataStorage->activateFileSystemStorage(); } protected function deactivateFsStorage(): void { $themeTemplatesFolder = $this->viewsDataStorage->getFileSystem()->getBaseFolder(); $this->viewsDataStorage->deactivateFileSystemStorage(); $this->cardsDataStorage->deactivateFileSystemStorage(); $isRemoved = $this->viewsDataStorage->getFileSystem()->removeFolderRecursively($themeTemplatesFolder); if (true === $isRemoved) { return; } $this->savedMessage = __( 'Fail to deactivate the file system storage. Check your FS permissions.', 'acf-views' ); } public function addPage(): void { // do not use 'acf_add_options_page', as the global options-related functions may be unavailable // (in case of the manual include) if (!function_exists('acf_options_page')) { return; } $resultMessage = sanitize_text_field($_GET['resultMessage'] ?? ''); $updatedMessage = '' === $resultMessage ? __('Settings successfully updated.', 'acf-views') : $resultMessage; acf_options_page()->add_page([ 'slug' => self::SLUG, 'page_title' => __('Settings', 'acf-views'), 'menu_title' => __('Settings', 'acf-views'), 'parent_slug' => sprintf('edit.php?post_type=%s', ViewsCpt::NAME), 'position' => 2, 'update_button' => __('Save changes', 'acf-views'), 'updated_message' => $updatedMessage, ]); } /** * @param mixed $postId */ public function maybeCatchValues($postId): void { 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 = (string)($field['name'] ?? ''); // convert repeater format. don't check simply 'is_array(value)' as not every array is a repeater // also check to make sure it's array (can be empty string) if (SettingsData::getAcfFieldName(SettingsData::FIELD_GIT_REPOSITORIES) === $fieldName && true === is_array($value)) { $value = Group::convertRepeaterFieldValues($fieldName, $value); } $this->values[$fieldName] = $value; // avoid saving to the postmeta return true; }, 10, 4); } public function maybeInjectValues(): void { if (false === $this->isMySource('options')) { return; } add_filter('acf/pre_load_value', function ($value, $postId, $field) { // extra check, as probably it's about another post if (false === $this->isMySource($postId)) { return $value; } $fieldName = $field['name']; $value = ''; switch ($fieldName) { case SettingsData::getAcfFieldName(SettingsData::FIELD_IS_DEV_MODE): $value = $this->settings->isDevMode(); break; case SettingsData::getAcfFieldName(SettingsData::FIELD_IS_FILE_SYSTEM_STORAGE): $value = '' !== $this->viewsDataStorage->getFileSystem()->getBaseFolder(); break; case SettingsData::getAcfFieldName(SettingsData::FIELD_GIT_REPOSITORIES): $this->settingsData->gitRepositories = []; foreach ($this->settings->getGitRepositories() as $gitRepositoryData) { $gitRepository = $this->gitRepository->getDeepClone(); $gitRepository->id = $gitRepositoryData['id']; $gitRepository->accessToken = $gitRepositoryData['accessToken']; $gitRepository->name = $gitRepositoryData['name']; $this->settingsData->gitRepositories[] = $gitRepository; } $gitRepositoriesFieldName = SettingsData::getAcfFieldName(SettingsData::FIELD_GIT_REPOSITORIES); $value = $this->settingsData->getFieldValues()[$gitRepositoriesFieldName] ?? []; $value = true === is_array($value) ? Group::convertRepeaterFieldValues($fieldName, $value, false) : []; $this->settingsData->gitRepositories = []; break; } return $value; }, 10, 3); } /** * @param mixed $postId * @throws Exception */ public function maybeProcess($postId): void { if (!$this->isMySource($postId) || [] === $this->values) { return; } $this->settingsData->load(false, '', $this->values); $this->settings->setIsDevMode($this->settingsData->isDevMode); $gitRepositories = []; foreach ($this->settingsData->gitRepositories as $gitRepository) { $gitRepositories[] = [ 'id' => $gitRepository->id, 'accessToken' => $gitRepository->accessToken, 'name' => $gitRepository->name, ]; } $this->settings->setGitRepositories($gitRepositories); if (true === $this->settingsData->isFileSystemStorage && false === $this->viewsDataStorage->getFileSystem()->isActive()) { $this->activateFsStorage(); } if (false === $this->settingsData->isFileSystemStorage && true === $this->viewsDataStorage->getFileSystem()->isActive()) { $this->deactivateFsStorage(); } $this->settings->save(); if ('' === $this->savedMessage) { return; } wp_redirect(add_query_arg([ 'message' => 1, 'resultMessage' => $this->savedMessage, ])); exit; } public function setHooks(CurrentScreen $currentScreen): void { if (false === $currentScreen->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); add_action('acf/input/admin_head', [$this, 'maybeInjectValues']); } }