JFIFxxC      C  " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr{ gilour

File "CrupdateLink.php"

Full Path: /home/markqprx/iniasli.pro/app-20260222054312/Actions/Link/CrupdateLink.php
File size: 8.2 KB
MIME-type: text/x-php
Charset: utf-8

<?php

namespace App\Actions\Link;

use App\Models\Link;
use Arr;
use Auth;
use Common\Auth\Roles\Role;
use Common\Tags\Tag;
use Illuminate\Support\Facades\Http;
use Str;
use Symfony\Component\DomCrawler\Crawler;

class CrupdateLink
{
    public function __construct(protected bool $fetchMetadata = true)
    {
    }

    public function execute(Link $link, array $data): Link
    {
        $longUrl =
            parse_url($data['long_url'], PHP_URL_SCHEME) === null
                ? 'https://' . $data['long_url']
                : $data['long_url'];

        if (!mb_check_encoding($longUrl, 'ASCII')) {
            $longUrl = idn_to_ascii($longUrl, 0, INTL_IDNA_VARIANT_UTS46);
        }

        $attributes = !$link->exists
            ? array_merge($this->getMetadataFromUrl($longUrl), [
                'user_id' => Auth::id(),
                'hash' => Str::random(5),
                'active' => true,
            ])
            : [];

        $data = $this->filterOutDataBasedOnUserPermissions($data);

        foreach ($data as $key => $value) {
            if ($key === 'long_url') {
                $attributes[$key] = $longUrl;
            } elseif ($key === 'expires_at') {
                $attributes[$key] = $value ?? null;
            } elseif ($key === 'activates_at') {
                $attributes[$key] = $value ?? null;
            } elseif ($key === 'active') {
                $attributes[$key] = $value ?? false;
            } elseif ($key === 'type') {
                $attributes[$key] = $value ?? 'direct';
            } elseif ($key === 'type_id') {
                $attributes[$key] = $value ?? null;
            } elseif ($key === 'utm') {
                $attributes[$key] = $value ?? null;
            } elseif ($key === 'user_id') {
                $attributes[$key] = $value ?? Auth::id();
            } elseif ($key === 'domain_id') {
                $attributes[$key] = is_int($value) ? $value : null; // can be 0
            } elseif ($key === 'alias') {
                $attributes[$key] = $value ?? null;
            } elseif ($key === 'name') {
                $attributes[$key] = $value ?? Arr::get($attributes, 'name');
            } elseif ($key === 'image') {
                $attributes[$key] = $value ?? Arr::get($attributes, 'image');
            } elseif ($key === 'description') {
                $attributes[$key] =
                    $value ?? Arr::get($attributes, 'description');
            } elseif ($key === 'name') {
                $attributes[$key] = $value ?? Arr::get($attributes, 'name');
            } elseif ($key === 'animation') {
                $attributes[$key] = $value ?? null;
            } elseif ($key === 'password') {
                $attributes[$key] = $value ?: null;
            } elseif ($key === 'expires_at') {
                // restore link if user has removed expires_at date from expired link
                if (is_null($value)) {
                    $attributes['deleted_at'] = null;
                }
            }
        }

        $link->fill($attributes)->save();

        $this->saveLinkRules($link, $data);

        if (Arr::get($data, 'exp_clicks_rule.key')) {
            $link
                ->rules()
                ->updateOrCreate(
                    ['type' => 'exp_clicks'],
                    $data['exp_clicks_rule'],
                );
        }

        if (isset($data['tags'])) {
            $tags = app(Tag::class)->insertOrRetrieve(
                $data['tags'],
                Tag::DEFAULT_TYPE,
            );
            $link->tags()->sync($tags);
            $link->setRelation('tags', $tags);
        }

        if (isset($data['pixels'])) {
            $link->pixels()->sync(Arr::get($data, 'pixels'));
        }

        if (isset($data['groups'])) {
            $link->groups()->sync(Arr::get($data, 'groups'));
        }

        return $link;
    }

    protected function saveLinkRules(Link $link, array $data): void
    {
        $mergedRules = $data['rules'] ?? null;
        $ruleGroups = ['geo', 'device', 'platform'];

        foreach ($ruleGroups as $groupName) {
            foreach ($data["{$groupName}_rules"] ?? [] as $rule) {
                if (is_null($mergedRules)) {
                    $mergedRules = [];
                }
                $mergedRules[] = [
                    'type' => $groupName,
                    'key' => $rule['key'],
                    'value' => $rule['value'],
                ];
            }
        }

        // if no rules are sent at all, don't do anything, if empty array is sent, clear all rules
        if (!is_null($mergedRules)) {
            $link->rules()->delete();
            $mergedRules = $link->rules()->createMany($mergedRules);
            $link->setRelation('rules', $mergedRules);
        }
    }

    protected function getMetadataFromUrl(string $url): array
    {
        if (!$this->fetchMetadata) {
            return [];
        }

        $parsed = parse_url($url);
        $default = [
            'name' => $parsed['host'] ?? null,
            'description' => null,
            'image' => null,
        ];

        // don't try to parse video or image urls, only html and text
        if (!$this->isTextContent($url)) {
            return [];
        }

        // in case url is not reachable
        try {
            $content = Http::get($url)->body();
        } catch (\Exception $e) {
            return [];
        }

        if (!$content) {
            return [];
        }

        // if JSON response was returned
        if (is_array($content)) {
            return $default;
        }

        $crawler = new Crawler($content);
        $title =
            head($crawler->filter('title')->extract(['_text'])) ?:
            head(
                $crawler
                    ->filter('meta[property="og:title"]')
                    ->extract(['content']),
            );
        $description =
            head(
                $crawler
                    ->filter('meta[name="description"]')
                    ->extract(['content']),
            ) ?:
            head(
                $crawler
                    ->filter('meta[property="og:description"]')
                    ->extract(['content']),
            );
        $image = head(
            $crawler->filter('meta[property="og:image"]')->extract(['content']),
        );

        if (strlen($image) > 200) {
            $image = null;
        }

        return [
            'name' => Str::limit($title ?: $default['name'], 100),
            'description' => $description
                ? Str::limit($description, 180)
                : null,
            'image' => $image ?: null,
        ];
    }

    protected function isTextContent(string $url): bool
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_NOBODY, true);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_HEADER, true);
        curl_exec($curl);
        $info = curl_getinfo($curl);
        curl_close($curl);

        return Str::startsWith($info['content_type'] ?? '', 'text');
    }

    private function filterOutDataBasedOnUserPermissions(array $data): array
    {
        $authModel = Auth::user() ?: Role::where('guests', true)->first();

        if ($authModel->hasPermission('admin')) {
            return $data;
        }

        if (!settings('links.enable_type')) {
            $data['type'] = settings()->get('links.default_type', 'direct');
        }

        if (!$authModel->getRestrictionValue('links.create', 'alias')) {
            unset($data['alias']);
        }

        if (!$authModel->getRestrictionValue('links.create', 'expiration')) {
            unset($data['expires_at']);
            unset($data['activates_at']);
            unset($data['exp_clicks_rule']);
        }

        if (!$authModel->getRestrictionValue('links.create', 'password')) {
            unset($data['password']);
        }

        if (!$authModel->getRestrictionValue('links.create', 'utm')) {
            unset($data['utm']);
            unset($data['utm_custom']);
        }

        if (!$authModel->getRestrictionValue('links.create', 'retargeting')) {
            unset($data['device_rules']);
            unset($data['geo_rules']);
            unset($data['platform_rules']);
        }

        return $data;
    }
}