validateNonce($nonceName, $nonceAction)) { return; } try { $saved = (new PageSaver($this))->save($validate); } catch (PageInvalid $invalid) { return; } if ($saved['created']) { wp_safe_redirect(add_query_arg('optionpage', $saved['id'], SubpageEdit::url())); exit; } $optionNames = (get_current_screen()->id === 'admin_page_' . SubpageEdit::SLUG) ? Data::getAllOptionNamesExcludeCurrentPageFieldNames() : Data::getAllOptionNames(); wp_localize_script(Plugin::PREFIX . 'app-js', Plugin::PREFIX_ . 'option_names_js', $optionNames); wp_localize_script(Plugin::PREFIX . 'app-js', Plugin::PREFIX_ . 'script_data_js', Data::js()->toArray()); Notice::succes(__('Updated.', 'admin-options-pages')); } /** * Get the serialized data/fields from the pages with a custom menu. * * @param Validation $validate * * @return array */ public function customMenuFields(Validation $validate) { $menuPage = $this->menuPageForCustomMenu($validate); return [ 'menu_slug' => $menuPage['menu_slug'], 'stored_value' => PageRepository::encode([ 'menu_id' => $validate->number(Request::post()['menu_id']), 'menu' => $menuPage, 'pages' => $this->subpagesForCustomMenu($validate), 'side_bar_visible' => ($validate->toggle(Request::post()['side_bar_visible']) === 'true') ? true : false ]) ]; } /** * Get the serialized data/fields from the pages with an exiting menu. * * @param Validation $validate * * @return array */ public function exitingMenuFields(Validation $validate) { return [ 'menu_slug' => $this->existingMenuParentSlug($validate), 'stored_value' => PageRepository::encode([ 'menu_id' => $validate->number(Request::post()['menu_id']), 'existing_menu' => [ 'menu_name' => $this->existingMenuMenuName($validate), 'menu_slug' => $this->existingMenuParentSlug($validate) ], 'pages' => $this->subpagesForExistingMenu($validate), 'side_bar_visible' => $validate->toggle(Request::post()['side_bar_visible']) === 'true' ]) ]; } /** * Get the menu name for the exiting menu. * * @param Validation $validate * * @return string */ public function existingMenuMenuName(Validation $validate) { return Data::getDefaultWordpressMenus() ->where('slug', $this->existingMenuParentSlug($validate)) ->values() ->flatten(1) ->first(); } /** * Get the parent slug for the exiting menu. * * @param Validation $validate * * @return string */ public function existingMenuParentSlug(Validation $validate) { return $validate->existingMenuMenuSlug(); } /** * Get the parent slug for the custom menu. * * @param Validation $validate * * @return string */ public function parentSlugForCustomMenu(Validation $validate) { return $validate->alphanumericDashesAll(Request::post()['pages'][0]['subpage_slug']); } /** * Get the parent slug for the custom menu. * * @param Validation $validate * * @return string */ public function parentSlugSuffixForCustomMenu(Validation $validate) { return $validate->alphanumeric(Request::post()['pages'][0]['subpage_slug_suffix']); } /** * Get menu items for custom menu. * * @param Validation $validate * * @return array */ public function menuPageForCustomMenu(Validation $validate) { return [ 'page_title' => $validate->already(Request::post()['menu']['menu_title'], $validate->customMenu()), 'menu_title' => $validate->already(Request::post()['menu']['menu_title'], $validate->customMenu()), 'capability' => $validate->capabilityMenu(), 'menu_slug' => $validate->already($this->parentSlugForCustomMenu($validate)), 'menu_slug_suffix' => $validate->already($this->parentSlugSuffixForCustomMenu($validate)), 'icon_url' => $validate->already(Request::post()['menu']['menu_icon'], $validate->customMenu()), 'position' => $validate->already(Request::post()['menu']['menu_position'], $validate->customMenu()), ]; } /** * Get sub pages for existing menu types. * * @param Validation $validate * * @return array */ protected function subpagesForExistingMenu(Validation $validate) { if (!isset(Request::post()['pages'])) { return []; } return Collection::make(Request::post()['pages'])->map(function ($subpage, $key) use ($validate) { $validatedSubpageTab = isset($subpage['subpage_tab']) && $subpage['subpage_tab'] === '1'; $roles = $validate->roles($subpage); $validatedParentSlug = $validate->already($this->existingMenuParentSlug($validate), $validate->existingMenu()); $validatedSubpageToggle = $validate->toggle($subpage['subpage_toggle']) === 'true'; return [ 'parent_slug' => $validatedParentSlug, 'page_title' => '', 'subpage_title' => $validate->textField($subpage['subpage_title']), 'menu_title' => $validate->textField($subpage['menu_title']), 'capability' => Access::legacyCapability($roles), 'roles' => $roles, 'menu_slug' => $validate->alphanumericDashesAll($subpage['subpage_slug']), 'subpage_slug_suffix' => $validate->alphanumeric($subpage['subpage_slug_suffix']), 'include_tab' => $validatedSubpageTab, 'select_title' => $validate->textField($subpage['select_title']), 'subpage_toggle' => $validatedSubpageToggle, 'fields' => $this->fields($subpage, $validate, $this->preservedFor($subpage, $key)), 'subpage_position' => $validate->integerOrEmpty($subpage['subpage_position']) ]; })->toArray(); } /** * Get subpages for custom menu types. * * @param Validation $validate * * @return array */ protected function subpagesForCustomMenu(Validation $validate) { if (!isset(Request::post()['pages'])) { return []; } return Collection::make(Request::post()['pages'])->map(function ($subpage, $key) use ($validate) { $validatedSubpageTab = isset($subpage['subpage_tab']) && $subpage['subpage_tab'] === '1'; $roles = $validate->roles($subpage); $validatedParentSlug = $this->parentSlugForCustomMenu($validate); $validatedMenuSlug = ($key === 0) ? $validatedParentSlug : $validate->alphanumericDashesAll($subpage['subpage_slug']); return [ 'parent_slug' => $validatedParentSlug, 'page_title' => $validate->textField(Request::post()['menu']['menu_title']), 'subpage_title' => $validate->textField($subpage['subpage_title']), 'menu_title' => $validate->textField($subpage['menu_title']), 'capability' => Access::legacyCapability($roles), 'roles' => $roles, 'menu_slug' => $validatedMenuSlug, 'subpage_slug_suffix' => $validate->alphanumeric($subpage['subpage_slug_suffix']), 'include_tab' => $validatedSubpageTab, 'select_title' => $validate->textField($subpage['select_title']), 'subpage_toggle' => $validate->toggleBoolean($subpage['subpage_toggle']), 'fields' => $this->fields($subpage, $validate, $this->preservedFor($subpage, $key)) ]; })->toArray(); } /** * Get all fields from specific subpage, as they are stored: every field goes through the settings schema of * its field type. Fields of a type that is not available (an add-on that is not active) are not posted by the * editor; they are kept as they were stored ($preserved). * * @param array $subpage Subpage with fields. * @param Validation $validate * @param array $preserved Stored fields of unavailable types that belong to this subpage. * * @return array */ protected function fields($subpage, Validation $validate, array $preserved = []) { if (!isset($subpage['fields'])) { return $preserved; } $registry = FieldRegistry::instance(); $fields = Collection::make($subpage['fields']); // A type that is not registered cannot come from the editor: refuse it. $validate->fieldTypes($fields, array_keys($registry->all())); return $fields->map(function ($field) use ($registry, $validate) { return $registry->get($field['type'])->sanitizeSettings($field, $validate); })->concat($preserved)->sortBy('field_key')->values()->all(); } /** * The stored fields of unavailable types that go with this subpage of the post. When the subpage was renamed * or removed they move to the first subpage, so their options stay under control of the plugin. * * @param array $subpage * @param int|string $key * * @return array */ private function preservedFor($subpage, $key): array { $unavailable = Data::unknownTypeFields(); if (!$unavailable) { return []; } $posted = Request::post()['pages'] ?? []; $slugs = array_column($posted, 'subpage_slug'); $firstKey = array_key_first($posted); $slug = $subpage['subpage_slug'] ?? ''; return Collection::make($unavailable)->filter(function ($item) use ($slug, $slugs, $key, $firstKey) { return $item['subpage'] === $slug || (!in_array($item['subpage'], $slugs, true) && $key === $firstKey); })->pluck('field')->all(); } }