| Current Path : /home/x/b/o/xbodynamge/namtation/wp-content/ |
| Current File : /home/x/b/o/xbodynamge/namtation/wp-content/EmailReports.tar |
Mail.php 0000666 00000001163 15114521462 0006145 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\EmailReports;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Mail class.
*
* @since 4.7.2
*/
class Mail {
/**
* Send the email.
*
* @since 4.7.2
*
* @param mixed $to Receiver.
* @param mixed $subject Email subject.
* @param mixed $message Message.
* @param array $headers Email headers.
* @return bool Whether the email was sent successfully.
*/
public function send( $to, $subject, $message, $headers = [ 'Content-Type: text/html; charset=UTF-8' ] ) {
return wp_mail( $to, $subject, $message, $headers );
}
} Summary/Content.php 0000666 00000050371 15114521462 0010337 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\EmailReports\Summary;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use AIOSEO\Plugin\Common\Models;
/**
* Summary content class.
*
* @since 4.7.2
*/
class Content {
/**
* The date range data.
*
* @since 4.7.2
*
* @var array
*/
public $dateRange;
/**
* The SEO Statistics data.
*
* @since 4.7.2
*
* @var array
*/
private $seoStatistics = [];
/**
* The Keywords data.
*
* @since 4.7.2
*
* @var array
*/
private $keywords = [];
/**
* The Search Statistics page URL.
*
* @since 4.7.2
*
* @var string
*/
public $searchStatisticsUrl;
/**
* The featured image placeholder URL.
*
* @since 4.7.3
*
* @var string
*/
public $featuredImagePlaceholder = 'https://static.aioseo.io/report/ste/featured-image-placeholder.png';
/**
* Class constructor.
*
* @since 4.7.2
*
* @param array $dateRange The date range data.
* @return void
*/
public function __construct( $dateRange ) {
$this->dateRange = $dateRange;
$this->searchStatisticsUrl = admin_url( 'admin.php?page=aioseo-search-statistics' );
$this->setSeoStatistics();
$this->setKeywords();
}
/**
* Sets the SEO Statistics data.
*
* @since 4.7.2
*
* @return void
*/
private function setSeoStatistics() {
try {
$seoStatistics = aioseo()->searchStatistics->getSeoStatisticsData( [
'startDate' => gmdate( 'Y-m-d', $this->dateRange['startDateRaw'] ),
'endDate' => gmdate( 'Y-m-d', $this->dateRange['endDateRaw'] ),
'orderBy' => 'clicks',
'orderDir' => 'desc',
'limit' => '5',
'offset' => '0',
'filter' => 'all',
] );
if ( empty( $seoStatistics['data'] ) ) {
return;
}
$this->seoStatistics = $seoStatistics['data'];
} catch ( \Exception $e ) {
// Do nothing.
}
}
/**
* Sets the Keywords data.
*
* @since 4.7.2
*
* @return void
*/
private function setKeywords() {
try {
$keywords = aioseo()->searchStatistics->getKeywordsData( [
'startDate' => gmdate( 'Y-m-d', $this->dateRange['startDateRaw'] ),
'endDate' => gmdate( 'Y-m-d', $this->dateRange['endDateRaw'] ),
'orderBy' => 'clicks',
'orderDir' => 'desc',
'limit' => '5',
'offset' => '0',
'filter' => 'all',
] );
if ( empty( $keywords['data'] ) ) {
return;
}
$this->keywords = $keywords['data'];
} catch ( \Exception $e ) {
// Do nothing.
}
}
/**
* Retrieves the content performance data.
*
* @since 4.7.2
*
* @return array The content performance data or an empty array.
*/
public function getPostsStatistics() {
if ( ! $this->seoStatistics ) {
return [];
}
$result = [
'winning' => [
'url' => add_query_arg( [
'aioseo-scroll' => 'aioseo-search-statistics-post-table',
'aioseo-tab' => 'seo-statistics',
'table-filter' => 'TopWinningPages'
], $this->searchStatisticsUrl ),
'items' => []
],
'losing' => [
'url' => add_query_arg( [
'aioseo-scroll' => 'aioseo-search-statistics-post-table',
'aioseo-tab' => 'seo-statistics',
'table-filter' => 'TopLosingPages'
], $this->searchStatisticsUrl ),
'items' => []
]
];
foreach ( array_slice( $this->seoStatistics['pages']['topWinning']['rows'], 0, 3 ) as $row ) {
$postId = $row['objectId'] ?? 0;
$result['winning']['items'][] = [
'title' => $row['objectTitle'],
'url' => get_permalink( $postId ),
'tru_seo' => aioseo()->helpers->isTruSeoEligible( $postId ) ? $this->parseSeoScore( $row['seoScore'] ?? 0 ) : [],
'clicks' => $this->parseClicks( $row['clicks'] ),
'difference' => [
'clicks' => $this->parseDifference( $row['difference']['clicks'] ?? '' ),
]
];
}
$result['winning']['show_tru_seo'] = ! empty( array_filter( array_column( $result['winning']['items'], 'tru_seo' ) ) );
foreach ( array_slice( $this->seoStatistics['pages']['topLosing']['rows'], 0, 3 ) as $row ) {
$postId = $row['objectId'] ?? 0;
$result['losing']['items'][] = [
'title' => $row['objectTitle'],
'url' => get_permalink( $postId ),
'tru_seo' => aioseo()->helpers->isTruSeoEligible( $postId ) ? $this->parseSeoScore( $row['seoScore'] ?? 0 ) : [],
'clicks' => $this->parseClicks( $row['clicks'] ),
'difference' => [
'clicks' => $this->parseDifference( $row['difference']['clicks'] ?? '' ),
]
];
}
$result['losing']['show_tru_seo'] = ! empty( array_filter( array_column( $result['losing']['items'], 'tru_seo' ) ) );
return $result;
}
/**
* Retrieves the milestones data.
*
* @since 4.7.2
*
* @return array The milestones data or an empty array.
*/
public function getMilestones() { // phpcs:ignore Generic.Files.LineLength.MaxExceeded
$milestones = [];
if ( ! $this->seoStatistics ) {
return $milestones;
}
$currentData = [
'impressions' => $this->seoStatistics['statistics']['impressions'] ?? null,
'clicks' => $this->seoStatistics['statistics']['clicks'] ?? null,
'ctr' => $this->seoStatistics['statistics']['ctr'] ?? null,
'keywords' => $this->seoStatistics['statistics']['keywords'] ?? null,
];
$difference = [
'impressions' => $this->seoStatistics['statistics']['difference']['impressions'] ?? null,
'clicks' => $this->seoStatistics['statistics']['difference']['clicks'] ?? null,
'ctr' => $this->seoStatistics['statistics']['difference']['ctr'] ?? null,
'keywords' => $this->seoStatistics['statistics']['difference']['keywords'] ?? null,
];
if ( is_numeric( $currentData['impressions'] ) && is_numeric( $difference['impressions'] ) ) {
$intDifference = intval( $difference['impressions'] );
$message = esc_html__( 'Your site has received the same number of impressions compared to the previous period.', 'all-in-one-seo-pack' );
if ( $intDifference > 0 ) {
// Translators: 1 - The number of impressions, 2 - The percentage increase.
$message = esc_html__( 'Your site has received %1$s more impressions compared to the previous period, which is a %2$s increase.', 'all-in-one-seo-pack' );
}
if ( $intDifference < 0 ) {
// Translators: 1 - The number of impressions, 2 - The percentage increase.
$message = esc_html__( 'Your site has received %1$s fewer impressions compared to the previous period, which is a %2$s decrease.', 'all-in-one-seo-pack' );
}
if ( false !== strpos( $message, '%1' ) ) {
$percentageDiff = 0 === absint( $currentData['impressions'] )
? 100
: round( ( absint( $intDifference ) / absint( $currentData['impressions'] ) ) * 100, 2 );
$percentageDiff = false !== strpos( $percentageDiff, '.' )
? number_format_i18n( $percentageDiff, count( explode( '.', $percentageDiff ) ) )
: $percentageDiff;
$message = sprintf(
$message,
'<strong>' . aioseo()->helpers->compactNumber( absint( $intDifference ) ) . '</strong>',
'<strong>' . $percentageDiff . '%</strong>'
);
}
$milestones[] = [
'message' => $message,
'background' => '#f0f6ff',
'color' => '#004F9D',
'icon' => 'icon-milestone-impressions'
];
}
if ( is_numeric( $currentData['clicks'] ) && is_numeric( $difference['clicks'] ) ) {
$intDifference = intval( $difference['clicks'] );
$message = esc_html__( 'Your site has received the same number of clicks compared to the previous period.', 'all-in-one-seo-pack' );
if ( $intDifference > 0 ) {
// Translators: 1 - The number of clicks, 2 - The percentage increase.
$message = esc_html__( 'Your site has received %1$s more clicks compared to the previous period, which is a %2$s increase.', 'all-in-one-seo-pack' );
}
if ( $intDifference < 0 ) {
// Translators: 1 - The number of clicks, 2 - The percentage increase.
$message = esc_html__( 'Your site has received %1$s fewer clicks compared to the previous period, which is a %2$s decrease.', 'all-in-one-seo-pack' );
}
if ( false !== strpos( $message, '%1' ) ) {
$percentageDiff = 0 === absint( $currentData['clicks'] )
? 100
: round( ( absint( $intDifference ) / absint( $currentData['clicks'] ) ) * 100, 2 );
$percentageDiff = false !== strpos( $percentageDiff, '.' )
? number_format_i18n( $percentageDiff, count( explode( '.', $percentageDiff ) ) )
: $percentageDiff;
$message = sprintf(
$message,
'<strong>' . aioseo()->helpers->compactNumber( absint( $intDifference ) ) . '</strong>',
'<strong>' . $percentageDiff . '%</strong>'
);
}
$milestones[] = [
'message' => $message,
'background' => '#ecfdf5',
'color' => '#077647',
'icon' => 'icon-milestone-clicks'
];
}
if ( is_numeric( $currentData['ctr'] ) && is_numeric( $difference['ctr'] ) ) {
$intDifference = floatval( $difference['ctr'] );
$message = esc_html__( 'Your site has the same CTR compared to the previous period.', 'all-in-one-seo-pack' );
if ( $intDifference > 0 ) {
// Translators: 1 - The CTR.
$message = esc_html__( 'Your site has a %1$s higher CTR compared to the previous period.', 'all-in-one-seo-pack' );
}
if ( $intDifference < 0 ) {
// Translators: 1 - The CTR.
$message = esc_html__( 'Your site has a %1$s lower CTR compared to the previous period.', 'all-in-one-seo-pack' );
}
if ( false !== strpos( $message, '%1' ) ) {
$message = sprintf(
$message,
'<strong>' . number_format_i18n( abs( $intDifference ), count( explode( '.', $intDifference ) ) ) . '%</strong>'
);
}
$milestones[] = [
'message' => $message,
'background' => '#fffbeb',
'color' => '#be6903',
'icon' => 'icon-milestone-ctr'
];
}
if ( is_numeric( $currentData['keywords'] ) && is_numeric( $difference['keywords'] ) ) {
$intDifference = intval( $difference['keywords'] );
$message = esc_html__( 'Your site ranked for the same number of keywords compared to the previous period.', 'all-in-one-seo-pack' );
if ( $intDifference > 0 ) {
// Translators: 1 - The number of keywords, 2 - The percentage increase.
$message = esc_html__( 'Your site ranked for %1$s more keywords compared to the previous period, which is a %2$s increase.', 'all-in-one-seo-pack' );
}
if ( $intDifference < 0 ) {
// Translators: 1 - The number of keywords, 2 - The percentage increase.
$message = esc_html__( 'Your site ranked for %1$s fewer keywords compared to the previous period, which is a %2$s decrease.', 'all-in-one-seo-pack' );
}
if ( false !== strpos( $message, '%1' ) ) {
$percentageDiff = 0 === absint( $currentData['keywords'] )
? 100
: round( ( absint( $intDifference ) / absint( $currentData['keywords'] ) ) * 100, 2 );
$percentageDiff = false !== strpos( $percentageDiff, '.' )
? number_format_i18n( $percentageDiff, count( explode( '.', $percentageDiff ) ) )
: $percentageDiff;
$message = sprintf(
$message,
'<strong>' . aioseo()->helpers->compactNumber( absint( $intDifference ) ) . '</strong>',
'<strong>' . $percentageDiff . '%</strong>'
);
}
$milestones[] = [
'message' => $message,
'background' => '#fef2f2',
'color' => '#ab2039',
'icon' => 'icon-milestone-keywords'
];
}
return $milestones;
}
/**
* Retrieves the keyword performance data.
*
* @since 4.7.2
*
* @return array The keyword performance data or an empty array.
*/
public function getKeywords() {
if ( ! $this->keywords ) {
return [];
}
$result = [
'winning' => [
'url' => add_query_arg( [
'aioseo-scroll' => 'aioseo-search-statistics-keywords-table',
'aioseo-tab' => 'keyword-rank-tracker',
'tab' => 'AllKeywords',
'table-filter' => 'TopWinningKeywords'
], $this->searchStatisticsUrl ),
'items' => []
],
'losing' => [
'url' => add_query_arg( [
'aioseo-scroll' => 'aioseo-search-statistics-keywords-table',
'aioseo-tab' => 'keyword-rank-tracker',
'tab' => 'AllKeywords',
'table-filter' => 'TopLosingKeywords'
], $this->searchStatisticsUrl ),
'items' => []
]
];
foreach ( array_slice( $this->keywords['topWinning'], 0, 3 ) as $row ) {
$result['winning']['items'][] = [
'title' => $row['keyword'],
'clicks' => $this->parseClicks( $row['clicks'] ),
'difference' => [
'clicks' => $this->parseDifference( $row['difference']['clicks'] ?? '' ),
]
];
}
foreach ( array_slice( $this->keywords['topLosing'], 0, 3 ) as $row ) {
$result['losing']['items'][] = [
'title' => $row['keyword'],
'clicks' => $this->parseClicks( $row['clicks'] ),
'difference' => [
'clicks' => $this->parseDifference( $row['difference']['clicks'] ?? '' ),
]
];
}
return $result;
}
/**
* Retrieves the posts data.
*
* @since 4.7.2
*
* @return array The posts' data.
*/
public function getAioPosts() {
$result = [
'publish' => [],
'optimize' => [],
'cta' => [
'text' => esc_html__( 'Create New Post', 'all-in-one-seo-pack' ),
'url' => admin_url( 'post-new.php' )
],
];
// 1. Retrieve the published posts.
$publishPosts = aioseo()->core->db
->start( 'posts as wp' )
->select( 'wp.ID, wp.post_title, aio.seo_score' )
->join( 'aioseo_posts as aio', 'aio.post_id = wp.ID', 'INNER' )
->whereIn( 'wp.post_type', [ 'post' ] )
->whereIn( 'wp.post_status', [ 'publish' ] )
->orderBy( 'wp.post_date DESC' )
->limit( 5 )
->run()
->result();
if ( $publishPosts ) {
$items = $this->parsePosts( $publishPosts );
$result['publish'] = [
'url' => admin_url( 'edit.php?post_status=publish&post_type=post' ),
'items' => $items,
'show_stats' => ! empty( array_filter( array_column( $items, 'stats' ) ) ),
'show_tru_seo' => ! empty( array_filter( array_column( $items, 'tru_seo' ) ) ),
];
}
// 2. Retrieve the posts to optimize.
$optimizePosts = aioseo()->searchStatistics->getContentRankingsData( [
'endDate' => gmdate( 'Y-m-d', $this->dateRange['endDateRaw'] ),
'orderBy' => 'decayPercent',
'orderDir' => 'asc',
'limit' => '3',
'offset' => '0',
'filter' => 'all',
] );
if ( is_array( $optimizePosts['data']['paginated']['rows'] ?? '' ) ) {
$items = [];
foreach ( array_slice( $optimizePosts['data']['paginated']['rows'], 0, 3 ) as $i => $row ) {
$postId = $row['objectId'] ?? 0;
$items[ $i ] = [
'title' => $row['objectTitle'],
'url' => get_permalink( $postId ),
'image_url' => $this->getThumbnailUrl( $postId ),
'tru_seo' => aioseo()->helpers->isTruSeoEligible( $postId ) ? $this->parseSeoScore( $row['seoScore'] ?? 0 ) : [],
'decay_percent' => $this->parseDifference( $row['decayPercent'] ?? '', true ),
'issues' => [
'url' => add_query_arg( [
'aioseo-tab' => 'post-detail',
'post' => $postId
], $this->searchStatisticsUrl ),
'items' => []
]
];
$aioPost = Models\Post::getPost( $postId );
if ( $aioPost ) {
$items[ $i ]['issues']['items'] = aioseo()->searchStatistics->helpers->getSuggestedChanges( $aioPost );
}
}
$result['optimize'] = [
'url' => add_query_arg( [
'aioseo-tab' => 'content-rankings',
], $this->searchStatisticsUrl ),
'items' => $items,
'show_tru_seo' => ! empty( array_filter( array_column( $items, 'tru_seo' ) ) ),
];
}
return $result;
}
/**
* Retrieves the resources data.
*
* @since 4.7.2
*
* @return array The resources' data.
*/
public function getResources() {
$items = aioseo()->helpers->fetchAioseoArticles( true );
return array_slice( array_filter( $items ), 0, 3 );
}
/**
* Returns if Search Statistics content is allowed.
*
* @since 4.7.3
*
* @return bool Whether Search Statistics content is allowed.
*/
public function allowSearchStatistics() {
static $return = null;
if ( isset( $return ) ) {
return $return;
}
$return = aioseo()->searchStatistics->api->auth->isConnected() &&
aioseo()->license &&
aioseo()->license->hasCoreFeature( 'search-statistics', 'seo-statistics' ) &&
aioseo()->license->hasCoreFeature( 'search-statistics', 'keyword-rankings' );
return $return;
}
/**
* Parses the SEO score.
*
* @since 4.7.2
*
* @param int|string $score The SEO score.
* @return array The parsed SEO score.
*/
private function parseSeoScore( $score ) {
$score = intval( $score );
$parsed = [
'value' => $score,
'color' => '#a1a1a1',
'text' => $score ? "$score/100" : esc_html__( 'N/A', 'all-in-one-seo-pack' ),
];
if ( $parsed['value'] > 79 ) {
$parsed['color'] = '#00aa63';
} elseif ( $parsed['value'] > 49 ) {
$parsed['color'] = '#ff8c00';
} elseif ( $parsed['value'] > 0 ) {
$parsed['color'] = '#df2a4a';
}
return $parsed;
}
/**
* Parses a difference.
*
* @since 4.7.2
*
* @param int|string $number The number to parse.
* @param bool $percentage Whether to return the text result as a percentage.
* @return array The parsed result.
*/
private function parseDifference( $number, $percentage = false ) {
$parsed = [
'color' => '#a1a1a1',
'text' => esc_html__( 'N/A', 'all-in-one-seo-pack' ),
];
if ( ! is_numeric( $number ) ) {
return $parsed;
}
$number = intval( $number );
$parsed['text'] = aioseo()->helpers->compactNumber( absint( $number ) );
if ( $percentage ) {
$parsed['text'] = $number . '%';
}
if ( $number > 0 ) {
$parsed['color'] = '#00aa63';
} elseif ( $number < 0 ) {
$parsed['color'] = '#df2a4a';
}
return $parsed;
}
/**
* Parses the clicks number.
*
* @since 4.7.2
*
* @param float|int|string $number The number of clicks.
* @return string The parsed number of clicks.
*/
private function parseClicks( $number ) {
return aioseo()->helpers->compactNumber( $number );
}
/**
* Parses the posts data.
*
* @since 4.7.2
*
* @param array $posts The posts.
* @return array The parsed posts' data.
*/
private function parsePosts( $posts ) {
$parsed = [];
foreach ( $posts as $k => $item ) {
$parsed[ $k ] = [
'title' => aioseo()->helpers->truncate( $item->post_title, 75 ),
'url' => get_permalink( $item->ID ),
'image_url' => $this->getThumbnailUrl( $item->ID ),
'tru_seo' => aioseo()->helpers->isTruSeoEligible( $item->ID ) ? $this->parseSeoScore( $item->seo_score ?? 0 ) : [],
'stats' => []
];
try {
$statistics = [];
if (
$this->allowSearchStatistics() &&
method_exists( aioseo()->searchStatistics, 'getPostDetailSeoStatisticsData' )
) {
$statistics = aioseo()->searchStatistics->getPostDetailSeoStatisticsData( [
'startDate' => gmdate( 'Y-m-d', $this->dateRange['startDateRaw'] ),
'endDate' => gmdate( 'Y-m-d', $this->dateRange['endDateRaw'] ),
'postId' => $item->ID,
], false );
}
if ( isset( $statistics['data']['statistics']['position'] ) ) {
$parsed[ $k ]['stats'][] = [
'icon' => 'position',
'label' => esc_html__( 'Position', 'all-in-one-seo-pack' ),
'value' => round( floatval( $statistics['data']['statistics']['position'] ) ),
];
}
if ( isset( $statistics['data']['statistics']['ctr'] ) ) {
$value = round( floatval( $statistics['data']['statistics']['ctr'] ), 2 );
$parsed[ $k ]['stats'][] = [
'icon' => 'ctr',
'label' => 'CTR',
'value' => ( number_format_i18n( $value, count( explode( '.', $value ) ) ) ) . '%',
];
}
if ( isset( $statistics['data']['statistics']['impressions'] ) ) {
$parsed[ $k ]['stats'][] = [
'icon' => 'impressions',
'label' => esc_html__( 'Impressions', 'all-in-one-seo-pack' ),
'value' => aioseo()->helpers->compactNumber( $statistics['data']['statistics']['impressions'] ),
];
}
} catch ( \Exception $e ) {
// Do nothing.
}
}
return $parsed;
}
/**
* Retrieves the thumbnail URL.
*
* @since 4.7.2
*
* @param int $postId The post ID.
* @return string The post featured image URL (thumbnail size).
*/
private function getThumbnailUrl( $postId ) {
$imageUrl = get_the_post_thumbnail_url( $postId );
return $imageUrl ?: $this->featuredImagePlaceholder;
}
} Summary/Summary.php 0000666 00000022740 15114521462 0010361 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\EmailReports\Summary;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Summary class.
*
* @since 4.7.2
*/
class Summary {
/**
* The action hook to execute when the event is run.
*
* @since 4.7.2
*
* @var string
*/
public $actionHook = 'aioseo_report_summary';
/**
* Recipient for the email. Multiple recipients can be separated by a comma.
*
* @since 4.7.2
*
* @var string
*/
private $recipient;
/**
* Email chosen frequency. Can be either 'weekly' or 'monthly'.
*
* @since 4.7.2
*
* @var string
*/
private $frequency;
/**
* Class constructor.
*
* @since 4.7.2
*/
public function __construct() {
// No need to run any of this during a WP AJAX request.
if ( wp_doing_ajax() ) {
return;
}
// No need to keep trying scheduling unless on admin.
add_action( 'admin_init', [ $this, 'maybeSchedule' ], 20 );
add_action( $this->actionHook, [ $this, 'cronTrigger' ] );
}
/**
* The summary cron callback.
* Hooked into `{@see self::$actionHook}` action hook.
*
* @since 4.7.2
*
* @param string $frequency The frequency of the email.
* @return void
*/
public function cronTrigger( $frequency ) {
// Keep going only if the feature is enabled.
if (
! aioseo()->options->advanced->emailSummary->enable ||
! apply_filters( 'aioseo_report_summary_enable', true, $frequency )
) {
return;
}
// Get all recipients for the given frequency.
$recipients = wp_list_filter( aioseo()->options->advanced->emailSummary->recipients, [ 'frequency' => $frequency ] );
if ( ! $recipients ) {
return;
}
try {
// Get only the email addresses.
$recipients = array_column( $recipients, 'email' );
$this->run( [
'recipient' => implode( ',', $recipients ),
'frequency' => $frequency,
] );
} catch ( \Exception $e ) {
// Do nothing.
}
}
/**
* Trigger the sending of the summary.
*
* @since 4.7.2
*
* @param array $data All the initial data needed for the summary to be sent.
* @throws \Exception If the email could not be sent.
* @return void
*/
public function run( $data ) {
try {
$this->recipient = $data['recipient'] ?? '';
$this->frequency = $data['frequency'] ?? '';
aioseo()->emailReports->mail->send( $this->getRecipient(), $this->getSubject(), $this->getContentHtml(), $this->getHeaders() );
} catch ( \Exception $e ) {
throw new \Exception( esc_html( $e->getMessage() ), esc_html( $e->getCode() ) );
}
}
/**
* Maybe (re)schedule the summary.
*
* @since 4.7.2
*
* @return void
*/
public function maybeSchedule() {
$allowedFrequencies = $this->getAllowedFrequencies();
// Add at least 6 hours after the day starts.
$addToStart = HOUR_IN_SECONDS * 6;
// Add the timezone offset.
$addToStart -= aioseo()->helpers->getTimeZoneOffset();
// Add a random time offset to avoid all emails being sent at the same time. 1440 * 3 = 3 days range.
$addToStart += aioseo()->helpers->generateRandomTimeOffset( aioseo()->helpers->getSiteDomain( true ), 1440 * 3 ) * MINUTE_IN_SECONDS;
foreach ( $allowedFrequencies as $frequency => $data ) {
aioseo()->actionScheduler->scheduleRecurrent( $this->actionHook, $data['start'] + $addToStart, $data['interval'], compact( 'frequency' ) );
}
}
/**
* Get one or more valid recipients.
*
* @since 4.7.2
*
* @throws \Exception If no valid recipient was set for the email.
* @return string The valid recipients.
*/
private function getRecipient() {
$recipients = array_map( 'trim', explode( ',', $this->recipient ) );
$recipients = array_filter( $recipients, 'is_email' );
if ( empty( $recipients ) ) {
throw new \Exception( 'No valid recipient was set for the email.' ); // Not shown to the user.
}
return implode( ',', $recipients );
}
/**
* Get email subject.
*
* @since 4.7.2
*
* @return string The email subject.
*/
private function getSubject() {
// Translators: 1 - Date range.
$out = esc_html__( 'Your SEO Performance Report for %1$s', 'all-in-one-seo-pack' );
$dateRange = $this->getDateRange();
$suffix = date_i18n( 'F', $dateRange['endDateRaw'] );
if ( 'weekly' === $this->frequency ) {
$suffix = $dateRange['range'];
}
return sprintf( $out, $suffix );
}
/**
* Get content html.
*
* @since 4.7.2
*
* @return string The email content.
*/
private function getContentHtml() { // phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
$dateRange = $this->getDateRange();
$content = new Content( $dateRange );
$upsell = [
'search-statistics' => []
];
$preHeader = sprintf(
// Translators: 1 - The plugin short name ("AIOSEO").
esc_html__( 'Dive into your top-performing pages with %1$s and uncover growth opportunities.', 'all-in-one-seo-pack' ),
AIOSEO_PLUGIN_SHORT_NAME
);
$iconCalendar = 'weekly' === $this->frequency
? 'icon-calendar-weekly'
: 'icon-calendar-monthly';
$heading = 'weekly' === $this->frequency
? esc_html__( 'Your Weekly SEO Email Summary', 'all-in-one-seo-pack' )
: esc_html__( 'Your Monthly SEO Email Summary', 'all-in-one-seo-pack' );
$subheading = 'weekly' === $this->frequency
? esc_html__( 'Let\'s take a look at your SEO updates and content progress this week.', 'all-in-one-seo-pack' )
: esc_html__( 'Let\'s take a look at your SEO updates and content progress this month.', 'all-in-one-seo-pack' );
$statisticsReport = [
'posts' => [],
'keywords' => [],
'milestones' => [],
'cta' => [
'text' => esc_html__( 'See All SEO Statistics', 'all-in-one-seo-pack' ),
'url' => $content->searchStatisticsUrl
],
];
if ( ! $content->allowSearchStatistics() ) {
$upsell['search-statistics'] = [
'cta' => [
'text' => esc_html__( 'Unlock Search Statistics', 'all-in-one-seo-pack' ),
'url' => $content->searchStatisticsUrl,
],
];
}
if ( ! $upsell['search-statistics'] ) {
$subheading = 'weekly' === $this->frequency
? esc_html__( 'Let\'s take a look at how your site has performed in search results this week.', 'all-in-one-seo-pack' )
: esc_html__( 'Let\'s take a look at how your site has performed in search results this month.', 'all-in-one-seo-pack' );
$statisticsReport['posts'] = $content->getPostsStatistics();
$statisticsReport['keywords'] = $content->getKeywords();
$statisticsReport['milestones'] = $content->getMilestones();
}
$mktUrl = trailingslashit( AIOSEO_MARKETING_URL );
$medium = 'email-report-summary';
$posts = $content->getAioPosts();
$resources = [
'posts' => array_map( function ( $item ) use ( $medium, $content ) {
return array_merge( $item, [
'url' => aioseo()->helpers->utmUrl( $item['url'], $medium ),
'image' => [
'url' => ! empty( $item['image']['sizes']['medium']['source_url'] )
? $item['image']['sizes']['medium']['source_url']
: $content->featuredImagePlaceholder
]
] );
}, $content->getResources() ),
'cta' => [
'text' => esc_html__( 'See All Resources', 'all-in-one-seo-pack' ),
'url' => aioseo()->helpers->utmUrl( 'https://aioseo.com/blog/', $medium ),
],
];
$links = [
'disable' => admin_url( 'admin.php?page=aioseo-settings&aioseo-scroll=aioseo-email-summary-row&aioseo-highlight=aioseo-email-summary-row&aioseo-tab=advanced' ),
'update' => admin_url( 'update-core.php' ),
'marketing-site' => aioseo()->helpers->utmUrl( $mktUrl, $medium ),
'facebook' => aioseo()->helpers->utmUrl( $mktUrl . 'plugin/facebook', $medium ),
'linkedin' => aioseo()->helpers->utmUrl( $mktUrl . 'plugin/linkedin', $medium ),
'youtube' => aioseo()->helpers->utmUrl( $mktUrl . 'plugin/youtube', $medium ),
'twitter' => aioseo()->helpers->utmUrl( $mktUrl . 'plugin/twitter', $medium ),
];
ob_start();
require AIOSEO_DIR . '/app/Common/Views/report/summary.php';
return ob_get_clean();
}
/**
* Get email headers.
*
* @since 4.7.2
*
* @return array The email headers.
*/
private function getHeaders() {
return [ 'Content-Type: text/html; charset=UTF-8' ];
}
/**
* Get all allowed frequencies.
*
* @since 4.7.2
*
* @return array The email allowed frequencies.
*/
private function getAllowedFrequencies() {
$time = time();
$secondsTillNow = $time - strtotime( 'today' );
return [
'weekly' => [
'interval' => WEEK_IN_SECONDS,
'start' => strtotime( 'next Monday' ) - $time
],
'monthly' => [
'interval' => MONTH_IN_SECONDS,
'start' => ( strtotime( 'first day of next month' ) + ( DAY_IN_SECONDS * 2 ) - $secondsTillNow ) - $time
]
];
}
/**
* Retrieves the date range data based on the frequency.
*
* @since 4.7.3
*
* @return array The date range data.
*/
private function getDateRange() {
$dateFormat = get_option( 'date_format' );
// If frequency is 'monthly'.
$endDateRaw = strtotime( 'last day of last month' );
$startDateRaw = strtotime( 'first day of last month' );
// If frequency is 'weekly'.
if ( 'weekly' === $this->frequency ) {
$endDateRaw = strtotime( 'last Saturday' );
$startDateRaw = strtotime( 'last Sunday', $endDateRaw );
}
$endDate = date_i18n( $dateFormat, $endDateRaw );
$startDate = date_i18n( $dateFormat, $startDateRaw );
return [
'endDate' => $endDate,
'endDateRaw' => $endDateRaw,
'startDate' => $startDate,
'startDateRaw' => $startDateRaw,
'range' => "$startDate - $endDate",
];
}
} EmailReports.php 0000666 00000004222 15114521462 0007670 0 ustar 00 <?php
namespace AIOSEO\Plugin\Common\EmailReports;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use AIOSEO\Plugin\Common\Models;
/**
* EmailReports class.
*
* @since 4.7.2
*/
class EmailReports {
/**
* Mail object.
*
* @since 4.7.2
*
* @var Mail
*/
public $mail = null;
/**
* Summary object.
*
* @since 4.7.2
*
* @var Summary\Summary
*/
public $summary;
/**
* Class constructor.
*
* @since 4.7.2
*/
public function __construct() {
$this->mail = new Mail();
$this->summary = new Summary\Summary();
add_action( 'aioseo_email_reports_enable_reminder', [ $this, 'enableReminder' ] );
}
/**
* Enable reminder.
*
* @since 4.7.7
*
* @return void
*/
public function enableReminder() {
// User already enabled email reports.
if ( aioseo()->options->advanced->emailSummary->enable ) {
return;
}
// Check if notification exists.
$notification = Models\Notification::getNotificationByName( 'email-reports-enable-reminder' );
if ( $notification->exists() ) {
return;
}
// Add notification.
Models\Notification::addNotification( [
'slug' => uniqid(),
'notification_name' => 'email-reports-enable-reminder',
'title' => __( 'Email Reports', 'all-in-one-seo-pack' ),
'content' => __( 'Stay ahead in SEO with our new email digest! Get the latest tips, trends, and tools delivered right to your inbox, helping you optimize smarter and faster. Enable it today and never miss an update that can take your rankings to the next level.', 'all-in-one-seo-pack' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded
'type' => 'info',
'level' => [ 'all' ],
'button1_label' => __( 'Enable Email Reports', 'all-in-one-seo-pack' ),
'button1_action' => 'https://route#aioseo-settings&aioseo-scroll=aioseo-email-summary-row&aioseo-highlight=aioseo-email-summary-row:advanced',
'button2_label' => __( 'All Good, I\'m already getting it', 'all-in-one-seo-pack' ),
'button2_action' => 'http://action#notification/email-reports-enable',
'start' => gmdate( 'Y-m-d H:i:s' )
] );
}
}