chore: change Android Apk download status to upcoming
This commit is contained in:
@@ -18,26 +18,31 @@ class SiteController extends Controller
|
|||||||
{
|
{
|
||||||
/** @var array<string,string> */
|
/** @var array<string,string> */
|
||||||
private const CATEGORY_TO_SLUG = [
|
private const CATEGORY_TO_SLUG = [
|
||||||
'Smileys & Emotion' => 'smileys',
|
"Smileys & Emotion" => "smileys",
|
||||||
'People & Body' => 'people',
|
"People & Body" => "people",
|
||||||
'Animals & Nature' => 'animals',
|
"Animals & Nature" => "animals",
|
||||||
'Food & Drink' => 'food',
|
"Food & Drink" => "food",
|
||||||
'Travel & Places' => 'travel',
|
"Travel & Places" => "travel",
|
||||||
'Activities' => 'activities',
|
"Activities" => "activities",
|
||||||
'Objects' => 'objects',
|
"Objects" => "objects",
|
||||||
'Symbols' => 'symbols',
|
"Symbols" => "symbols",
|
||||||
'Flags' => 'flags',
|
"Flags" => "flags",
|
||||||
];
|
];
|
||||||
|
|
||||||
private function billingMode(): string
|
private function billingMode(): string
|
||||||
{
|
{
|
||||||
$settings = app(SettingsService::class);
|
$settings = app(SettingsService::class);
|
||||||
$preferred = (string) ($settings->get('billing_mode', config('dewemoji.billing.mode', 'sandbox')) ?: 'sandbox');
|
$preferred =
|
||||||
|
(string) ($settings->get(
|
||||||
|
"billing_mode",
|
||||||
|
config("dewemoji.billing.mode", "sandbox"),
|
||||||
|
) ?:
|
||||||
|
"sandbox");
|
||||||
if ($this->paypalConfiguredMode($preferred)) {
|
if ($this->paypalConfiguredMode($preferred)) {
|
||||||
return $preferred;
|
return $preferred;
|
||||||
}
|
}
|
||||||
|
|
||||||
$fallback = $preferred === 'live' ? 'sandbox' : 'live';
|
$fallback = $preferred === "live" ? "sandbox" : "live";
|
||||||
if ($this->paypalConfiguredMode($fallback)) {
|
if ($this->paypalConfiguredMode($fallback)) {
|
||||||
return $fallback;
|
return $fallback;
|
||||||
}
|
}
|
||||||
@@ -47,111 +52,122 @@ class SiteController extends Controller
|
|||||||
|
|
||||||
public function home(Request $request): View
|
public function home(Request $request): View
|
||||||
{
|
{
|
||||||
return view('site.home', [
|
return view("site.home", [
|
||||||
'initialQuery' => trim((string) $request->query('q', '')),
|
"initialQuery" => trim((string) $request->query("q", "")),
|
||||||
'initialCategory' => trim((string) $request->query('category', '')),
|
"initialCategory" => trim((string) $request->query("category", "")),
|
||||||
'initialSubcategory' => trim((string) $request->query('subcategory', '')),
|
"initialSubcategory" => trim(
|
||||||
'canonicalPath' => '/',
|
(string) $request->query("subcategory", ""),
|
||||||
'userTier' => $request->user()?->tier,
|
),
|
||||||
|
"canonicalPath" => "/",
|
||||||
|
"userTier" => $request->user()?->tier,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function browse(Request $request): RedirectResponse|View
|
public function browse(Request $request): RedirectResponse|View
|
||||||
{
|
{
|
||||||
$cat = strtolower(trim((string) $request->query('cat', 'all')));
|
$cat = strtolower(trim((string) $request->query("cat", "all")));
|
||||||
if ($cat !== '' && $cat !== 'all' && array_key_exists($cat, $this->categorySlugMap())) {
|
if (
|
||||||
return redirect('/'.$cat, 301);
|
$cat !== "" &&
|
||||||
|
$cat !== "all" &&
|
||||||
|
array_key_exists($cat, $this->categorySlugMap())
|
||||||
|
) {
|
||||||
|
return redirect("/" . $cat, 301);
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('site.home', [
|
return view("site.home", [
|
||||||
'initialQuery' => trim((string) $request->query('q', '')),
|
"initialQuery" => trim((string) $request->query("q", "")),
|
||||||
'initialCategory' => trim((string) $request->query('category', '')),
|
"initialCategory" => trim((string) $request->query("category", "")),
|
||||||
'initialSubcategory' => trim((string) $request->query('subcategory', '')),
|
"initialSubcategory" => trim(
|
||||||
'canonicalPath' => '/browse',
|
(string) $request->query("subcategory", ""),
|
||||||
'userTier' => $request->user()?->tier,
|
),
|
||||||
|
"canonicalPath" => "/browse",
|
||||||
|
"userTier" => $request->user()?->tier,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function category(string $categorySlug): View
|
public function category(string $categorySlug): View
|
||||||
{
|
{
|
||||||
if ($categorySlug === 'all') {
|
if ($categorySlug === "all") {
|
||||||
return view('site.home', [
|
return view("site.home", [
|
||||||
'initialQuery' => '',
|
"initialQuery" => "",
|
||||||
'initialCategory' => '',
|
"initialCategory" => "",
|
||||||
'initialSubcategory' => '',
|
"initialSubcategory" => "",
|
||||||
'canonicalPath' => '/',
|
"canonicalPath" => "/",
|
||||||
'userTier' => request()->user()?->tier,
|
"userTier" => request()->user()?->tier,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$categoryLabel = $this->categorySlugMap()[$categorySlug] ?? '';
|
$categoryLabel = $this->categorySlugMap()[$categorySlug] ?? "";
|
||||||
abort_if($categoryLabel === '', 404);
|
abort_if($categoryLabel === "", 404);
|
||||||
|
|
||||||
return view('site.home', [
|
return view("site.home", [
|
||||||
'initialQuery' => '',
|
"initialQuery" => "",
|
||||||
'initialCategory' => $categoryLabel,
|
"initialCategory" => $categoryLabel,
|
||||||
'initialSubcategory' => '',
|
"initialSubcategory" => "",
|
||||||
'canonicalPath' => '/'.$categorySlug,
|
"canonicalPath" => "/" . $categorySlug,
|
||||||
'userTier' => request()->user()?->tier,
|
"userTier" => request()->user()?->tier,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function categorySubcategory(string $categorySlug, string $subcategorySlug): View
|
public function categorySubcategory(
|
||||||
{
|
string $categorySlug,
|
||||||
if ($categorySlug === 'all') {
|
string $subcategorySlug,
|
||||||
|
): View {
|
||||||
|
if ($categorySlug === "all") {
|
||||||
abort(404);
|
abort(404);
|
||||||
}
|
}
|
||||||
|
|
||||||
$categoryLabel = $this->categorySlugMap()[$categorySlug] ?? '';
|
$categoryLabel = $this->categorySlugMap()[$categorySlug] ?? "";
|
||||||
abort_if($categoryLabel === '', 404);
|
abort_if($categoryLabel === "", 404);
|
||||||
|
|
||||||
return view('site.home', [
|
return view("site.home", [
|
||||||
'initialQuery' => '',
|
"initialQuery" => "",
|
||||||
'initialCategory' => $categoryLabel,
|
"initialCategory" => $categoryLabel,
|
||||||
'initialSubcategory' => $subcategorySlug,
|
"initialSubcategory" => $subcategorySlug,
|
||||||
'canonicalPath' => '/'.$categorySlug.'/'.$subcategorySlug,
|
"canonicalPath" => "/" . $categorySlug . "/" . $subcategorySlug,
|
||||||
'userTier' => request()->user()?->tier,
|
"userTier" => request()->user()?->tier,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function apiDocs(): View
|
public function apiDocs(): View
|
||||||
{
|
{
|
||||||
return view('site.api-docs');
|
return view("site.api-docs");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function pricing(): View
|
public function pricing(): View
|
||||||
{
|
{
|
||||||
$user = request()->user();
|
$user = request()->user();
|
||||||
$currencyPref = strtoupper((string) session('pricing_currency', ''));
|
$currencyPref = strtoupper((string) session("pricing_currency", ""));
|
||||||
if (!in_array($currencyPref, ['IDR', 'USD'], true)) {
|
if (!in_array($currencyPref, ["IDR", "USD"], true)) {
|
||||||
$currencyPref = $this->detectPricingCurrency(request());
|
$currencyPref = $this->detectPricingCurrency(request());
|
||||||
session(['pricing_currency' => $currencyPref]);
|
session(["pricing_currency" => $currencyPref]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$rate = (int) config('dewemoji.pricing.usd_rate', 15000);
|
$rate = (int) config("dewemoji.pricing.usd_rate", 15000);
|
||||||
$plans = PricingPlan::where('status', 'active')->get()->keyBy('code');
|
$plans = PricingPlan::where("status", "active")->get()->keyBy("code");
|
||||||
$defaults = config('dewemoji.pricing.defaults', []);
|
$defaults = config("dewemoji.pricing.defaults", []);
|
||||||
$fallback = collect($defaults)->keyBy('code');
|
$fallback = collect($defaults)->keyBy("code");
|
||||||
|
|
||||||
$getPlanAmount = function (string $code) use ($plans, $fallback): int {
|
$getPlanAmount = function (string $code) use ($plans, $fallback): int {
|
||||||
$plan = $plans->get($code) ?? $fallback->get($code);
|
$plan = $plans->get($code) ?? $fallback->get($code);
|
||||||
return (int) ($plan['amount'] ?? $plan->amount ?? 0);
|
return (int) ($plan["amount"] ?? ($plan->amount ?? 0));
|
||||||
};
|
};
|
||||||
|
|
||||||
$pricing = [
|
$pricing = [
|
||||||
'personal_monthly' => [
|
"personal_monthly" => [
|
||||||
'idr' => $getPlanAmount('personal_monthly'),
|
"idr" => $getPlanAmount("personal_monthly"),
|
||||||
],
|
],
|
||||||
'personal_annual' => [
|
"personal_annual" => [
|
||||||
'idr' => $getPlanAmount('personal_annual'),
|
"idr" => $getPlanAmount("personal_annual"),
|
||||||
],
|
],
|
||||||
'personal_lifetime' => [
|
"personal_lifetime" => [
|
||||||
'idr' => $getPlanAmount('personal_lifetime'),
|
"idr" => $getPlanAmount("personal_lifetime"),
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($pricing as $key => $row) {
|
foreach ($pricing as $key => $row) {
|
||||||
$pricing[$key]['usd'] = $rate > 0 ? round($row['idr'] / $rate, 2) : 0;
|
$pricing[$key]["usd"] =
|
||||||
|
$rate > 0 ? round($row["idr"] / $rate, 2) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$hasActiveLifetime = false;
|
$hasActiveLifetime = false;
|
||||||
@@ -159,49 +175,75 @@ class SiteController extends Controller
|
|||||||
$pendingCooldownRemaining = 0;
|
$pendingCooldownRemaining = 0;
|
||||||
if ($user) {
|
if ($user) {
|
||||||
$hasActiveLifetime = Subscription::query()
|
$hasActiveLifetime = Subscription::query()
|
||||||
->where('user_id', $user->id)
|
->where("user_id", $user->id)
|
||||||
->where('plan', 'personal_lifetime')
|
->where("plan", "personal_lifetime")
|
||||||
->where('status', 'active')
|
->where("status", "active")
|
||||||
->where(function ($query) {
|
->where(function ($query) {
|
||||||
$query->whereNull('expires_at')
|
$query
|
||||||
->orWhere('expires_at', '>', now());
|
->whereNull("expires_at")
|
||||||
|
->orWhere("expires_at", ">", now());
|
||||||
})
|
})
|
||||||
->exists();
|
->exists();
|
||||||
$hasPendingPayment = Payment::query()
|
$hasPendingPayment = Payment::query()
|
||||||
->where('user_id', $user->id)
|
->where("user_id", $user->id)
|
||||||
->where('status', 'pending')
|
->where("status", "pending")
|
||||||
->exists();
|
->exists();
|
||||||
$cooldown = (int) config('dewemoji.billing.pending_cooldown_seconds', 120);
|
$cooldown = (int) config(
|
||||||
|
"dewemoji.billing.pending_cooldown_seconds",
|
||||||
|
120,
|
||||||
|
);
|
||||||
if ($cooldown > 0) {
|
if ($cooldown > 0) {
|
||||||
$latestPending = Payment::query()
|
$latestPending = Payment::query()
|
||||||
->where('user_id', $user->id)
|
->where("user_id", $user->id)
|
||||||
->where('status', 'pending')
|
->where("status", "pending")
|
||||||
->orderByDesc('id')
|
->orderByDesc("id")
|
||||||
->first();
|
->first();
|
||||||
if ($latestPending && $latestPending->created_at) {
|
if ($latestPending && $latestPending->created_at) {
|
||||||
$age = max(0, now()->getTimestamp() - $latestPending->created_at->getTimestamp());
|
$age = max(
|
||||||
|
0,
|
||||||
|
now()->getTimestamp() -
|
||||||
|
$latestPending->created_at->getTimestamp(),
|
||||||
|
);
|
||||||
$pendingCooldownRemaining = max(0, $cooldown - $age);
|
$pendingCooldownRemaining = max(0, $cooldown - $age);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('site.pricing', [
|
return view("site.pricing", [
|
||||||
'currencyPref' => $currencyPref,
|
"currencyPref" => $currencyPref,
|
||||||
'usdRate' => $rate,
|
"usdRate" => $rate,
|
||||||
'pricing' => $pricing,
|
"pricing" => $pricing,
|
||||||
'payments' => [
|
"payments" => [
|
||||||
'qris_url' => (string) config('dewemoji.payments.qris_url', ''),
|
"qris_url" => (string) config("dewemoji.payments.qris_url", ""),
|
||||||
'paypal_url' => (string) config('dewemoji.payments.paypal_url', ''),
|
"paypal_url" => (string) config(
|
||||||
|
"dewemoji.payments.paypal_url",
|
||||||
|
"",
|
||||||
|
),
|
||||||
],
|
],
|
||||||
'pakasirEnabled' => (bool) config('dewemoji.billing.providers.pakasir.enabled', false)
|
"pakasirEnabled" =>
|
||||||
&& (string) config('dewemoji.billing.providers.pakasir.api_base', '') !== ''
|
(bool) config(
|
||||||
&& (string) config('dewemoji.billing.providers.pakasir.api_key', '') !== ''
|
"dewemoji.billing.providers.pakasir.enabled",
|
||||||
&& (string) config('dewemoji.billing.providers.pakasir.project', '') !== '',
|
false,
|
||||||
'paypalEnabled' => $this->paypalEnabled($this->billingMode()),
|
) &&
|
||||||
'paypalPlans' => $this->paypalPlanAvailability($this->billingMode()),
|
(string) config(
|
||||||
'hasActiveLifetime' => $hasActiveLifetime,
|
"dewemoji.billing.providers.pakasir.api_base",
|
||||||
'hasPendingPayment' => $hasPendingPayment,
|
"",
|
||||||
'pendingCooldownRemaining' => $pendingCooldownRemaining,
|
) !== "" &&
|
||||||
|
(string) config(
|
||||||
|
"dewemoji.billing.providers.pakasir.api_key",
|
||||||
|
"",
|
||||||
|
) !== "" &&
|
||||||
|
(string) config(
|
||||||
|
"dewemoji.billing.providers.pakasir.project",
|
||||||
|
"",
|
||||||
|
) !== "",
|
||||||
|
"paypalEnabled" => $this->paypalEnabled($this->billingMode()),
|
||||||
|
"paypalPlans" => $this->paypalPlanAvailability(
|
||||||
|
$this->billingMode(),
|
||||||
|
),
|
||||||
|
"hasActiveLifetime" => $hasActiveLifetime,
|
||||||
|
"hasPendingPayment" => $hasPendingPayment,
|
||||||
|
"pendingCooldownRemaining" => $pendingCooldownRemaining,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,23 +253,43 @@ class SiteController extends Controller
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$fallback = $mode === 'live' ? 'sandbox' : 'live';
|
$fallback = $mode === "live" ? "sandbox" : "live";
|
||||||
return $this->paypalConfiguredMode($fallback);
|
return $this->paypalConfiguredMode($fallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function paypalConfiguredMode(string $mode): bool
|
private function paypalConfiguredMode(string $mode): bool
|
||||||
{
|
{
|
||||||
$enabled = (bool) config('dewemoji.billing.providers.paypal.enabled', false);
|
$enabled = (bool) config(
|
||||||
$clientId = (string) config("dewemoji.billing.providers.paypal.{$mode}.client_id", '');
|
"dewemoji.billing.providers.paypal.enabled",
|
||||||
$clientSecret = (string) config("dewemoji.billing.providers.paypal.{$mode}.client_secret", '');
|
false,
|
||||||
$apiBase = (string) config("dewemoji.billing.providers.paypal.{$mode}.api_base", '');
|
);
|
||||||
|
$clientId = (string) config(
|
||||||
|
"dewemoji.billing.providers.paypal.{$mode}.client_id",
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
$clientSecret = (string) config(
|
||||||
|
"dewemoji.billing.providers.paypal.{$mode}.client_secret",
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
$apiBase = (string) config(
|
||||||
|
"dewemoji.billing.providers.paypal.{$mode}.api_base",
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
|
||||||
return $enabled && $clientId !== '' && $clientSecret !== '' && $apiBase !== '';
|
return $enabled &&
|
||||||
|
$clientId !== "" &&
|
||||||
|
$clientSecret !== "" &&
|
||||||
|
$apiBase !== "";
|
||||||
}
|
}
|
||||||
|
|
||||||
private function paypalPlanAvailability(string $mode): array
|
private function paypalPlanAvailability(string $mode): array
|
||||||
{
|
{
|
||||||
$plans = PricingPlan::whereIn('code', ['personal_monthly', 'personal_annual'])->get()->keyBy('code');
|
$plans = PricingPlan::whereIn("code", [
|
||||||
|
"personal_monthly",
|
||||||
|
"personal_annual",
|
||||||
|
])
|
||||||
|
->get()
|
||||||
|
->keyBy("code");
|
||||||
|
|
||||||
$fromDb = function (string $code) use ($plans, $mode): bool {
|
$fromDb = function (string $code) use ($plans, $mode): bool {
|
||||||
$plan = $plans->get($code);
|
$plan = $plans->get($code);
|
||||||
@@ -235,157 +297,188 @@ class SiteController extends Controller
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$meta = $plan->meta ?? [];
|
$meta = $plan->meta ?? [];
|
||||||
return (string) ($meta['paypal'][$mode]['plan']['id'] ?? '') !== '';
|
return (string) ($meta["paypal"][$mode]["plan"]["id"] ?? "") !== "";
|
||||||
};
|
};
|
||||||
|
|
||||||
$fromEnv = function (string $code) use ($mode): bool {
|
$fromEnv = function (string $code) use ($mode): bool {
|
||||||
return (string) config("dewemoji.billing.providers.paypal.plan_ids.{$mode}.{$code}", '') !== '';
|
return (string) config(
|
||||||
|
"dewemoji.billing.providers.paypal.plan_ids.{$mode}.{$code}",
|
||||||
|
"",
|
||||||
|
) !== "";
|
||||||
};
|
};
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'personal_monthly' => $fromDb('personal_monthly') || $fromEnv('personal_monthly'),
|
"personal_monthly" =>
|
||||||
'personal_annual' => $fromDb('personal_annual') || $fromEnv('personal_annual'),
|
$fromDb("personal_monthly") || $fromEnv("personal_monthly"),
|
||||||
|
"personal_annual" =>
|
||||||
|
$fromDb("personal_annual") || $fromEnv("personal_annual"),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setPricingCurrency(Request $request): RedirectResponse
|
public function setPricingCurrency(Request $request): RedirectResponse
|
||||||
{
|
{
|
||||||
$data = $request->validate([
|
$data = $request->validate([
|
||||||
'currency' => 'required|string|in:IDR,USD',
|
"currency" => "required|string|in:IDR,USD",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
session(['pricing_currency' => $data['currency']]);
|
session(["pricing_currency" => $data["currency"]]);
|
||||||
return back();
|
return back();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function support(): View
|
public function support(): View
|
||||||
{
|
{
|
||||||
return view('site.support');
|
return view("site.support");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function download(): View
|
public function download(): View
|
||||||
{
|
{
|
||||||
$downloadBaseUrl = rtrim((string) config('dewemoji.apk_release.public_base_url', ''), '/');
|
$downloadBaseUrl = rtrim(
|
||||||
$androidEnabled = (bool) config('dewemoji.apk_release.enabled', false) && $downloadBaseUrl !== '';
|
(string) config("dewemoji.apk_release.public_base_url", ""),
|
||||||
|
"/",
|
||||||
|
);
|
||||||
|
$androidEnabled = false;
|
||||||
|
|
||||||
return view('site.download', [
|
return view("site.download", [
|
||||||
'androidEnabled' => $androidEnabled,
|
"androidEnabled" => $androidEnabled,
|
||||||
'androidVersionJsonUrl' => $androidEnabled ? $downloadBaseUrl.'/version.json' : '',
|
"androidVersionJsonUrl" => $androidEnabled
|
||||||
'androidLatestApkUrl' => $androidEnabled ? $downloadBaseUrl.'/dewemoji-latest.apk' : '',
|
? $downloadBaseUrl . "/version.json"
|
||||||
|
: "",
|
||||||
|
"androidLatestApkUrl" => $androidEnabled
|
||||||
|
? $downloadBaseUrl . "/dewemoji-latest.apk"
|
||||||
|
: "",
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function downloadVersionJson(Request $request): RedirectResponse|JsonResponse
|
public function downloadVersionJson(
|
||||||
{
|
Request $request,
|
||||||
$target = $this->apkReleaseTargetUrl('version_json');
|
): RedirectResponse|JsonResponse {
|
||||||
if ($target === '') {
|
$target = $this->apkReleaseTargetUrl("version_json");
|
||||||
return response()->json(['ok' => false, 'error' => 'apk_release_not_configured'], 404);
|
if ($target === "") {
|
||||||
|
return response()->json(
|
||||||
|
["ok" => false, "error" => "apk_release_not_configured"],
|
||||||
|
404,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->away($target, 302, [
|
return redirect()->away($target, 302, [
|
||||||
'Cache-Control' => 'no-store, no-cache, must-revalidate',
|
"Cache-Control" => "no-store, no-cache, must-revalidate",
|
||||||
'Pragma' => 'no-cache',
|
"Pragma" => "no-cache",
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function downloadLatestApk(Request $request): RedirectResponse|JsonResponse
|
public function downloadLatestApk(
|
||||||
{
|
Request $request,
|
||||||
$target = $this->apkReleaseTargetUrl('latest_apk');
|
): RedirectResponse|JsonResponse {
|
||||||
if ($target === '') {
|
$target = $this->apkReleaseTargetUrl("latest_apk");
|
||||||
return response()->json(['ok' => false, 'error' => 'apk_release_not_configured'], 404);
|
if ($target === "") {
|
||||||
|
return response()->json(
|
||||||
|
["ok" => false, "error" => "apk_release_not_configured"],
|
||||||
|
404,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->away($target, 302, [
|
return redirect()->away($target, 302, [
|
||||||
'Cache-Control' => 'no-store, no-cache, must-revalidate',
|
"Cache-Control" => "no-store, no-cache, must-revalidate",
|
||||||
'Pragma' => 'no-cache',
|
"Pragma" => "no-cache",
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function assetLinks(): JsonResponse
|
public function assetLinks(): JsonResponse
|
||||||
{
|
{
|
||||||
$appId = trim((string) config('dewemoji.apk_release.app_id', ''));
|
$appId = trim((string) config("dewemoji.apk_release.app_id", ""));
|
||||||
$rawFingerprints = (array) config('dewemoji.apk_release.assetlinks.fingerprints', []);
|
$rawFingerprints = (array) config(
|
||||||
|
"dewemoji.apk_release.assetlinks.fingerprints",
|
||||||
|
[],
|
||||||
|
);
|
||||||
$fingerprints = [];
|
$fingerprints = [];
|
||||||
foreach ($rawFingerprints as $fingerprint) {
|
foreach ($rawFingerprints as $fingerprint) {
|
||||||
$normalized = $this->normalizeApkCertFingerprint((string) $fingerprint);
|
$normalized = $this->normalizeApkCertFingerprint(
|
||||||
if ($normalized !== '') {
|
(string) $fingerprint,
|
||||||
|
);
|
||||||
|
if ($normalized !== "") {
|
||||||
$fingerprints[] = $normalized;
|
$fingerprints[] = $normalized;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$fingerprints = array_values(array_unique($fingerprints));
|
$fingerprints = array_values(array_unique($fingerprints));
|
||||||
|
|
||||||
if ($appId === '' || $fingerprints === []) {
|
if ($appId === "" || $fingerprints === []) {
|
||||||
return response()->json([], 200, [
|
return response()->json([], 200, [
|
||||||
'Cache-Control' => 'no-store, no-cache, must-revalidate',
|
"Cache-Control" => "no-store, no-cache, must-revalidate",
|
||||||
'Pragma' => 'no-cache',
|
"Pragma" => "no-cache",
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json([
|
return response()->json(
|
||||||
[
|
[
|
||||||
'relation' => [
|
[
|
||||||
'delegate_permission/common.handle_all_urls',
|
"relation" => [
|
||||||
|
"delegate_permission/common.handle_all_urls",
|
||||||
],
|
],
|
||||||
'target' => [
|
"target" => [
|
||||||
'namespace' => 'android_app',
|
"namespace" => "android_app",
|
||||||
'package_name' => $appId,
|
"package_name" => $appId,
|
||||||
'sha256_cert_fingerprints' => $fingerprints,
|
"sha256_cert_fingerprints" => $fingerprints,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
], 200, [
|
],
|
||||||
'Cache-Control' => 'no-store, no-cache, must-revalidate',
|
200,
|
||||||
'Pragma' => 'no-cache',
|
[
|
||||||
]);
|
"Cache-Control" => "no-store, no-cache, must-revalidate",
|
||||||
|
"Pragma" => "no-cache",
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function privacy(): View
|
public function privacy(): View
|
||||||
{
|
{
|
||||||
return view('site.privacy');
|
return view("site.privacy");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function terms(): View
|
public function terms(): View
|
||||||
{
|
{
|
||||||
return view('site.terms');
|
return view("site.terms");
|
||||||
}
|
}
|
||||||
|
|
||||||
private function detectPricingCurrency(Request $request): string
|
private function detectPricingCurrency(Request $request): string
|
||||||
{
|
{
|
||||||
$country = strtoupper((string) ($request->header('CF-IPCountry')
|
$country = strtoupper(
|
||||||
?? $request->header('X-Country-Code')
|
(string) ($request->header("CF-IPCountry") ??
|
||||||
?? $request->header('X-Geo-Country')
|
($request->header("X-Country-Code") ??
|
||||||
?? $request->header('X-Appengine-Country')
|
($request->header("X-Geo-Country") ??
|
||||||
?? $request->header('CloudFront-Viewer-Country')
|
($request->header("X-Appengine-Country") ??
|
||||||
?? ''));
|
($request->header("CloudFront-Viewer-Country") ??
|
||||||
|
""))))),
|
||||||
|
);
|
||||||
|
|
||||||
return $country === 'ID' ? 'IDR' : 'USD';
|
return $country === "ID" ? "IDR" : "USD";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function emojiDetail(string $slug): View|Response
|
public function emojiDetail(string $slug): View|Response
|
||||||
{
|
{
|
||||||
$dataPath = $this->datasetPath();
|
$dataPath = $this->datasetPath();
|
||||||
if (!is_file($dataPath)) {
|
if (!is_file($dataPath)) {
|
||||||
abort(500, 'Emoji dataset file not found.');
|
abort(500, "Emoji dataset file not found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$raw = file_get_contents($dataPath);
|
$raw = file_get_contents($dataPath);
|
||||||
if ($raw === false) {
|
if ($raw === false) {
|
||||||
abort(500, 'Emoji dataset file could not be read.');
|
abort(500, "Emoji dataset file could not be read.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$decoded = json_decode($raw, true);
|
$decoded = json_decode($raw, true);
|
||||||
if (!is_array($decoded)) {
|
if (!is_array($decoded)) {
|
||||||
abort(500, 'Emoji dataset JSON is invalid.');
|
abort(500, "Emoji dataset JSON is invalid.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$items = $decoded['emojis'] ?? [];
|
$items = $decoded["emojis"] ?? [];
|
||||||
$match = null;
|
$match = null;
|
||||||
$byEmoji = [];
|
$byEmoji = [];
|
||||||
foreach ($items as $item) {
|
foreach ($items as $item) {
|
||||||
$char = (string) ($item['emoji'] ?? '');
|
$char = (string) ($item["emoji"] ?? "");
|
||||||
if ($char !== '' && !isset($byEmoji[$char])) {
|
if ($char !== "" && !isset($byEmoji[$char])) {
|
||||||
$byEmoji[$char] = $item;
|
$byEmoji[$char] = $item;
|
||||||
}
|
}
|
||||||
if (($item['slug'] ?? '') === $slug) {
|
if (($item["slug"] ?? "") === $slug) {
|
||||||
$match = $item;
|
$match = $item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -395,98 +488,143 @@ class SiteController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
$relatedDetails = [];
|
$relatedDetails = [];
|
||||||
foreach (array_slice($match['related'] ?? [], 0, 8) as $relatedEmoji) {
|
foreach (array_slice($match["related"] ?? [], 0, 8) as $relatedEmoji) {
|
||||||
$relatedEmoji = (string) $relatedEmoji;
|
$relatedEmoji = (string) $relatedEmoji;
|
||||||
$ref = $byEmoji[$relatedEmoji] ?? null;
|
$ref = $byEmoji[$relatedEmoji] ?? null;
|
||||||
$relatedDetails[] = [
|
$relatedDetails[] = [
|
||||||
'emoji' => $relatedEmoji,
|
"emoji" => $relatedEmoji,
|
||||||
'slug' => (string) ($ref['slug'] ?? ''),
|
"slug" => (string) ($ref["slug"] ?? ""),
|
||||||
'name' => (string) ($ref['name'] ?? $relatedEmoji),
|
"name" => (string) ($ref["name"] ?? $relatedEmoji),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = request()->user();
|
$user = request()->user();
|
||||||
$canManageKeywords = (bool) $user;
|
$canManageKeywords = (bool) $user;
|
||||||
$isPersonal = $user && (string) $user->tier === 'personal';
|
$isPersonal = $user && (string) $user->tier === "personal";
|
||||||
$freeLimit = (int) config('dewemoji.pagination.free_max_limit', 20);
|
$freeLimit = (int) config("dewemoji.pagination.free_max_limit", 20);
|
||||||
$keywordLimit = $isPersonal ? null : $freeLimit;
|
$keywordLimit = $isPersonal ? null : $freeLimit;
|
||||||
$userKeywords = [];
|
$userKeywords = [];
|
||||||
$activeKeywordCount = 0;
|
$activeKeywordCount = 0;
|
||||||
if ($canManageKeywords) {
|
if ($canManageKeywords) {
|
||||||
$activeKeywordCount = UserKeyword::where('user_id', $user->id)
|
$activeKeywordCount = UserKeyword::where("user_id", $user->id)
|
||||||
->where('is_active', true)
|
->where("is_active", true)
|
||||||
->count();
|
->count();
|
||||||
$userKeywords = UserKeyword::where('user_id', $user->id)
|
$userKeywords = UserKeyword::where("user_id", $user->id)
|
||||||
->where('emoji_slug', $slug)
|
->where("emoji_slug", $slug)
|
||||||
->orderByDesc('id')
|
->orderByDesc("id")
|
||||||
->get();
|
->get();
|
||||||
}
|
}
|
||||||
$limitReached = $keywordLimit !== null && $activeKeywordCount >= $keywordLimit;
|
$limitReached =
|
||||||
|
$keywordLimit !== null && $activeKeywordCount >= $keywordLimit;
|
||||||
|
|
||||||
return view('site.emoji-detail', [
|
return view("site.emoji-detail", [
|
||||||
'emoji' => $match,
|
"emoji" => $match,
|
||||||
'relatedDetails' => $relatedDetails,
|
"relatedDetails" => $relatedDetails,
|
||||||
'canonicalPath' => '/emoji/'.$slug,
|
"canonicalPath" => "/emoji/" . $slug,
|
||||||
'userKeywords' => $userKeywords,
|
"userKeywords" => $userKeywords,
|
||||||
'canManageKeywords' => $canManageKeywords,
|
"canManageKeywords" => $canManageKeywords,
|
||||||
'keywordLimit' => $keywordLimit,
|
"keywordLimit" => $keywordLimit,
|
||||||
'limitReached' => $limitReached,
|
"limitReached" => $limitReached,
|
||||||
'activeKeywordCount' => $activeKeywordCount,
|
"activeKeywordCount" => $activeKeywordCount,
|
||||||
'userTier' => $user?->tier,
|
"userTier" => $user?->tier,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function robotsTxt(): Response
|
public function robotsTxt(): Response
|
||||||
{
|
{
|
||||||
$base = rtrim(config('app.url', request()->getSchemeAndHttpHost()), '/');
|
$base = rtrim(
|
||||||
$body = "User-agent: *\nAllow: /\n\nSitemap: ".$base."/sitemap.xml\n";
|
config("app.url", request()->getSchemeAndHttpHost()),
|
||||||
|
"/",
|
||||||
|
);
|
||||||
|
$body =
|
||||||
|
"User-agent: *\nAllow: /\n\nSitemap: " . $base . "/sitemap.xml\n";
|
||||||
|
|
||||||
return response($body, 200)->header('Content-Type', 'text/plain; charset=UTF-8');
|
return response($body, 200)->header(
|
||||||
|
"Content-Type",
|
||||||
|
"text/plain; charset=UTF-8",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sitemapXml(): Response
|
public function sitemapXml(): Response
|
||||||
{
|
{
|
||||||
$data = $this->loadDataset();
|
$data = $this->loadDataset();
|
||||||
$items = is_array($data['emojis'] ?? null) ? $data['emojis'] : [];
|
$items = is_array($data["emojis"] ?? null) ? $data["emojis"] : [];
|
||||||
$base = rtrim(config('app.url', request()->getSchemeAndHttpHost()), '/');
|
$base = rtrim(
|
||||||
|
config("app.url", request()->getSchemeAndHttpHost()),
|
||||||
|
"/",
|
||||||
|
);
|
||||||
|
|
||||||
$lastUpdatedTs = isset($data['last_updated_ts']) ? (int) $data['last_updated_ts'] : time();
|
$lastUpdatedTs = isset($data["last_updated_ts"])
|
||||||
$lastUpdated = gmdate('Y-m-d\TH:i:s\Z', $lastUpdatedTs);
|
? (int) $data["last_updated_ts"]
|
||||||
|
: time();
|
||||||
|
$lastUpdated = gmdate("Y-m-d\TH:i:s\Z", $lastUpdatedTs);
|
||||||
|
|
||||||
$urls = [
|
$urls = [
|
||||||
['loc' => $base.'/', 'priority' => '0.8', 'changefreq' => 'daily'],
|
[
|
||||||
['loc' => $base.'/api-docs', 'priority' => '0.5', 'changefreq' => 'weekly'],
|
"loc" => $base . "/",
|
||||||
['loc' => $base.'/pricing', 'priority' => '0.7', 'changefreq' => 'weekly'],
|
"priority" => "0.8",
|
||||||
['loc' => $base.'/privacy', 'priority' => '0.3', 'changefreq' => 'monthly'],
|
"changefreq" => "daily",
|
||||||
['loc' => $base.'/terms', 'priority' => '0.3', 'changefreq' => 'monthly'],
|
],
|
||||||
['loc' => $base.'/support', 'priority' => '0.4', 'changefreq' => 'weekly'],
|
[
|
||||||
|
"loc" => $base . "/api-docs",
|
||||||
|
"priority" => "0.5",
|
||||||
|
"changefreq" => "weekly",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"loc" => $base . "/pricing",
|
||||||
|
"priority" => "0.7",
|
||||||
|
"changefreq" => "weekly",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"loc" => $base . "/privacy",
|
||||||
|
"priority" => "0.3",
|
||||||
|
"changefreq" => "monthly",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"loc" => $base . "/terms",
|
||||||
|
"priority" => "0.3",
|
||||||
|
"changefreq" => "monthly",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"loc" => $base . "/support",
|
||||||
|
"priority" => "0.4",
|
||||||
|
"changefreq" => "weekly",
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($items as $item) {
|
foreach ($items as $item) {
|
||||||
$slug = trim((string) ($item['slug'] ?? ''));
|
$slug = trim((string) ($item["slug"] ?? ""));
|
||||||
if ($slug === '' || $this->shouldHideForSitemap($item)) {
|
if ($slug === "" || $this->shouldHideForSitemap($item)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$urls[] = [
|
$urls[] = [
|
||||||
'loc' => $base.'/emoji/'.$slug,
|
"loc" => $base . "/emoji/" . $slug,
|
||||||
'priority' => '0.6',
|
"priority" => "0.6",
|
||||||
'changefreq' => 'weekly',
|
"changefreq" => "weekly",
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
$xml = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
|
$xml = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
|
||||||
$xml .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'."\n";
|
$xml .=
|
||||||
|
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' .
|
||||||
|
"\n";
|
||||||
foreach ($urls as $url) {
|
foreach ($urls as $url) {
|
||||||
$xml .= " <url>\n";
|
$xml .= " <url>\n";
|
||||||
$xml .= ' <loc>'.htmlspecialchars((string) $url['loc'], ENT_XML1)."</loc>\n";
|
$xml .=
|
||||||
$xml .= ' <lastmod>'.$lastUpdated."</lastmod>\n";
|
" <loc>" .
|
||||||
$xml .= ' <changefreq>'.$url['changefreq']."</changefreq>\n";
|
htmlspecialchars((string) $url["loc"], ENT_XML1) .
|
||||||
$xml .= ' <priority>'.$url['priority']."</priority>\n";
|
"</loc>\n";
|
||||||
|
$xml .= " <lastmod>" . $lastUpdated . "</lastmod>\n";
|
||||||
|
$xml .= " <changefreq>" . $url["changefreq"] . "</changefreq>\n";
|
||||||
|
$xml .= " <priority>" . $url["priority"] . "</priority>\n";
|
||||||
$xml .= " </url>\n";
|
$xml .= " </url>\n";
|
||||||
}
|
}
|
||||||
$xml .= '</urlset>'."\n";
|
$xml .= "</urlset>" . "\n";
|
||||||
|
|
||||||
return response($xml, 200)->header('Content-Type', 'application/xml; charset=UTF-8');
|
return response($xml, 200)->header(
|
||||||
|
"Content-Type",
|
||||||
|
"application/xml; charset=UTF-8",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -509,17 +647,17 @@ class SiteController extends Controller
|
|||||||
{
|
{
|
||||||
$dataPath = $this->datasetPath();
|
$dataPath = $this->datasetPath();
|
||||||
if (!is_file($dataPath)) {
|
if (!is_file($dataPath)) {
|
||||||
return ['emojis' => []];
|
return ["emojis" => []];
|
||||||
}
|
}
|
||||||
|
|
||||||
$raw = file_get_contents($dataPath);
|
$raw = file_get_contents($dataPath);
|
||||||
if ($raw === false) {
|
if ($raw === false) {
|
||||||
return ['emojis' => []];
|
return ["emojis" => []];
|
||||||
}
|
}
|
||||||
|
|
||||||
$decoded = json_decode($raw, true);
|
$decoded = json_decode($raw, true);
|
||||||
if (!is_array($decoded)) {
|
if (!is_array($decoded)) {
|
||||||
return ['emojis' => []];
|
return ["emojis" => []];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $decoded;
|
return $decoded;
|
||||||
@@ -528,45 +666,49 @@ class SiteController extends Controller
|
|||||||
private function datasetPath(): string
|
private function datasetPath(): string
|
||||||
{
|
{
|
||||||
$settings = app(SettingsService::class);
|
$settings = app(SettingsService::class);
|
||||||
$activePath = (string) $settings->get('emoji_dataset_active_path', '');
|
$activePath = (string) $settings->get("emoji_dataset_active_path", "");
|
||||||
if ($activePath !== '' && is_file($activePath)) {
|
if ($activePath !== "" && is_file($activePath)) {
|
||||||
return $activePath;
|
return $activePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (string) config('dewemoji.data_path');
|
return (string) config("dewemoji.data_path");
|
||||||
}
|
}
|
||||||
|
|
||||||
private function apkReleaseTargetUrl(string $key): string
|
private function apkReleaseTargetUrl(string $key): string
|
||||||
{
|
{
|
||||||
if (!(bool) config('dewemoji.apk_release.enabled', false)) {
|
if (!(bool) config("dewemoji.apk_release.enabled", false)) {
|
||||||
return '';
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
$base = trim((string) config('dewemoji.apk_release.r2_public_base_url', ''));
|
$base = trim(
|
||||||
$objectKey = trim((string) config("dewemoji.apk_release.r2_keys.{$key}", ''));
|
(string) config("dewemoji.apk_release.r2_public_base_url", ""),
|
||||||
if ($base === '' || $objectKey === '') {
|
);
|
||||||
return '';
|
$objectKey = trim(
|
||||||
|
(string) config("dewemoji.apk_release.r2_keys.{$key}", ""),
|
||||||
|
);
|
||||||
|
if ($base === "" || $objectKey === "") {
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return rtrim($base, '/').'/'.ltrim($objectKey, '/');
|
return rtrim($base, "/") . "/" . ltrim($objectKey, "/");
|
||||||
}
|
}
|
||||||
|
|
||||||
private function normalizeApkCertFingerprint(string $value): string
|
private function normalizeApkCertFingerprint(string $value): string
|
||||||
{
|
{
|
||||||
$clean = strtoupper(trim($value));
|
$clean = strtoupper(trim($value));
|
||||||
if ($clean === '') {
|
if ($clean === "") {
|
||||||
return '';
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preg_match('/^[0-9A-F]{64}$/', $clean) === 1) {
|
if (preg_match('/^[0-9A-F]{64}$/', $clean) === 1) {
|
||||||
return implode(':', str_split($clean, 2));
|
return implode(":", str_split($clean, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preg_match('/^[0-9A-F]{2}(?::[0-9A-F]{2}){31}$/', $clean) === 1) {
|
if (preg_match('/^[0-9A-F]{2}(?::[0-9A-F]{2}){31}$/', $clean) === 1) {
|
||||||
return $clean;
|
return $clean;
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -574,39 +716,39 @@ class SiteController extends Controller
|
|||||||
*/
|
*/
|
||||||
private function shouldHideForSitemap(array $emoji): bool
|
private function shouldHideForSitemap(array $emoji): bool
|
||||||
{
|
{
|
||||||
$name = strtolower(trim((string) ($emoji['name'] ?? '')));
|
$name = strtolower(trim((string) ($emoji["name"] ?? "")));
|
||||||
$category = strtolower(trim((string) ($emoji['category'] ?? '')));
|
$category = strtolower(trim((string) ($emoji["category"] ?? "")));
|
||||||
$subcategory = strtolower(trim((string) ($emoji['subcategory'] ?? '')));
|
$subcategory = strtolower(trim((string) ($emoji["subcategory"] ?? "")));
|
||||||
|
|
||||||
if ($subcategory === 'family' || str_starts_with($name, 'family:')) {
|
if ($subcategory === "family" || str_starts_with($name, "family:")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (preg_match('~\bwoman: beard\b~i', $name)) {
|
if (preg_match("~\bwoman: beard\b~i", $name)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (preg_match('~\bmen with bunny ears\b~i', $name)) {
|
if (preg_match("~\bmen with bunny ears\b~i", $name)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (preg_match('~\bpregnant man\b~i', $name)) {
|
if (preg_match("~\bpregnant man\b~i", $name)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if ($category === 'people & body') {
|
if ($category === "people & body") {
|
||||||
if (preg_match('~\bmen holding hands\b~i', $name)) {
|
if (preg_match("~\bmen holding hands\b~i", $name)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (preg_match('~\bwomen holding hands\b~i', $name)) {
|
if (preg_match("~\bwomen holding hands\b~i", $name)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (preg_match('~kiss:.*\bman,\s*man\b~i', $name)) {
|
if (preg_match("~kiss:.*\bman,\s*man\b~i", $name)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (preg_match('~kiss:.*\bwoman,\s*woman\b~i', $name)) {
|
if (preg_match("~kiss:.*\bwoman,\s*woman\b~i", $name)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (preg_match('~couple.*\bman,\s*man\b~i', $name)) {
|
if (preg_match("~couple.*\bman,\s*man\b~i", $name)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (preg_match('~couple.*\bwoman,\s*woman\b~i', $name)) {
|
if (preg_match("~couple.*\bwoman,\s*woman\b~i", $name)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user