getBaseDir() . substr($hash, 0, 2); if (!is_dir($dir)) { mkdir($dir); } return "$dir/$hash." . $this->getFileExtension(); } public function getURL(): string { $hash = $this->getRecord()->hash; $filetype = $this->getFileExtension(); switch (OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["mode"]) { default: case "default": case "basic": return "http://" . $_SERVER['HTTP_HOST'] . "/blob_" . substr($hash, 0, 2) . "/$hash.$filetype"; break; case "accelerated": return "http://" . $_SERVER['HTTP_HOST'] . "/openvk-datastore/$hash.$filetype"; break; case "server": $settings = (object) OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["server"]; return ( $settings->protocol ?? ovk_scheme() . "://" . $settings->host . $settings->path . substr($hash, 0, 2) . "/$hash.$filetype" ); break; } } protected function saveFile(string $filename, string $hash): bool { move_uploaded_file($filename, $this->pathFromHash($hash)); return true; } protected function makePreview(string $tmp_name, string $filename, int $owner): bool { $preview_photo = new Photo(); $preview_photo->setOwner($owner); $preview_photo->setDescription("internal use"); $preview_photo->setCreated(time()); $preview_photo->setSystem(1); $preview_photo->setFile([ "tmp_name" => $tmp_name, "error" => 0, ]); $preview_photo->save(); $this->stateChanges("preview", "photo_" . $preview_photo->getId()); return true; } private function updateHash(string $hash): bool { $this->stateChanges("hash", $hash); return true; } public function setFile(array $file): void { if ($file["error"] !== UPLOAD_ERR_OK) { throw new ISE("File uploaded is corrupted"); } $original_name = $file["name"]; $file_format = end(explode(".", $original_name)); $file_size = $file["size"]; $type = Document::detectTypeByFormat($file_format); if (!$file_format) { throw new \TypeError("No file format"); } if (!in_array(mb_strtolower($file_format), OPENVK_ROOT_CONF["openvk"]["preferences"]["docs"]["allowedFormats"])) { throw new \TypeError("Forbidden file format"); } if ($file_size < 1 || $file_size > (OPENVK_ROOT_CONF["openvk"]["preferences"]["docs"]["maxSize"] * 1024 * 1024)) { throw new \ValueError("Invalid filesize"); } $hash = hash_file("whirlpool", $file["tmp_name"]); $this->stateChanges("original_name", ovk_proc_strtr($original_name, 255)); $this->tmp_format = mb_strtolower($file_format); $this->stateChanges("format", mb_strtolower($file_format)); $this->stateChanges("filesize", $file_size); $this->stateChanges("hash", $hash); $this->stateChanges("access_key", bin2hex(random_bytes(9))); $this->stateChanges("type", $type); if (in_array($type, [3, 4])) { $this->makePreview($file["tmp_name"], $original_name, $file["preview_owner"]); } $this->saveFile($file["tmp_name"], $hash); } public function hasPreview(): bool { return $this->getRecord()->preview != null; } public function isOwnerHidden(): bool { return (bool) $this->getRecord()->owner_hidden; } public function isCopy(): bool { return $this->getRecord()->copy_of != null; } public function isLicensed(): bool { return false; } public function isUnsafe(): bool { return false; } public function isAnonymous(): bool { return false; } public function isPrivate(): bool { return $this->getFolder() == Document::VKAPI_FOLDER_PRIVATE; } public function isImage(): bool { return in_array($this->getVKAPIType(), [3, 4]); } public function isBook(): bool { return in_array($this->getFileExtension(), ["pdf"]); } public function isAudio(): bool { return in_array($this->getVKAPIType(), [Document::VKAPI_TYPE_AUDIO]); } public function isGif(): bool { return $this->getVKAPIType() == 3; } public function isCopiedBy($user = null): bool { if (!$user) { return false; } if ($user->getRealId() === $this->getOwnerID()) { return true; } return DatabaseConnection::i()->getContext()->table("documents")->where([ "owner" => $user->getRealId(), "copy_of" => $this->getId(), "deleted" => 0, ])->count() > 0; } public function copy(User $user): Document { $item = DatabaseConnection::i()->getContext()->table("documents")->where([ "owner" => $user->getId(), "copy_of" => $this->getId(), ]); if ($item->count() > 0) { $older = new Document($item->fetch()); } $this_document_array = $this->getRecord()->toArray(); $new_document = new Document(); $new_document->setOwner($user->getId()); $new_document->updateHash($this_document_array["hash"]); $new_document->setOwner_hidden(1); $new_document->setCopy_of($this->getId()); $new_document->setName($this->getId()); $new_document->setOriginal_name($this->getOriginalName()); $new_document->setAccess_key(bin2hex(random_bytes(9))); $new_document->setFormat($this_document_array["format"]); $new_document->setType($this->getVKAPIType()); $new_document->setFolder_id(0); $new_document->setPreview($this_document_array["preview"]); $new_document->setTags($this_document_array["tags"]); $new_document->setFilesize($this_document_array["filesize"]); $new_document->save(); return $new_document; } public function setTags(?string $tags): bool { if (is_null($tags)) { $this->stateChanges("tags", null); return true; } $parsed = explode(",", $tags); if (sizeof($parsed) < 1 || $parsed[0] == "") { $this->stateChanges("tags", null); return true; } $result = ""; foreach ($parsed as $tag) { $result .= trim($tag) . ($tag != end($parsed) ? "," : ''); } $this->stateChanges("tags", ovk_proc_strtr($result, 500)); return true; } public function getOwner(bool $real = false): RowModel { $oid = (int) $this->getRecord()->owner; if ($oid > 0) { return (new Users())->get($oid); } else { return (new Clubs())->get($oid * -1); } } public function getFileExtension(): string { if ($this->tmp_format) { return $this->tmp_format; } return $this->getRecord()->format; } public function getPrettyId(): string { return $this->getVirtualId() . "_" . $this->getId(); } public function getPrettiestId(): string { return $this->getVirtualId() . "_" . $this->getId() . "_" . $this->getAccessKey(); } public function getOriginal(): Document { return $this->getRecord()->copy_of; } public function getName(): string { return $this->getRecord()->name; } public function getOriginalName(): string { return $this->getRecord()->original_name; } public function getVKAPIType(): int { return $this->getRecord()->type; } public function getFolder(): int { return $this->getRecord()->folder_id; } public function getTags(): array { $tags = $this->getRecord()->tags; if (!$tags) { return []; } return explode(",", $tags ?? ""); } public function getFilesize(): int { return $this->getRecord()->filesize; } public function getPreview(): ?RowModel { $preview_array = $this->getRecord()->preview; $preview = explode(",", $this->getRecord()->preview)[0]; $model = null; $exploded = explode("_", $preview); switch ($exploded[0]) { case "photo": $model = (new Photos())->get((int) $exploded[1]); break; } return $model; } public function getOwnerID(): int { return $this->getRecord()->owner; } public function toApiPreview(): object { $preview = $this->getPreview(); if ($preview instanceof Photo) { return (object) [ "photo" => [ "sizes" => array_values($preview->getVkApiSizes()), ], ]; } } public function canBeModifiedBy(User $user = null): bool { if (!$user) { return false; } if ($this->getOwnerID() < 0) { return (new Clubs())->get(abs($this->getOwnerID()))->canBeModifiedBy($user); } return $this->getOwnerID() === $user->getId(); } public function toVkApiStruct(?User $user = null, bool $return_tags = false): object { $res = new \stdClass(); $res->id = $this->getId(); $res->owner_id = $this->getVirtualId(); $res->title = $this->getName(); $res->size = $this->getFilesize(); $res->ext = $this->getFileExtension(); $res->url = $this->getURL(); $res->date = $this->getPublicationTime()->timestamp(); $res->type = $this->getVKAPIType(); $res->is_hidden = (int) $this->isOwnerHidden(); $res->is_licensed = (int) $this->isLicensed(); $res->is_unsafe = (int) $this->isUnsafe(); $res->folder_id = (int) $this->getFolder(); $res->access_key = $this->getAccessKey(); $res->private_url = ""; if ($user) { $res->can_manage = $this->canBeModifiedBy($user); } if ($this->hasPreview()) { $res->preview = $this->toApiPreview(); } if ($return_tags) { $res->tags = $this->getTags(); } return $res; } public function delete(bool $softly = true, bool $all_copies = false): void { if ($all_copies) { $ctx = DatabaseConnection::i()->getContext(); $ctx->table("documents")->where("copy_of", $this->getId())->delete(); } parent::delete($softly); } public static function detectTypeByFormat(string $format) { switch (mb_strtolower($format)) { case "txt": case "docx": case "doc": case "odt": case "pptx": case "ppt": case "xlsx": case "xls": case "md": return 1; case "zip": case "rar": case "7z": return 2; case "gif": case "apng": return 3; case "jpg": case "jpeg": case "png": case "psd": case "ps": case "webp": return 4; case "mp3": return 5; case "mp4": case "avi": return 6; case "pdf": case "djvu": case "epub": case "fb2": return 7; default: return 8; } } }