getPluginLogic()->userIsPluginAdmin()) {
$connector->getLogger()->logUserCapabilities("echoDashboardNotification");
return;
}
ABJ_404_Solution_WordPress_Connector::echoAdminRuntimeErrorNotice();
global $pagenow;
global $abj404view;
$isPluginPage = array_key_exists('page', $_GET) && $_GET['page'] == ABJ404_PP;
$isDashboard = $pagenow == 'index.php' && !isset($_GET['page']);
if ($isPluginPage) {
$dbNotice = get_transient('abj404_plugin_db_notice');
if (is_array($dbNotice) && isset($dbNotice['message']) && is_string($dbNotice['message'])) {
$type = isset($dbNotice['type']) && is_string($dbNotice['type']) ? $dbNotice['type'] : 'warning';
// Per owner directive: collation issues must NEVER surface as user notices.
if ($type === 'collation') {
// intentionally do not render
} else {
$warningTypes = array(
'stale_permalink_cache',
'warning',
'read_only',
'disk_full',
'query_quota',
);
$cssClass = in_array($type, $warningTypes, true) ? 'notice-warning' : 'notice-error';
echo '
' .
esc_html($dbNotice['message']) . '
';
}
}
}
if ($isPluginPage || $isDashboard) {
$captured404Count = $connector->getCapturedCountForNotification();
if ($connector->getPluginLogic()->shouldNotifyAboutCaptured404s($captured404Count)) {
$msg = $abj404view->getDashboardNotificationCaptured($captured404Count);
echo $msg;
}
self::maybeShowReviewRequest();
}
}
/**
* Handle review GET redirects and feedback POST submission on admin_init.
*
* @return void
*/
static function handleResponseRedirects() {
if (!is_admin()) {
return;
}
if (!isset($_GET['page']) || $_GET['page'] !== ABJ404_PP) {
return;
}
if (isset($_GET['abj404_review_response'])) {
$rawResponseNonce = isset($_GET['_wpnonce']) ? $_GET['_wpnonce'] : '';
$responseNonce = sanitize_text_field(ABJ_404_Solution_WordPress_Connector::normalizeRequestScalar($rawResponseNonce));
if ($responseNonce === '' || !wp_verify_nonce($responseNonce, 'abj404_review_response')) {
return;
}
$response = sanitize_text_field(ABJ_404_Solution_WordPress_Connector::normalizeRequestScalar($_GET['abj404_review_response']));
$allowedResponses = array('yes', 'not_yet', 'ask_later', 'close_x', 'never');
if (!in_array($response, $allowedResponses, true)) {
return;
}
if ($response === 'yes') {
update_user_meta(get_current_user_id(), 'abj404_review_step', 'show_review_link');
delete_user_meta(get_current_user_id(), 'abj404_review_remind_later');
} elseif ($response === 'not_yet') {
update_user_meta(get_current_user_id(), 'abj404_review_step', 'show_feedback');
delete_user_meta(get_current_user_id(), 'abj404_review_remind_later');
} elseif ($response === 'ask_later') {
update_user_meta(get_current_user_id(), 'abj404_review_remind_later', time() + (self::ASK_LATER_DELAY_DAYS * 86400));
delete_user_meta(get_current_user_id(), 'abj404_review_step');
} elseif ($response === 'close_x') {
update_user_meta(get_current_user_id(), 'abj404_review_remind_later', time() + (self::CLOSE_X_SNOOZE_DAYS * 86400));
delete_user_meta(get_current_user_id(), 'abj404_review_step');
} elseif ($response === 'never') {
update_user_meta(get_current_user_id(), 'abj404_review_dismissed', 'permanent');
delete_user_meta(get_current_user_id(), 'abj404_review_step');
delete_user_meta(get_current_user_id(), 'abj404_review_remind_later');
}
wp_safe_redirect(remove_query_arg(array('abj404_review_response', '_wpnonce')));
exit;
}
if (isset($_GET['abj404_leaving_review'])) {
$rawLeavingReviewNonce = isset($_GET['_wpnonce']) ? $_GET['_wpnonce'] : '';
$leavingReviewNonce = sanitize_text_field(ABJ_404_Solution_WordPress_Connector::normalizeRequestScalar($rawLeavingReviewNonce));
if ($leavingReviewNonce !== '' && wp_verify_nonce($leavingReviewNonce, 'abj404_leaving_review')) {
update_user_meta(get_current_user_id(), 'abj404_review_dismissed', 'permanent');
delete_user_meta(get_current_user_id(), 'abj404_review_step');
delete_user_meta(get_current_user_id(), 'abj404_review_remind_later');
$html = ABJ_404_Solution_Functions::readFileContents(__DIR__ . "/html/reviewRedirectScript.html");
$f = abj_service('functions');
$html = $f->str_replace('{review_url}', esc_js('https://wordpress.org/support/plugin/404-solution/reviews/#new-post'), $html);
echo $html;
wp_safe_redirect(remove_query_arg(array('abj404_leaving_review', '_wpnonce')));
exit;
}
}
$rawFeedbackNonce = isset($_POST['abj404_feedback_nonce']) ? $_POST['abj404_feedback_nonce'] : '';
$feedbackNonce = sanitize_text_field(ABJ_404_Solution_WordPress_Connector::normalizeRequestScalar($rawFeedbackNonce));
if (isset($_POST['abj404_submit_feedback']) &&
$feedbackNonce !== '' &&
wp_verify_nonce($feedbackNonce, 'abj404_submit_feedback')) {
$issuesRaw = isset($_POST['feedback_issues']) ? $_POST['feedback_issues'] : array();
$issues = ABJ_404_Solution_WordPress_Connector::sanitizeFeedbackIssues($issuesRaw);
$feedbackDetailsRaw = isset($_POST['feedback_details']) ? $_POST['feedback_details'] : '';
$feedback_details = sanitize_textarea_field(ABJ_404_Solution_WordPress_Connector::normalizeRequestScalar($feedbackDetailsRaw));
$feedback_data = array(
'timestamp' => current_time('mysql'),
'user_id' => get_current_user_id(),
'site_url' => get_site_url(),
'issues' => $issues,
'details' => $feedback_details,
'wp_version' => get_bloginfo('version'),
'plugin_version' => ABJ404_VERSION,
'php_version' => PHP_VERSION
);
$all_feedback_raw = get_option('abj404_user_feedback', array());
$all_feedback = is_array($all_feedback_raw) ? $all_feedback_raw : array();
$all_feedback[] = $feedback_data;
update_option('abj404_user_feedback', $all_feedback);
self::emailFeedback($feedback_data);
update_user_meta(get_current_user_id(), 'abj404_review_dismissed', 'permanent');
delete_user_meta(get_current_user_id(), 'abj404_review_step');
delete_user_meta(get_current_user_id(), 'abj404_review_remind_later');
self::$feedbackSubmitted = true;
}
}
/**
* Display a review request notification after sustained plugin use.
*
* @return void
*/
private static function maybeShowReviewRequest() {
if (!isset($_GET['page']) || $_GET['page'] !== ABJ404_PP) {
return;
}
if (self::$feedbackSubmitted) {
$html = ABJ_404_Solution_Functions::readFileContents(__DIR__ . "/html/feedbackSuccessNotice.html");
echo $html;
return;
}
$dismissed = get_user_meta(get_current_user_id(), 'abj404_review_dismissed', true);
if ($dismissed === 'permanent') {
return;
}
$remind_later = get_user_meta(get_current_user_id(), 'abj404_review_remind_later', true);
if ($remind_later && time() < $remind_later) {
return;
}
$installed_time = get_option('abj404_installed_time');
if (!$installed_time) {
$installed_time = time();
update_option('abj404_installed_time', $installed_time);
return;
}
$days_installed = (time() - $installed_time) / 86400;
if ($days_installed < self::INITIAL_DELAY_DAYS) {
return;
}
$review_step = get_user_meta(get_current_user_id(), 'abj404_review_step', true);
if ($review_step === 'show_review_link') {
self::showReviewLinkNotice();
} elseif ($review_step === 'show_feedback') {
self::showFeedbackFormNotice();
} else {
self::showQualificationQuestion();
}
}
/**
* @param array $feedback_data
* @return void
*/
private static function emailFeedback($feedback_data) {
$to = '404solution@ajexperience.com';
$subject = '404 Solution Feedback from ' . get_bloginfo('name');
$message = "New feedback received from 404 Solution plugin\n\n";
$message .= "Site: " . $feedback_data['site_url'] . "\n";
$message .= "Date: " . $feedback_data['timestamp'] . "\n";
$message .= "WordPress Version: " . $feedback_data['wp_version'] . "\n";
$message .= "Plugin Version: " . $feedback_data['plugin_version'] . "\n";
$message .= "PHP Version: " . $feedback_data['php_version'] . "\n\n";
$message .= "Issues Selected:\n";
$feedbackIssues = isset($feedback_data['issues']) && is_array($feedback_data['issues']) ? $feedback_data['issues'] : array();
if (!empty($feedbackIssues)) {
foreach ($feedbackIssues as $issue) {
$issueStr = is_string($issue) ? $issue : (string)$issue;
$message .= " - " . ucfirst(str_replace('_', ' ', $issueStr)) . "\n";
}
} else {
$message .= " None selected\n";
}
$message .= "\nAdditional Details:\n";
$message .= $feedback_data['details'] ? $feedback_data['details'] : "(No additional details provided)\n";
$headers = array('Content-Type: text/plain; charset=UTF-8');
wp_mail($to, $subject, $message, $headers);
}
/** @return void */
private static function showQualificationQuestion() {
$yes_url = wp_nonce_url(
add_query_arg('abj404_review_response', 'yes'),
'abj404_review_response'
);
$not_yet_url = wp_nonce_url(
add_query_arg('abj404_review_response', 'not_yet'),
'abj404_review_response'
);
$ask_later_url = wp_nonce_url(
add_query_arg('abj404_review_response', 'ask_later'),
'abj404_review_response'
);
$never_url = wp_nonce_url(
add_query_arg('abj404_review_response', 'never'),
'abj404_review_response'
);
$close_url = wp_nonce_url(
add_query_arg('abj404_review_response', 'close_x'),
'abj404_review_response'
);
$html = ABJ_404_Solution_Functions::readFileContents(__DIR__ . "/html/reviewQualificationQuestion.html");
$f = abj_service('functions');
$html = $f->str_replace('{yes_url}', esc_attr($yes_url), $html);
$html = $f->str_replace('{not_yet_url}', esc_attr($not_yet_url), $html);
$html = $f->str_replace('{ask_later_url}', esc_attr($ask_later_url), $html);
$html = $f->str_replace('{never_url}', esc_attr($never_url), $html);
$html = $f->str_replace('{close_url}', esc_attr($close_url), $html);
echo $html;
}
/** @return void */
private static function showReviewLinkNotice() {
$review_link_url = wp_nonce_url(
add_query_arg('abj404_leaving_review', '1'),
'abj404_leaving_review'
);
$never_url = wp_nonce_url(
add_query_arg('abj404_review_response', 'never'),
'abj404_review_response'
);
$close_url = wp_nonce_url(
add_query_arg('abj404_review_response', 'close_x'),
'abj404_review_response'
);
$html = ABJ_404_Solution_Functions::readFileContents(__DIR__ . "/html/reviewLinkNotice.html");
$f = abj_service('functions');
$html = $f->str_replace('{review_link_url}', esc_attr($review_link_url), $html);
$html = $f->str_replace('{never_url}', esc_attr($never_url), $html);
$html = $f->str_replace('{close_url}', esc_attr($close_url), $html);
echo $html;
}
/** @return void */
private static function showFeedbackFormNotice() {
$never_url = wp_nonce_url(
add_query_arg('abj404_review_response', 'never'),
'abj404_review_response'
);
$close_url = wp_nonce_url(
add_query_arg('abj404_review_response', 'close_x'),
'abj404_review_response'
);
ob_start();
wp_nonce_field('abj404_submit_feedback', 'abj404_feedback_nonce');
$nonce_field = ob_get_clean();
if ($nonce_field === false) { $nonce_field = ''; }
$html = ABJ_404_Solution_Functions::readFileContents(__DIR__ . "/html/feedbackFormNotice.html");
$f = abj_service('functions');
$html = $f->str_replace('{nonce_field}', $nonce_field, $html);
$html = $f->str_replace('{never_url}', esc_attr($never_url), $html);
$html = $f->str_replace('{close_url}', esc_attr($close_url), $html);
echo $html;
}
}