369 lines
12 KiB
PHP
369 lines
12 KiB
PHP
<?php
|
|
/**
|
|
* IP 归属地 — shared storage, format, and display helpers.
|
|
*
|
|
* Display positions (ip_geo_display): comment | user_home | user_center | post
|
|
* Meta:
|
|
* comment → aksrt_comment_addr (array)
|
|
* user → aksrt_user_addr (array), aksrt_register_ip (string)
|
|
*
|
|
* @package AKSRT
|
|
*/
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
if (!function_exists('aksrt_get_ip_geo_config')) :
|
|
/**
|
|
* @return array<string,mixed>
|
|
*/
|
|
function aksrt_get_ip_geo_config(): array {
|
|
if (class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'get_ip_geo_settings')) {
|
|
return \AKSRT\Settings\Settings::get_ip_geo_settings();
|
|
}
|
|
return [
|
|
'ip_geo_enabled' => false,
|
|
'ip_geo_display' => ['comment'],
|
|
'ip_geo_format' => 'province_city',
|
|
'ip_geo_fallback_text' => '',
|
|
];
|
|
}
|
|
endif;
|
|
|
|
if (!function_exists('aksrt_ip_geo_master_enabled')) :
|
|
function aksrt_ip_geo_master_enabled(): bool {
|
|
$c = aksrt_get_ip_geo_config();
|
|
return !empty($c['ip_geo_enabled']);
|
|
}
|
|
endif;
|
|
|
|
if (!function_exists('aksrt_ip_geo_display_enabled')) :
|
|
/**
|
|
* Whether a surface may show IP 归属地 badges.
|
|
*
|
|
* @param string $location comment|user_home|user_center|post
|
|
*/
|
|
function aksrt_ip_geo_display_enabled(string $location): bool {
|
|
if (!aksrt_ip_geo_master_enabled()) {
|
|
return false;
|
|
}
|
|
$location = sanitize_key($location);
|
|
$c = aksrt_get_ip_geo_config();
|
|
$display = $c['ip_geo_display'] ?? [];
|
|
if (!is_array($display)) {
|
|
$display = [$display];
|
|
}
|
|
$display = array_values(array_unique(array_filter(array_map('sanitize_key', $display))));
|
|
|
|
// Legacy saves only had "comment". When master is on, expand so user/post
|
|
// surfaces work without forcing a re-save of the checkbox group.
|
|
if ($display === [] || $display === ['comment']) {
|
|
$display = ['comment', 'user_home', 'user_center', 'post'];
|
|
}
|
|
|
|
// Zibll: user home/center are gated by user_city_s, not a separate surface list.
|
|
// Still honour an explicit opt-out if the admin removed them from the list after upgrade.
|
|
if (in_array($location, ['user_home', 'user_center'], true)) {
|
|
if (!aksrt_user_city_enabled()) {
|
|
return false;
|
|
}
|
|
// If list explicitly includes any user surface, require membership; else allow.
|
|
$has_user_keys = (bool) array_intersect($display, ['user_home', 'user_center']);
|
|
if ($has_user_keys) {
|
|
return in_array($location, $display, true);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return in_array($location, $display, true);
|
|
}
|
|
endif;
|
|
|
|
if (!function_exists('aksrt_ip_geo_format_key')) :
|
|
function aksrt_ip_geo_format_key(): string {
|
|
$c = aksrt_get_ip_geo_config();
|
|
$format = sanitize_key((string) ($c['ip_geo_format'] ?? 'province_city'));
|
|
return in_array($format, ['province_city', 'province', 'country_province'], true)
|
|
? $format
|
|
: 'province_city';
|
|
}
|
|
endif;
|
|
|
|
if (!function_exists('aksrt_user_city_enabled')) :
|
|
/**
|
|
* Users feature switch (user home / user center), independent of comment city_s.
|
|
*/
|
|
function aksrt_user_city_enabled(): bool {
|
|
if (!class_exists('\AKSRT\Settings\Settings')) {
|
|
return true; // zibll default on
|
|
}
|
|
$all = \AKSRT\Settings\Settings::get_all();
|
|
// Unset → on (zibll _pz default true). Explicit false turns off.
|
|
if (!array_key_exists('user_city_s', $all)) {
|
|
return true;
|
|
}
|
|
return !empty($all['user_city_s']);
|
|
}
|
|
endif;
|
|
|
|
if (!function_exists('aksrt_user_city_type')) :
|
|
/**
|
|
* User-facing precision (maps to shared format keys).
|
|
*/
|
|
function aksrt_user_city_type(): string {
|
|
if (!class_exists('\AKSRT\Settings\Settings')) {
|
|
return aksrt_ip_geo_format_key();
|
|
}
|
|
$all = \AKSRT\Settings\Settings::get_all();
|
|
$type = sanitize_key((string) ($all['user_city_type'] ?? ''));
|
|
// zibll: pro / city
|
|
if ($type === 'pro' || $type === 'province') {
|
|
return 'province';
|
|
}
|
|
if ($type === 'city' || $type === 'province_city') {
|
|
return 'province_city';
|
|
}
|
|
if ($type === 'country_province') {
|
|
return 'country_province';
|
|
}
|
|
return aksrt_ip_geo_format_key();
|
|
}
|
|
endif;
|
|
|
|
if (!function_exists('aksrt_normalize_geo_addr')) :
|
|
/**
|
|
* @param mixed $raw Meta value.
|
|
* @return array{country:string,province:string,city:string}
|
|
*/
|
|
function aksrt_normalize_geo_addr($raw): array {
|
|
if (!is_array($raw)) {
|
|
return ['country' => '', 'province' => '', 'city' => ''];
|
|
}
|
|
// Accept zibll-shaped nation → country.
|
|
$country = trim((string) ($raw['country'] ?? $raw['nation'] ?? ''));
|
|
return [
|
|
'country' => $country,
|
|
'province' => trim((string) ($raw['province'] ?? '')),
|
|
'city' => trim((string) ($raw['city'] ?? '')),
|
|
];
|
|
}
|
|
endif;
|
|
|
|
if (!function_exists('aksrt_format_geo_label')) :
|
|
/**
|
|
* @param array<string,string> $addr Addr.
|
|
* @param string $type Format key.
|
|
*/
|
|
function aksrt_format_geo_label(array $addr, string $type = 'province_city'): string {
|
|
$addr = aksrt_normalize_geo_addr($addr);
|
|
if (class_exists('\AKSRT\Extensions\IPGeo')) {
|
|
$label = \AKSRT\Extensions\IPGeo::get_instance()->format($addr, $type);
|
|
if (is_string($label) && $label !== '') {
|
|
return $label;
|
|
}
|
|
}
|
|
// Local fallback (mirrors IPGeo::format).
|
|
switch ($type) {
|
|
case 'province':
|
|
return $addr['province'] !== '' ? $addr['province'] : $addr['city'];
|
|
case 'country_province':
|
|
return trim($addr['country'] . ' ' . $addr['province']);
|
|
case 'province_city':
|
|
default:
|
|
$parts = array_filter([$addr['province'], $addr['city']]);
|
|
if (empty($parts) && $addr['country'] !== '') {
|
|
return $addr['country'];
|
|
}
|
|
return implode(' ', $parts);
|
|
}
|
|
}
|
|
endif;
|
|
|
|
if (!function_exists('aksrt_get_geo_badge_html')) :
|
|
/**
|
|
* @param array<string,string>|mixed $addr Addr meta.
|
|
* @param string $type Format.
|
|
* @param string $class CSS classes.
|
|
* @param string $title Title attr.
|
|
*/
|
|
function aksrt_get_geo_badge_html($addr, string $type = 'province_city', string $class = 'badg badg-sm', string $title = ''): string {
|
|
$label = aksrt_format_geo_label(is_array($addr) ? $addr : [], $type);
|
|
if ($label === '') {
|
|
$fallback = (string) (aksrt_get_ip_geo_config()['ip_geo_fallback_text'] ?? '');
|
|
if ($fallback === '') {
|
|
return '';
|
|
}
|
|
$label = $fallback;
|
|
}
|
|
if ($title === '') {
|
|
$title = __('IP 属地', 'aksrt');
|
|
}
|
|
$class = trim($class . ' aksrt-ip-geo-badge');
|
|
return '<span class="' . esc_attr($class) . '" title="' . esc_attr($title) . '">' . esc_html($label) . '</span>';
|
|
}
|
|
endif;
|
|
|
|
if (!function_exists('aksrt_lookup_geo_for_ip')) :
|
|
/**
|
|
* @return array{country:string,province:string,city:string}
|
|
*/
|
|
function aksrt_lookup_geo_for_ip(string $ip): array {
|
|
$ip = trim($ip);
|
|
if ($ip === '' || !class_exists('\AKSRT\Extensions\IPGeo')) {
|
|
return ['country' => '', 'province' => '', 'city' => ''];
|
|
}
|
|
$geo = \AKSRT\Extensions\IPGeo::get_instance()->lookup($ip);
|
|
return aksrt_normalize_geo_addr(is_array($geo) ? $geo : []);
|
|
}
|
|
endif;
|
|
|
|
if (!function_exists('aksrt_store_user_addr')) :
|
|
/**
|
|
* Resolve current client IP and persist user meta.
|
|
* Zibll: refresh on every login when user_city_s is on.
|
|
* Also refresh when article (post) display needs author addr.
|
|
*/
|
|
function aksrt_store_user_addr(int $user_id, ?string $ip = null): void {
|
|
if ($user_id <= 0 || !aksrt_ip_geo_master_enabled()) {
|
|
return;
|
|
}
|
|
$need = aksrt_user_city_enabled()
|
|
|| aksrt_ip_geo_display_enabled('post')
|
|
|| aksrt_ip_geo_display_enabled('user_home')
|
|
|| aksrt_ip_geo_display_enabled('user_center');
|
|
if (!$need) {
|
|
return;
|
|
}
|
|
|
|
if ($ip === null || $ip === '') {
|
|
$ip = function_exists('aksrt_get_client_ip') ? (string) aksrt_get_client_ip() : (string) ($_SERVER['REMOTE_ADDR'] ?? '');
|
|
}
|
|
$ip = trim($ip);
|
|
if ($ip === '') {
|
|
return;
|
|
}
|
|
|
|
$addr = aksrt_lookup_geo_for_ip($ip);
|
|
if ($addr['country'] === '' && $addr['province'] === '' && $addr['city'] === '') {
|
|
return;
|
|
}
|
|
update_user_meta($user_id, 'aksrt_user_addr', $addr);
|
|
}
|
|
endif;
|
|
|
|
if (!function_exists('aksrt_get_user_addr')) :
|
|
/**
|
|
* @param int $user_id User ID.
|
|
* @return array{country:string,province:string,city:string}
|
|
*/
|
|
function aksrt_get_user_addr(int $user_id): array {
|
|
if ($user_id <= 0) {
|
|
return ['country' => '', 'province' => '', 'city' => ''];
|
|
}
|
|
$raw = get_user_meta($user_id, 'aksrt_user_addr', true);
|
|
// Legacy / accidental zibll key.
|
|
if ((!is_array($raw) || empty($raw)) && metadata_exists('user', $user_id, 'user_addr')) {
|
|
$raw = get_user_meta($user_id, 'user_addr', true);
|
|
}
|
|
$addr = aksrt_normalize_geo_addr($raw);
|
|
if ($addr['country'] !== '' || $addr['province'] !== '' || $addr['city'] !== '') {
|
|
return $addr;
|
|
}
|
|
|
|
// Lazy backfill from stored IPs so pages work before next login.
|
|
$ips = [];
|
|
foreach (['aksrt_register_ip', 'register_ip'] as $k) {
|
|
$v = trim((string) get_user_meta($user_id, $k, true));
|
|
if ($v !== '') {
|
|
$ips[] = $v;
|
|
}
|
|
}
|
|
// Own profile: refresh from current client IP (zibll updates on login; this covers open sessions).
|
|
if ((int) get_current_user_id() === $user_id) {
|
|
$cur = function_exists('aksrt_get_client_ip') ? (string) aksrt_get_client_ip() : (string) ($_SERVER['REMOTE_ADDR'] ?? '');
|
|
$cur = trim($cur);
|
|
if ($cur !== '') {
|
|
array_unshift($ips, $cur);
|
|
}
|
|
}
|
|
$ips = array_values(array_unique(array_filter($ips)));
|
|
foreach ($ips as $ip) {
|
|
$looked = aksrt_lookup_geo_for_ip($ip);
|
|
if ($looked['country'] !== '' || $looked['province'] !== '' || $looked['city'] !== '') {
|
|
update_user_meta($user_id, 'aksrt_user_addr', $looked);
|
|
return $looked;
|
|
}
|
|
}
|
|
return $addr;
|
|
}
|
|
endif;
|
|
|
|
if (!function_exists('aksrt_get_user_city_badge_html')) :
|
|
/**
|
|
* Badge for user home / user center (requires user_city_s + display location).
|
|
*
|
|
* @param int $user_id User.
|
|
* @param string $location user_home|user_center
|
|
* @param string $class CSS.
|
|
*/
|
|
function aksrt_get_user_city_badge_html(int $user_id, string $location = 'user_home', string $class = 'badg'): string {
|
|
if ($user_id <= 0) {
|
|
return '';
|
|
}
|
|
// aksrt_ip_geo_display_enabled already enforces master + user_city for user surfaces.
|
|
if (!aksrt_ip_geo_display_enabled($location)) {
|
|
return '';
|
|
}
|
|
return aksrt_get_geo_badge_html(aksrt_get_user_addr($user_id), aksrt_user_city_type(), $class);
|
|
}
|
|
endif;
|
|
|
|
if (!function_exists('aksrt_get_post_author_city_badge_html')) :
|
|
/**
|
|
* Article detail author IP 属地 (uses author last-login addr).
|
|
*/
|
|
function aksrt_get_post_author_city_badge_html(int $author_id = 0, string $class = 'badg badg-sm'): string {
|
|
if (!aksrt_ip_geo_display_enabled('post')) {
|
|
return '';
|
|
}
|
|
if ($author_id <= 0) {
|
|
$author_id = (int) get_the_author_meta('ID');
|
|
}
|
|
if ($author_id <= 0) {
|
|
return '';
|
|
}
|
|
// Prefer user_city type when feature on; else global format.
|
|
$type = aksrt_user_city_enabled() ? aksrt_user_city_type() : aksrt_ip_geo_format_key();
|
|
return aksrt_get_geo_badge_html(aksrt_get_user_addr($author_id), $type, $class);
|
|
}
|
|
endif;
|
|
|
|
// ── Hooks: persist on login / register ──────────────────────────────────────
|
|
|
|
add_action('wp_login', static function ($user_login, $user = null): void {
|
|
$user_id = 0;
|
|
if ($user instanceof WP_User) {
|
|
$user_id = (int) $user->ID;
|
|
} elseif (is_string($user_login) && $user_login !== '') {
|
|
$u = get_user_by('login', $user_login);
|
|
$user_id = $u instanceof WP_User ? (int) $u->ID : 0;
|
|
}
|
|
if ($user_id > 0 && function_exists('aksrt_store_user_addr')) {
|
|
aksrt_store_user_addr($user_id);
|
|
}
|
|
}, 20, 2);
|
|
|
|
add_action('user_register', static function ($user_id): void {
|
|
$user_id = (int) $user_id;
|
|
if ($user_id <= 0) {
|
|
return;
|
|
}
|
|
$ip = function_exists('aksrt_get_client_ip') ? (string) aksrt_get_client_ip() : (string) ($_SERVER['REMOTE_ADDR'] ?? '');
|
|
$ip = trim($ip);
|
|
if ($ip !== '') {
|
|
update_user_meta($user_id, 'aksrt_register_ip', $ip);
|
|
}
|
|
if (function_exists('aksrt_store_user_addr')) {
|
|
aksrt_store_user_addr($user_id, $ip !== '' ? $ip : null);
|
|
}
|
|
}, 20, 1);
|