خطاA non-numeric value encountered - Line 4045
فایل/home/rkcxsznd/modello.ir/cms/class/UFO_Options.php
  1. <?php
  2. /**
  3.  * Copyright (c) 2022-2024 UFOCMS
  4.  *
  5.  * This software is licensed under the GPLv3 license.
  6.  * See the LICENSE file for more information.
  7.  */
  8. final class UFO_Options {
  9.     protected array $SAVER = [];
  10.     protected array $FLOAT = [];
  11.     /**
  12.      * @throws Exception
  13.      */
  14.     public function __construct () {
  15.         /**
  16.          * Load all saves
  17.          */
  18.         if ($this->isset_post()) {
  19.             $this->SAVER = json_decode(
  20.                 file_get_contents($this->slash_folder(
  21.                     dirname(__FILE__) . "/../content/cache/saver.json"
  22.                 )),
  23.                 JSON_UNESCAPED_UNICODE
  24.             );
  25.         }
  26.         $this->add_default();
  27.     }
  28.     /**
  29.      * @return void
  30.      * @throws Exception
  31.      */
  32.     protected function add_default () {
  33.         try {
  34.             $this->add_kv("this_page", $this->this_page());
  35.         } catch (Exception $e) {
  36.             $this->error($e);
  37.         }
  38.     }
  39.     /**
  40.      * @return array
  41.      */
  42.     public function get_saver (): array {
  43.         return $this->SAVER;
  44.     }
  45.     /**
  46.      * @return array
  47.      */
  48.     public function get_package (): array {
  49.         $file = (defined("ADMIN") ? $this->back_folder() : "") . _PRIVATE_ . "package.json";
  50.         if (file_exists($file))
  51.             return json_decode(file_get_contents($file), true);
  52.         return [];
  53.     }
  54.     /**
  55.      * @param string $md5
  56.      * @return bool
  57.      */
  58.     public function valid_md5 (string $md5): bool {
  59.         return preg_match('/^[a-f0-9]{32}$/', $md5);
  60.     }
  61.     /**
  62.      * @param string $algo
  63.      * @return string
  64.      */
  65.     public function hash_generator (string $algo = "sha512"): string {
  66.         return hash($algo, rand());
  67.     }
  68.     /**
  69.      * Standard UUID version 4
  70.      *
  71.      * @return string
  72.      * @throws Exception
  73.      */
  74.     public function uuid ( ): string {
  75.         $data = random_bytes(16);
  76.         $data[6] = chr(ord($data[6]) & 0x0f | 0x40); // Set version to 0100
  77.         $data[8] = chr(ord($data[8]) & 0x3f | 0x80); // Set bits 6-7 to 10
  78.         return vsprintf("%s%s-%s-%s-%s-%s%s%s", str_split(bin2hex($data), 4));
  79.     }
  80.     /**
  81.      * @param int $length
  82.      * @param int $min
  83.      * @param int $max
  84.      * @throws Exception
  85.      * @return int
  86.      */
  87.     public function random_digits (int $length, int $min = 1, int $max = 9): int {
  88.         $result = "";
  89.         for ($i = 0; $i < $length; $i++)
  90.             $result .= random_int($min, $max);
  91.         return (int) $result;
  92.     }
  93.     /**
  94.      * @param int $length
  95.      * @return string
  96.      * @throws Exception
  97.      */
  98.     public function random_alphabet (int $length = 10): string {
  99.         $alphabets = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  100.         $chars_len = strlen($alphabets);
  101.         $generated = "";
  102.         for ($i = 0; $i < $length; $i++)
  103.             $generated .= $alphabets[random_int(0, $chars_len - 1)];
  104.         return $generated;
  105.     }
  106.     /**
  107.      * @param $pass
  108.      * @return string
  109.      */
  110.     public function create_password ($pass): string {
  111.         $md5    = $this->valid_md5($pass) ? $pass : md5($pass);
  112.         $sha256 = hash("sha256", $md5);
  113.         $sha512 = hash("sha512", $sha256);
  114.         return md5($sha512);
  115.     }
  116.     /**
  117.      * @param bool $json
  118.      * @return array|string
  119.      * @throws Exception
  120.      */
  121.     public function verify_code (bool $json = false) {
  122.         global $db;
  123.         $structure = $this->do_work("ufo_structure_verify_code");
  124.         $structure = [
  125.             "code"   => strtoupper(
  126.                 $this->random_digits($structure["numbers"]
  127.             ) . $this->random_alphabet($structure["alphabets"])),
  128.             "expire" => $this->addTime((int) $db->verify_timeout, "s")
  129.         ];
  130.         return $json ? json_encode($structure) : $structure;
  131.     }
  132.     /**
  133.      * @param $time
  134.      * @return string
  135.      * @throws Exception
  136.      */
  137.     public function readable_timestamp ($time): string {
  138.         $date = new DateTime("@$time");
  139.         return $date->format("Y-m-d H:i:s");
  140.     }
  141.     /**
  142.      * @param string $format
  143.      * @return string
  144.      */
  145.     public function dateTime (string $format = "Y-m-d H:i:s"): string {
  146.         return date($format);
  147.     }
  148.     /**
  149.      * @param $dy
  150.      * @param $dm
  151.      * @param $dd
  152.      * @return array
  153.      */
  154.     protected function __solarDate ($dy, $dm, $dd): array {
  155.         list($dy, $dm, $dd) = explode('_', $this->replace_number($dy . '_' . $dm . '_' . $dd));
  156.         $g_d_m = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
  157.         $gy2 = ($dm > 2) ? ($dy + 1) : $dy;
  158.         $days = 355666 + (365 * $dy) + ((int)(($gy2 + 3) / 4)) - ((int)(($gy2 + 99) / 100)) + ((int)(($gy2 + 399) / 400)) + $dd + $g_d_m[$dm - 1];
  159.         $uy = -1595 + (33 * ((int)($days / 12053)));
  160.         $days %= 12053;
  161.         $uy += 4 * ((int)($days / 1461));
  162.         $days %= 1461;
  163.         if ($days > 365) {
  164.             $uy += (int)(($days - 1) / 365);
  165.             $days = ($days - 1) % 365;
  166.         }
  167.         if ($days < 186) {
  168.             $um = 1 + (int)($days / 31);
  169.             $ud = 1 + ($days % 31);
  170.         } else {
  171.             $um = 7 + (int)(($days - 186) / 30);
  172.             $ud = 1 + (($days - 186) % 30);
  173.         }
  174.         return [$uy, $um, $ud];
  175.     }
  176.     /**
  177.      * @param string $date
  178.      * @return string
  179.      */
  180.     public function solarDateTime (string $date): string {
  181.         if (empty($date)) return "";
  182.         /**
  183.          * Check Isset Time
  184.          */
  185.         $time = explode(" ", $date)[1] ?? "";
  186.         /**
  187.          * Date To Array
  188.          */
  189.         $dateArray = explode("-", explode(" ", $date)[0]);
  190.         /**
  191.          * Implode Result
  192.          */
  193.         return $time . " " . implode("/", $this->__solarDate($dateArray[0], $dateArray[1], $dateArray[2]));
  194.     }
  195.     /**
  196.      * @param string $dateTime
  197.      * @return string
  198.      * @throws Exception
  199.      */
  200.     public function cTime (string $dateTime): string {
  201.         global $db;
  202.         return ($db->meta("type_time") === "solar" ? $this->solarDateTime($dateTime) : $dateTime);
  203.     }
  204.     /**
  205.      * @param string $operator
  206.      * @param int|array $number
  207.      * @param string|array $unit
  208.      * @return string
  209.      */
  210.     public function reduceAddTime (string $operator, $number, $unit): string {
  211.         if (is_array($number) && is_array($unit)) {
  212.             $time = "";
  213.             foreach ($number as $k => $v)
  214.                 if (isset($unit[$k]))
  215.                     $time .= $operator . $v . " " . $this->abbreviationUnitTime($unit[$k]) . " ";
  216.             return $time;
  217.         }
  218.         return $operator . $number . " " . $this->abbreviationUnitTime($unit);
  219.     }
  220.     /**
  221.      * @param int|array $number
  222.      * @param string|array $unit
  223.      * @return string
  224.      */
  225.     public function addTime ($number, $unit): string {
  226.         return date("Y-m-d H:i:s", strtotime(
  227.             $this->reduceAddTime("+", $number, $unit),
  228.             time()
  229.         ));
  230.     }
  231.     /**
  232.      * @param int|array $number
  233.      * @param string|array $unit
  234.      * @return string
  235.      */
  236.     public function reduceTime ($number, $unit): string {
  237.         return date("Y-m-d H:i:s", strtotime(
  238.             $this->reduceAddTime("-", $number, $unit),
  239.             time()
  240.         ));
  241.     }
  242.     /**
  243.      * @param string $unit
  244.      * @return string
  245.      */
  246.     public function abbreviationUnitTime (string $unit): string {
  247.         return ["y" => "year", "m" => "month", "d" => "day", "h" => "hours", "i" => "minutes", "s" => "second"][$unit] ?? $unit;
  248.     }
  249.     /**
  250.      * @param string $dateTime
  251.      * @param bool $cTime
  252.      * @return string
  253.      * @throws Exception
  254.      */
  255.     public function structureDateTime (string $dateTime, bool $cTime = false): string {
  256.         global $db;
  257.         if ($cTime)
  258.             $dateTime = $this->cTime($dateTime);
  259.         $structure = $db->meta("structure_datetime");
  260.         if (empty($structure)) {
  261.             $db->update_meta("structure_datetime", "Y-m-d H:i:s");
  262.             $structure = $db->meta("structure_datetime");
  263.         }
  264.         $customStructure = $this->do_work("ufo_structure_datetime", [
  265.             "dateTime" => $dateTime,
  266.             "cTime"    => $cTime
  267.         ]);
  268.         if (!empty($customStructure))
  269.             return $customStructure;
  270.         return (new DateTime($dateTime))->format($structure);
  271.     }
  272.     /**
  273.      * @param string $datetime
  274.      * @param string $format
  275.      * @return bool
  276.      */
  277.     public function validateDateTime (string $datetime, string $format = "Y-m-d H:i:s"): bool {
  278.         $d = DateTime::createFromFormat($format, $datetime);
  279.         return $d && $d->format($format) === $datetime;
  280.     }
  281.     /**
  282.      * @param string $dateTime1
  283.      * @param string|null $dateTime2
  284.      * @return object
  285.      * @throws Exception
  286.      */
  287.     public function dateInterval (string $dateTime1, string $dateTime2 = null): object {
  288.         $dateTime  = new DateTime($dateTime2 ?? $this->dateTime());
  289.         $dateTime2 = new DateTime($dateTime1);
  290.         $interval  = $dateTime->diff($dateTime2);
  291.         $total_seconds = (
  292.             ($interval->y * 365.25 * 24 * 60 * 60) + // Convert years to seconds
  293.             ($interval->m * 30 * 24 * 60 * 60) +     // Convert months to seconds
  294.             ($interval->d * 24 * 60 * 60) +          // Convert days to seconds
  295.             ($interval->h * 60 * 60) +               // Convert hours to seconds
  296.             ($interval->i * 60) +                    // Convert minutes to seconds
  297.             ($interval->s)                           // Add remaining seconds
  298.         );
  299.         $result = [
  300.             "years"   => $interval->y,
  301.             "months"  => $interval->m,
  302.             "days"    => $interval->d,
  303.             "hours"   => $interval->h,
  304.             "minutes" => $interval->i,
  305.             "seconds" => $interval->s,
  306.             "total_seconds" => $total_seconds
  307.         ];
  308.         // Check if the first date is in the past
  309.         if ($interval->invert === 1) {
  310.             foreach ($result as &$value)
  311.                 $value = 0;
  312.         }
  313.         return (object) $result;
  314.     }
  315.     /**
  316.      * @param int $n
  317.      * @param int $n2
  318.      * @return float
  319.      */
  320.     public function calculatePercentage (int $n, int $n2): float {
  321.         if ($n == 0 || $n2 == 0) return 0;
  322.         return round(abs(($n / $n2) * 100));
  323.     }
  324.     /**
  325.      * @return string
  326.      * @throws Exception
  327.      */
  328.     public function this_title (): string {
  329.         global $db, $_;
  330.         return $_["title"] ?? $db->meta("web_name");
  331.     }
  332.     /**
  333.      * @return string
  334.      * @throws Exception
  335.      */
  336.     public function web_link (): string {
  337.         global $db, $_;
  338.         return $_["web_url"] ?? $db->meta("web_url");
  339.     }
  340.     /**
  341.      * @return string
  342.      * @throws Exception
  343.      */
  344.     public function admin_url (): string {
  345.         global $db;
  346.         return $db->meta("web_admin_url");
  347.     }
  348.     /**
  349.      * @return string
  350.      */
  351.     public function web_logo (): string {
  352.         return WEB_ICON;
  353.     }
  354.     /**
  355.      * @return string
  356.      * @throws Exception
  357.      */
  358.     public function copyright (): string {
  359.         global $db;
  360.         return $db->meta("copyright");
  361.     }
  362.     /**
  363.      * @return string
  364.      */
  365.     public function admin_path (): string {
  366.         global $admin_folder;
  367.         if ($this->isset_key($this->get_package(), "admin_path"))
  368.             return $this->get_package()["admin_path"];
  369.         return $admin_folder;
  370.     }
  371.     /**
  372.      * @return bool
  373.      */
  374.     public function is_admin (): bool {
  375.         return defined("ADMIN");
  376.     }
  377.     /**
  378.      * @return bool
  379.      */
  380.     public function is_front (): bool {
  381.         return defined("FRONT");
  382.     }
  383.     /**
  384.      * @param int|string $id
  385.      * @return array|mixed
  386.      * @throws Exception
  387.      */
  388.     public function get_admin ($id = null) {
  389.         global $db;
  390.         if (!empty($id))
  391.             return $db->get("admins", "id", $id)[0] ?? false;
  392.         if (isset($_COOKIE[$db->meta("admin_cookie")])) {
  393.             $Get = $db->get("admins", "hash_login", $_COOKIE[$db->meta("admin_cookie")]);
  394.             if (isset($Get[0]))
  395.                 return $Get[0];
  396.             else {
  397.                 setcookie($db->meta("admin_cookie"), "", time() - 300, "/");
  398.                 $this->redirect($db->meta("admin_url"));
  399.                 return false;
  400.             }
  401.         }
  402.         return false;
  403.     }
  404.     /**
  405.      * @param int $id
  406.      * @return array|null
  407.      * @throws ReflectionException
  408.      */
  409.     public function has_admin (int $id): ?array {
  410.         global $db;
  411.         return $db->helper->where("id", $id)->getOne("admins");
  412.     }
  413.     /**
  414.      * @param array $data
  415.      * @return array
  416.      * @throws ReflectionException
  417.      * @throws Exception
  418.      */
  419.     public function login_admin (array $data): array {
  420.         global $db, $ufo;
  421.         $db->where("login_name", $db->sanitize_string(
  422.             $data["login_name"]
  423.         ));
  424.         $db->where("password", $this->create_password(
  425.             $data["password"]
  426.         ));
  427.         $admin  = $db->helper->getOne("admins");
  428.         $result = [403, $this->lng("Incorrect information")];
  429.         if (!empty($admin)) {
  430.             $update = $db->update("admins", [
  431.                 "last_login" => $ufo->dateTime()
  432.             ], "id", $admin["id"]);
  433.             $result = [
  434.                 $update ? 200 : 503,
  435.                 $update ? $ufo->lng("You entered") : $ufo->lng("System error")
  436.             ];
  437.             if ($this->equal($result[0], 200)) setcookie(
  438.                 $db->meta("admin_cookie"),
  439.                 $admin["hash_login"], time() + (86400 * 30), "/"
  440.             );
  441.         }
  442.         return $result;
  443.     }
  444.     /**
  445.      * @return bool
  446.      * @throws Exception
  447.      */
  448.     public function check_login_admin (): bool {
  449.         global $db;
  450.         $cookie = $db->admin_cookie;
  451.         if (isset($_COOKIE[$cookie])) {
  452.             $admin = $db
  453.                 ->where("hash_login", $_COOKIE[$cookie])
  454.                 ->getOne("admins");
  455.             return !empty($admin);
  456.         }
  457.         return false;
  458.     }
  459.     /**
  460.      * @param array $data
  461.      * @return int|bool
  462.      * @throws ReflectionException
  463.      * @throws Exception
  464.      */
  465.     public function add_member (array $data) {
  466.         global $db;
  467.         $db->where("username", $data["username"]);
  468.         if ($this->isset_key($data, "email"))
  469.             $db->where("email", $data["email"], "=", "OR");
  470.         if ($this->isset_key($data, "no"))
  471.             $db->where("no", $data["no"], "=", "OR");
  472.         $exists = $db->helper->getValue("members", "uid");
  473.         if (!empty($exists))
  474.             return 0;
  475.         if ($this->isset_key($data, "more")) {
  476.             if (!is_array($data["more"]))
  477.                 return 503;
  478.         }
  479.         $insert = $db->insert("members", [
  480.             "name"       => $data["name"] ?? "",
  481.             "last_name"  => $data["last_name"] ?? "",
  482.             "username"   => $data["username"],
  483.             "email"      => $data["email"] ?? "",
  484.             "no"         => $data["no"] ?? "",
  485.             "password"   => $this->create_password($data["password"]),
  486.             "photo"      => $data["photo"] ?? $db->meta("unknown_photo"),
  487.             "hash"       => $this->hash_generator("md5"),
  488.             "last_login" => "",
  489.             "last_ip"    => $this->viewer_ip(),
  490.             "dateTime"   => $this->dateTime(),
  491.             "verify"     => $data["verify"] ?? ($db->meta("accept-member") == "true" ? 1 : 0),
  492.             "more"       => json_encode($data["more"] ?? [], JSON_UNESCAPED_UNICODE)
  493.         ]);
  494.         return $insert ? $db->insert_id() : false;
  495.     }
  496.     /**
  497.      * @param array $data
  498.      * @param ?int $uid
  499.      * @return bool
  500.      * @throws Exception
  501.      */
  502.     public function update_member (array $data, int $uid = null): bool {
  503.         global $db;
  504.         if (empty($uid))
  505.             $uid = $this->get_member()["uid"] ?? $this->die($this->lng("Member not found"));
  506.         $beforeData = $db->get("members", "uid", $uid)[0];
  507.         $changedPassword = $this->isset_key($data, "password") && $beforeData["password"] != $data["password"];
  508.         $more = $beforeData["more"];
  509.         $this->is_json($more, $more);
  510.         if (!is_array($more))
  511.             $more = [];
  512.         $more = $this->default($more, $data["more"] ?? []);
  513.         return $db->update("members", [
  514.             "name"       => $data["name"] ?? $beforeData["name"],
  515.             "last_name"  => $data["last_name"] ?? $beforeData["last_name"],
  516.             "username"   => $data["username"] ?? $beforeData["username"],
  517.             "email"      => $data["email"] ?? $beforeData["email"],
  518.             "no"         => $data["no"] ?? $beforeData["no"],
  519.             "password"   => !$changedPassword ? $beforeData["password"] : $this->create_password($data["password"]),
  520.             "photo"      => empty($data["photo"]) ? $beforeData["photo"] : $data["photo"],
  521.             "hash"       => !$changedPassword ? $beforeData["hash"] : $this->hash_generator("md5"),
  522.             "last_login" => $data["last_login"] ?? $beforeData["last_login"],
  523.             "last_ip"    => $data["last_ip"] ?? $beforeData["last_ip"],
  524.             "verify"     => $data["verify"] ?? $beforeData["verify"],
  525.             "more"       => json_encode($more, JSON_UNESCAPED_UNICODE)
  526.         ], "uid", $uid);
  527.     }
  528.     /**
  529.      * @param $args
  530.      * @return mixed
  531.      * @throws Exception
  532.      */
  533.     public function login_member ($args) {
  534.         global $db, $ufo;
  535.         $where  = [
  536.             "password" => $this->create_password($args["password"])
  537.         ];
  538.         if (filter_var($args["username"], FILTER_VALIDATE_EMAIL))
  539.             $where["email"] = $args["username"];
  540.         else if (is_numeric($args["username"]))
  541.             $where["no"] = $args["username"];
  542.         else if (is_string($args["username"]))
  543.             $where["username"] = $args["username"];
  544.         else
  545.             return $this->lng("Not valid username");
  546.         $member = $db->get("members", $where);
  547.         if (isset($member[0])) {
  548.             if ($db->update("members", [
  549.                 "last_login" => $ufo->dateTime(),
  550.                 "last_ip"    => $ufo->viewer_ip()
  551.             ], "uid", $member[0]["uid"])) {
  552.                 setcookie($db->meta("member_cookie"), $member[0]["hash"], time() + (86400 * 30), "/");
  553.                 return $member[0];
  554.             }
  555.         }
  556.         return false;
  557.     }
  558.     /**
  559.      * @return bool
  560.      * @throws Exception
  561.      */
  562.     public function logout_member (): bool {
  563.         global $db;
  564.         return $this->unset_cookie($db->member_cookie);
  565.     }
  566.     /**
  567.      * @param $uid
  568.      * @throws Exception
  569.      * @return bool
  570.      */
  571.     public function verified_member ($uid = null): bool {
  572.         return ((int) $this->get_member($uid)["verify"] ?? 0) == 1;
  573.     }
  574.     /**
  575.      * @param $mid
  576.      * @return array
  577.      * @throws Exception
  578.      */
  579.     public function get_member ($mid = null): array {
  580.         global $db;
  581.         $cookie = $db->meta("member_cookie");
  582.         if (!empty($mid))
  583.             return $db->get("members", "uid", $mid)[0] ?? [];
  584.         if ($this->isset_cookie($cookie))
  585.             return $db->get("members", [
  586.                 "hash" => $_COOKIE[$cookie]
  587.             ])[0] ?? [];
  588.         return [];
  589.     }
  590.     /**
  591.      * @param int $id
  592.      * @return array|null
  593.      * @throws ReflectionException
  594.      */
  595.     public function has_member (int $id): ?array {
  596.         global $db;
  597.         return $db->helper->where("uid", $id)->getOne("members");
  598.     }
  599.     /**
  600.      * @param int|null $mid
  601.      * @param ?string $role
  602.      * @return bool|array
  603.      * @throws Exception
  604.      */
  605.     public function member_roles (int $mid = null, ?string $role = null) {
  606.         global $db;
  607.         $mid   = $this->get_member($mid);
  608.         $roles = $mid["roles"];
  609.         $roles = json_decode($this->is_array(
  610.             $roles
  611.         ) ? $roles : "[]", true);
  612.         if (empty($role))
  613.             return $roles;
  614.         if (is_array($roles)) {
  615.             if (!in_array($role, $roles))
  616.                 $roles[] = $role;
  617.         } else
  618.             $roles = [$role];
  619.         return $db->update("members", [
  620.             "roles" => json_encode($roles)
  621.         ], "uid", $mid["uid"]);
  622.     }
  623.     /**
  624.      * @return false
  625.      * @throws Exception
  626.      */
  627.     public function check_login_member (): bool {
  628.         global $db;
  629.         $cookie = $db->member_cookie;
  630.         if (isset($_COOKIE[$cookie])) {
  631.             $member = $db->where("hash", $_COOKIE[$cookie])->getOne("members");
  632.             return !empty($member);
  633.         }
  634.         return false;
  635.     }
  636.     /**
  637.      * @param $plugin
  638.      * @return string
  639.      */
  640.     public function plugin_url ($plugin): string {
  641.         return URL_PLUGINS . $plugin . "/";
  642.     }
  643.     /**
  644.      * @param string $plugin
  645.      * @return string
  646.      */
  647.     public function plugin_dir (string $plugin = ""): string {
  648.         return PLUGINS . $plugin . SLASH;
  649.     }
  650.     /**
  651.      * @return string
  652.      * @throws Exception
  653.      */
  654.     public function theme_path (): string {
  655.         global $db, $_, $admin_folder;
  656.         if (!empty($_["this_template"]["path"]))
  657.             return $_["this_template"]["path"];
  658.         $theme = $_COOKIE["ufo_theme"] ?? ($_SESSION["ufo_theme"] ?? $db->meta("theme"));
  659.         $templates = new UFO_Json($admin_folder . "content/cache/templates.json");
  660.         $theme = $templates->where("id", $theme)->get()[0] ?? [];
  661.         return $this->slash_folder(THEMES . (
  662.             $_["ufo_theme"] ?? ($theme["path"] ?? "") . SLASH
  663.         ));
  664.     }
  665.     /**
  666.      * @return string
  667.      * @throws Exception
  668.      */
  669.     public function theme_url (): string {
  670.         global $db, $_, $admin_folder;
  671.         if (!empty($_["this_template"]))
  672.             return $_["this_template"]["link"];
  673.         $templates = (new UFO_Json($admin_folder . "content/cache/templates.json"));
  674.         $template  = $_COOKIE["ufo_theme"] ?? ($_SESSION["ufo_theme"] ?? $db->meta("theme"));
  675.         $template  = $templates->where("id", $template)->get()[0] ?? [];
  676.         return $this->sanitize_link(URL_THEME . (
  677.             $_["ufo_theme"] ?? ($template["path"] ?? "") . SLASH
  678.         ));
  679.     }
  680.     /**
  681.      * @return string
  682.      */
  683.     public function get_url (): string {
  684.         $location = (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] === "on" ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
  685.         if ($this->isset_post("location")) {
  686.             if (filter_var($_POST["location"], FILTER_VALIDATE_URL))
  687.                 return parse_url($location)["host"] == parse_url($_POST["location"])["host"] ? $_POST["location"] : $location;
  688.         }
  689.         return $location;
  690.     }
  691.     // HTML, some tags do not require a closing tag
  692.     public array $html_tags_nc = [
  693.         "meta", "link", "hr", "img", "input", "br"
  694.     ];
  695.     /**
  696.      * @param string $tag
  697.      * @param string|callable|array $content
  698.      * @param array $attr
  699.      * @param int $num
  700.      * @return string
  701.      */
  702.     public function tag (string $tag, $content = "", array $attr = [], int $num = 1): string {
  703.         $html  = "";
  704.         $attrs = "";
  705.         if (!is_string($content)) {
  706.             if ($this->is_function($content))
  707.                 $content = $content();
  708.             if (is_array($content))
  709.                 $content = implode("", $content);
  710.         }
  711.         if ($this->has_keys($attr))
  712.             foreach ($attr as $k => $v)
  713.                 if (!empty($k))
  714.                     $attrs .= " " . $k . '="' . $v . '"';
  715.         for ($i = 0; $i < $num; $i++) {
  716.             $html .= "<$tag$attrs>";
  717.             $html .= in_array($tag, $this->html_tags_nc) ? "" : $content;
  718.             $html .= in_array($tag, $this->html_tags_nc) ? "" : "</$tag>";
  719.         }
  720.         return $html;
  721.     }
  722.     /**
  723.      * @param array $css
  724.      * @return string
  725.      */
  726.     public function css (array $css = []): string {
  727.         $join = "";
  728.         foreach ($css as $k => $v)
  729.             $join .= "$k:$v;";
  730.         return $join;
  731.     }
  732.     /**
  733.      * @param int $badge
  734.      * @return string
  735.      */
  736.     public function css_badge (int $badge): string {
  737.         return ["primary", "info", "warning", "danger", "success", "secondary", "light", "dark"][$badge];
  738.     }
  739.     /**
  740.      * @param array $keys
  741.      * @return void
  742.      */
  743.     public function add_meta (array $keys): void {
  744.         if (!isset($this->FLOAT["head"]))
  745.             $this->FLOAT["head"] = [];
  746.         if (!isset($this->FLOAT["head"]["meta"]))
  747.             $this->FLOAT["head"]["meta"] = [];
  748.         $this->FLOAT["head"]["meta"][] = $keys;
  749.     }
  750.     /**
  751.      * @param array $keys
  752.      * @return void
  753.      */
  754.     public function add_link (array $keys): void {
  755.         if (!isset($this->FLOAT["head"]))
  756.             $this->FLOAT["head"] = [];
  757.         if (!isset($this->FLOAT["head"]["link"]))
  758.             $this->FLOAT["head"]["link"] = [];
  759.         $this->FLOAT["head"]["link"][] = $keys;
  760.     }
  761.     /**
  762.      * @param string $address
  763.      * @return void
  764.      */
  765.     public function add_style (string $address): void {
  766.         if ($this->is_url($address)) {
  767.             if (!isset($this->FLOAT["head"]))
  768.                 $this->FLOAT["head"] = [];
  769.             if (!isset($this->FLOAT["head"]["css"]))
  770.                 $this->FLOAT["head"]["css"] = [];
  771.             $name = isset(pathinfo($address)["filename"]) ? pathinfo($address)["filename"] : rand();
  772.             $name = str_replace(".", "-", $name);
  773.             $this->FLOAT["head"]["css"][] = "\t<link data-name='$name' rel='stylesheet' href='$address'>\n";
  774.         }
  775.     }
  776.     /**
  777.      * @param string $name
  778.      * @param string $address
  779.      * @param ?string $after
  780.      * @param string $location
  781.      * @return void
  782.      */
  783.     public function add_script (string $name, string $address, string $after = null, string $location = "bottom"): void {
  784.         if ($this->is_url($address)) {
  785.             $script = "\t" . $this->tag("script", "", [
  786.                 "data-name" => $name,
  787.                 "src"       => $address
  788.             ]) . "\n";
  789.             if (!isset($this->FLOAT["head"]["script"][$location][$name]))
  790.                 $this->FLOAT["head"]["script"][$location][$name] = [];
  791.             if (empty($after)) {
  792.                 $this->FLOAT["head"]["script"][$location][$name][] = $script;
  793.             } else {
  794.                 if (!isset($this->FLOAT["head"]["script"][$location][$after]))
  795.                     $this->FLOAT["head"]["script"][$location][$after] = [];
  796.                 $this->FLOAT["head"]["script"][$location][$after][] = $script;
  797.             }
  798.         }
  799.     }
  800.     /**
  801.      * @param string $source
  802.      * @return void
  803.      */
  804.     public function add_source (string $source): void {
  805.         if (!isset($this->FLOAT["tag_script"]))
  806.             $this->FLOAT["tag_script"] = [];
  807.         $this->FLOAT["tag_script"][] = $source;
  808.     }
  809.     /**
  810.      * @param string $name
  811.      * @param array $data
  812.      * @return void
  813.      */
  814.     public function localize_script (string $name, array $data = []): void {
  815.         $dataArray = empty($data) ? $this->FLOAT["localize"][$name] : $data;
  816.         echo "\t<script>\n";
  817.         echo "\t\tconst " . $name . " = " . json_encode($dataArray, JSON_UNESCAPED_UNICODE) . ";\n";
  818.         echo "\t</script>";
  819.     }
  820.     /**
  821.      * @param string $_name
  822.      * @param string $_key
  823.      * @param $_value
  824.      * @return void
  825.      */
  826.     public function add_localize_script (string $_name, string $_key, $_value): void {
  827.         if (!isset($this->FLOAT["localize"][$_name]))
  828.             $this->FLOAT["localize"][$_name] = [];
  829.         $this->FLOAT["localize"][$_name][$_key] = $_value;
  830.     }
  831.     /**
  832.      * @param $_name
  833.      * @param $_key
  834.      * @return void
  835.      */
  836.     public function unset_localize_script (string $_name, ?string $_key = null): void {
  837.         if (empty($_key)) {
  838.             if (isset($this->FLOAT["localize"][$_name]))
  839.                 unset($this->FLOAT["localize"][$_name]);
  840.         } else if (isset($this->FLOAT["localize"][$_name][$_key]))
  841.             unset($this->FLOAT["localize"][$_name][$_key]);
  842.     }
  843.     /**
  844.      * @return string
  845.      */
  846.     public function loop_load_meta (): string {
  847.         if (!isset($this->FLOAT["head"]))
  848.             $this->FLOAT["head"] = [];
  849.         if (!isset($this->FLOAT["head"]["meta"]))
  850.             $this->FLOAT["head"]["meta"] = [];
  851.         $metas = "";
  852.         foreach ($this->FLOAT["head"]["meta"] as $item) {
  853.             $keys = '';
  854.             foreach ($item as $k => $v)
  855.                 $keys .= $k . '="' . $v . '" ';
  856.             $metas .= "\t<meta ".rtrim($keys, " ").">\n";
  857.         }
  858.         return $metas;
  859.     }
  860.     /**
  861.      * @return string
  862.      */
  863.     public function loop_load_link (): string {
  864.         if (!isset($this->FLOAT["head"]))
  865.             $this->FLOAT["head"] = [];
  866.         if (!isset($this->FLOAT["head"]["link"]))
  867.             $this->FLOAT["head"]["link"] = [];
  868.         $links = "";
  869.         foreach ($this->FLOAT["head"]["link"] as $item) {
  870.             $keys = '';
  871.             foreach ($item as $k => $v)
  872.                 $keys .= $k . '="' . $v . '" ';
  873.             $links .= "\t<link " . rtrim($keys, " ") . ">\n";
  874.         }
  875.         return $links;
  876.     }
  877.     /**
  878.      * @return void
  879.      */
  880.     public function loop_load_styles (): void {
  881.         if (!isset($this->FLOAT["head"]))
  882.             $this->FLOAT["head"] = [];
  883.         if (!isset($this->FLOAT["head"]["css"]))
  884.             $this->FLOAT["head"]["css"] = [];
  885.         foreach ($this->FLOAT["head"]["css"] as $items)
  886.             echo $items;
  887.     }
  888.     /**
  889.      * @param string $location
  890.      * @return string
  891.      */
  892.     public function loop_load_scripts (string $location = "bottom"): string {
  893.         if (!isset($this->FLOAT["head"]))
  894.             $this->FLOAT["head"] = [];
  895.         if (!isset($this->FLOAT["head"]["script"]))
  896.             $this->FLOAT["head"]["script"] = [];
  897.         if (isset($this->FLOAT["head"]["script"][$location])) {
  898.             foreach ($this->FLOAT["head"]["script"][$location] as $k => $i) {
  899.                 foreach ($i as $item)
  900.                     echo $item;
  901.             }
  902.         }
  903.         return "";
  904.     }
  905.     /**
  906.      * @return void
  907.      */
  908.     public function loop_source (): void {
  909.         if (!isset($this->FLOAT["tag_script"]))
  910.             $this->FLOAT["tag_script"] = [];
  911.         foreach ($this->FLOAT["tag_script"] as $item)
  912.             echo "\t" . $this->tag("script", $item) . "\n";
  913.     }
  914.     /**
  915.      * @return false|void
  916.      */
  917.     public function localize_all_script () {
  918.         if (!isset($this->FLOAT["localize"]))
  919.             $this->FLOAT["localize"] = [];
  920.         $this->FLOAT["localize"] = array_reverse($this->FLOAT["localize"]);
  921.         if (empty($this->FLOAT["localize"]))
  922.             return false;
  923.         echo "\t<script>\n";
  924.         foreach ($this->FLOAT["localize"] as $k => $v)
  925.             echo "\t\tconst " . $k . " = " . json_encode($v, JSON_UNESCAPED_UNICODE) . ";\n";
  926.         echo "\t</script>\n";
  927.     }
  928.     /**
  929.      * @return void
  930.      */
  931.     public function clear_link (): void {
  932.         if (!$this->is_admin())
  933.             $this->FLOAT["head"]["link"] = [];
  934.     }
  935.     /**
  936.      * @return void
  937.      */
  938.     public function clear_style (): void {
  939.         if (!$this->is_admin())
  940.             $this->FLOAT["head"]["css"] = [];
  941.     }
  942.     /**
  943.      * @return void
  944.      */
  945.     public function clear_meta (): void {
  946.         if (!$this->is_admin())
  947.             $this->FLOAT["head"]["meta"] = [];
  948.     }
  949.     /**
  950.      * @return void
  951.      */
  952.     public function clear_script (): void {
  953.         if (!$this->is_admin())
  954.             $this->FLOAT["head"]["script"] = [];
  955.     }
  956.     /**
  957.      * @return void
  958.      */
  959.     public function clear_localize_script (): void {
  960.         if (!$this->is_admin())
  961.             $this->FLOAT["localize"] = [];
  962.     }
  963.     /**
  964.      * @param string|null $replacement
  965.      * @return void
  966.      */
  967.     public function clear_body (?string $replacement = null) {
  968.         global $_;
  969.         $_["ufo_clear_body"] = $replacement;
  970.     }
  971.     /**
  972.      * @param array $args
  973.      * @return void
  974.      */
  975.     public function add_menu (array $args = []): void {
  976.         if (empty($args))
  977.             return;
  978.         if (!isset($this->FLOAT["menu"]))
  979.             $this->FLOAT["menu"] = [];
  980.         $menu = [
  981.             "title" => $args["title"],
  982.             "page"  => $args["page"]
  983.         ];
  984.         /**
  985.          * Img & Icon menu
  986.          */
  987.         if (isset($args["img"])) {
  988.             $menu["img"] = $args["img"];
  989.         } else if (isset($args["icon"])) {
  990.             $menu["icon"] = $args["icon"];
  991.         }
  992.         /**
  993.          * Is plugin
  994.          */
  995.         if (isset($args["plugin"]))
  996.             $menu["plugin"] = $args["plugin"];
  997.         else
  998.             $menu["plugin"] = false;
  999.         /**
  1000.          * Badge number
  1001.          */
  1002.         if (isset($args["badge"])) {
  1003.             if (is_numeric($args["badge"])) {
  1004.                 if ($args["badge"] > 0)
  1005.                     $menu["badge"] = ((int) $args["badge"] > 99 ? "99+" : $args["badge"]);
  1006.             }
  1007.         }
  1008.         if (isset($args["position"])) {
  1009.             $this->insert_array($this->FLOAT["menu"], (int) $args["position"], [$menu]);
  1010.         } else {
  1011.             $this->FLOAT["menu"][] = $menu;
  1012.         }
  1013.     }
  1014.     /**
  1015.      * @return string
  1016.      */
  1017.     public function loop_menu (): string {
  1018.         if (!isset($this->FLOAT["menu"]))
  1019.             $this->FLOAT["menu"] = [];
  1020.         $MENU   = $this->FLOAT["menu"];
  1021.         $JOIN   = "";
  1022.         $active = $this->reloadedHere();
  1023.         foreach ($MENU as $item) {
  1024.             $JOIN .= $this->tag("li",
  1025.                 $this->tag("div",
  1026.                     (isset($item["badge"]) ? $this->tag("div", $item["badge"], ["class" => "admin-menu-badge"]) : "") .
  1027.                     (isset($item["icon"]) ? '<i class="' . $item["icon"] . '" data-icon="' . $item["icon"] . '"></i>' : (isset($item['img']) ? '<img src="' . $item['img'] . '">' : '<i class="ufo-icon-question" data-icon="ufo-icon-question"></i>')) .
  1028.                     $this->tag("span", ($item["title"] ?? "")),
  1029.                     ["class" => "flex flex-start align-center height-100-cent"]),
  1030.                 [
  1031.                     "class" => "item-menu" . ($active ? "" : " active"),
  1032.                     "data-" . ($item["plugin"] ? "plugin" : "page") => ($item["plugin"] ?? $item["page"]),
  1033.                     "data-page" => $item["page"]
  1034.                 ]
  1035.             );
  1036.             $active = true;
  1037.         }
  1038.         return $JOIN;
  1039.     }
  1040.     /**
  1041.      * @param string $_key
  1042.      * @param array $data
  1043.      * @return void
  1044.      */
  1045.     public function add_input (string $_key, array $data = []): void {
  1046.         $this->FLOAT["inputs"][$_key] = $this->FLOAT["inputs"][$_key] ?? [];
  1047.         $this->FLOAT["inputs"][$_key][] = $data;
  1048.     }
  1049.     /**
  1050.      * @param string $_key
  1051.      * @param string|null $end
  1052.      * @param bool $array
  1053.      * @return bool|array|void
  1054.      */
  1055.     public function loop_inputs (string $_key, ?string $end = null, bool $array = false) {
  1056.         if (empty($end))
  1057.             $end = "<div class='mb-5 p-5px'></div>";
  1058.         if (!isset($this->FLOAT["inputs"][$_key]))
  1059.             return $array ? [] : false;
  1060.         if ($array)
  1061.             return $this->FLOAT["inputs"][$_key];
  1062.         foreach ($this->FLOAT["inputs"][$_key] as $i) {
  1063.             $join  = "";
  1064.             $class = "form-control";
  1065.             foreach ($i as $k => $v) {
  1066.                 if ($k == "class")
  1067.                     $class = $class . " " . $i["class"];
  1068.                 else
  1069.                     $join .= " " . $k . "='" . $v . "'";
  1070.             }
  1071.             echo '<input class="' . $class . '" ' . $join . '>' . $end;
  1072.         }
  1073.     }
  1074.     /**
  1075.      * @param array $arg
  1076.      * @return string
  1077.      */
  1078.     public function single_input (array $arg = []): string {
  1079.         $join  = "";
  1080.         $class = "form-control";
  1081.         foreach ($arg as $k => $v) {
  1082.             if (!empty($k)) {
  1083.                 if ($k == "class")
  1084.                     $class = $class . " " . $arg["class"];
  1085.                 else if ($k != "end")
  1086.                     $join .= " " . $k . "='" . $v . "'";
  1087.             }
  1088.         }
  1089.         return '<input class="'.$class.'" '.$join.'>' . ($arg["end"] ?? "<div class='mt-10 p-5px'></div>");
  1090.     }
  1091.     /**
  1092.      * @param string $id
  1093.      * @param array $args [
  1094.      *     "page"  => String (Default = $db->slug_blog)
  1095.      *     "name"  => String,
  1096.      *     "class" => String,
  1097.      *     "placeholder"  => String,
  1098.      *     "autocomplete" => String (on,off)
  1099.      * ]
  1100.      * @return void
  1101.      * @throws Exception
  1102.      */
  1103.     public function search_form (string $id, array $args = []): void {
  1104.         global $db;
  1105.         extract($args);
  1106.         echo $this->tag("form",
  1107.             $this->tag("input", null, [
  1108.                 "type"  => "search",
  1109.                 "name"  => $name ?? "search",
  1110.                 "class" => "form-control",
  1111.                 "placeholder"  => $placeholder ?? $this->lng("Search"),
  1112.                 "autocomplete" => $autocomplete ?? "off",
  1113.                 "minlength"    => 3
  1114.             ]) .
  1115.             $this->tag("button", $this->tag("i", null, [
  1116.                 "class" => "ufo-icon-search"
  1117.             ]), [
  1118.                 "class" => "btn btn-primary font-size-20px"
  1119.             ]), [
  1120.                 "id"    => $id,
  1121.                 "class" => "ufo-search-form flex flex-start " . ($class ?? ""),
  1122.                 "data-page" => $page ?? $db->slug("blog")
  1123.             ]
  1124.         );
  1125.     }
  1126.     /**
  1127.      * @param string $file
  1128.      * @param mixed ...$args
  1129.      * @return string
  1130.      * @throws Exception
  1131.      */
  1132.     public function require (string $file, ...$args): ?string {
  1133.         global $ufo, $db, $_, $admin_folder;
  1134.         $file = $this->slash_folder(str_replace(
  1135.             ".php", "", $file
  1136.         ) . ".php");
  1137.         if (is_file($file)) {
  1138.             ob_start();
  1139.             require $this->slash_folder($file);
  1140.             return ob_get_clean();
  1141.         }
  1142.         $this->error($this->lng("File not found") . " ($file)");
  1143.         return null;
  1144.     }
  1145.     /**
  1146.      * @param $file
  1147.      * @param bool|string $layout
  1148.      * @param string $format
  1149.      * @param mixed $arg
  1150.      * @return mixed (Because it returns composite data from the file,
  1151.      *                but it actually returns `boolean`)
  1152.      * @throws Exception
  1153.      */
  1154.     public function load_layout ($file, $layout = true, string $format = ".php", $arg = []) {
  1155.         global $ufo, $db, $_, $admin_folder;
  1156.         if ($layout)
  1157.             $layout = LAYOUT;
  1158.         else {
  1159.             $layout = "";
  1160.             if ($_SERVER["REQUEST_METHOD"] == "POST")
  1161.                 $format = "";
  1162.         }
  1163.         $file = $this->slash_folder($layout . $file . $format);
  1164.         if (file_exists($file))
  1165.             return require $file;
  1166.         if (defined("ADMIN"))
  1167.             return $this->load_layout("404");
  1168.         echo $this->error($this->lng("File not found") . " ($file)", "", false);
  1169.         return false;
  1170.     }
  1171.     /**
  1172.      * @param $file
  1173.      * @param bool|string $layout
  1174.      * @param string $format
  1175.      * @param mixed $arg
  1176.      * @return string
  1177.      * @throws Exception
  1178.      */
  1179.     public function return_layout ($file, $layout = true, string $format = ".php", $arg = []): string {
  1180.         ob_start();
  1181.         $this->load_layout($file, $layout, $format, $arg);
  1182.         return ob_get_clean();
  1183.     }
  1184.     /**
  1185.      * @param $file
  1186.      * @param mixed $args
  1187.      * @param bool $error
  1188.      * @return void
  1189.      * @throws Exception
  1190.      */
  1191.     public function from_theme ($file, $args = [], bool $error = false): void {
  1192.         global $ufo, $db, $_, $admin_folder;
  1193.         $file = $this->slash_folder($this->theme_path() . $file . ".php");
  1194.         if (
  1195.             file_exists($file) && is_file($file) && defined("FRONT") && $ufo->isset_key($_, "this_template") &&
  1196.             (($_["this_template"]["set"] || $_["this_template"]["multi"]) || $ufo->isset_key($_SESSION, "ufo_theme_admin_preview"))
  1197.         )
  1198.             require $file;
  1199.         else if ($error)
  1200.             $this->error("File not found in template!");
  1201.     }
  1202.     /**
  1203.      * @param $file
  1204.      * @param mixed $args
  1205.      * @param bool $error
  1206.      * @return void
  1207.      * @throws Exception
  1208.      */
  1209.     public function return_from_theme ($file, $args = [], bool $error = false): string {
  1210.         ob_start();
  1211.         $this->from_theme($file, $args, $error);
  1212.         return ob_get_clean();
  1213.     }
  1214.     /**
  1215.      * @param string $folder
  1216.      * @return bool
  1217.      * @throws Exception
  1218.      */
  1219.     public function folder_exists_theme (string $folder): bool {
  1220.         return is_dir($this->slash_folder($this->theme_path() . $folder));
  1221.     }
  1222.     /**
  1223.      * @param $file
  1224.      * @param string $type
  1225.      * @return bool
  1226.      * @throws Exception
  1227.      */
  1228.     public function file_exists_theme ($file, string $type = "php"): bool {
  1229.         return file_exists($this->slash_folder(
  1230.             $this->theme_path() . $file . "." . $type
  1231.         ));
  1232.     }
  1233.     /**
  1234.      * @return array
  1235.      */
  1236.     public function manifest_theme (): array {
  1237.         global $_;
  1238.         return $_["this_template"]["manifest"] ?? [];
  1239.     }
  1240.     /**
  1241.      * @return string
  1242.      * @throws Exception
  1243.      */
  1244.     public function dir (): string {
  1245.         global $db, $_;
  1246.         try {
  1247.             return $_["dir"] ?? $db->meta("dir");
  1248.         } catch (Exception $e) {
  1249.             $this->error($e);
  1250.             return "ltr";
  1251.         }
  1252.     }
  1253.     /**
  1254.      * @return string
  1255.      * @throws Exception
  1256.      */
  1257.     public function charset (): string {
  1258.         global $db, $_;
  1259.         try {
  1260.             return $_["charset"] ?? $db->meta("charset");
  1261.         } catch (Exception $e) {
  1262.             $this->error($e);
  1263.             return "UTF-8";
  1264.         }
  1265.     }
  1266.     /**
  1267.      * @param $text
  1268.      * @param string $classes
  1269.      * @param string $style
  1270.      * @param array $attrs
  1271.      * @return string
  1272.      */
  1273.     public function btn ($text, string $classes = "", string $style = "btn btn-primary", array $attrs = []): string {
  1274.         return $this->tag("button", $text, $this->default([
  1275.             "class" => "$style $classes"
  1276.         ], $attrs));
  1277.     }
  1278.     /**
  1279.      * @return string
  1280.      * @throws Exception
  1281.      */
  1282.     public function reverse_float (): string {
  1283.         return $this->dir() == "ltr" ? "f-right" : "f-left";
  1284.     }
  1285.     /**
  1286.      * Space left and right based on direction
  1287.      * @param int $px
  1288.      * @return string
  1289.      * @throws Exception
  1290.      */
  1291.     public function space_lr_by_dir (int $px = 5): string {
  1292.         return "m" . ($this->dir() == "rtl" ? "r" : "l") . "-$px";
  1293.     }
  1294.     /**
  1295.      * @param string $string
  1296.      * @param bool $condition
  1297.      * @param string|null $else
  1298.      * @return void
  1299.      */
  1300.     public function echo (string $string, bool $condition, ?string $else = null): void {
  1301.         if ($condition)
  1302.             echo $string;
  1303.         else if (!empty($else))
  1304.             echo $else;
  1305.     }
  1306.     /**
  1307.      * @param mixed $value
  1308.      * @param bool $condition
  1309.      * @param mixed $else
  1310.      * @return mixed|void
  1311.      */
  1312.     public function return ($value, bool $condition, $else = null) {
  1313.         if ($condition)
  1314.             return $value;
  1315.         else if (!empty($else))
  1316.             return $else;
  1317.     }
  1318.     /**
  1319.      * @param string $name
  1320.      * @param string $value
  1321.      * @param bool $condition
  1322.      * @return void
  1323.      */
  1324.     public function attr (string $name, string $value, bool $condition): void {
  1325.         if ($condition) echo " $name='$value' ";
  1326.     }
  1327.     /**
  1328.      * @param string|null $space
  1329.      * @param int $number
  1330.      * @return string
  1331.      */
  1332.     public function space (?string $space = null, int $number = 1): string {
  1333.         $spaces = "";
  1334.         for ($i = 0; $i < $number; $i++) {
  1335.             if (empty($space))
  1336.                 $spaces .= "<div class='mt-10 p-5px width-100-cent db'></div>";
  1337.             else
  1338.                 $spaces .= $space;
  1339.         }
  1340.         return $spaces;
  1341.     }
  1342.     /**
  1343.      * @param string|int $id
  1344.      * @param bool $for_table
  1345.      * @return string
  1346.      */
  1347.     public function checkbox ($id = 0, bool $for_table = false): string {
  1348.         $rand = "ch" . rand(1, 100000);
  1349.         return '<input type="checkbox" id="'.$rand.'" class="dn" data-id="'.$id.'"><label for="'.$rand.'" class="check'.($for_table ? " for-table" : "").'"><svg width="18px" height="18px" viewBox="0 0 18 18"><path d="M1,9 L1,3.5 C1,2 2,1 3.5,1 L14.5,1 C16,1 17,2 17,3.5 L17,14.5 C17,16 16,17 14.5,17 L3.5,17 C2,17 1,16 1,14.5 L1,9 Z"></path><polyline points="1 9 7 14 15 4"></polyline></svg></label>';
  1350.     }
  1351.     /**
  1352.      * @param string $table
  1353.      * @param array $head
  1354.      * @param array $row
  1355.      * @param array $id
  1356.      * @param bool $checkbox
  1357.      * @return bool
  1358.      */
  1359.     public function modern_table (string $table, array $head, array $row, array $id, bool $checkbox = false): bool {
  1360.         if (!isset($this->FLOAT["modern-tables"][$table]))
  1361.             $this->SAVER["modern-tables"][$table] = [];
  1362.         $this->FLOAT["modern-tables"][$table] = [
  1363.             "head" => $head,
  1364.             "row"  => $row,
  1365.             "id"   => $id,
  1366.             "checkbox" => $checkbox
  1367.         ];
  1368.         return true;
  1369.     }
  1370.     /**
  1371.      * @param string $table
  1372.      * @param bool $unset
  1373.      * @return string
  1374.      */
  1375.     public function get_modern_table (string $table, bool $unset = true): string {
  1376.         if (!isset($this->FLOAT["modern-tables"][$table]))
  1377.             return "";
  1378.         $name   = $table;
  1379.         $table  = $this->FLOAT["modern-tables"][$name];
  1380.         $header = "";
  1381.         $rows   = "";
  1382.         $checkbox = $table["checkbox"];
  1383.         foreach ($table["head"] as $k => $v) {
  1384.             if ($checkbox) {
  1385.                 $header .= '<th><div class="width-100-cent flex">' . $this->checkbox('all', true) . $v . '</div></th>';
  1386.                 $checkbox = false;
  1387.             } else
  1388.                 $header .= '<th>' . $v . '</th>';
  1389.         }
  1390.         foreach ($table["row"] as $key => $items) {
  1391.             $id = $table["id"][$key] ?? 0;
  1392.             $rows .= '<tr data-id="' . $id . '">';
  1393.             foreach ($items as $k => $v) {
  1394.                 if ($k == 0 && $table["checkbox"])
  1395.                     $rows .= '<td class="flex" data-label="' . $table["head"][$k] . '"><div class="width-100-cent height-100-cent flex flex-start">' . $this->checkbox($id, true) . $v . '</div></td>';
  1396.                 else
  1397.                     $rows .= '<td class="p-content" data-label="' . $table["head"][$k] . '">' . $v . '</td>';
  1398.             }
  1399.             $rows .= '</tr>';
  1400.         }
  1401.         if ($unset) // Free memory
  1402.             unset($this->FLOAT["modern-tables"][$name]);
  1403.         return '<table class="ufo-table ufo-table-' . $name . ' ' . ($table["checkbox"] ? " has-checkbox" : "") . '">
  1404.                     <thead>' . $header . '</thead>
  1405.                     <tbody>' . $rows . '</tbody>
  1406.                 </table>';
  1407.     }
  1408.     /**
  1409.      * @param array $data
  1410.      * @return array
  1411.      * @throws Exception
  1412.      */
  1413.     public function add_comment (array $data): array {
  1414.         global $db;
  1415.         if (!$this->has_in_array(["pid", "comment", "for"], $data))
  1416.             return ["add" => false, "page" => []];
  1417.         $page = (new UFO_Pages())->get($data["pid"]);
  1418.         if (!$page)
  1419.             return ["add" => false, "page" => []];
  1420.         return [
  1421.             "add"  => $db->insert("comments", [
  1422.                 "mid"      => (int) ($data["mid"] ?? (!isset($data["aid"]) ? ($this->get_member()["uid"] ?? 0) : 0)),
  1423.                 "aid"      => (int) ($data["aid"] ?? (!isset($data["mid"]) ? ($this->get_admin()["id"] ?? 0) : 0)),
  1424.                 "pid"      => (int) $page["id"],
  1425.                 "guest"    => isset($data["guest"]) ? json_encode($data["guest"], JSON_UNESCAPED_UNICODE) : null,
  1426.                 "comment"  => $data["comment"],
  1427.                 "dateTime" => $this->dateTime(),
  1428.                 "rate"     => (int) ($data["rate"] ?? 0),
  1429.                 "more"     => isset($data["more"]) && is_array($data["more"]) ? json_encode($data["more"], JSON_UNESCAPED_UNICODE) : null,
  1430.                 "_for"     => $data["for"],
  1431.                 "_reply"   => (int) ($data["replay"] ?? 0),
  1432.                 "accept"   => $db->meta("accept_comment") == "true" ? 1 : 0
  1433.             ]),
  1434.             "page" => $page
  1435.         ];
  1436.     }
  1437.     /**
  1438.      * @param string $for
  1439.      * @param bool $paging
  1440.      * @param int $page
  1441.      * @param int $limit
  1442.      * @param array $where
  1443.      * @param string|null $paging_action
  1444.      * @param string $orderDirection
  1445.      * @return array
  1446.      * @throws Exception
  1447.      */
  1448.     public function get_comments (string $for = "article", bool $paging = false, int $page = 1, int $limit = 0, array $where = [], ?string $paging_action = null, string $orderDirection = "DESC"): array {
  1449.         global $db;
  1450.         try {
  1451.             if ($limit == 0)
  1452.                 $limit = (int) $db->meta("table_rows");
  1453.             if (!empty($for))
  1454.                 $where["_for"] = $for;
  1455.             $db->helper->orderBy("id", $orderDirection);
  1456.             if ($paging) {
  1457.                 $row = $db->pagination("comments", [
  1458.                     "page"  => $page,
  1459.                     "limit" => $limit,
  1460.                     "paging_action" => $paging_action ?? "comments-table-paging"
  1461.                 ], $where);
  1462.             } else {
  1463.                 $db->helper->orderBy("id");
  1464.                 $row = ["rows" => $db->get("comments", $where)];
  1465.             } $fix = [];
  1466.             foreach ($row["rows"] as $item) {
  1467.                 $admin = $item["aid"] != 0 ? $this->get_admin((int) $item["aid"]) : false;
  1468.                 $reply = function ($list) {
  1469.                     global $db; $fix = [];
  1470.                     foreach ($list as $item) {
  1471.                         $admin = $item["aid"] != 0 ? $this->get_admin((int) $item["aid"]) : false;
  1472.                         $fix[] = [
  1473.                             "id"       => $item["id"],
  1474.                             "page"     => (new UFO_Pages())->get($item["pid"]),
  1475.                             ($item["mid"] != 0 ? "member" : (!empty($item["guest"]) ? "guest" : ($admin ? "admin" : "unknown"))) => ($item["mid"] != 0 ? $this->get_member((int) $item["mid"]) : (!empty($item["guest"]) ? json_decode($item["guest"], true) : ($admin ?? []))),
  1476.                             "comment"  => $item["comment"],
  1477.                             "dateTime" => $item["dateTime"],
  1478.                             "for"      => $item["_for"],
  1479.                             "reply"    => $db->get("comments", "_reply", $item["id"]),
  1480.                             "is_reply" => true,
  1481.                             "rate"     => $item["rate"],
  1482.                             "more"     => $this->is_json($item["more"]) ? json_decode($item["more"], true) : $item["more"],
  1483.                             "accept"   => $item["accept"] ?? false
  1484.                         ];
  1485.                     }
  1486.                     return $fix;
  1487.                 };
  1488.                 if ($item["_reply"] != 0) continue;
  1489.                 $fix[] = [
  1490.                     "id"       => $item["id"],
  1491.                     "page"     => (new UFO_Pages())->get($item["pid"]),
  1492.                     ($item["mid"] != 0 ? "member" : (!empty($item["guest"]) ? "guest" : ($admin ? "admin" : "unknown"))) => ($item["mid"] != 0 ? $this->get_member((int) $item["mid"]) : (!empty($item["guest"]) ? json_decode($item["guest"], true) : ($admin ?? []))),
  1493.                     "comment"  => $item["comment"],
  1494.                     "dateTime" => $item["dateTime"],
  1495.                     "for"      => $item["_for"],
  1496.                     "reply"    => $reply($db->get("comments", "_reply", $item["id"])),
  1497.                     "rate"     => $item["rate"],
  1498.                     "is_reply" => false,
  1499.                     "more"     => $this->is_json($item["more"]) ? json_decode($item["more"], true) : $item["more"],
  1500.                     "accept"   => $item["accept"] ?? false
  1501.                 ];
  1502.             }
  1503.             $row["rows"] = $fix;
  1504.             return $row;
  1505.         } catch (Exception $e) {
  1506.             $this->error($e);
  1507.             return ["rows" => []];
  1508.         }
  1509.     }
  1510.     /**
  1511.      * @param int $id
  1512.      * @return array|false
  1513.      * @throws Exception
  1514.      */
  1515.     public function get_comment (int $id) {
  1516.         global $db;
  1517.         $comment = $db->get("comments", "id", $id);
  1518.         if (isset($comment[0])) {
  1519.             $admin = $comment[0]["aid"] != 0 ? $this->get_admin((int) $comment[0]["aid"]) : false;
  1520.             $reply = function ($list) {
  1521.                 global $db; $fix = [];
  1522.                 foreach ($list as $item) {
  1523.                     $admin = $item["aid"] != 0 ? $this->get_admin((int) $item["aid"]) : false;
  1524.                     $fix[] = [
  1525.                         "id"       => $item["id"],
  1526.                         "page"     => (new UFO_Pages())->get($item["pid"]),
  1527.                         ($item["mid"] != 0 ? "member" : (!empty($item["guest"]) ? "guest" : ($admin ? "admin" : "unknown"))) => ($item["mid"] != 0 ? $this->get_member((int) $item["mid"]) : (!empty($item["guest"]) ? json_decode($item["guest"], true) : ($admin ?? []))),
  1528.                         "comment"  => $item["comment"],
  1529.                         "dateTime" => $item["dateTime"],
  1530.                         "for"      => $item["_for"],
  1531.                         "reply"    => $db->get("comments", "_reply", $item["id"]),
  1532.                         "is_reply" => true,
  1533.                         "rate"     => $item["rate"],
  1534.                         "more"     => $this->is_json($item["more"]) ? json_decode($item["more"], true) : $item["more"],
  1535.                         "accept"   => $item["accept"] ?? false
  1536.                     ];
  1537.                 }
  1538.                 return $fix;
  1539.             };
  1540.             return [
  1541.                 "id"       => $comment[0]["id"],
  1542.                 "page"     => (new UFO_Pages())->get($comment[0]["pid"]),
  1543.                 ($comment[0]["mid"] != 0 ? "member" : (!empty($comment[0]["guest"]) ? "guest" : ($admin ? "admin" : "unknown"))) => ($comment[0]["mid"] != 0 ? $this->get_member((int) $comment[0]["mid"]) : (!empty($comment[0]["guest"]) ? json_decode($comment[0]["guest"], true) : ($admin ?? []))),
  1544.                 "comment"  => $comment[0]["comment"],
  1545.                 "dateTime" => $comment[0]["dateTime"],
  1546.                 "for"      => $comment[0]["_for"],
  1547.                 "reply"    => $reply($db->get("comments", "_reply", $comment[0]["id"])),
  1548.                 "rate"     => $comment[0]["rate"],
  1549.                 "is_reply" => false,
  1550.                 "more"     => $this->is_json($comment[0]["more"]) ? json_decode($comment[0]["more"], true) : $comment[0]["more"],
  1551.                 "accept"   => $comment[0]["accept"] ?? false
  1552.             ];
  1553.         }
  1554.         return false;
  1555.     }
  1556.     /**
  1557.      * @param int $id
  1558.      * @return bool
  1559.      * @throws Exception
  1560.      */
  1561.     public function accept_comment (int $id): bool {
  1562.         global $db;
  1563.         return $db->update("comments", [
  1564.             "accept" => 1
  1565.         ], "id", $id);
  1566.     }
  1567.     /**
  1568.      * @param int $id
  1569.      * @return bool
  1570.      * @throws Exception
  1571.      */
  1572.     public function remove_comment (int $id) : bool {
  1573.         global $db;
  1574.         return $db->remove("comments", "id", $id);
  1575.     }
  1576.     /**
  1577.      * @param int $sender
  1578.      * @param int $newSender
  1579.      * @param bool $admin
  1580.      * @return bool
  1581.      * @throws Exception
  1582.      */
  1583.     public function transformCommentsTo (int $sender, int $newSender, bool $admin = true): bool {
  1584.         global $db;
  1585.         if ($admin) {
  1586.             return $db->update("comments", [
  1587.                 "aid" => $newSender
  1588.             ], "aid", $sender);
  1589.         } else {
  1590.             return $db->update("comments", [
  1591.                 "mid" => $newSender
  1592.             ], "mid", $sender);
  1593.         }
  1594.     }
  1595.     /**
  1596.      * @param int $pid
  1597.      * @return array
  1598.      * @throws Exception
  1599.      */
  1600.     public function avgCommentsRate (int $pid): array {
  1601.         global $db;
  1602.         $row = $db->query("SELECT COUNT(`id`) as total, ROUND(AVG(`rate`), 1) as rate FROM `%prefix%comments` WHERE `pid`='$pid' AND `accept`=1")[0];
  1603.         if (empty($row["rate"]))
  1604.             $row["rate"] = 0;
  1605.         return $row;
  1606.     }
  1607.     /**
  1608.      * @param int $pid
  1609.      * @return int
  1610.      * @throws Exception
  1611.      */
  1612.     public function countComments (int $pid): int {
  1613.         global $db;
  1614.         return (int) $db->query("SELECT COUNT(`id`) as c FROM `%prefix%comments` WHERE `pid`='$pid' AND `accept`=1")[0]["c"] ?? 0;
  1615.     }
  1616.     /**
  1617.      * @return void
  1618.      */
  1619.     public function default_page (): void {
  1620.         $this->load_layout("pages" . SLASH . $this->FLOAT["menu"][0]["page"]);
  1621.     }
  1622.     /**
  1623.      * @return string
  1624.      * @throws Exception
  1625.      */
  1626.     public function admin_ajax (): string {
  1627.         return URL_ADMIN . "ajax.php?ajax_key=" . ($this->isset_key($this->get_admin(), "ajax_key") ? $this->get_admin()["ajax_key"] : "");
  1628.     }
  1629.     /**
  1630.      * @param string $str
  1631.      * @param string $char
  1632.      * @return bool
  1633.      */
  1634.     public function has_char (string $str, string $char): bool {
  1635.         return strpos($str, $char) !== false;
  1636.     }
  1637.     /**
  1638.      * @param $array
  1639.      * @param $key
  1640.      * @return array
  1641.      */
  1642.     public function minifyArray ($array, string $key): array {
  1643.         $results = [];
  1644.         if (is_array($array)) {
  1645.             if (isset($array[$key]) && key($array) == $key)
  1646.                 $results[] = $array[$key];
  1647.             foreach ($array as $sub_array)
  1648.                 $results = array_merge($results, $this->minifyArray($sub_array, $key));
  1649.         }
  1650.         return $results;
  1651.     }
  1652.     /**
  1653.      * @param $lang
  1654.      * @param $address
  1655.      * @return void
  1656.      */
  1657.     public function add_lng (string $json_file): void {
  1658.         $this->FLOAT["lang"] = $this->FLOAT["lang"] ?? [];
  1659.         if (!file_exists($json_file))
  1660.             return;
  1661.         if (!$this->is_json(
  1662.             file_get_contents($json_file),
  1663.             $json_file
  1664.         )) return;
  1665.         $this->FLOAT["lang"] = array_merge(
  1666.             $this->FLOAT["lang"], $json_file
  1667.         );
  1668.     }
  1669.     /**
  1670.      * @param string $text
  1671.      * @return string
  1672.      */
  1673.     public function lng (string $text): string {
  1674.         return $this->FLOAT["lang"][$text] ?? $text;
  1675.     }
  1676.     /**
  1677.      * Replace language text
  1678.      *
  1679.      * @param string $text
  1680.      * @param mixed ...$values
  1681.      * @return string
  1682.      */
  1683.     public function rlng (string $text, ...$values): string {
  1684.         $explode = explode("%n", $this->lng($text));
  1685.         $newText = "";
  1686.         for ($i = 0; $i < count($explode); $i++)
  1687.             $newText .= $explode[$i] . ($values[$i] ?? "");
  1688.         return $newText;
  1689.     }
  1690.     /**
  1691.      * @return array
  1692.      */
  1693.     public function all_lng (): array {
  1694.         return $this->FLOAT["lang"];
  1695.     }
  1696.     /**
  1697.      * @param string $url
  1698.      * @return bool
  1699.      */
  1700.     public function redirect (string $url): bool {
  1701.         ob_start();
  1702.         header("Location: $url");
  1703.         ob_end_flush();
  1704.         return true;
  1705.     }
  1706.     /**
  1707.      * @param string|array $key
  1708.      * @param string|null $val
  1709.      * @param string|null $url
  1710.      * @param bool $redirect
  1711.      * @return string
  1712.      * @throws Exception
  1713.      */
  1714.     public function urlAddParam ($key, ?string $val = null, ?string $url = null, bool $redirect = true): string {
  1715.         $key = !is_array($key) ? [$key => $val] : $key;
  1716.         $url = $this->url_info($url ?? $this->get_url());
  1717.         $url["queries"] = array_merge(
  1718.             $url["queries"], $key
  1719.         );
  1720.         $url = $this->rebuild_url($url);
  1721.         return $redirect ? $this->redirect($url) : $url;
  1722.     }
  1723.     /**
  1724.      * @param string|array|null $param
  1725.      * @param string|null $url
  1726.      * @param bool $redirect
  1727.      * @return string
  1728.      * @throws Exception
  1729.      */
  1730.     public function urlRemoveParam ($param = null, ?string $url = null, bool $redirect = true): string {
  1731.         $param = !is_array($param) && !$this->equal($param, null) ? [$param] : $param;
  1732.         $url = empty($url) ? $this->get_url() : $url;
  1733.         $url = $this->url_info($url);
  1734.         if (empty($param))
  1735.             $url["queries"] = [];
  1736.         else {
  1737.             foreach ($param as $query) {
  1738.                 foreach ($url["queries"] as $key => $v) {
  1739.                     if ($this->equal($query, $key))
  1740.                         unset($url["queries"][$key]);
  1741.                 }
  1742.             }
  1743.         }
  1744.         $url = $this->rebuild_url($url);
  1745.         return $redirect ? $this->redirect($url) : $url;
  1746.     }
  1747.     /**
  1748.      * @param string|null $url
  1749.      * @param bool $redirect
  1750.      * @return bool|string
  1751.      * @throws Exception
  1752.      */
  1753.     public function clearUrlParams (?string $url = null, bool $redirect = true) {
  1754.         $url = empty($url) ? $this->get_url() : $url;
  1755.         $url = $this->url_info($url);
  1756.         $url["queries"] = [];
  1757.         $url = $this->rebuild_url($url);
  1758.         return $redirect ? $this->redirect($url) : $url;
  1759.     }
  1760.     /**
  1761.      * @return bool
  1762.      */
  1763.     public function reloadedHere (): bool {
  1764.         if ($this->isset_key($_SERVER, "HTTP_CACHE_CONTROL")) {
  1765.             return (
  1766.                 $this->equal($_SERVER["HTTP_CACHE_CONTROL"], "max-age=0") ||
  1767.                 $this->equal($_SERVER["HTTP_CACHE_CONTROL"], "no-cache")
  1768.             ) == 1;
  1769.         } else return false;
  1770.     }
  1771.     /**
  1772.      * @return array
  1773.      */
  1774.     public function lastPage (): array {
  1775.         return $_SESSION["ufo_last_page"] ?? [
  1776.             "page" => "dashboard"
  1777.         ];
  1778.     }
  1779.     /**
  1780.      * @param object|array $object
  1781.      * @return array|object
  1782.      */
  1783.     public function object_to_array ($object) {
  1784.         if (is_object($object))
  1785.             return array_map([
  1786.                 $this, "object_to_array"
  1787.             ], get_object_vars($object));
  1788.         else if (is_array($object))
  1789.             return array_map([$this, "object_to_array"], $object);
  1790.         return $object;
  1791.     }
  1792.     /**
  1793.      * @param string|null $type
  1794.      * @param array $where
  1795.      * @return array|bool
  1796.      */
  1797.     public function this_page (?string $type = "page", array $where = []) {
  1798.         global $db;
  1799.         try {
  1800.             $url = $this->this_url_info();
  1801.             if (!isset($where["link"])) $where["link"] = urldecode(
  1802.                 end($url["slashes"])
  1803.             );
  1804.             if (!empty($type))
  1805.                 $where["type"] = $type;
  1806.             foreach ($where as $k => $v)
  1807.                 $db->where($k, $v);
  1808.             $page = $db->helper->getOne("pages");
  1809.             // Prevent this page from being recognized as the main page
  1810.             if (!empty($page["link"]))
  1811.                 return $page;
  1812.         } catch (Exception $e) {}
  1813.         return false;
  1814.     }
  1815.     /**
  1816.      * @return array|bool
  1817.      * @throws Exception
  1818.      */
  1819.     public function this_article () {
  1820.         return $this->this_page("article");
  1821.     }
  1822.     /**
  1823.      * @throws Exception
  1824.      * @return bool|array|UFO_Explorer
  1825.      */
  1826.     public function here () {
  1827.         $place = $this->this_page(null);
  1828.         if (is_array($place)) {
  1829.             if ($place["type"] == "page" || $place["type"] == "article") {
  1830.                 $place = new UFO_Explorer([
  1831.                     "type"  => $place["type"],
  1832.                     "limit" => false,
  1833.                     "where" => [
  1834.                         "id" => $place["id"]
  1835.                     ],
  1836.                     "reset" => false
  1837.                 ]);
  1838.             } else {
  1839.                 $place = new UFO_Explorer([
  1840.                     "hunter" => "single-" . $place["type"],
  1841.                     "limit"  => false,
  1842.                     "id"     => $place["id"],
  1843.                     "reset"  => false
  1844.                 ]);
  1845.             }
  1846.         }
  1847.         return is_object($place) ? ($place->hunt(function ($explorer) {
  1848.             return $explorer;
  1849.         })[0] ?? false) : false;
  1850.     }
  1851.     /**
  1852.      * @return string
  1853.      * @throws Exception
  1854.      */
  1855.     public function this_urn (): string {
  1856.         $url     = $this->this_url_info();
  1857.         $slashes = join("/", $url["slashes"]);
  1858.         $queries = http_build_query($url["queries"]);
  1859.         return urldecode("/" . trim(
  1860.             "$slashes?$queries" . (!empty($url["fragment"]) ? "#$url[fragment]" : ""), "/"
  1861.         ));
  1862.     }
  1863.     /**
  1864.      * @return string
  1865.      */
  1866.     public function full_url (): string {
  1867.         return (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] === "on" ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
  1868.     }
  1869.     /**
  1870.      * @param string $page
  1871.      * @return bool
  1872.      */
  1873.     public function match_page (string $page): bool {
  1874.         return (bool) strpos($this->full_url(), $this->sanitize_link($page));
  1875.     }
  1876.     /**
  1877.      * @return string
  1878.      * @throws Exception
  1879.      */
  1880.     public function end_url (): string {
  1881.         return (string) end($this->this_url_info()["slashes"]);
  1882.     }
  1883.     /**
  1884.      * @param array $parsed
  1885.      * @return string
  1886.      */
  1887.     public function rebuild_url (array $parsed): string {
  1888.         $url = "";
  1889.         if (!empty($parsed["protocol"]))
  1890.             $url .= "$parsed[protocol]://";
  1891.         else if (!empty($parsed["scheme"]))
  1892.             $url .= "$parsed[scheme]://";
  1893.         if (!empty($parsed["user"])) {
  1894.             $url .= $parsed["user"];
  1895.             if (!empty($parsed["pass"]))
  1896.                 $url .= ":$parsed[pass]";
  1897.             $url .= "@";
  1898.         }
  1899.         $url .= $parsed["host"] ?? "";
  1900.         if (!empty($parsed["port"]))
  1901.             $url .= ":$parsed[port]";
  1902.         if (!empty($parsed["slashes"]))
  1903.             $url .= "/" . implode("/", $parsed["slashes"]);
  1904.         else
  1905.             $url .= $parsed["path"] ?? "";
  1906.         $queries = $parsed["queries"] ?? $parsed["query"];
  1907.         if (!empty($queries))
  1908.             $url .= "?" . (is_array($queries) ? http_build_query($queries) : $queries);
  1909.         if (!empty($parsed["fragment"]))
  1910.             $url .= "#$parsed[fragment]";
  1911.         return $url;
  1912.     }
  1913.     /**
  1914.      * @param $url
  1915.      * @return mixed
  1916.      */
  1917.     public function is_url ($url) {
  1918.         return filter_var($url, FILTER_VALIDATE_URL);
  1919.     }
  1920.     /**
  1921.      * @param array|string $page
  1922.      * @param array|string $rule
  1923.      * @param array|string $title
  1924.      * @param $callback
  1925.      * @return void
  1926.      */
  1927.     public function add_rule ($page, $rule, $title = null, $callback = null) {
  1928.         $array = is_array($page) ? $page : (
  1929.             is_array($rule) ? $rule : []
  1930.         );
  1931.         if (!empty($array)) {
  1932.             for ($i = 0; $i < count($array); $i++) {
  1933.                 $this->add_rule(
  1934.                     is_array($page) ? ($page[$i] ?? $page) : $page,
  1935.                     is_array($rule) ? ($rule[$i] ?? $rule) : $rule,
  1936.                     is_array($title) ? ($title[$i] ?? $title) : $title,
  1937.                     is_array($callback) ? $callback[$i] : $callback
  1938.                 );
  1939.             }
  1940.             return;
  1941.         }
  1942.         if (!isset($this->FLOAT["rules"]))
  1943.             $this->FLOAT["rules"] = [];
  1944.         $rule = trim($rule, "/");
  1945.         $this->FLOAT["rules"][$rule] = [
  1946.             "rule"  => $this->sanitize_link("/" . $rule),
  1947.             "title" => $title,
  1948.             "path"  => $this->slash_folder($page),
  1949.             "callback" => $callback
  1950.         ];
  1951.     }
  1952.     /**
  1953.      * @return array
  1954.      */
  1955.     public function get_rules (): array {
  1956.         if (!isset($this->FLOAT["rules"]))
  1957.             $this->FLOAT["rules"] = [];
  1958.         $join = [];
  1959.         foreach ($this->FLOAT["rules"] as $k => $v)
  1960.             $join[$v["rule"]] = $v["path"];
  1961.         return $join;
  1962.     }
  1963.     /**
  1964.      * @return array|mixed
  1965.      */
  1966.     public function get_full_rules () {
  1967.         if (!isset($this->FLOAT["rules"]))
  1968.             $this->FLOAT["rules"] = [];
  1969.         return $this->FLOAT["rules"];
  1970.     }
  1971.     /**
  1972.      * @param string $url
  1973.      * @throws Exception
  1974.      * @return array
  1975.      */
  1976.     public function url_info (string $url): array {
  1977.         $parsed  = parse_url(
  1978.             $this->sanitize_xss($url)
  1979.         );
  1980.         $details = [
  1981.             "protocol" => $parsed["scheme"]   ?? null,
  1982.             "host"     => $parsed["host"]     ?? null,
  1983.             "port"     => $parsed["port"]     ?? null,
  1984.             "fragment" => $parsed["fragment"] ?? null
  1985.         ];
  1986.         $details["slashes"] = explode("/",
  1987.             trim($parsed["path"] ?? "", "/")
  1988.         );
  1989.         parse_str($parsed["query"] ?? "", $details["queries"]);
  1990.         return $details;
  1991.     }
  1992.     /**
  1993.      * @return array
  1994.      * @throws Exception
  1995.      */
  1996.     public function this_url_info (): array {
  1997.         $parsed = parse_url($this->web_link());
  1998.         $link   = str_replace(
  1999.             $parsed["path"] ?? "", "/",
  2000.             $this->get_url()
  2001.         );
  2002.         return $this->url_info($link) + [
  2003.             "base_path" => trim($parsed["path"] ?? "", "/")
  2004.         ];
  2005.     }
  2006.     /**
  2007.      * @param string $slug
  2008.      * @return string
  2009.      * @throws Exception
  2010.      */
  2011.     public function slug (string $slug): string {
  2012.         global $db;
  2013.         return URL_WEBSITE . $db->slug($slug) . "/";
  2014.     }
  2015.     /**
  2016.      * @param array $array
  2017.      * @return bool
  2018.      */
  2019.     public function values_true (array $array): bool {
  2020.         foreach ($array as $value)
  2021.             if ($value !== true)
  2022.                 return false;
  2023.         return true;
  2024.     }
  2025.     /**
  2026.      * @param array $keys
  2027.      * @param array $array
  2028.      * @return bool
  2029.      */
  2030.     public function has_in_array (array $keys, array $array): bool {
  2031.         return count(array_intersect(
  2032.             $keys, array_keys($array)
  2033.         )) == count($keys);
  2034.     }
  2035.     /**
  2036.      * @param $list
  2037.      * @return array
  2038.      */
  2039.     public function v_sort ($list): array {
  2040.         asort($list);
  2041.         $new_list = array_reverse($list, true);
  2042.         $list     = [];
  2043.         foreach ($new_list as $k => $v)
  2044.             $list[] = $k;
  2045.         return $list;
  2046.     }
  2047.     /**
  2048.      * @param $array
  2049.      * @param $position
  2050.      * @param $insert
  2051.      * @return void
  2052.      */
  2053.     public function insert_array (&$array, $position, $insert) {
  2054.         if (is_int($position)) {
  2055.             array_splice($array, $position, 0, $insert);
  2056.         } else {
  2057.             $pos = array_search($position, array_keys($array));
  2058.             $array = array_merge(
  2059.                 array_slice($array, 0, $pos),
  2060.                 $insert,
  2061.                 array_slice($array, $pos)
  2062.             );
  2063.         }
  2064.     }
  2065.     /**
  2066.      * @param $array
  2067.      * @param array $keys
  2068.      * @return mixed
  2069.      */
  2070.     public function array_exclude ($array, array $keys) {
  2071.         if ($this->has_keys($array)) {
  2072.             foreach ($keys as $key) {
  2073.                 if ($this->isset_key($array, $key))
  2074.                     unset($array[$key]);
  2075.             }
  2076.         }
  2077.         return $array;
  2078.     }
  2079.     /**
  2080.      * @param $array
  2081.      * @return bool
  2082.      * @throws Exception
  2083.      */
  2084.     public function order_array (&$array): bool {
  2085.         $is_array = $this->is_array($array);
  2086.         if (!is_array($array) && !$is_array)
  2087.             $this->error("UFO Options (order_array) : The array is not valid");
  2088.         if ($is_array)
  2089.             $array = json_decode($array, true);
  2090.         return usort($array, fn($a, $b) =>
  2091.             ($a["order"] ?? $a["position"]) <=> ($b["order"] ?? $b["position"])
  2092.         );
  2093.     }
  2094.     /**
  2095.      * @param array $array
  2096.      * @return bool
  2097.      */
  2098.     public function has_keys (array $array): bool {
  2099.         $keys = array_keys($array);
  2100.         return $keys !== array_keys($keys);
  2101.     }
  2102.     /**
  2103.      * @param array $array
  2104.      * @param string|int $prefix
  2105.      * @param string|int $suffix
  2106.      * @return void
  2107.      */
  2108.     public function prefix_suffix_array (array &$array, $prefix, $suffix): void {
  2109.         if ($this->has_keys($array)) {
  2110.             foreach ($array as $key => $value) {
  2111.                 $newKey = $prefix . $key . $suffix;
  2112.                 $array[$newKey] = $value;
  2113.                 unset($array[$key]);
  2114.             }
  2115.         } else {
  2116.             foreach ($array as &$value) {
  2117.                 $value = $prefix . $value . $suffix;
  2118.             }
  2119.             // Since we're using a reference in the loop, unset the reference after the loop.
  2120.             unset($value);
  2121.         }
  2122.     }
  2123.     /**
  2124.      * @param array $array1
  2125.      * @param array $array2
  2126.      * @param bool $remove_excess
  2127.      * @return array
  2128.      */
  2129.     public function default (array $array1, array $array2, bool $remove_excess = false): array {
  2130.         /**
  2131.          * Merging sub-arrays
  2132.          */
  2133.         foreach ($array1 as $k1 => $item) {
  2134.             if (is_array($item)) {
  2135.                 foreach ($array2 as $k2 => &$item2) {
  2136.                     if ($this->equal($k1, $k2)) {
  2137.                         $item2 = $this->default(
  2138.                             $item, $item2, $remove_excess
  2139.                         );
  2140.                     }
  2141.                 }
  2142.             }
  2143.         }
  2144.         return array_merge($array1, $remove_excess ? array_intersect_key(
  2145.             $array1, $array2
  2146.         ) : $array2);
  2147.     }
  2148.     /**
  2149.      * @param $name
  2150.      * @param $args
  2151.      * @param bool $float
  2152.      * @return void
  2153.      */
  2154.     public function add_array ($name, $args, bool $float = true) {
  2155.         if (!$float) {
  2156.             if (!$this->isset_key($this->SAVER, "args"))
  2157.                 $this->SAVER["args"] = [];
  2158.             if (!$this->isset_key($this->SAVER["args"], $name))
  2159.                 $this->SAVER["args"][$name] = [];
  2160.             $this->SAVER["args"][$name][] = $args;
  2161.         } else {
  2162.             if (!$this->isset_key($this->FLOAT, "args"))
  2163.                 $this->FLOAT["args"] = [];
  2164.             if (!$this->isset_key($this->FLOAT["args"], $name))
  2165.                 $this->FLOAT["args"][$name] = [];
  2166.             $this->FLOAT["args"][$name][] = $args;
  2167.         }
  2168.     }
  2169.     /**
  2170.      * @param $name
  2171.      * @param bool $float
  2172.      * @return mixed
  2173.      */
  2174.     public function get_array ($name, bool $float = true) {
  2175.         if (!$float)
  2176.             return $this->isset_key($this->SAVER["args"], $name) ? $this->SAVER["args"][$name] : [];
  2177.         else {
  2178.             if (!$this->isset_key($this->FLOAT, "args"))
  2179.                 $this->FLOAT["args"] = [];
  2180.             return $this->isset_key($this->FLOAT["args"], $name) ? $this->FLOAT["args"][$name] : [];
  2181.         }
  2182.     }
  2183.     /**
  2184.      * @param $key
  2185.      * @param $value
  2186.      * @param bool $float
  2187.      * @return void
  2188.      */
  2189.     public function add_kv ($key, $value, bool $float = true) {
  2190.         if (!$float) {
  2191.             $this->SAVER["kv"][$key] = $value;
  2192.         } else {
  2193.             if (!$this->isset_key($this->FLOAT, "kv")) $this->FLOAT["kv"] = [];
  2194.             $this->FLOAT["kv"][$key] = $value;
  2195.         }
  2196.     }
  2197.     /**
  2198.      * @param $key
  2199.      * @param bool $float
  2200.      * @return false|mixed
  2201.      */
  2202.     public function get_kv ($key, bool $float = true) {
  2203.         if (!$float) {
  2204.             if (!$this->isset_key($this->SAVER, "kv")) $this->SAVER["kv"] = [];
  2205.             return $this->isset_key($this->SAVER["kv"], $key) ? $this->SAVER["kv"][$key] : false;
  2206.         } else {
  2207.             if (!$this->isset_key($this->FLOAT, "kv")) $this->FLOAT["kv"] = [];
  2208.             return $this->isset_key($this->FLOAT["kv"], $key) ? $this->FLOAT["kv"][$key] : false;
  2209.         }
  2210.     }
  2211.     /**
  2212.      * @param array $target
  2213.      * @param array $array
  2214.      * @return int|mixed
  2215.      */
  2216.     public function find_by_kv (array $target, array $array) {
  2217.         foreach ($array as $key => $item) {
  2218.             if (isset($item[$target[0]])) {
  2219.                 if ($item[$target[0]] == $target[1]) {
  2220.                     if (isset($target[2]))
  2221.                         return $item[$target[2]];
  2222.                     return $key;
  2223.                 }
  2224.             }
  2225.         }
  2226.         return false;
  2227.     }
  2228.     /**
  2229.      * @param object|array $array
  2230.      * @param int|string|array $key
  2231.      * @return mixed
  2232.      */
  2233.     public function isset_key ($array, $key, bool $return = false) {
  2234.         $array = (array) $array;
  2235.         if (is_array($key)) {
  2236.             $exists = [];
  2237.             foreach ($key as $k)
  2238.                 $exists[$k] = $this->isset_key($array, $k);
  2239.             return $this->values_true($exists);
  2240.         }
  2241.         $isset = isset($array[$key]) || array_key_exists($key, $array);
  2242.         if ($isset && $return)
  2243.             $isset = is_bool($array[$key]) ? true : $array[$key];
  2244.         return $isset;
  2245.     }
  2246.     /**
  2247.      * @param string|object|array $keys
  2248.      * @param bool $return
  2249.      * @return mixed
  2250.      * @throws Exception
  2251.      */
  2252.     public function isset_get ($keys = null, bool $return = false) {
  2253.         if ($keys === null)
  2254.             return $this->equal($_SERVER["REQUEST_METHOD"], "GET");
  2255.         return $this->isset_key($this->this_url_info()["queries"], $keys, $return);
  2256.     }
  2257.     /**
  2258.      * @param ?string|object|array $keys
  2259.      * @param bool $return
  2260.      * @return mixed
  2261.      */
  2262.     public function isset_post ($keys = null, bool $return = false) {
  2263.         if ($keys === null)
  2264.             return $this->equal($_SERVER["REQUEST_METHOD"], "POST");
  2265.         return $this->isset_key($_POST, $keys, $return);
  2266.     }
  2267.     /**
  2268.      * @param string|object|array $keys
  2269.      * @param bool $return
  2270.      * @return mixed
  2271.      */
  2272.     public function isset_session ($keys, bool $return = false) {
  2273.         return $this->isset_key($_SESSION, $keys, $return);
  2274.     }
  2275.     /**
  2276.      * @param string|object|array $keys
  2277.      * @param bool $return
  2278.      * @return mixed
  2279.      */
  2280.     public function isset_cookie ($keys, bool $return = false) {
  2281.         return $this->isset_key($_COOKIE, $keys, $return);
  2282.     }
  2283.     /**
  2284.      * @param $string
  2285.      * @param string $style
  2286.      * @param bool $clean
  2287.      * @param bool $out_process
  2288.      * @return string
  2289.      * @throws Exception
  2290.      */
  2291.     public function error ($string, string $style = "", bool $clean = true, bool $out_process = false): string {
  2292.         $html = !$out_process ? "<div class='flex flex-center align-center'><div class='system-notice system-notice-danger mt-20' style='max-width: 80%;".$style."'><span style='font-family: ufocms'>".$string."</span></div></div>" : "<div style='display:flex;justify-content: center'><div style='width: 80%;height: 80px;background: whitesmoke;border-radius: 0 8px 8px 0;font-family: system-ui;font-weight: bolder;display: flex;align-items: center;padding: 0 10px;box-sizing: border-box;border-".($this->dir() == "ltr" ? "left: 5px solid red;" : "right: 5px solid red;")."direction:".$this->dir().";$style'>$string</div></div>";
  2293.         if ($clean) {
  2294.             ob_clean();
  2295.             $this->load_layout("document");
  2296.             echo $html;
  2297.             $this->load_layout("endDoc");
  2298.         }
  2299.         return $html;
  2300.     }
  2301.     /**
  2302.      * @param string $msg
  2303.      * @param int $status
  2304.      * @param bool $html
  2305.      * @return void
  2306.      * @throws Exception
  2307.      */
  2308.     public function die (string $msg = "", int $status = 403, bool $html = false) {
  2309.         echo ($html ? $this->error($msg) : $msg);
  2310.         exit ($status);
  2311.     }
  2312.     /**
  2313.      * @param $status
  2314.      * @param $message
  2315.      * @param array $data
  2316.      * @param bool $returnArray
  2317.      * @return array|string
  2318.      */
  2319.     public function status ($status, $message = null, array $data = [], bool $returnArray = false) {
  2320.         $data = [
  2321.             "status"  => $status,
  2322.             "message" => empty($message) ? (
  2323.                 $this->success($status) ? $this->lng(
  2324.                     "Done successfully"
  2325.                 ) : $this->lng("System error")
  2326.             ) : $message,
  2327.         ] + $data;
  2328.         return !$returnArray ? json_encode($data, JSON_UNESCAPED_UNICODE) : $data;
  2329.     }
  2330.     /**
  2331.      * @param bool|numeric|string|array $status
  2332.      * @return bool
  2333.      */
  2334.     public function success ($status): bool {
  2335.         if (is_numeric($status))
  2336.             return $this->equal($status, 200);
  2337.         if (is_bool($status))
  2338.             return $status;
  2339.         $this->is_json($status, $status);
  2340.         if (is_array($status))
  2341.             return $this->equal($status["status"] ?? ($status[0] ?? 0), 200);
  2342.         return $this->equal($status, "true");
  2343.     }
  2344.     /**
  2345.      * @param array $array
  2346.      * @param array $data
  2347.      * @return array
  2348.      * @throws Exception
  2349.      */
  2350.     public function pagination (array $array, array $data = []): array {
  2351.         global $db;
  2352.         extract($data);
  2353.         $page  = $page ?? 1;
  2354.         $total = count($array);
  2355.         $limit = $limit ?? $db->table_rows;
  2356.         $pages = ceil($total / $limit);
  2357.         $page  = max($page, 1);
  2358.         $page  = min($page, $pages);
  2359.         $offset = ($page - 1) * $limit;
  2360.         if ($offset < 0) $offset = 0;
  2361.         return [
  2362.             "total"  => $total,
  2363.             "limit"  => $limit,
  2364.             "pages"  => $pages,
  2365.             "rows"   => array_slice($array, $offset, $limit),
  2366.             "paging" => $this->paging([
  2367.                 "page"   => $page,
  2368.                 "total"  => $pages,
  2369.                 "action" => $action ?? null
  2370.             ])
  2371.         ];
  2372.     }
  2373.     /**
  2374.      * @param $data
  2375.      * @return string
  2376.      * @throws Exception
  2377.      */
  2378.     public function paging ($data): string {
  2379.         extract($data);
  2380.         $next_page = $page + 1;
  2381.         $prev_page = $page - 1;
  2382.         if ($this->dir() == "rtl") {
  2383.             $paging  = $this->tag('span', $this->tag('i', null, ["class" => "ufo-icon-chevrons-left"]), [
  2384.                 "class" => "modern-paging-item",
  2385.                 "data-page" => $total,
  2386.                 "data-disabled" => $page >= $total ? "true" : "false",
  2387.                 "data-action"  => $action
  2388.             ]);
  2389.             $paging .= $this->tag('span', $this->tag('i', null, ["class" => "ufo-icon-chevron-left"]), [
  2390.                 "class" => "modern-paging-item",
  2391.                 "data-page" => $next_page,
  2392.                 "data-disabled" => $total < $next_page ? "true" : "false",
  2393.                 "data-action"   => $action
  2394.             ]);
  2395.             $paging .= $this->tag('span', $this->lng("page") . " " . $page . " " . $this->lng("of") . " " . $total, [
  2396.                 "class" => "of_page"
  2397.             ]);
  2398.             $paging .= $this->tag('span', $this->tag('i', null, ["class" => "ufo-icon-chevron-right"]), [
  2399.                 "class" => "modern-paging-item",
  2400.                 "data-page" => $prev_page,
  2401.                 "data-disabled" => $page > 1 ? "false" : "true",
  2402.                 "data-action"   => $action
  2403.             ]);
  2404.             $paging .= $this->tag('span', $this->tag('i', null, ["class" => "ufo-icon-chevrons-right"]), [
  2405.                 "class" => "modern-paging-item",
  2406.                 "data-page" => 1,
  2407.                 "data-disabled" => $page > 1 ? "false" : "true",
  2408.                 "data-action"   => $action
  2409.             ]);
  2410.         } else {
  2411.             $paging  = $this->tag('span', $this->tag('i', null, ["class" => "ufo-icon-chevrons-left"]), [
  2412.                 "class" => "modern-paging-item",
  2413.                 "data-page" => 1,
  2414.                 "data-disabled" => $page > 1 ? "false" : "true",
  2415.                 "data-action"   => $action
  2416.             ]);
  2417.             $paging .= $this->tag('span', $this->tag('i', null, ["class" => "ufo-icon-chevron-left"]), [
  2418.                 "class" => "modern-paging-item",
  2419.                 "data-page" => $prev_page,
  2420.                 "data-disabled" => $page > 1 ? "false" : "true",
  2421.                 "data-action"   => $action
  2422.             ]);
  2423.             $paging .= $this->tag('span', $this->lng("page") . " " . $page . " " . $this->lng("of") . " " . $total, [
  2424.                 "class" => "of_page"
  2425.             ]);
  2426.             $paging .= $this->tag('span', $this->tag('i', null, ["class" => "ufo-icon-chevron-right"]), [
  2427.                 "class" => "modern-paging-item",
  2428.                 "data-page" => $next_page,
  2429.                 "data-disabled" => $total < $next_page ? "true" : "false",
  2430.                 "data-action"   => $action
  2431.             ]);
  2432.             $paging .= $this->tag('span', $this->tag('i', null, ["class" => "ufo-icon-chevrons-right"]), [
  2433.                 "class" => "modern-paging-item",
  2434.                 "data-page" => $total,
  2435.                 "data-disabled" => $page >= $total ? "true" : "false",
  2436.                 "data-action"   => $action
  2437.             ]);
  2438.         }
  2439.         return $this->tag("div", $paging, ["class" => "modern-paging"]);
  2440.     }
  2441.     /**
  2442.      * @param array $array
  2443.      * @return mixed|string|void
  2444.      */
  2445.     public function new_shortcode (array $array) {
  2446.         global $_;
  2447.         if ($this->isset_key($array, "name")) {
  2448.             /**
  2449.              * Add - Shortcodes Array
  2450.              */
  2451.             if (!$this->isset_key($this->FLOAT, "shortcodes"))
  2452.                 $this->FLOAT["shortcodes"] = [];
  2453.             /**
  2454.              * Add for this plugin
  2455.              */
  2456.             if (!isset($this->FLOAT["plugin_shortcodes"]))
  2457.                 $this->FLOAT["plugin_shortcodes"] = [];
  2458.             if ($this->isset_key($_, "this_plugin") && !empty($_["this_plugin"])) {
  2459.                 $plugin = $_["this_plugin"]["manifest"]["name"];
  2460.                 if (!isset($this->FLOAT["plugin_shortcodes"][$plugin]))
  2461.                     $this->FLOAT["plugin_shortcodes"][$plugin] = [];
  2462.                 $this->FLOAT["plugin_shortcodes"][$plugin][] = $array;
  2463.             }
  2464.             /**
  2465.              * Add for this template
  2466.              */
  2467.             if (!isset($this->FLOAT["template_shortcodes"]))
  2468.                 $this->FLOAT["template_shortcodes"] = [];
  2469.             if ($this->isset_key($_, "this_template") && !empty($_["this_template"])) {
  2470.                 $template = $_["this_template"]["manifest"]["name"];
  2471.                 if (!isset($this->FLOAT["template_shortcodes"][$template]))
  2472.                     $this->FLOAT["template_shortcodes"][$template] = [];
  2473.                 $this->FLOAT["template_shortcodes"][$template][] = $array;
  2474.             }
  2475.             /**
  2476.              * Append To Shortcodes
  2477.              */
  2478.             $this->FLOAT["shortcodes"][] = [
  2479.                 "name"    => $array["name"],
  2480.                 "content" => $array["content"] ?? "",
  2481.                 "editor"  => $this->isset_key($array, "editor", true)
  2482.             ];
  2483.         } else return $this->lng("Error: Please set name shortcode");
  2484.     }
  2485.     /**
  2486.      * @param string $string
  2487.      * @return string
  2488.      */
  2489.     public function run_shortcodes (string $string): string {
  2490.         return $this->do_work("ufo_render_shortcodes", $string);
  2491.     }
  2492.     /**
  2493.      * @param string $name
  2494.      * @return false|array
  2495.      */
  2496.     public function get_shortcode (string $name) {
  2497.         $shortcode = [];
  2498.         foreach ($this->FLOAT["shortcodes"] ?? [] as $item) {
  2499.             if ($name == $item["name"]) {
  2500.                 $shortcode = $item;
  2501.                 break;
  2502.             }
  2503.         }
  2504.         return $shortcode;
  2505.     }
  2506.     /**
  2507.      * @return array
  2508.      */
  2509.     public function get_all_shortcodes (): array {
  2510.         return array_merge($this->FLOAT["shortcodes"] ?? [], [
  2511.             "plugins"   => $this->FLOAT["plugin_shortcodes"] ?? [],
  2512.             "templates" => $this->FLOAT["template_shortcodes"] ?? []
  2513.         ]);
  2514.     }
  2515.     /**
  2516.      * @param array|string $name
  2517.      * @param $fn
  2518.      * @return void
  2519.      */
  2520.     public function add_work ($name, $fn) {
  2521.         global $_;
  2522.         /**
  2523.          * Add multiple (works) simultaneously
  2524.          */
  2525.         if (is_array($name)) {
  2526.             foreach ($name as $work)
  2527.                 $this->add_work($work, $fn);
  2528.             return;
  2529.         }
  2530.         /**
  2531.          * Add - UFO works to float
  2532.          */
  2533.         $this->FLOAT["ufo_works"] = $this->FLOAT["ufo_works"] ?? [];
  2534.         /**
  2535.          * Check exists
  2536.          */
  2537.         if (isset($this->FLOAT["ufo_works"][$name]))
  2538.             return;
  2539.         /**
  2540.          * Add for this plugin
  2541.          */
  2542.         if (!isset($this->FLOAT["ufo_plugin_works"]))
  2543.             $this->FLOAT["ufo_plugin_works"] = [];
  2544.         if ($this->isset_key($_, "this_plugin") && !empty($_["this_plugin"])) {
  2545.             $plugin = $_["this_plugin"]["manifest"]["name"];
  2546.             if (!isset($this->FLOAT["ufo_plugin_works"][$plugin]))
  2547.                 $this->FLOAT["ufo_plugin_works"][$plugin] = [];
  2548.             $this->FLOAT["ufo_plugin_works"][$plugin][] = $name;
  2549.         }
  2550.         /**
  2551.          * Add for this template
  2552.          */
  2553.         if (!isset($this->FLOAT["ufo_template_works"]))
  2554.             $this->FLOAT["ufo_template_works"] = [];
  2555.         if ($this->isset_key($_, "this_template") && !empty($_["this_template"])) {
  2556.             $template = $_["this_template"]["manifest"]["name"];
  2557.             if (!isset($this->FLOAT["ufo_template_works"][$template]))
  2558.                 $this->FLOAT["ufo_template_works"][$template] = [];
  2559.             $this->FLOAT["ufo_template_works"][$template][] = $name;
  2560.         }
  2561.         $this->FLOAT["ufo_works"][$name] = $fn;
  2562.     }
  2563.     /**
  2564.      * @param array|string $name
  2565.      * @param mixed $arg
  2566.      * @param bool $now
  2567.      * @return mixed
  2568.      */
  2569.     public function do_work ($name, $arg = [], bool $now = true) {
  2570.         global $_;
  2571.         if (is_array($name)) {
  2572.             $result = [];
  2573.             foreach ($name as $work)
  2574.                 $result[$work] = $this->do_work($work, $arg, $now);
  2575.             return $result;
  2576.         }
  2577.         if (!$now) {
  2578.             if (!isset($_["waite_works"]))
  2579.                 $_["works"] = [];
  2580.             $_["works"][$name] = $arg;
  2581.             return true;
  2582.         } else {
  2583.             if (!isset($this->FLOAT["ufo_works"][$name]))
  2584.                 return false;
  2585.             return $this->call($this->FLOAT["ufo_works"][$name], $arg);
  2586.         }
  2587.     }
  2588.     /**
  2589.      * @return array
  2590.      */
  2591.     public function get_all_works (): array {
  2592.         return ($this->FLOAT["ufo_works"] ?? []) + [
  2593.             "plugins"   => $this->FLOAT["ufo_plugin_works"],
  2594.             "templates" => $this->FLOAT["ufo_template_works"]
  2595.         ];
  2596.     }
  2597.     /**
  2598.      * @param string $name
  2599.      * @param $callback
  2600.      * @param bool $guest
  2601.      * @param $key
  2602.      * @return void
  2603.      */
  2604.     public function add_ajax (string $name, $callback, bool $guest = false, $key = null) {
  2605.         if (!isset($this->FLOAT["ufo_ajax"]))
  2606.             $this->FLOAT["ufo_ajax"] = [];
  2607.         $this->FLOAT["ufo_ajax"][$name] = [
  2608.             "callback" => $callback,
  2609.             "guest"    => $guest,
  2610.             "key"      => empty($key) ? ($this->do_work("ufo_ajax_key") ?? false) : $key
  2611.         ];
  2612.     }
  2613.     /**
  2614.      * @param string $name
  2615.      * @param $key
  2616.      * @return false|int|mixed|string
  2617.      * @throws Exception
  2618.      */
  2619.     public function do_ajax (string $name, $key = null) {
  2620.         if (!$_SERVER["REQUEST_METHOD"] == "POST")
  2621.             return false;
  2622.         if (!isset($this->FLOAT["ufo_ajax"]))
  2623.             $this->FLOAT["ufo_ajax"] = [];
  2624.         if (!isset($this->FLOAT["ufo_ajax"][$name]))
  2625.             return false;
  2626.         $ajax = $this->FLOAT["ufo_ajax"][$name];
  2627.         if (!$ajax["guest"]) {
  2628.             if ($this->is_admin() && !$this->check_login_admin())
  2629.                 return false;
  2630.             if (defined("AJAX_FRONT") && !$this->check_login_member())
  2631.                 return false;
  2632.         }
  2633.         if ($ajax["key"] == (empty($key) ? $this->do_work("ufo_ajax_key") : $key)) {
  2634.             if ($this->is_function($ajax["callback"])) {
  2635.                 ob_start();
  2636.                 if (empty($run = $ajax["callback"]())) {
  2637.                     $run = ob_get_flush();
  2638.                     ob_clean();
  2639.                 }
  2640.                 return $run;
  2641.             }
  2642.         }
  2643.         return false;
  2644.     }
  2645.     /**
  2646.      * @return array
  2647.      */
  2648.     public function prevent_ajax (): array {
  2649.         if ($_SERVER["REQUEST_METHOD"] == "POST" && $this->isset_post("prevent_ajax")) {
  2650.             $ex = explode(",", $_POST["prevent_ajax"]); $ed = [];
  2651.             foreach ($ex as $items)
  2652.                 $ed[$items] = false;
  2653.             return $ed;
  2654.         } else return [];
  2655.     }
  2656.     /**
  2657.      * @param string $title
  2658.      * @param string $string
  2659.      * @param bool $float
  2660.      * @return bool
  2661.      */
  2662.     public function string (string $title, string $string, bool $float = true): bool {
  2663.         if ($float) {
  2664.             $this->FLOAT["float_string"] = $this->FLOAT["float_string"] ?? [];
  2665.             $this->FLOAT["float_string"][$title] = $string;
  2666.         } else {
  2667.             $this->SAVER["float_string"] = $this->FLOAT["float_string"] ?? [];
  2668.             $this->SAVER["float_string"][$title] = $string;
  2669.         }
  2670.         return true;
  2671.     }
  2672.     /**
  2673.      * @param string $string
  2674.      * @return array
  2675.      */
  2676.     public function str_split (string $string): array {
  2677.         $len    = mb_strlen($string);
  2678.         $result = [];
  2679.         for ($i = 0; $i < $len; $i++)
  2680.             $result[] = mb_substr($string, $i, 1);
  2681.         return $result;
  2682.     }
  2683.     /**
  2684.      * @param $numbers
  2685.      * @param string $type
  2686.      * @return string
  2687.      */
  2688.     public function replace_number ($numbers, string $type = "en"): string {
  2689.         $EN = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.'];
  2690.         $FA = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹', ','];
  2691.         return $type == 'fa' ? str_replace($EN, $FA, $numbers) : str_replace($FA, $EN, $numbers);
  2692.     }
  2693.     /**
  2694.      * @param $number
  2695.      * @param string $separator
  2696.      * @param bool|array $unit
  2697.      * @return array|false|string
  2698.      */
  2699.     public function minify_number ($number, string $separator = ",", $unit = true) {
  2700.         $absNumber = abs($number);
  2701.         $unitIndex = min(intdiv(log10($absNumber), 3), 4); // Maximum index is 4 (for quintillion)
  2702.         // Calculate the percentage number for each unit
  2703.         $percent = 10 ** ($unitIndex * 3);
  2704.         $units  = is_array($unit) ? $unit : [
  2705.             "", "K", "M", "B", "T"
  2706.         ];
  2707.         $number = number_format(
  2708.             $percent > 0 ? $number / $percent : $number,
  2709.             $percent == 0 ? 0 : 3,
  2710.             $separator,
  2711.             $separator
  2712.         );
  2713.         // Find the position of the symbol
  2714.         $symbolPosition = strpos($number, $separator);
  2715.         // If the symbol is not found or is the last character, return the original string
  2716.         if ($symbolPosition === false || $symbolPosition === strlen($number) - 1)
  2717.             return $unit ? [$number, $units[$unitIndex]] : $number;
  2718.         // Extract the substring up to the symbol (including the symbol)
  2719.         $result = substr($number, 0, $symbolPosition);
  2720.         // Check if the first character after the symbol is '0'
  2721.         if ($number[$symbolPosition + 1] === '0')
  2722.             return $unit ? [$result, $units[$unitIndex]] : $result;
  2723.         // Otherwise, return the original string
  2724.         return $unit ? [$number, $units[$unitIndex]] : $number;
  2725.     }
  2726.     /**
  2727.      * @param int $number
  2728.      * @return string
  2729.      */
  2730.     public function back_folder (int $number = 1): string {
  2731.         $join = "";
  2732.         for ($i = 0; $i < $number; $i++)
  2733.             $join .= $this->slash_folder("../");
  2734.         return $join;
  2735.     }
  2736.     /**
  2737.      * @param $address
  2738.      * @return string
  2739.      */
  2740.     public function slash_folder ($address): string {
  2741.         return str_replace(["/","\/","//","\\","\\\\","////"], DIRECTORY_SEPARATOR, $address);
  2742.     }
  2743.     /**
  2744.      * @param $f
  2745.      * @return bool
  2746.      */
  2747.     public function is_function ($f): bool {
  2748.         return (is_string($f) && function_exists($f)) || (is_object($f) && ($f instanceof Closure));
  2749.     }
  2750.     /**
  2751.      * @param $object
  2752.      * @param $method
  2753.      * @return bool
  2754.      */
  2755.     public function is_callable ($object, $method): bool {
  2756.         if (method_exists($object, $method)) {
  2757.             $reflection = new ReflectionMethod($object, $method);
  2758.             return $reflection->isPublic();
  2759.         }
  2760.         return false;
  2761.     }
  2762.     /**
  2763.      * @param mixed $fn
  2764.      * @param mixed $data
  2765.      * @return array
  2766.      */
  2767.     public function fn ($fn, $data = []): array {
  2768.         return [
  2769.             "data" => $data,
  2770.             "fn"   => $this->is_function($fn) ? $fn : fn() => $fn
  2771.         ];
  2772.     }
  2773.     /**
  2774.      * @param $target
  2775.      * @param ...$args
  2776.      * @return mixed
  2777.      */
  2778.     public function call ($target, ...$args) {
  2779.         $result = null;
  2780.         // Function
  2781.         if ($this->is_function($target))
  2782.             $result = $target(...$args);
  2783.         // Class
  2784.         elseif (is_array($target) && (isset($target[0]) && (
  2785.             is_object($target[0]) || is_string($target[0])
  2786.         ))) {
  2787.             $target[0] = new $target[0];
  2788.             if ($this->is_callable($target[0], $target[1]))
  2789.                 $result = call_user_func($target, ...$args);
  2790.         }
  2791.         // Function name
  2792.         elseif (is_string($target) && function_exists($target))
  2793.             $result = call_user_func($target, ...$args);
  2794.         // Array function with data, $ufo->fn
  2795.         elseif (is_array($target)) {
  2796.             if (isset($target["data"]) && isset($target["fn"])) {
  2797.                 if ($this->is_function($target["fn"]))
  2798.                     $result = $target["fn"]($target["data"], ...$args);
  2799.             }
  2800.         }
  2801.         return $result;
  2802.     }
  2803.     /**
  2804.      * @param $folderName
  2805.      * @param string $fileType
  2806.      * @return array
  2807.      */
  2808.     public function get_file_list ($folderName, string $fileType = ""): array {
  2809.         $array_files = [];
  2810.         if (substr($folderName, strlen($folderName) - 1) != SLASH)
  2811.             $folderName .= SLASH;
  2812.         $folderName = $this->slash_folder($folderName);
  2813.         foreach (glob($folderName . "*" . $fileType) as $filename) {
  2814.             if (is_dir($filename))
  2815.                 $type = "folder";
  2816.             else
  2817.                 $type = "file";
  2818.             if ($type == "folder") {
  2819.                 if (is_dir($this->slash_folder($folderName . "/" . str_replace($folderName, '', $filename)))) {
  2820.                     $subFolder = $this->get_file_list($this->slash_folder($folderName . '/' . str_replace($folderName, "", $folderName . $filename)));
  2821.                     $array_files[] = [
  2822.                         "folderName" => str_replace($folderName, "", $filename),
  2823.                         "sub-folder" => $subFolder
  2824.                     ];
  2825.                 }
  2826.             } else {
  2827.                 $array_files[] = [$type => $filename];
  2828.             }
  2829.         }
  2830.         return $array_files;
  2831.     }
  2832.     /**
  2833.      * @param $folder
  2834.      * @param $format
  2835.      * @param string $types
  2836.      * @param string $sort_by
  2837.      * @return array
  2838.      */
  2839.     public function get_file_subfolder ($folder = null, $format = null, string $types = "*", string $sort_by = "time"): array {
  2840.         if (empty($folder))
  2841.             $folder = $this->slash_folder("../content/files/");
  2842.         if (is_dir($folder)) {
  2843.             $k_v = [];
  2844.             $new_sort_list   = [];
  2845.             $new_format_list = [];
  2846.             $new_types_list  = [];
  2847.             $get = array_diff(scandir($folder), [".", ".."]);
  2848.             $fix_link = substr(URL_WEBSITE, 0, -1);
  2849.             $fix_link = $this->sanitize_link($fix_link . $folder);
  2850.             foreach ($get as $item)
  2851.                 $k_v[$item] = $folder . $item;
  2852.             if ($sort_by == "time") {
  2853.                 foreach ($k_v as $items)
  2854.                     $new_sort_list[$items] = filectime($items);
  2855.                 $new_files = $this->v_sort($new_sort_list);
  2856.                 $new_sort_list = [];
  2857.                 foreach ($new_files as $k => $v)
  2858.                     if (is_file($v))
  2859.                         $new_sort_list[pathinfo($v)["filename"]] = $v;
  2860.                 $k_v = $new_sort_list;
  2861.             }
  2862.             if ($format == "link") {
  2863.                 foreach ($k_v as $item)
  2864.                     $new_format_list[] = $fix_link . pathinfo($item)["basename"];
  2865.                 $k_v = $new_format_list;
  2866.             }
  2867.             if ($types != "*") {
  2868.                 foreach ($k_v as $item)
  2869.                     if ($this->available_type($types, pathinfo($item)["extension"]))
  2870.                         $new_types_list[] = $item;
  2871.                 $k_v = $new_types_list;
  2872.             }
  2873.             return $k_v;
  2874.         }
  2875.         return [];
  2876.     }
  2877.     /**
  2878.      * @param $dir
  2879.      * @param string $sort_by
  2880.      * @return array|false
  2881.      */
  2882.     public function all_folders ($dir = null, string $sort_by = "time") {
  2883.         if (empty($dir))
  2884.             $dir = $this->slash_folder("../content/files");
  2885.         $folders  = glob($this->slash_folder($dir . '/*') , GLOB_ONLYDIR);
  2886.         $new_list = [];
  2887.         if ($sort_by == "time") {
  2888.             foreach ($folders as $items)
  2889.                 $new_list[$items] = filectime($items);
  2890.             $folders = $this->v_sort($new_list);
  2891.         }
  2892.         return $folders;
  2893.     }
  2894.     /**
  2895.      * @return string
  2896.      */
  2897.     public function join_path (): string {
  2898.         $args  = func_get_args();
  2899.         $paths = [];
  2900.         foreach ($args as $arg)
  2901.             $paths = array_merge($paths, (array) $arg);
  2902.         return join("/", array_filter(array_map(function ($p) {
  2903.             return trim($p, "/");
  2904.         }, $paths)));
  2905.     }
  2906.     /**
  2907.      * @param $file
  2908.      * @return bool
  2909.      */
  2910.     public function delete_file ($file): bool {
  2911.         if (file_exists($file))
  2912.             return unlink($file);
  2913.         return false;
  2914.     }
  2915.     /**
  2916.      * @param $dir
  2917.      * @return bool
  2918.      */
  2919.     public function delete_folder ($dir): bool {
  2920.         $dir = $this->slash_folder($dir);
  2921.         if (!file_exists($dir))
  2922.             return true;
  2923.         if (!is_dir($dir))
  2924.             return unlink($dir);
  2925.         foreach (scandir($dir) as $item) {
  2926.             if ($item == "." || $item == "..") continue;
  2927.             if (!$this->delete_folder($this->slash_folder($dir . "/" . $item)))
  2928.                 return false;
  2929.         }
  2930.         return rmdir($dir);
  2931.     }
  2932.     /**
  2933.      * @return string[]
  2934.      */
  2935.     public function size_units (): array {
  2936.         return ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
  2937.     }
  2938.     /**
  2939.      * @param $size
  2940.      * @param int $decimals
  2941.      * @return array
  2942.      */
  2943.     public function convert_size ($size, int $decimals = 2): array {
  2944.         $power = $size > 0 ? floor(log($size, 1024)) : 0;
  2945.         return [
  2946.             "size" => number_format($size / pow(1024, $power), $decimals, ".", ","),
  2947.             "unit" => $this->size_units()[$power]
  2948.         ];
  2949.     }
  2950.     /**
  2951.      * @param $size
  2952.      * @param $type
  2953.      * @return float|int|string
  2954.      */
  2955.     public function convert_to_byte ($size, $type) {
  2956.         $types = $this->size_units();
  2957.         if($key = array_search($type, $types))
  2958.             return $size * pow(1024, $key);
  2959.         else return "invalid type";
  2960.     }
  2961.     /**
  2962.      * @param $folder
  2963.      * @return int
  2964.      */
  2965.     public function folder_size ($folder): int {
  2966.         $size  = 0;
  2967.         $files = glob($this->slash_folder($folder . '/*'));
  2968.         foreach ($files as $path) {
  2969.             is_file($path) && $size += filesize($path);
  2970.             is_dir($path) && $size  += $this->folder_size($path);
  2971.         }
  2972.         return $size;
  2973.     }
  2974.     /**
  2975.      * @param $file
  2976.      * @return array
  2977.      */
  2978.     public function file_size ($file): array {
  2979.         return $this->convert_size(file_exists($file) ? filesize($file) : 0);
  2980.     }
  2981.     /**
  2982.      * @param $file
  2983.      * @return array
  2984.      */
  2985.     public function info_file ($file): array {
  2986.         $data  = pathinfo($file) + [
  2987.             "size" => $this->file_size($file)
  2988.         ];
  2989.         return [
  2990.             "name" => $data["filename"],
  2991.             "link" => URL_WEBSITE . str_replace(["\\\\", "\\", "//", "\/"], "/", str_replace(["../", "..\\"], "", $file)),
  2992.             "size" => $data["size"],
  2993.             "type" => $data["extension"]
  2994.         ];
  2995.     }
  2996.     /**
  2997.      * @param string|null $type
  2998.      * @return mixed
  2999.      */
  3000.     public function file_type_icon (?string $type = null) {
  3001.         $types = $this->object_to_array($this->do_work("ufo_file_types"));
  3002.         if (!$type)
  3003.             return $types;
  3004.         else if ($this->isset_key($types, $type))
  3005.             return $types[$type];
  3006.         return false;
  3007.     }
  3008.     /**
  3009.      * @param string $source
  3010.      * @param string $destination
  3011.      * @return bool
  3012.      */
  3013.     public function file_copy (string $source, string $destination): bool {
  3014.         $source = $this->slash_folder($source);
  3015.         $destination = $this->slash_folder($destination);
  3016.         if (is_dir($source)) {
  3017.             if (!is_dir($destination))
  3018.                 @mkdir($destination);
  3019.             $dir = dir($source);
  3020.             while (FALSE !== ($entry = $dir->read())) {
  3021.                 if ($entry == '.' || $entry == '..')
  3022.                     continue;
  3023.                 $Entry = $this->slash_folder($source . '/' . $entry);
  3024.                 if (is_dir($Entry)) {
  3025.                     $this->file_copy($Entry, $this->slash_folder(
  3026.                         $destination . '/' . $entry
  3027.                     ));
  3028.                     continue;
  3029.                 }
  3030.                 copy($Entry, $this->slash_folder($destination . '/' . $entry));
  3031.             }
  3032.             $dir->close();
  3033.             return true;
  3034.         }
  3035.         return copy($source, $destination);
  3036.     }
  3037.     /**
  3038.      * @param string $filename
  3039.      * @param mixed $content
  3040.      * @return void
  3041.      */
  3042.     public function make_file (string $filename, $content) {
  3043.         global $_;
  3044.         if (is_array($content) || is_object($content))
  3045.             $content = json_encode($content, JSON_UNESCAPED_UNICODE);
  3046.         if ($this->has_char($filename, '$root'))
  3047.             $filename = BASE_PATH . $filename;
  3048.         else if ($this->isset_key($_, "this_plugin"))
  3049.             $filename = $_["this_plugin"]["path"] . $filename;
  3050.         file_put_contents($this->slash_folder($filename), $content);
  3051.     }
  3052.     /**
  3053.      * @param string $zipFile
  3054.      * @param string|null $extractTo
  3055.      * @return bool
  3056.      */
  3057.     public function unzip (string $zipFile, string $extractTo = null): bool {
  3058.         if (!extension_loaded("zip")) return false;
  3059.         $zipFile = $this->slash_folder($zipFile);
  3060.         if (!file_exists($zipFile))
  3061.             return false;
  3062.         $path = pathinfo(realpath($zipFile), PATHINFO_DIRNAME);
  3063.         $zip  = new ZipArchive;
  3064.         $open = $zip->open($zipFile);
  3065.         if ($open) {
  3066.             $zip->extractTo((empty($extractTo) ? $path : $extractTo));
  3067.             $zip->close();
  3068.             return true;
  3069.         }
  3070.         return false;
  3071.     }
  3072.     /**
  3073.      * @param string $string
  3074.      * @return array|string|string[]
  3075.      */
  3076.     public function sanitize_file_name (string $string) {
  3077.         $special_chars = ["?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", "%", "+"];
  3078.         return str_replace($special_chars, "", $string);
  3079.     }
  3080.     /**
  3081.      * @param string $string
  3082.      * @return array|string|string[]
  3083.      */
  3084.     public function sanitize_link (string $string = "") {
  3085.         return str_replace(["..", "../", "..\\", "./"], "", str_replace(["/", "////", "\\", "\\\\", "\/", "/\\"], "/", $string));
  3086.     }
  3087.     /**
  3088.      * @return array
  3089.      */
  3090.     public function get_lower_type_file (): array {
  3091.         $new_list = [];
  3092.         if (isset($this->get_package()["types"])) {
  3093.             $types = $this->get_package()["types"];
  3094.             foreach ($types as $value)
  3095.                 foreach ($value as $k => $v)
  3096.                     $new_list[$k] = $v["icon"];
  3097.         }
  3098.         return $new_list;
  3099.     }
  3100.     /**
  3101.      * @param $category
  3102.      * @param $type
  3103.      * @return bool
  3104.      */
  3105.     public function available_type ($category, $type): bool {
  3106.         $category = is_array($category) ? $category : explode(",", $category);
  3107.         $new_list = [];
  3108.         $result   = false;
  3109.         foreach ($category as $item)
  3110.             $new_list += $this->get_package()["types"][$item] ?? [];
  3111.         foreach ($new_list as $k => $v) {
  3112.             if ($type == $k) {
  3113.                 $result = true;
  3114.                 break;
  3115.             }
  3116.         }
  3117.         return $result;
  3118.     }
  3119.     /**
  3120.      * @param $link
  3121.      * @param array $options
  3122.      * @param mixed $data
  3123.      * @param array $header
  3124.      * @return array
  3125.      */
  3126.     public function curl ($link, array $options = [], $data = [], array $header = []): array {
  3127.         $curl = curl_init($link);
  3128.         /**
  3129.          * Config
  3130.          */
  3131.         curl_setopt($curl, CURLOPT_POST, true);
  3132.         curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
  3133.         curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  3134.         curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
  3135.         curl_setopt_array($curl, $options);
  3136.         /**
  3137.          * Result
  3138.          */
  3139.         $response = curl_exec($curl);
  3140.         /**
  3141.          * Status
  3142.          */
  3143.         $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
  3144.         /**
  3145.          * Get Error
  3146.          */
  3147.         $error  = curl_error($curl);
  3148.         /**
  3149.          * Close Curl
  3150.          */
  3151.         curl_close($curl);
  3152.         return [
  3153.             "status"   => $status,
  3154.             "error"    => $error,
  3155.             "response" => $response
  3156.         ];
  3157.     }
  3158.     /**
  3159.      * @param $to
  3160.      * @param $subject
  3161.      * @param $content
  3162.      * @param array $array
  3163.      * @return bool|string
  3164.      * @throws Exception
  3165.      */
  3166.     public function send_mail ($to, $subject, $content, array $array = []) {
  3167.         global $db;
  3168.         $array += unserialize($db->meta("smtp")) + unserialize($db->meta("mail"));
  3169.         return $this->do_work("ufo_send_email", $array + [
  3170.             "to"      => $to,
  3171.             "subject" => $subject,
  3172.             "content" => $content
  3173.         ]);
  3174.     }
  3175.     /**
  3176.      * @param $filename
  3177.      * @param array $search
  3178.      * @param array $replace
  3179.      * @return string
  3180.      * @throws Exception
  3181.      */
  3182.     public function mail_template ($filename, array $search = [], array $replace = []): string {
  3183.         global $admin_folder;
  3184.         $filename = $this->slash_folder(
  3185.             $admin_folder . "content/cache/emails/$filename.html"
  3186.         );
  3187.         $template = "";
  3188.         if (file_exists($filename))
  3189.             $template = file_get_contents($filename);
  3190.         $search  = array_merge([
  3191.             "web_title", "web_logo", "web_link"
  3192.         ], $search);
  3193.         $replace = array_merge([
  3194.             WEB_TITLE, WEB_ICON, URL_WEBSITE
  3195.         ], $replace);
  3196.         $this->prefix_suffix_array($search, "%", "%");
  3197.         return str_replace($search, $replace, $template);
  3198.     }
  3199.     /**
  3200.      * @return float|int
  3201.      */
  3202.     public function get_upload_max_size () {
  3203.         return (int) str_replace(["b","k","m","g","t","p","e","z","y"], "", strtolower(ini_get("upload_max_filesize")));
  3204.     }
  3205.     /**
  3206.      * @param $title
  3207.      * @param $msg
  3208.      * @param string $status
  3209.      * @return void
  3210.      */
  3211.     public function add_log ($title, $msg, string $status = "normal") {
  3212.         global $admin_folder;
  3213.         $available_status = ["normal" => 0, "warning" => 0, "danger" => 0];
  3214.         if ($this->isset_key($available_status, $status)) {
  3215.             $template = [
  3216.                 "title"    => $title,
  3217.                 "message"  => $msg,
  3218.                 "status"   => $status,
  3219.                 "dateTime" => $this->dateTime()
  3220.             ];
  3221.             $file = $this->slash_folder($admin_folder . "content/cache/admin/logs.json");
  3222.             $data = json_decode(file_get_contents($file), true);
  3223.             $has  = false;
  3224.             foreach ($data as $item) {
  3225.                 if ($item["title"] == $title) $has = true;
  3226.             }
  3227.             if (!$has) {
  3228.                 $data[] = $template;
  3229.                 file_put_contents($file, json_encode($data, JSON_UNESCAPED_UNICODE));
  3230.             }
  3231.         }
  3232.     }
  3233.     /**
  3234.      * @param $string
  3235.      * @param $decode
  3236.      * @return bool
  3237.      */
  3238.     public function is_bas64 ($string, &$decode = null): bool {
  3239.         if (!is_string($string) || !preg_match(
  3240.             "/^[a-zA-Z0-9\/\r\n+]*={0,2}$/", $string
  3241.         )) return false;
  3242.         $decoded = base64_decode($string, true);
  3243.         if (false === $decoded)
  3244.             return false;
  3245.         if (base64_encode($decoded) != $string)
  3246.             return false;
  3247.         return $decode ? $decode = $decoded : true;
  3248.     }
  3249.     /**
  3250.      * @param $target
  3251.      * @param &$variable
  3252.      * @return bool|array
  3253.      */
  3254.     public function is_array ($target, &$variable = null) {
  3255.         try {
  3256.             if (is_string($target)) {
  3257.                 $decode = json_decode($target, true);
  3258.                 return $variable === null ? is_array($decode) : $variable = $decode;
  3259.             }
  3260.         } catch (Exception $e) {}
  3261.         return false;
  3262.     }
  3263.     /**
  3264.      * @param $target
  3265.      * @param &$variable
  3266.      * @return bool|array
  3267.      */
  3268.     public function is_json ($target, &$variable = null) {
  3269.         return $this->is_array($target, $variable);
  3270.     }
  3271.     /**
  3272.      * @param string $string
  3273.      * @return string
  3274.      */
  3275.     public function sanitize_xss (string $string): string {
  3276.         $string = str_replace(["&amp;","&lt;","&gt;"], ["&amp;amp;","&amp;lt;","&amp;gt;"], $string);
  3277.         $string = preg_replace('/(&#*\w+)[\x00-\x20]+;/u', "$1;", $string);
  3278.         $string = preg_replace('/(&#x*[0-9A-F]+);*/iu', "$1;", $string);
  3279.         $string = html_entity_decode($string, ENT_COMPAT, "UTF-8");
  3280.         $string = preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu', '$1>', $string);
  3281.         $string = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2nojavascript...', $string);
  3282.         $string = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2novbscript...', $string);
  3283.         $string = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $string);
  3284.         $string = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i', '$1>', $string);
  3285.         $string = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '$1>', $string);
  3286.         $string = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu', '$1>', $string);
  3287.         $string = preg_replace('#</*\w+:\w[^>]*+>#i', '', $string);
  3288.         do {
  3289.             $old_data = $string;
  3290.             $string = preg_replace('#</*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|i(?:frame|layer)|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|title|xml)[^>]*+>#i', '', $string);
  3291.         } while ($old_data !== $string);
  3292.         return $string;
  3293.     }
  3294.     /**
  3295.      * @return mixed
  3296.      */
  3297.     public function viewer_ip () {
  3298.         if (!empty($_SERVER["HTTP_CLIENT_IP"]))
  3299.             return $_SERVER["HTTP_CLIENT_IP"];
  3300.         elseif (!empty($_SERVER["HTTP_X_FORWARDED_FOR"]))
  3301.             return $_SERVER["HTTP_X_FORWARDED_FOR"];
  3302.         return $_SERVER["REMOTE_ADDR"];
  3303.     }
  3304.     /**
  3305.      * @param $prop
  3306.      * @param $replace
  3307.      * @return false|int
  3308.      */
  3309.     public function rewrite_htaccess ($prop, $replace) {
  3310.         $htaccess = (defined("ADMIN") ? $this->back_folder() : "") . ".htaccess";
  3311.         if (is_file($htaccess)) {
  3312.             $rows = file_get_contents($htaccess); preg_match('/'.$prop.'/i', $rows, $output);
  3313.             if (!empty($output)) {
  3314.                 return file_put_contents($htaccess,  preg_replace('/'.$prop.'/i', $replace, $rows));
  3315.             } else {
  3316.                 return file_put_contents($htaccess, $rows . "\n" . $replace);
  3317.             }
  3318.         }
  3319.         return false;
  3320.     }
  3321.     /**
  3322.      * @param array $array
  3323.      * @return bool
  3324.      */
  3325.     public function add_admin_widget (array $array): bool {
  3326.         $this->FLOAT["ufo_admin_widgets"] = $this->FLOAT["ufo_admin_widgets"] ?? [];
  3327.         if (!$this->has_in_array(["column", "script", "title"], $array))
  3328.             return false;
  3329.         if (isset($array["include"]) || isset($array["html"])) {
  3330.             if ($array["column"] == 1 || $array["column"] == 2) {
  3331.                 $this->FLOAT["ufo_admin_widgets"]["0x" . rand()] = [
  3332.                     "title"  => $array["title"],
  3333.                     "column" => $array["column"],
  3334.                     isset($array["include"]) ? "include" : "html" => $array["html"] ?? $this->slash_folder($array["include"]),
  3335.                     "script" => empty($array["script"]) ? rand(0, 9999) : $array["script"]
  3336.                 ];
  3337.                 return true;
  3338.             }
  3339.         }
  3340.         return false;
  3341.     }
  3342.     /**
  3343.      * @return array|array[]
  3344.      */
  3345.     public function get_admin_widgets (): array {
  3346.         $columns = ["column" => [], "column2" => []];
  3347.         if (isset($this->FLOAT["ufo_admin_widgets"]) && $this->is_admin()) {
  3348.             foreach ($this->FLOAT["ufo_admin_widgets"] as $widget) {
  3349.                 if ($widget["column"] == 1)
  3350.                     $columns["column"][] = $widget;
  3351.             }
  3352.             foreach ($this->FLOAT["ufo_admin_widgets"] as $widget) {
  3353.                 if ($widget["column"] == 2)
  3354.                     $columns["column2"][] = $widget;
  3355.             }
  3356.         }
  3357.         return $columns;
  3358.     }
  3359.     /**
  3360.      * @param string $k
  3361.      * @param $v
  3362.      * @return void
  3363.      */
  3364.     public function set_session (string $k, $v) {
  3365.         if (!session_id()) session_start();
  3366.         $_SESSION[$k] = $v;
  3367.     }
  3368.     /**
  3369.      * @param string $session
  3370.      * @return void
  3371.      */
  3372.     public function unset_session (string $session) {
  3373.         if (!session_id()) session_start();
  3374.         if ($this->isset_key($_SESSION, $session))
  3375.             unset($_SESSION[$session]);
  3376.     }
  3377.     /**
  3378.      * @param string $cookie
  3379.      * @return bool
  3380.      */
  3381.     public function unset_cookie (string $cookie): bool {
  3382.         if (isset($_COOKIE[$cookie])) {
  3383.             unset($_COOKIE[$cookie]);
  3384.             return setcookie($cookie, "", 0, "/");
  3385.         }
  3386.         return true;
  3387.     }
  3388.     /**
  3389.      * @param array $array
  3390.      * @return bool
  3391.      * @throws Exception
  3392.      */
  3393.     public function add_task (array $array): bool {
  3394.         /**
  3395.          * Add tasks list
  3396.          */
  3397.         if (!$this->isset_key($this->FLOAT, "ufo_tasks")) {
  3398.             $this->FLOAT["ufo_tasks"] = [];
  3399.         }
  3400.         /**
  3401.          * Check parameters
  3402.          */
  3403.         if ($this->isset_key($array, "name") && $this->isset_key($array, "fn")) {
  3404.             if ($this->is_function($array["fn"])) {
  3405.                 $this->FLOAT["ufo_tasks"][$array["name"]] = $array["fn"];
  3406.             }
  3407.         }
  3408.         return (new UFO_Task())->add($array);
  3409.     }
  3410.     /**
  3411.      * @param string $name
  3412.      * @return bool
  3413.      */
  3414.     public function remove_task (string $name): bool {
  3415.         return (new UFO_Task())->remove($name);
  3416.     }
  3417.     /**
  3418.      * @param string $name
  3419.      * @return false|mixed
  3420.      */
  3421.     public function status_task (string $name) {
  3422.         return (new UFO_Task())->status($name);
  3423.     }
  3424.     /**
  3425.      * @param string $name
  3426.      * @return false|mixed
  3427.      */
  3428.     public function get_task (string $name) {
  3429.         return (new UFO_Task())->get($name);
  3430.     }
  3431.     /**
  3432.      * @return array
  3433.      */
  3434.     public function tasks (): array {
  3435.         return $this->FLOAT["ufo_tasks"] ?? [];
  3436.     }
  3437.     /**
  3438.      * @param mixed $one
  3439.      * @param mixed $two
  3440.      * @return bool
  3441.      */
  3442.     public function equal ($one, $two): bool {
  3443.         return $one == $two;
  3444.     }
  3445.     /**
  3446.      * Increase
  3447.      *
  3448.      * @param $num1
  3449.      * @param $num2
  3450.      * @return int
  3451.      */
  3452.     public function inc ($num1, $num2): int {
  3453.         return $num1 + $num2;
  3454.     }
  3455.     /**
  3456.      * Decrease
  3457.      *
  3458.      * @param $num1
  3459.      * @param $num2
  3460.      * @return int
  3461.      */
  3462.     public function dec ($num1, $num2): int {
  3463.         return $num1 - $num2;
  3464.     }
  3465.     /**
  3466.      * @param array $keys
  3467.      * @param string $str
  3468.      * @return false|mixed
  3469.      */
  3470.     public function match_keys_str (array $keys, string $str) {
  3471.         $matched = false; foreach ($keys as $key) {
  3472.             if ($key == $str) $matched = $key;
  3473.         } return $matched;
  3474.     }
  3475.     /**
  3476.      * @param string $name
  3477.      * @param array $data
  3478.      * @return UFO_Options
  3479.      */
  3480.     public function add_center (string $name, array $data): UFO_Options {
  3481.         $this->FLOAT["centers"] = $this->FLOAT["centers"] ?? [];
  3482.         $this->FLOAT["centers"][$name] = $this->FLOAT["centers"][$name] ?? [];
  3483.         $this->FLOAT["centers"][$name][] = $data;
  3484.         return $this;
  3485.     }
  3486.     /**
  3487.      * @param string $name
  3488.      * @return mixed|null
  3489.      */
  3490.     public function get_center (string $name) {
  3491.         return $this->FLOAT["centers"][$name] ?? null;
  3492.     }
  3493.     /**
  3494.      * @param string $html
  3495.      * @return string
  3496.      */
  3497.     public function minify_html (string $html): string {
  3498.         return preg_replace([
  3499.             '/(\n|^)(\x20+|\t)/', '/(\n|^)\/\/(.*?)(\n|$)/', '/\n/', '/\<\!--.*?-->/', '/(\x20+|\t)/', '/\>\s+\</', '/(\"|\')\s+\>/', '/=\s+(\"|\')/'
  3500.         ], [
  3501.             "\n", "\n", " ", "", " ", "><", "$1>", "=$1"
  3502.         ], $html);
  3503.     }
  3504.     /**
  3505.      * @param array|string $name
  3506.      * @param $fn
  3507.      * @return void
  3508.      */
  3509.     public function exert ($name, $fn) {
  3510.         if (!isset($this->FLOAT["exerts"]))
  3511.             $this->FLOAT["exerts"] = [];
  3512.         if (is_array($name)) {
  3513.             foreach ($name as $item)
  3514.                 $this->exert($item, $fn);
  3515.             return;
  3516.         }
  3517.         if (!isset($this->FLOAT["exerts"][$name]))
  3518.             $this->FLOAT["exerts"][$name] = [];
  3519.         $this->FLOAT["exerts"][$name][] = $fn;
  3520.     }
  3521.     /**
  3522.      * @param string|array $name
  3523.      * @param $args
  3524.      * @return array
  3525.      */
  3526.     public function fire ($name, ...$args): array {
  3527.         $name   = is_array($name) ? $name : [$name];
  3528.         $result = [];
  3529.         foreach ($name as $nexert)
  3530.             foreach ($this->FLOAT["exerts"][$nexert] ?? [] as $exert) {
  3531.                 $args[]   = $nexert;
  3532.                 $result[] = $this->call($exert, ...$args);
  3533.             }
  3534.         return $result;
  3535.     }
  3536.     /**
  3537.      * @param string $name
  3538.      * @param string|int $size (width)
  3539.      * @param array|int $attrs (height)
  3540.      * @param string|null $unit
  3541.      * @return false|string
  3542.      */
  3543.     public function thumbnail (string $name, $size, $attrs = [], ?string $unit = "px") {
  3544.         if (!$this->isset_key($this->FLOAT, "thumbnails"))
  3545.             $this->FLOAT["thumbnails"] = [];
  3546.         /**
  3547.          * Add a thumbnail to the list of thumbnails
  3548.          */
  3549.         if ((is_numeric($size) && $size != -1) || is_string($size)) {
  3550.             $this->FLOAT["thumbnails"][$name] = [
  3551.                 "width"  => $size,
  3552.                 "height" => empty($attrs) ? "auto" : $attrs,
  3553.                 "unit"   => $unit
  3554.             ];
  3555.             return true;
  3556.         }
  3557.         if (!$this->isset_key($this->FLOAT["thumbnails"], $name))
  3558.             return false;
  3559.         /**
  3560.          * Render thumbnail
  3561.          */
  3562.         $config = $this->FLOAT["thumbnails"][$name];
  3563.         $sizes  = $this->get_package()["thumbnail_sizes"];
  3564.         if (is_string($config["width"])) {
  3565.             if ($this->isset_key($sizes, $config["width"])) {
  3566.                 $config = array_merge($config, $sizes[$config["width"]]);
  3567.             } else {
  3568.                 if ($config["unit"] != null) {
  3569.                     throw new \RuntimeException(str_replace(
  3570.                         "%list",
  3571.                         implode(", ", array_keys($sizes)),
  3572.                         $this->lng("The desired image size is not correct. You can see the correct sizes in this list (%list)")
  3573.                     ));
  3574.                 }
  3575.             }
  3576.         }
  3577.         $config["width"]  .= $config["unit"];
  3578.         $config["height"] .= $config["height"] == "auto" ? "" : $config["unit"];
  3579.         return $this->tag("img", null, array_merge($config, (array) $attrs, [
  3580.             "style" => "width: " . $config["width"] . ";height: " . $config["height"] . ";"
  3581.         ]));
  3582.     }
  3583.     /**
  3584.      * @return void
  3585.      * @throws Exception
  3586.      */
  3587.     public function document () {
  3588.         $this->load_layout("document");
  3589.     }
  3590.     /**
  3591.      * @return void
  3592.      * @throws Exception
  3593.      */
  3594.     public function endDoc () {
  3595.         $this->load_layout("endDoc");
  3596.     }
  3597.     /**
  3598.      * @return void
  3599.      * @throws Exception
  3600.      */
  3601.     public function header () {
  3602.         $this->load_layout("document");
  3603.         $this->from_theme("header");
  3604.     }
  3605.     /**
  3606.      * @return void
  3607.      * @throws Exception
  3608.      */
  3609.     public function footer () {
  3610.         $this->from_theme("footer");
  3611.         $this->load_layout("endDoc");
  3612.     }
  3613. }