Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions src/assets/js/simply-rets-admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,20 @@ jQuery(function($) {
}

try { active = window.sessionStorage.getItem(storageKey) || active; } catch (error) { /* Use the account section. */ }
function linkedSection() {
var prefix = '#sr-section-';
if (window.location.hash.indexOf(prefix) !== 0) return null;
var section = window.location.hash.slice(prefix.length);
return panels.filter(function() { return $(this).attr('data-sr-panel') === section; }).length ? section : null;
}
active = linkedSection() || active;
root.addClass('sr-settings-enhanced');
root.find('.sr-settings-toolbar, .sr-settings-nav, .sr-settings-savebar-info, .sr-reveal-credentials').prop('hidden', false);
showSection(active, false);
$(window).on('hashchange', function() {
var section = linkedSection();
if (section) showSection(section, true);
});

nav.on('click', 'a', function(event) {
event.preventDefault();
Expand Down
69 changes: 68 additions & 1 deletion src/simply-rets-admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,63 @@

add_action("wp_loaded", array("SrAdminSettings", "createDemoPage"));
add_action("admin_notices", array("SrAdminSettings", "adminMessages"));
add_action('admin_notices', array('SrAdminSettings', 'legacyPagesNotice'));
add_action('admin_post_sr_dismiss_legacy_pages_notice', array('SrAdminSettings', 'dismissLegacyPagesNotice'));

class SrAdminSettings {

public static function canManageLegacyPages() {
$post_type = get_post_type_object('sr-listings');
return $post_type && current_user_can('manage_options') && current_user_can($post_type->cap->edit_posts);
}

public static function hasLegacyPages() {
global $wpdb;
// Look for persisted posts only, including drafts, private pages and trash.
// Automatic drafts are editor placeholders; dynamic routes are not stored.
return $wpdb->get_var($wpdb->prepare(
"SELECT ID FROM {$wpdb->posts} WHERE post_type = %s AND post_status NOT IN (%s, %s) LIMIT 1",
'sr-listings', 'auto-draft', 'inherit'
)) !== null;
}

public static function legacyPagesSettingsUrl() {
return admin_url('options-general.php?page=simplyrets-admin.php') . '#sr-section-legacy-pages';
}

public static function legacyPagesNotice() {
if ((defined('DOING_AJAX') && DOING_AJAX) ||
(defined('IFRAME_REQUEST') && IFRAME_REQUEST) ||
(function_exists('is_network_admin') && is_network_admin()) ||
!self::canManageLegacyPages() || !self::hasLegacyPages() ||
get_user_option('sr_legacy_pages_transition_dismissed')) {
return;
}
?>
<div class="notice notice-info updated sr-legacy-pages-notice">
<p><strong>SimplyRETS Pages are now legacy pages.</strong> Your existing pages continue to work. For new pages, use SimplyRETS shortcodes on ordinary WordPress pages.</p>
<p><a href="<?php echo esc_url(self::legacyPagesSettingsUrl()); ?>">Find your existing pages in Settings → SimplyRETS → Legacy Pages</a>.</p>
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
<input type="hidden" name="action" value="sr_dismiss_legacy_pages_notice" />
<?php wp_nonce_field('sr_dismiss_legacy_pages_notice'); ?>
<p><button type="submit" class="button">Dismiss this notice</button></p>
</form>
</div>
<?php
}

public static function dismissLegacyPagesNotice() {
if (!self::canManageLegacyPages()) {
wp_die('You do not have permission to dismiss this notice.');
}
check_admin_referer('sr_dismiss_legacy_pages_notice');
// Keep dismissal specific to this user and this site on multisite installs.
update_user_option(get_current_user_id(), 'sr_legacy_pages_transition_dismissed', 1, false);
$redirect = wp_get_referer();
wp_safe_redirect($redirect ? $redirect : self::legacyPagesSettingsUrl());
exit;
}

public static function add_to_admin_menu() {
add_options_page(
'SimplyRETS Settings',
Expand Down Expand Up @@ -174,6 +228,10 @@ public static function sr_admin_page() {
'analytics' => array('Analytics', 'ListHub tracking', 'chart-bar'),
'messages' => array('Custom messages', 'Disclaimers & empty results', 'editor-alignleft')
);
$show_legacy_pages = self::canManageLegacyPages() && self::hasLegacyPages();
if ($show_legacy_pages) {
$sections['legacy-pages'] = array('Legacy Pages', 'Manage existing pages', 'admin-page');
}

// If meta data refresh action was posted, verify nonce and perform action
$update_meta_nonce_field = 'sr_update_meta_data_nonce_field';
Expand Down Expand Up @@ -859,6 +917,15 @@ public static function sr_admin_page() {
</i>
</div>
</div>
<?php if ($show_legacy_pages) { ?>
<div class="sr-settings-panel" id="sr-section-legacy-pages" data-sr-panel="legacy-pages">
<h2>Legacy Pages</h2>
<p>The custom SimplyRETS page editor is deprecated. Your existing pages are still supported and continue to work, including their filters, templates, and URLs.</p>
<p>For new listing pages, add SimplyRETS shortcodes to ordinary WordPress pages. For example, use <code>[sr_map_search search_form="true" list_view="true"]</code> for a map and listing search.</p>
<p><a href="https://wordpress-demo.simplyrets.com/" target="_blank" rel="noopener noreferrer">Explore shortcode examples<span class="screen-reader-text"> (opens in a new tab)</span></a></p>
<p><a class="button" href="<?php echo esc_url(admin_url('edit.php?post_type=sr-listings')); ?>">Manage existing pages</a></p>
</div>
<?php } ?>
<div class="sr-settings-empty" hidden><h2>No matching settings</h2><p>Try a different keyword, or clear the search to browse all sections.</p></div>
<div class="sr-settings-savebar">
<div class="sr-settings-savebar-info" hidden><strong class="sr-save-status" role="status" aria-live="polite">No unsaved changes</strong><span>Save applies to all sections.</span></div>
Expand All @@ -869,4 +936,4 @@ public static function sr_admin_page() {
</div>
<?php
}
} ?>
} ?>
2 changes: 2 additions & 0 deletions src/simply-rets-setup.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ public static function srRegisterPostType() {
);
$args = array(
'public' => true,
// Keep saved pages and their editor/routes available through settings.
'show_in_menu' => false,
'has_archive' => false,
'labels' => $labels,
'description' => 'SimplyRETS property listings pages',
Expand Down
168 changes: 168 additions & 0 deletions src/tests/test-legacy-pages.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
<?php
if (!defined('ABSPATH')) {
exit;
}
require_once ABSPATH . 'wp-admin/includes/template.php';
require_once dirname(__FILE__) . '/../simply-rets-admin.php';

class LegacyPagesTest extends WP_UnitTestCase {

private function signIn($role) {
$user_id = $this->factory->user->create(array('role' => $role));
wp_set_current_user($user_id);
return $user_id;
}

private function noticeMarkup() {
ob_start();
SrAdminSettings::legacyPagesNotice();
return ob_get_clean();
}

private function dismissalIsRejected() {
$handler = function() {
return function() { throw new RuntimeException('Dismissal rejected'); };
};
add_filter('wp_die_handler', $handler);
try {
SrAdminSettings::dismissLegacyPagesNotice();
} catch (RuntimeException $error) {
return $error->getMessage() === 'Dismissal rejected';
} finally {
remove_filter('wp_die_handler', $handler);
}
return false;
}

public function testUnauthorizedUsersCannotDismissNotice() {
$user_id = $this->signIn('editor');
$original_request = $_REQUEST;
try {
$_REQUEST['_wpnonce'] = wp_create_nonce('sr_dismiss_legacy_pages_notice');
$this->assertTrue($this->dismissalIsRejected());
$this->assertFalse(get_user_option('sr_legacy_pages_transition_dismissed', $user_id));
} finally {
$_REQUEST = $original_request;
}
}

public function testMissingAndInvalidNoncesCannotDismissNotice() {
$user_id = $this->signIn('administrator');
$original_request = $_REQUEST;
try {
unset($_REQUEST['_wpnonce']);
$this->assertTrue($this->dismissalIsRejected());
$_REQUEST['_wpnonce'] = 'invalid';
$this->assertTrue($this->dismissalIsRejected());
$this->assertFalse(get_user_option('sr_legacy_pages_transition_dismissed', $user_id));
} finally {
$_REQUEST = $original_request;
}
}

public function testOrdinaryPagesAndAutomaticDraftsDoNotTriggerTransition() {
$this->signIn('administrator');
$this->factory->post->create(array('post_type' => 'page'));
$this->factory->post->create(array('post_type' => 'sr-listings', 'post_status' => 'auto-draft'));
$this->assertFalse(SrAdminSettings::hasLegacyPages());
$this->assertSame('', $this->noticeMarkup());
ob_start();
SrAdminSettings::sr_admin_page();
$this->assertFalse(strpos(ob_get_clean(), 'data-sr-panel="legacy-pages"'));
}

public function testSavedStatusesIncludingPrivateDraftAndTrashTriggerTransition() {
foreach (array('publish', 'draft', 'private', 'pending', 'future', 'trash') as $status) {
$post_id = $this->factory->post->create(array(
'post_type' => 'sr-listings',
'post_status' => $status,
'post_date' => $status === 'future' ? '2099-01-01 00:00:00' : '2020-01-01 00:00:00'
));
$this->assertTrue(SrAdminSettings::hasLegacyPages(), $status);
wp_delete_post($post_id, true);
$this->assertFalse(SrAdminSettings::hasLegacyPages(), $status . ' removed');
}
}

public function testSynthesizedListingRoutesDoNotTriggerTransition() {
global $wp_query;
$original_query = $wp_query;
try {
foreach (array('sr-single', 'sr-search', 'sr-openhouses') as $route) {
$wp_query = new WP_Query();
$wp_query->query = array('sr-listings' => $route);
$posts = SimplyRetsCustomPostPages::srCreateDynamicPost(array());
$this->assertCount(1, $posts);
$this->assertSame('sr-listings', $posts[0]->post_type);
$this->assertFalse(SrAdminSettings::hasLegacyPages());
}
} finally {
$wp_query = $original_query;
}
}

public function testNoticeAndTabRequireSettingsAndPageEditingPermissions() {
$this->factory->post->create(array('post_type' => 'sr-listings'));
foreach (array('subscriber', 'editor') as $role) {
$this->signIn($role);
$this->assertFalse(SrAdminSettings::canManageLegacyPages());
$this->assertSame('', $this->noticeMarkup());
}
$this->signIn('subscriber');
$user = wp_get_current_user();
$user->add_cap('manage_options');
$this->assertFalse(SrAdminSettings::canManageLegacyPages());
ob_start();
SrAdminSettings::sr_admin_page();
$this->assertFalse(strpos(ob_get_clean(), 'data-sr-panel="legacy-pages"'));
$user->add_cap('edit_pages');
$this->assertTrue(SrAdminSettings::canManageLegacyPages());
}

public function testNoticeDismissalIsPerUserAndSurvivesNewPageVisits() {
$this->factory->post->create(array('post_type' => 'sr-listings'));
$user_id = $this->signIn('administrator');
$markup = $this->noticeMarkup();
$this->assertNotFalse(strpos($markup, '#sr-section-legacy-pages'));
$this->assertNotFalse(strpos($markup, 'name="_wpnonce"'));
$this->assertNotFalse(strpos($markup, 'sr_dismiss_legacy_pages_notice'));
update_user_option($user_id, 'sr_legacy_pages_transition_dismissed', 1, false);
global $wpdb;
$this->assertSame('1', (string) get_user_meta($user_id, $wpdb->prefix . 'sr_legacy_pages_transition_dismissed', true));
$this->assertSame('', get_user_meta($user_id, 'sr_legacy_pages_transition_dismissed', true));
$this->assertSame('', $this->noticeMarkup());
$this->signIn('administrator');
$this->assertNotSame('', $this->noticeMarkup());
wp_set_current_user($user_id);
$this->assertSame('', $this->noticeMarkup());
}

public function testLegacyPanelKeepsManagementWithinSettings() {
$this->signIn('administrator');
$this->factory->post->create(array('post_type' => 'sr-listings', 'post_status' => 'trash'));
ob_start();
SrAdminSettings::sr_admin_page();
$markup = ob_get_clean();
$this->assertNotFalse(strpos($markup, 'data-sr-section="legacy-pages"'));
$this->assertNotFalse(strpos($markup, 'id="sr-section-legacy-pages"'));
$this->assertFalse(strpos($markup, 'data-sr-panel="legacy-pages" hidden'));
$this->assertNotFalse(strpos($markup, esc_url(admin_url('edit.php?post_type=sr-listings'))));
$this->assertTrue(strpos($markup, 'data-sr-section="messages"') < strpos($markup, 'data-sr-section="legacy-pages"'));
$this->assertTrue(strpos($markup, 'id="sr-section-legacy-pages"') < strpos($markup, 'Manage existing pages</a>'));
}

public function testPostTypeKeepsEditorAndRoutingWithoutSidebarEntry() {
$post_type = get_post_type_object('sr-listings');
$this->assertFalse($post_type->show_in_menu);
$this->assertTrue($post_type->show_ui);
$this->assertTrue($post_type->public);
$this->assertSame('sr-listings', $post_type->query_var);
$this->assertSame('edit_pages', $post_type->cap->edit_posts);
$this->assertTrue(post_type_supports('sr-listings', 'editor'));
$post_id = $this->factory->post->create(array('post_type' => 'sr-listings', 'post_status' => 'publish'));
$this->assertNotEmpty(get_permalink($post_id));
$this->assertNotFalse(has_filter('the_posts', array('SimplyRetsCustomPostPages', 'srCreateDynamicPost')));
$this->assertNotFalse(has_action('add_meta_boxes', array('SimplyRetsCustomPostPages', 'postFilterMetaBox')));
$this->assertNotFalse(has_filter('single_template', array('SimplyRetsCustomPostPages', 'srLoadPostTemplate')));
}
}
Loading