widgetTitle(), [$this, 'renderWidget'], null, null, $this->claimColumn()); } /** * Take the leftmost dashboard column no sibling plugin has claimed yet. */ private function claimColumn() { if ($this->context !== null) { return $this->context; } $claimed = isset($GLOBALS[self::COLUMN_CLAIM_KEY]) && is_array($GLOBALS[self::COLUMN_CLAIM_KEY]) ? $GLOBALS[self::COLUMN_CLAIM_KEY] : []; foreach (self::WIDGET_COLUMNS as $column) { if (!in_array($column, $claimed, true)) { $claimed[] = $column; $GLOBALS[self::COLUMN_CLAIM_KEY] = $claimed; $this->context = $column; return $column; } } // More DaftPlug widgets than the dashboard has columns: fall back to the // first and stack, which is what would have happened without any of this. $this->context = self::WIDGET_COLUMNS[0]; return $this->context; } /** * Promote the widget to the top of the dashboard's left column. * * Only sets the default order. Once a user drags any dashboard box, * WordPress stores a per-user meta-box order and replays it at the 'sorted' * priority, ahead of 'core' โ an explicit user choice should outrank ours. */ public function promoteWidget() { global $wp_meta_boxes; if ($this->context === null || empty($wp_meta_boxes['dashboard'][$this->context]['core'][self::WIDGET_ID])) { return; } $boxes = $wp_meta_boxes['dashboard'][$this->context]['core']; $ours = [self::WIDGET_ID => $boxes[self::WIDGET_ID]]; unset($boxes[self::WIDGET_ID]); $wp_meta_boxes['dashboard'][$this->context]['core'] = array_merge($ours, $boxes); } private function widgetTitle() { $title = esc_html__('Crownify', 'crownify'); if (Utils::hasActivePro()) { $title = esc_html__('Crownify Pro', 'crownify'); } return $title; } public function renderWidget() { $audit = SiteAudit::snapshot(); $ai = SiteAudit::aiReadiness(); $geo = $this->geoSummary(); $attention = SiteAudit::needsAttention(3); // Built here, in the pure-PHP half of the method, rather than beside the // markup that uses them. Prettier hoists comments out of the short PHP // islands inside a template and copies them on every run, while a // translators note has to sit immediately above its gettext call or it // never reaches the POT. Resolving the strings up front keeps both true. // // Nothing in this comment may contain a PHP closing tag: the lexer acts // on one even inside a line comment, which silently ends PHP mode and // turns the rest of the method into output. /* translators: 1: number of AI crawlers allowed, 2: total number of AI crawlers. */ $crawlerAccess = sprintf(__('%1$s of %2$s allowed', 'crownify'), number_format_i18n((int) $ai['totalCrawlers'] - (int) $ai['blockedCount']), number_format_i18n((int) $ai['totalCrawlers'])); /* translators: %s: comma-separated crawler names. */ $blockedCitation = empty($ai['blockedCitation']) ? '' : sprintf(__('%s cannot reach you, so this site cannot be cited in their answers.', 'crownify'), implode(', ', $ai['blockedCitation'])); /* translators: %s: number of AI crawler requests in the last seven days. */ $crawlerHits = sprintf(_n('%s request in 7 days', '%s requests in 7 days', (int) $geo['crawlerHits'], 'crownify'), number_format_i18n((int) $geo['crawlerHits'])); /* translators: %s: name of the AI crawler that read the most pages. */ $topBot = '' === $geo['topBot'] ? '' : sprintf(__('Most active: %s', 'crownify'), $geo['topBot']); $this->renderStyles(); ?>
false, 'crawlerHits' => 0, 'topBot' => '', 'referralsOn' => false, 'referralHits' => 0, 'referralSources' => [], 'broken' => 0, ]; if (class_exists('\DaftPlug\Rankify\Features\Geo\CrawlerLog') && 'on' === Utils::getSetting('aeoCitationTracking')) { $summary['logging'] = true; $totals = \DaftPlug\Rankify\Features\Geo\CrawlerLog::totals(7); $summary['crawlerHits'] = isset($totals['hits']) ? (int) $totals['hits'] : 0; $bots = \DaftPlug\Rankify\Features\Geo\CrawlerLog::byBot(7); if (!empty($bots)) { $first = reset($bots); $summary['topBot'] = isset($first['source']) ? (string) $first['source'] : ''; } } if (class_exists('\DaftPlug\Rankify\Features\Geo\Referrals') && 'on' === Utils::getSetting('aiReferralTracking')) { $summary['referralsOn'] = true; // Thirty days rather than seven: a citation sends traffic in a trickle // over weeks, so a seven-day window on a small site reads as zero far // more often than it should. $sources = \DaftPlug\Rankify\Features\Geo\Referrals::bySource(30); $summary['referralHits'] = (int) array_sum(wp_list_pluck($sources, 'hits')); foreach (array_slice($sources, 0, 3) as $source) { $summary['referralSources'][] = $source['source'] . ' ' . number_format_i18n((int) $source['hits']); } } if (class_exists('\DaftPlug\Rankify\Features\Redirects\Log')) { $summary['broken'] = (int) \DaftPlug\Rankify\Features\Redirects\Log::count(); } set_transient(self::GEO_CACHE_KEY, $summary, 15 * MINUTE_IN_SECONDS); return $summary; } /** * Headline row. "Discourage search engines" outranks everything else โ a site * with it on is invisible no matter how well the rest is configured. */ private function renderStatus() { if (!get_option('blog_public')) { ?>