Text::data(),
'existingAdminMenuTitles' => static::notUsedExistingMenuTitles(),
'ajaxNonce' => wp_create_nonce(SubpageImportExport::AJAX_NONCE),
'rest' => [
'root' => rest_url(),
'url' => rest_url(Rest\PagesController::NAMESPACE . '/'),
'nonce' => wp_create_nonce('wp_rest'),
],
'data' => static::pageData()
]);
}
/**
* Forget everything this class cached for the current request (request copies and table reads).
*/
public static function forgetRequestCaches(): void
{
foreach (array_keys((new \ReflectionClass(static::class))->getStaticProperties()) as $name) {
static::$$name = null;
}
Access::reset();
}
public static function getPostRequest()
{
$nonceName = SubpageEdit::$nonceName;
$nonceAction = SubpageEdit::$nonceAction;
if (!isset(Request::post()[$nonceName]) || !wp_verify_nonce(Request::post()[$nonceName], $nonceAction)) {
return [];
}
if (static::$getPostRequest === null) {
static::$getPostRequest = Request::post();
}
return static::$getPostRequest;
}
public static function getRequest()
{
if (static::$getRequest === null) {
static::$getRequest = $_REQUEST;
}
return static::$getRequest;
}
public static function getGetRequest()
{
$nonceName = SubpageEdit::$nonceName;
$nonceAction = SubpageEdit::$nonceAction;
if (!isset(Request::get()[$nonceName]) || !wp_verify_nonce(Request::get()[$nonceName], $nonceAction)) {
return [];
}
if (static::$getGetRequest === null) {
static::$getGetRequest = Request::get();
}
return static::$getGetRequest;
}
/**
* @return Collection|array
*/
public static function pageData()
{
if (Request::get()['page'] === SubpageCreate::SLUG) {
return Collection::make([
'registered_options' => static::getAllOptionNamesExcludeCurrentPageFieldNames()
]);
}
if (!isset(Request::get()['action']) && !isset(Request::get()['optionpage']) || Request::get()['page'] === SubpageMaster::SLUG) {
return [];
}
$currentPage = static::listTableData()->filter(function ($value) {
return Request::get()['optionpage'] === $value['ID'];
})->values();
$allRows = Request::post() ? PageRepository::rows() : static::getAllRowsFromPluginTable();
$allRowsFromPluginTable = Collection::make($allRows)->filter(function ($row) use ($currentPage) {
return $currentPage[0]['ID'] === $row->id;
})->flatten(1)->map(function ($value) {
return PageRepository::decode($value->page_value);
})->first();
return Collection::make($allRowsFromPluginTable)->merge([
'id' => $currentPage[0]['ID'],
'nonce' => wp_create_nonce($currentPage[0]['ID']),
'page' => Request::get()['page'],
'registered_options' => static::getAllOptionNamesExcludeCurrentPageFieldNames()
]);
}
/**
* Existing admin menu titles.
*
* @return object
* @global array $GLOBALS ['menu']
*/
public static function existingAdminMenuTitles()
{
return Collection::make($GLOBALS['menu'])->filter(function ($value) {
return in_array($value[2], static::defaultWordpressMenuPageSlugs());
})->map(function ($value) {
if (strpos($value[0], '')) {
$value[0] = substr($value[0], 0, strpos($value[0], ' $value[0], 'slug' => $value[2]];
})->values();
}
/**
* Existing admin menu title minus the ones who are already used.
*
* @return object
* @global array $GLOBALS ['menu']
*/
public static function notUsedExistingMenuTitles()
{
$parentSlug = static::getParentSlugFromSubpages()->values()->toArray();
if (isset($GLOBALS['menu'])) {
global $menu;
} else {
$menu = get_option(Plugin::PREFIX_ . 'admin_menu_list');
}
return Collection::make($menu)->filter(function ($value) use ($parentSlug) {
return in_array($value[2], static::defaultWordpressMenuPageSlugs()) && !in_array($value[2], $parentSlug);
})->map(function ($value) {
if (strpos($value[0], '') !== false) {
$value[0] = substr($value[0], 0, strpos($value[0], ' $value[0], 'slug' => $value[2]];
})->values();
}
/**
* Get all default WordPress menu titles and slugs.
*
* @return object
*/
public static function getDefaultWordpressMenus()
{
if (isset($GLOBALS['menu'])) {
global $menu;
} else {
$menu = get_option(Plugin::PREFIX_ . 'admin_menu_list');
}
return Collection::make($menu)->filter(function ($value) {
return in_array($value[2], static::defaultWordpressMenuPageSlugs());
})->map(function ($value, $key) {
if (strpos($value[0], '') !== false) {
$value[0] = substr($value[0], 0, strpos($value[0], ' $value[0],
'slug' => $value[2],
'position' => $key
];
})->values();
}
public static function defaultWordpressMenuPageSlugs()
{
return [
'index.php',
'edit.php',
'upload.php',
'edit.php?post_type=page',
'edit-comments.php',
'themes.php',
'plugins.php',
'users.php',
'tools.php',
'options-general.php'
];
}
/**
* @return Collection
*/
public static function getParentSlugFromSubpages()
{
return static::getSubpages()
->pluck('parent_slug')
->unique();
}
/**
* @param null $id
*
* @return Collection
*/
public static function getSubpages($id = null)
{
$value = ($id === null)
? static::getAllFromPageValueColumn()
: static::getPageValueFromSpecificId($id);
// dump(static::getAllFromPageValueColumn());
// dump($value);
return Collection::make($value)
->pluck('pages')
->flatten(1);
}
/**
* @return Collection|void
*/
public static function requestGetSubpages()
{
if (!isset(Request::post()['pages'])) {
return;
}
return Collection::make(Request::post()['pages']);
}
/**
* @return Collection
*/
public static function getFields()
{
return static::getSubpages()
->pluck('fields')
->flatten(1);
}
/**
* @param $id
*
* @return Collection
*/
public static function getFieldsfromId($id)
{
return static::getSubpages($id)
->pluck('fields')
->flatten(1);
}
/**
* @param $id
*
* @return Collection
*/
public static function getOptionNamesFromSettingPagesById($id)
{
return static::getFieldsfromId($id)->filter(function ($field) {
return isset($field['field_name']);
})->pluck('field_name')->flatten(1);
}
/**
* @return Collection
*/
public static function getPageNamesFromOptionsPages()
{
return static::getSubpages()->map(function ($page) {
return $page['menu_slug'];
});
}
/**
* @return Collection
*/
public static function adminPagesIds()
{
return Collection::make([
// The screen id depends on the (translatable) menu title, so let WordPress build it.
get_plugin_page_hookname(SubpageCreate::SLUG, SubpageMaster::SLUG),
'admin_page_' . SubpageEdit::SLUG,
'toplevel_page_' . SubpageMaster::SLUG
]);
}
/**
* Get all options names from options table.
*
* @return array
*/
public static function getAllOptionNames()
{
if (static::$getAllOptionNames === null) {
static::$getAllOptionNames = Collection::make(PageRepository::optionNamesInOptionsTable())
->pluck('option_name')
->merge(static::getAllCreatedOptionNames());
}
return static::$getAllOptionNames;
}
/**
* Get all options names from options table exclude current subpage field names.
*
* @return array
*/
public static function getAllOptionNamesExcludeCurrentPageFieldNames()
{
if (static::$getAllOptionNamesExcludeCurrentPageFieldNames === null) {
static::$getAllOptionNamesExcludeCurrentPageFieldNames = Collection::make(static::getAllOptionNames())
->diff(static::getAllRegisteredFieldNames())
// An import may take over option names that the plugin made itself.
->diff(\AOP\App\ImportExport\AllowedOptionNames::all())
->all();
}
return static::$getAllOptionNamesExcludeCurrentPageFieldNames;
}
/**
* Get all options names from options table created by this plugin.
* To be clear: field_name === option_name.
*
* @return object
*/
public static function getAllCreatedOptionNames()
{
if (is_null(static::$getAllFromPageValueColumn)) {
return;
}
return static::$getAllFromPageValueColumn
->pluck('pages')->flatten(1)
->pluck('fields')->flatten(1)
->pluck('field_name');
}
/**
* Get all field names which already registered.
*
* @return array
*/
public static function getAllRegisteredFieldNames()
{
if (!isset(Request::get()['optionpage'])) {
return [];
}
return static::getFieldsfromId(Request::get()['optionpage'])->pluck('field_name');
}
/**
* @return Collection
*/
public static function getFieldsWithFieldNames()
{
// Fields of unavailable types are not posted but they stay: their names count.
$preservedNames = Collection::make(static::unknownTypeFields())->pluck('field.field_name');
return Collection::make(Request::post()['pages'])
->pluck('fields')->flatten(1)
->pluck('field_name')
->merge($preservedNames)
->filter();
}
/**
* The stored fields of the page that is being edited whose type is not available (an add-on that is not
* active). They are kept when the page is saved and their options are never deleted.
*
* @return array
*/
public static function unknownTypeFields(): array
{
$id = Request::get()['optionpage'] ?? null;
if (!$id || (Request::get()['page'] ?? '') !== SubpageEdit::SLUG) {
return [];
}
$known = FieldRegistry::instance()->all();
$removed = self::removedFieldIds();
$found = [];
foreach (static::getSubpages((int) $id) as $page) {
foreach ($page['fields'] ?? [] as $field) {
if (!isset($field['type']) || isset($known[$field['type']])) {
continue;
}
// Deleted in the editor: not kept, so its name is gone too and PageSaver deletes its option.
if (in_array((string) ($field['id'] ?? ''), $removed, true)) {
continue;
}
$found[] = ['subpage' => (string) ($page['menu_slug'] ?? ''), 'field' => $field];
}
}
return $found;
}
/**
* The ids the editor posts as `removed_fields[]`: fields of an unavailable type that the user deleted. Only
* unknownTypeFields() reads them, so they can only drop such a field of the page being edited, never another.
*
* @return array
*/
private static function removedFieldIds(): array
{
$posted = Request::post()['removed_fields'] ?? [];
if (!is_array($posted)) {
return [];
}
$ids = array_map(function ($id) {
return is_scalar($id) ? preg_replace('/[^0-9]/', '', (string) $id) : '';
}, $posted);
return array_values(array_filter($ids, 'strlen'));
}
/**
* All data from column page_value.
*
* @return object
*/
public static function getAllFromPageValueColumn()
{
if (static::$getAllFromPageValueColumn === null) {
static::$getAllFromPageValueColumn = Collection::make(PageRepository::pageValueRows())
->pluck('page_value')
->map(function ($item) {
$item = PageRepository::decode($item);
if (!isset($item['menu'])) {
return $item;
}
$pages = Collection::make($item['pages'])->filter(function ($item) {
return current_user_can(Access::pageCapability($item));
});
if ($pages->isEmpty()) {
return $item;
}
$firstPage = $pages->first();
if ($item['menu']['menu_slug'] === $firstPage['menu_slug']) {
return $item;
}
$item['menu']['menu_slug'] = $firstPage['menu_slug'];
$item['menu']['menu_slug_suffix'] = $firstPage['subpage_slug_suffix'];
$item['pages'] = $pages->map(function ($page) use ($item) {
$page['parent_slug'] = $item['menu']['menu_slug'];
return $page;
});
return $item;
});
}
return static::$getAllFromPageValueColumn;
}
public static function getAllRowsFromPluginTable()
{
if (static::$getAllRowsFromPluginTable === null) {
static::$getAllRowsFromPluginTable = PageRepository::rows();
}
return static::$getAllRowsFromPluginTable;
}
// public function getIdFromTableRow()
// {
// return Collection::make(static::getAllRowsFromPluginTable())->pluck('id');
// }
public function getIdfromOptionPage($slug)
{
return Collection::make(static::getAllRowsFromPluginTable())
->where('menu_slug', $slug)
->pluck('id')
->first();
}
/**
* The menu_id from all created menus.
*
* @return object
*/
public function menuIdFromAllCreatedMenus()
{
return static::getAllFromPageValueColumn()->map(function ($page) {
return [
'menu_id' => $page['menu_id'],
'menu_type' => $page['menu'] ? 'menu' : 'existing_menu'
];
});
}
/**
* The menu_id from all created menus.
*
* @return object
*/
public function menuIdFromAllExistingMenus()
{
return static::getAllFromPageValueColumn()->filter(function ($page) {
return $page['existing_menu'];
})->pluck('menu_id');
}
/**
* Get page_value from specific id.
*
* @return object
*/
private static function getPageValueFromSpecificId($id)
{
return Collection::make(PageRepository::rowsById((int) $id))->map(function ($row) {
return PageRepository::decode($row->page_value);
});
}
/**
* @return Collection
*/
public static function listTableData()
{
return Collection::make(static::getAllRowsFromPluginTable())->map(function ($row) {
$pageValue = PageRepository::decode($row->page_value);
if (isset($pageValue['existing_menu'])) {
$menuItems = Collection::make($GLOBALS['menu'])->filter(function ($value) {
return in_array($value[2], static::defaultWordpressMenuPageSlugs());
})->filter(function ($item) use ($pageValue) {
return $item[2] === $pageValue['existing_menu']['menu_slug'];
})->map(function ($item, $key) {
return array_merge($item, ['position' => $key]);
})->flatten(1);
$dashicon = $menuItems[6];
$position = $menuItems->last();
// $menuName = '' . $pageValue['existing_menu']['menu_name'];
$menuIcon = $dashicon;
$menuName = $pageValue['existing_menu']['menu_name'];
} else {
$position = $pageValue['menu']['position'];
// $menuName = '' . $pageValue['menu']['menu_title'];
$menuIcon = $pageValue['menu']['icon_url'];
$menuName = $pageValue['menu']['menu_title'];
}
$totalPages = count($pageValue['pages']);
return [
'ID' => $row->id,
'title' => $menuName,
'icon' => $menuIcon,
'position' => (float)$position,
'pages' => (string)$totalPages
];
});
}
/**
* Delete an options page and its option values.
*
* Only from the overview or the edit page, for users with manage_options and with a valid nonce.
*
* @return void
*/
public static function deleteRowFromSpecificId()
{
if (!isset($_GET['action'], $_GET['optionpage'], $_GET['nonce']) || $_GET['action'] !== 'delete') {
return;
}
if (!(SubpageEdit::isCurrentPage() || SubpageMaster::isCurrentPage()) || !current_user_can('manage_options')) {
return;
}
$id = absint($_GET['optionpage']);
if (!$id || !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['nonce'])), (string) $id)) {
wp_die(esc_html__('The link you followed has expired.', 'admin-options-pages'), 403);
}
static::deletePage($id);
wp_safe_redirect(SubpageMaster::url());
exit;
}
/**
* Delete one page and the options of its fields. The overview screen tidies `aop_option_names` and shows
* the notice on its next load, from the flash data set here.
*/
public static function deletePage(int $id): void
{
static::getOptionNamesFromSettingPagesById($id)->map(function ($optionName) {
return delete_option($optionName);
});
Flash::add([
'pages_deleted' => true,
'pages_deleted_multiple' => static::getSubpages($id)->count() > 1 ? true : false
]);
PageRepository::delete($id);
OptionNames::sync();
}
/**
* Page ID from specific edit page.
*
* @return int
*/
public static function IdEditPage()
{
if (!isset(Request::get()['optionpage'])) {
return;
}
return static::listTableData()->filter(function ($value) {
return Request::get()['optionpage'] === $value['ID'];
})->values()[0]['ID'];
}
}