mirror of
https://github.com/openvk/openvk
synced 2025-02-03 21:46:12 +03:00
feat(documents) (#1205)
* create document entity * add upload, previews, most of api methods * ui start * better ui, search, uploading (icons by myslivets) Co-Authored-By: Daniel <60743585+myslivets@users.noreply.github.com> * add editing functions * add viewer and gallery * preparations for picker * things * add counter on tab * add tags * fix gif processing * fix png processing * picker * addd search * add fast uploader * openvk midn. support, change midn.photomodal color * fix low register format chekc * add gif play on click * unauthorized limitations --------- Co-authored-by: Daniel <60743585+myslivets@users.noreply.github.com>
This commit is contained in:
parent
504801520b
commit
2d83003b6c
45 changed files with 2550 additions and 37 deletions
215
VKAPI/Handlers/Docs.php
Normal file
215
VKAPI/Handlers/Docs.php
Normal file
|
@ -0,0 +1,215 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\VKAPI\Handlers;
|
||||||
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
use openvk\Web\Models\Entities\Document;
|
||||||
|
use openvk\Web\Models\Repositories\Documents;
|
||||||
|
|
||||||
|
final class Docs extends VKAPIRequestHandler
|
||||||
|
{
|
||||||
|
function add(int $owner_id, int $doc_id, ?string $access_key): string
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
$doc = (new Documents)->getDocumentById($owner_id, $doc_id);
|
||||||
|
if(!$doc || $doc->isDeleted())
|
||||||
|
$this->fail(1150, "Invalid document id");
|
||||||
|
|
||||||
|
if(!$doc->checkAccessKey($access_key))
|
||||||
|
$this->fail(15, "Access denied");
|
||||||
|
|
||||||
|
if($doc->isCopiedBy($this->getUser()))
|
||||||
|
$this->fail(100, "One of the parameters specified was missing or invalid: this document already added");
|
||||||
|
|
||||||
|
$new_doc = $doc->copy($this->getUser());
|
||||||
|
|
||||||
|
return $new_doc->getPrettyId();
|
||||||
|
}
|
||||||
|
|
||||||
|
function delete(int $owner_id, int $doc_id): int
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
$doc = (new Documents)->getDocumentByIdUnsafe($owner_id, $doc_id);
|
||||||
|
if(!$doc || $doc->isDeleted())
|
||||||
|
$this->fail(1150, "Invalid document id");
|
||||||
|
|
||||||
|
if(!$doc->canBeModifiedBy($this->getUser()))
|
||||||
|
$this->fail(1153, "Access to document is denied");
|
||||||
|
|
||||||
|
$doc->delete();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function restore(int $owner_id, int $doc_id): int
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
return $this->add($owner_id, $doc_id, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function edit(int $owner_id, int $doc_id, ?string $title = "", ?string $tags = "", ?int $folder_id = 0, int $owner_hidden = -1): int
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
$doc = (new Documents)->getDocumentByIdUnsafe($owner_id, $doc_id);
|
||||||
|
if(!$doc || $doc->isDeleted())
|
||||||
|
$this->fail(1150, "Invalid document id");
|
||||||
|
if(!$doc->canBeModifiedBy($this->getUser()))
|
||||||
|
$this->fail(1153, "Access to document is denied");
|
||||||
|
if(iconv_strlen($title ?? "") > 128 || iconv_strlen($title ?? "") < 0)
|
||||||
|
$this->fail(1152, "Invalid document title");
|
||||||
|
if(iconv_strlen($tags ?? "") > 256)
|
||||||
|
$this->fail(1154, "Invalid tags");
|
||||||
|
|
||||||
|
if($title)
|
||||||
|
$doc->setName($title);
|
||||||
|
|
||||||
|
$doc->setTags($tags);
|
||||||
|
if(in_array($folder_id, [0, 3]))
|
||||||
|
$doc->setFolder_id($folder_id);
|
||||||
|
if(in_array($owner_hidden, [0, 1]))
|
||||||
|
$doc->setOwner_hidden($owner_hidden);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$doc->setEdited(time());
|
||||||
|
$doc->save();
|
||||||
|
} catch(\Throwable $e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get(int $count = 30, int $offset = 0, int $type = -1, int $owner_id = NULL, int $return_tags = 0, int $order = 0): object
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
if(!$owner_id)
|
||||||
|
$owner_id = $this->getUser()->getId();
|
||||||
|
|
||||||
|
if($owner_id > 0 && $owner_id != $this->getUser()->getId())
|
||||||
|
$this->fail(15, "Access denied");
|
||||||
|
|
||||||
|
$documents = (new Documents)->getDocumentsByOwner($owner_id, $order, $type);
|
||||||
|
$res = (object)[
|
||||||
|
"count" => $documents->size(),
|
||||||
|
"items" => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach($documents->offsetLimit($offset, $count) as $doc) {
|
||||||
|
$res->items[] = $doc->toVkApiStruct($this->getUser(), $return_tags == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getById(string $docs, int $return_tags = 0): array
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
$item_ids = explode(",", $docs);
|
||||||
|
$response = [];
|
||||||
|
if(sizeof($item_ids) < 1) {
|
||||||
|
$this->fail(100, "One of the parameters specified was missing or invalid: docs is undefined");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($item_ids as $id) {
|
||||||
|
$splitted_id = explode("_", $id);
|
||||||
|
$doc = (new Documents)->getDocumentById((int)$splitted_id[0], (int)$splitted_id[1], $splitted_id[2]);
|
||||||
|
if(!$doc || $doc->isDeleted())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$response[] = $doc->toVkApiStruct($this->getUser(), $return_tags === 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTypes(?int $owner_id)
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
if(!$owner_id)
|
||||||
|
$owner_id = $this->getUser()->getId();
|
||||||
|
|
||||||
|
if($owner_id > 0 && $owner_id != $this->getUser()->getId())
|
||||||
|
$this->fail(15, "Access denied");
|
||||||
|
|
||||||
|
$types = (new Documents)->getTypes($owner_id);
|
||||||
|
return [
|
||||||
|
"count" => sizeof($types),
|
||||||
|
"items" => $types,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTags(?int $owner_id, ?int $type = 0)
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
if(!$owner_id)
|
||||||
|
$owner_id = $this->getUser()->getId();
|
||||||
|
|
||||||
|
if($owner_id > 0 && $owner_id != $this->getUser()->getId())
|
||||||
|
$this->fail(15, "Access denied");
|
||||||
|
|
||||||
|
$tags = (new Documents)->getTags($owner_id, $type);
|
||||||
|
return $tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
function search(string $q = "", int $search_own = -1, int $order = -1, int $count = 30, int $offset = 0, int $return_tags = 0, int $type = 0, ?string $tags = NULL): object
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
$params = [];
|
||||||
|
$o_order = ["type" => "id", "invert" => false];
|
||||||
|
|
||||||
|
if(iconv_strlen($q) > 512)
|
||||||
|
$this->fail(100, "One of the parameters specified was missing or invalid: q should be not more 512 letters length");
|
||||||
|
|
||||||
|
if(in_array($type, [1,2,3,4,5,6,7,8]))
|
||||||
|
$params["type"] = $type;
|
||||||
|
|
||||||
|
if(iconv_strlen($tags ?? "") < 512)
|
||||||
|
$params["tags"] = $tags;
|
||||||
|
|
||||||
|
if($search_own === 1)
|
||||||
|
$params["from_me"] = $this->getUser()->getId();
|
||||||
|
|
||||||
|
$documents = (new Documents)->find($q, $params, $o_order);
|
||||||
|
$res = (object)[
|
||||||
|
"count" => $documents->size(),
|
||||||
|
"items" => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach($documents->offsetLimit($offset, $count) as $doc) {
|
||||||
|
$res->items[] = $doc->toVkApiStruct($this->getUser(), $return_tags == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUploadServer(?int $group_id = NULL)
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWallUploadServer(?int $group_id = NULL)
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function save(string $file, string $title, string $tags, ?int $return_tags = 0)
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -117,6 +117,11 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"type" => "audio",
|
"type" => "audio",
|
||||||
"audio" => $attachment->toVkApiStruct($this->getUser()),
|
"audio" => $attachment->toVkApiStruct($this->getUser()),
|
||||||
];
|
];
|
||||||
|
} else if ($attachment instanceof \openvk\Web\Models\Entities\Document) {
|
||||||
|
$attachments[] = [
|
||||||
|
"type" => "doc",
|
||||||
|
"doc" => $attachment->toVkApiStruct($this->getUser()),
|
||||||
|
];
|
||||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Post) {
|
} else if ($attachment instanceof \openvk\Web\Models\Entities\Post) {
|
||||||
$repostAttachments = [];
|
$repostAttachments = [];
|
||||||
|
|
||||||
|
@ -333,6 +338,11 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"type" => "audio",
|
"type" => "audio",
|
||||||
"audio" => $attachment->toVkApiStruct($this->getUser())
|
"audio" => $attachment->toVkApiStruct($this->getUser())
|
||||||
];
|
];
|
||||||
|
} else if ($attachment instanceof \openvk\Web\Models\Entities\Document) {
|
||||||
|
$attachments[] = [
|
||||||
|
"type" => "doc",
|
||||||
|
"doc" => $attachment->toVkApiStruct($this->getUser()),
|
||||||
|
];
|
||||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Post) {
|
} else if ($attachment instanceof \openvk\Web\Models\Entities\Post) {
|
||||||
$repostAttachments = [];
|
$repostAttachments = [];
|
||||||
|
|
||||||
|
@ -577,7 +587,7 @@ final class Wall extends VKAPIRequestHandler
|
||||||
if($signed == 1)
|
if($signed == 1)
|
||||||
$flags |= 0b01000000;
|
$flags |= 0b01000000;
|
||||||
|
|
||||||
$parsed_attachments = parseAttachments($attachments, ['photo', 'video', 'note', 'poll', 'audio']);
|
$parsed_attachments = parseAttachments($attachments, ['photo', 'video', 'note', 'poll', 'audio', 'doc']);
|
||||||
$final_attachments = [];
|
$final_attachments = [];
|
||||||
$should_be_suggested = $owner_id < 0 && !$wallOwner->canBeModifiedBy($this->getUser()) && $wallOwner->getWallType() == 2;
|
$should_be_suggested = $owner_id < 0 && !$wallOwner->canBeModifiedBy($this->getUser()) && $wallOwner->getWallType() == 2;
|
||||||
foreach($parsed_attachments as $attachment) {
|
foreach($parsed_attachments as $attachment) {
|
||||||
|
@ -670,7 +680,7 @@ final class Wall extends VKAPIRequestHandler
|
||||||
if(preg_match('/(wall|video|photo)((?:-?)[0-9]+)_([0-9]+)/', $object, $postArray) == 0)
|
if(preg_match('/(wall|video|photo)((?:-?)[0-9]+)_([0-9]+)/', $object, $postArray) == 0)
|
||||||
$this->fail(100, "One of the parameters specified was missing or invalid: object is incorrect");
|
$this->fail(100, "One of the parameters specified was missing or invalid: object is incorrect");
|
||||||
|
|
||||||
$parsed_attachments = parseAttachments($attachments, ['photo', 'video', 'note', 'audio']);
|
$parsed_attachments = parseAttachments($attachments, ['photo', 'video', 'note', 'audio', 'doc']);
|
||||||
$final_attachments = [];
|
$final_attachments = [];
|
||||||
foreach($parsed_attachments as $attachment) {
|
foreach($parsed_attachments as $attachment) {
|
||||||
if($attachment && !$attachment->isDeleted() && $attachment->canBeViewedBy($this->getUser()) &&
|
if($attachment && !$attachment->isDeleted() && $attachment->canBeViewedBy($this->getUser()) &&
|
||||||
|
@ -780,6 +790,11 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"type" => "audio",
|
"type" => "audio",
|
||||||
"audio" => $attachment->toVkApiStruct($this->getUser()),
|
"audio" => $attachment->toVkApiStruct($this->getUser()),
|
||||||
];
|
];
|
||||||
|
} else if ($attachment instanceof \openvk\Web\Models\Entities\Document) {
|
||||||
|
$attachments[] = [
|
||||||
|
"type" => "doc",
|
||||||
|
"doc" => $attachment->toVkApiStruct($this->getUser()),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -867,6 +882,11 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"type" => "audio",
|
"type" => "audio",
|
||||||
"audio" => $attachment->toVkApiStruct($this->getUser()),
|
"audio" => $attachment->toVkApiStruct($this->getUser()),
|
||||||
];
|
];
|
||||||
|
} else if ($attachment instanceof \openvk\Web\Models\Entities\Document) {
|
||||||
|
$attachments[] = [
|
||||||
|
"type" => "doc",
|
||||||
|
"doc" => $attachment->toVkApiStruct($this->getUser()),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -928,7 +948,7 @@ final class Wall extends VKAPIRequestHandler
|
||||||
if($post->getTargetWall() < 0)
|
if($post->getTargetWall() < 0)
|
||||||
$club = (new ClubsRepo)->get(abs($post->getTargetWall()));
|
$club = (new ClubsRepo)->get(abs($post->getTargetWall()));
|
||||||
|
|
||||||
$parsed_attachments = parseAttachments($attachments, ['photo', 'video', 'note', 'audio']);
|
$parsed_attachments = parseAttachments($attachments, ['photo', 'video', 'note', 'audio', 'doc']);
|
||||||
$final_attachments = [];
|
$final_attachments = [];
|
||||||
foreach($parsed_attachments as $attachment) {
|
foreach($parsed_attachments as $attachment) {
|
||||||
if($attachment && !$attachment->isDeleted() && $attachment->canBeViewedBy($this->getUser()) &&
|
if($attachment && !$attachment->isDeleted() && $attachment->canBeViewedBy($this->getUser()) &&
|
||||||
|
@ -1014,7 +1034,7 @@ final class Wall extends VKAPIRequestHandler
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
$this->willExecuteWriteAction();
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
$parsed_attachments = parseAttachments($attachments, ['photo', 'video', 'note', 'audio', 'poll']);
|
$parsed_attachments = parseAttachments($attachments, ['photo', 'video', 'note', 'audio', 'poll', 'doc']);
|
||||||
$final_attachments = [];
|
$final_attachments = [];
|
||||||
foreach($parsed_attachments as $attachment) {
|
foreach($parsed_attachments as $attachment) {
|
||||||
if($attachment && !$attachment->isDeleted() && $attachment->canBeViewedBy($this->getUser()) &&
|
if($attachment && !$attachment->isDeleted() && $attachment->canBeViewedBy($this->getUser()) &&
|
||||||
|
@ -1083,7 +1103,7 @@ final class Wall extends VKAPIRequestHandler
|
||||||
$this->willExecuteWriteAction();
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
$comment = (new CommentsRepo)->get($comment_id);
|
$comment = (new CommentsRepo)->get($comment_id);
|
||||||
$parsed_attachments = parseAttachments($attachments, ['photo', 'video', 'note', 'audio']);
|
$parsed_attachments = parseAttachments($attachments, ['photo', 'video', 'note', 'audio', 'doc']);
|
||||||
$final_attachments = [];
|
$final_attachments = [];
|
||||||
foreach($parsed_attachments as $attachment) {
|
foreach($parsed_attachments as $attachment) {
|
||||||
if($attachment && !$attachment->isDeleted() && $attachment->canBeViewedBy($this->getUser()) &&
|
if($attachment && !$attachment->isDeleted() && $attachment->canBeViewedBy($this->getUser()) &&
|
||||||
|
|
|
@ -433,6 +433,14 @@ class Club extends RowModel
|
||||||
return $this->isEveryoneCanUploadAudios() || $this->canBeModifiedBy($user);
|
return $this->isEveryoneCanUploadAudios() || $this->canBeModifiedBy($user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canUploadDocs(?User $user): bool
|
||||||
|
{
|
||||||
|
if(!$user)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return $this->canBeModifiedBy($user);
|
||||||
|
}
|
||||||
|
|
||||||
function getAudiosCollectionSize()
|
function getAudiosCollectionSize()
|
||||||
{
|
{
|
||||||
return (new \openvk\Web\Models\Repositories\Audios)->getClubCollectionSize($this);
|
return (new \openvk\Web\Models\Repositories\Audios)->getClubCollectionSize($this);
|
||||||
|
|
413
Web/Models/Entities/Document.php
Normal file
413
Web/Models/Entities/Document.php
Normal file
|
@ -0,0 +1,413 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Entities;
|
||||||
|
use openvk\Web\Models\Repositories\{Clubs, Users, Photos};
|
||||||
|
use openvk\Web\Models\Entities\{Photo};
|
||||||
|
use openvk\Web\Models\RowModel;
|
||||||
|
use Nette\InvalidStateException as ISE;
|
||||||
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
|
||||||
|
class Document extends Media
|
||||||
|
{
|
||||||
|
protected $tableName = "documents";
|
||||||
|
protected $fileExtension = "gif";
|
||||||
|
private $tmp_format = NULL;
|
||||||
|
|
||||||
|
const VKAPI_TYPE_TEXT = 1;
|
||||||
|
const VKAPI_TYPE_ARCHIVE = 2;
|
||||||
|
const VKAPI_TYPE_GIF = 3;
|
||||||
|
const VKAPI_TYPE_IMAGE = 4;
|
||||||
|
const VKAPI_TYPE_AUDIO = 5;
|
||||||
|
const VKAPI_TYPE_VIDEO = 6;
|
||||||
|
const VKAPI_TYPE_BOOKS = 7;
|
||||||
|
const VKAPI_TYPE_UNKNOWN = 8;
|
||||||
|
|
||||||
|
const VKAPI_FOLDER_PRIVATE = 0;
|
||||||
|
const VKAPI_FOLDER_STUDY = 1;
|
||||||
|
const VKAPI_FOLDER_BOOK = 2;
|
||||||
|
const VKAPI_FOLDER_PUBLIC = 3;
|
||||||
|
|
||||||
|
protected function pathFromHash(string $hash): string
|
||||||
|
{
|
||||||
|
$dir = $this->getBaseDir() . substr($hash, 0, 2);
|
||||||
|
if(!is_dir($dir))
|
||||||
|
mkdir($dir);
|
||||||
|
|
||||||
|
return "$dir/$hash." . $this->getFileExtension();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasPreview(): bool
|
||||||
|
{
|
||||||
|
return $this->getRecord()->preview != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isOwnerHidden(): bool
|
||||||
|
{
|
||||||
|
return (bool) $this->getRecord()->owner_hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCopy(): bool
|
||||||
|
{
|
||||||
|
return $this->getRecord()->copy_of != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isLicensed(): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isUnsafe(): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAnonymous(): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPrivate(): bool
|
||||||
|
{
|
||||||
|
return $this->getFolder() == Document::VKAPI_FOLDER_PRIVATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isImage(): bool
|
||||||
|
{
|
||||||
|
return in_array($this->getVKAPIType(), [3, 4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isGif(): bool
|
||||||
|
{
|
||||||
|
return $this->getVKAPIType() == 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFileExtension(): string
|
||||||
|
{
|
||||||
|
if($this->tmp_format) {
|
||||||
|
return $this->tmp_format;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->getRecord()->format;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPrettyId(): string
|
||||||
|
{
|
||||||
|
return $this->getVirtualId() . "_" . $this->getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPrettiestId(): string
|
||||||
|
{
|
||||||
|
return $this->getVirtualId() . "_" . $this->getId() . "_" . $this->getAccessKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOriginal(): Document
|
||||||
|
{
|
||||||
|
return $this->getRecord()->copy_of;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getName(): string
|
||||||
|
{
|
||||||
|
return $this->getRecord()->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOriginalName(): string
|
||||||
|
{
|
||||||
|
return $this->getRecord()->original_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVKAPIType(): int
|
||||||
|
{
|
||||||
|
return $this->getRecord()->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFolder(): int
|
||||||
|
{
|
||||||
|
return $this->getRecord()->folder_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTags(): array
|
||||||
|
{
|
||||||
|
$tags = $this->getRecord()->tags;
|
||||||
|
if(!$tags)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
return explode(",", $tags ?? "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFilesize(): int
|
||||||
|
{
|
||||||
|
return $this->getRecord()->filesize;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOwnerID(): int
|
||||||
|
{
|
||||||
|
return $this->getRecord()->owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toApiPreview(): object
|
||||||
|
{
|
||||||
|
$preview = $this->getPreview();
|
||||||
|
if($preview instanceof Photo) {
|
||||||
|
return (object)[
|
||||||
|
"photo" => [
|
||||||
|
"sizes" => array_values($preview->getVkApiSizes()),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -97,16 +97,27 @@ class Photo extends Media
|
||||||
|
|
||||||
protected function saveFile(string $filename, string $hash): bool
|
protected function saveFile(string $filename, string $hash): bool
|
||||||
{
|
{
|
||||||
$image = new \Imagick;
|
$input_image = new \Imagick;
|
||||||
$image->readImage($filename);
|
$input_image->readImage($filename);
|
||||||
$h = $image->getImageHeight();
|
$h = $input_image->getImageHeight();
|
||||||
$w = $image->getImageWidth();
|
$w = $input_image->getImageWidth();
|
||||||
if(($h >= ($w * Photo::ALLOWED_SIDE_MULTIPLIER)) || ($w >= ($h * Photo::ALLOWED_SIDE_MULTIPLIER)))
|
if(($h >= ($w * Photo::ALLOWED_SIDE_MULTIPLIER)) || ($w >= ($h * Photo::ALLOWED_SIDE_MULTIPLIER)))
|
||||||
throw new ISE("Invalid layout: image is too wide/short");
|
throw new ISE("Invalid layout: image is too wide/short");
|
||||||
|
|
||||||
|
# gif fix 10.01.2025
|
||||||
|
if($input_image->getImageFormat() === 'GIF')
|
||||||
|
$input_image->setIteratorIndex(0);
|
||||||
|
|
||||||
|
# png workaround (transparency to white)
|
||||||
|
$image = new \Imagick();
|
||||||
|
$bg = new \ImagickPixel('white');
|
||||||
|
$image->newImage($w, $h, $bg);
|
||||||
|
$image->compositeImage($input_image, \Imagick::COMPOSITE_OVER, 0, 0);
|
||||||
|
|
||||||
$sizes = Image::calculateSize(
|
$sizes = Image::calculateSize(
|
||||||
$image->getImageWidth(), $image->getImageHeight(), 8192, 4320, Image::SHRINK_ONLY | Image::FIT
|
$image->getImageWidth(), $image->getImageHeight(), 8192, 4320, Image::SHRINK_ONLY | Image::FIT
|
||||||
);
|
);
|
||||||
|
|
||||||
$image->resizeImage($sizes[0], $sizes[1], \Imagick::FILTER_HERMITE, 1);
|
$image->resizeImage($sizes[0], $sizes[1], \Imagick::FILTER_HERMITE, 1);
|
||||||
$image->writeImage($this->pathFromHash($hash));
|
$image->writeImage($this->pathFromHash($hash));
|
||||||
$this->saveImageResizedCopies($image, $filename, $hash);
|
$this->saveImageResizedCopies($image, $filename, $hash);
|
||||||
|
|
|
@ -106,6 +106,25 @@ abstract class Postable extends Attachable
|
||||||
yield $user;
|
yield $user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAccessKey(): string
|
||||||
|
{
|
||||||
|
return $this->getRecord()->access_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAccessKey(?string $access_key): bool
|
||||||
|
{
|
||||||
|
if($this->getAccessKey() === $access_key) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !$this->isPrivate();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPrivate(): bool
|
||||||
|
{
|
||||||
|
return (bool) $this->getRecord()->unlisted;
|
||||||
|
}
|
||||||
|
|
||||||
function isAnonymous(): bool
|
function isAnonymous(): bool
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,7 +5,7 @@ use Nette\Database\Table\ActiveRow;
|
||||||
use openvk\Web\Models\RowModel;
|
use openvk\Web\Models\RowModel;
|
||||||
use openvk\Web\Models\Entities\Club;
|
use openvk\Web\Models\Entities\Club;
|
||||||
use Chandler\Database\DatabaseConnection;
|
use Chandler\Database\DatabaseConnection;
|
||||||
use openvk\Web\Models\Repositories\{Applications, Comments, Notes, Reports, Audios, Users, Posts, Photos, Videos, Clubs};
|
use openvk\Web\Models\Repositories\{Applications, Comments, Notes, Reports, Audios, Documents, Users, Posts, Photos, Videos, Clubs};
|
||||||
use Chandler\Database\DatabaseConnection as DB;
|
use Chandler\Database\DatabaseConnection as DB;
|
||||||
use Nette\InvalidStateException as ISE;
|
use Nette\InvalidStateException as ISE;
|
||||||
use Nette\Database\Table\Selection;
|
use Nette\Database\Table\Selection;
|
||||||
|
@ -75,6 +75,7 @@ class Report extends RowModel
|
||||||
else if ($this->getContentType() == "app") return (new Applications)->get($this->getContentId());
|
else if ($this->getContentType() == "app") return (new Applications)->get($this->getContentId());
|
||||||
else if ($this->getContentType() == "user") return (new Users)->get($this->getContentId());
|
else if ($this->getContentType() == "user") return (new Users)->get($this->getContentId());
|
||||||
else if ($this->getContentType() == "audio") return (new Audios)->get($this->getContentId());
|
else if ($this->getContentType() == "audio") return (new Audios)->get($this->getContentId());
|
||||||
|
else if ($this->getContentType() == "doc") return (new Documents)->get($this->getContentId());
|
||||||
else return null;
|
else return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -512,6 +512,7 @@ class User extends RowModel
|
||||||
"links",
|
"links",
|
||||||
"poster",
|
"poster",
|
||||||
"apps",
|
"apps",
|
||||||
|
"docs",
|
||||||
],
|
],
|
||||||
])->get($id);
|
])->get($id);
|
||||||
}
|
}
|
||||||
|
@ -1117,6 +1118,7 @@ class User extends RowModel
|
||||||
"links",
|
"links",
|
||||||
"poster",
|
"poster",
|
||||||
"apps",
|
"apps",
|
||||||
|
"docs",
|
||||||
],
|
],
|
||||||
])->set($id, (int) $status)->toInteger();
|
])->set($id, (int) $status)->toInteger();
|
||||||
|
|
||||||
|
|
161
Web/Models/Repositories/Documents.php
Normal file
161
Web/Models/Repositories/Documents.php
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Repositories;
|
||||||
|
use openvk\Web\Models\Entities\Document;
|
||||||
|
use Nette\Database\Table\ActiveRow;
|
||||||
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
use openvk\Web\Models\Repositories\Util\EntityStream;
|
||||||
|
|
||||||
|
class Documents
|
||||||
|
{
|
||||||
|
private $context;
|
||||||
|
private $documents;
|
||||||
|
|
||||||
|
function __construct()
|
||||||
|
{
|
||||||
|
$this->context = DatabaseConnection::i()->getContext();
|
||||||
|
$this->documents = $this->context->table("documents");
|
||||||
|
}
|
||||||
|
|
||||||
|
private function toDocument(?ActiveRow $ar): ?Document
|
||||||
|
{
|
||||||
|
return is_null($ar) ? NULL : new Document($ar);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get(int $id): ?Document
|
||||||
|
{
|
||||||
|
return $this->toDocument($this->documents->get($id));
|
||||||
|
}
|
||||||
|
|
||||||
|
# By "Virtual ID" and "Absolute ID" (to not leak owner's id).
|
||||||
|
function getDocumentById(int $virtual_id, int $real_id, string $access_key = NULL): ?Document
|
||||||
|
{
|
||||||
|
$doc = $this->documents->where(['virtual_id' => $virtual_id, 'id' => $real_id]);
|
||||||
|
/*if($access_key) {
|
||||||
|
$doc->where("access_key", $access_key);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
$doc = $doc->fetch();
|
||||||
|
if(is_null($doc))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
$n_doc = new Document($doc);
|
||||||
|
if(!$n_doc->checkAccessKey($access_key))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return $n_doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDocumentByIdUnsafe(int $virtual_id, int $real_id): ?Document
|
||||||
|
{
|
||||||
|
$doc = $this->documents->where(['virtual_id' => $virtual_id, 'id' => $real_id]);
|
||||||
|
|
||||||
|
$doc = $doc->fetch();
|
||||||
|
if(is_null($doc))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
$n_doc = new Document($doc);
|
||||||
|
|
||||||
|
return $n_doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDocumentsByOwner(int $owner, int $order = 0, int $type = -1): EntityStream
|
||||||
|
{
|
||||||
|
$search = $this->documents->where([
|
||||||
|
"owner" => $owner,
|
||||||
|
"unlisted" => 0,
|
||||||
|
"deleted" => 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if(in_array($type, [1,2,3,4,5,6,7,8])) {
|
||||||
|
$search->where("type", $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch($order) {
|
||||||
|
case 0:
|
||||||
|
$search->order("id DESC");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
$search->order("name DESC");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
$search->order("filesize DESC");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new EntityStream("Document", $search);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTypes(int $owner_id): array
|
||||||
|
{
|
||||||
|
$result = DatabaseConnection::i()->getConnection()->query("SELECT `type`, COUNT(*) AS `count` FROM `documents` WHERE `owner` = ? AND `deleted` = 0 AND `unlisted` = 0 GROUP BY `type` ORDER BY `type`", $owner_id);
|
||||||
|
$response = [];
|
||||||
|
foreach($result as $res) {
|
||||||
|
if($res->count < 1 || $res->type == 0) continue;
|
||||||
|
|
||||||
|
$name = tr("document_type_".$res->type);
|
||||||
|
$response[] = [
|
||||||
|
"count" => $res->count,
|
||||||
|
"type" => $res->type,
|
||||||
|
"name" => $name,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTags(int $owner_id, ?int $type = 0): array
|
||||||
|
{
|
||||||
|
$query = "SELECT `tags` FROM `documents` WHERE `owner` = ? AND `deleted` = 0 AND `unlisted` = 0 ";
|
||||||
|
if($type > 0 && $type < 9) {
|
||||||
|
$query .= "AND `type` = $type";
|
||||||
|
}
|
||||||
|
|
||||||
|
$query .= " AND `tags` IS NOT NULL ORDER BY `id`";
|
||||||
|
$result = DatabaseConnection::i()->getConnection()->query($query, $owner_id);
|
||||||
|
$tags = [];
|
||||||
|
foreach($result as $res) {
|
||||||
|
$tags[] = $res->tags;
|
||||||
|
}
|
||||||
|
$imploded_tags = implode(",", $tags);
|
||||||
|
$exploded_tags = array_values(array_unique(explode(",", $imploded_tags)));
|
||||||
|
if($exploded_tags[0] == "")
|
||||||
|
return [];
|
||||||
|
|
||||||
|
return array_slice($exploded_tags, 0, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
function find(string $query, array $params = [], array $order = ['type' => 'id', 'invert' => false]): Util\EntityStream
|
||||||
|
{
|
||||||
|
$result = $this->documents->where("name LIKE ?", "%$query%")->where([
|
||||||
|
"deleted" => 0,
|
||||||
|
"folder_id != " => 0,
|
||||||
|
]);
|
||||||
|
$order_str = 'id';
|
||||||
|
|
||||||
|
switch($order['type']) {
|
||||||
|
case 'id':
|
||||||
|
$order_str = 'created ' . ($order['invert'] ? 'ASC' : 'DESC');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($params as $paramName => $paramValue) {
|
||||||
|
switch($paramName) {
|
||||||
|
case "type":
|
||||||
|
if($paramValue < 1 || $paramValue > 8) continue;
|
||||||
|
$result->where("type", $paramValue);
|
||||||
|
break;
|
||||||
|
case "tags":
|
||||||
|
$result->where("tags LIKE ?", "%$paramValue%");
|
||||||
|
break;
|
||||||
|
case "from_me":
|
||||||
|
$result->where("owner", $paramValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($order_str)
|
||||||
|
$result->order($order_str);
|
||||||
|
|
||||||
|
return new Util\EntityStream("Document", $result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,6 +27,8 @@ class Photos
|
||||||
$photo = $this->photos->where([
|
$photo = $this->photos->where([
|
||||||
"owner" => $owner,
|
"owner" => $owner,
|
||||||
"virtual_id" => $vId,
|
"virtual_id" => $vId,
|
||||||
|
"system" => 0,
|
||||||
|
"private" => 0,
|
||||||
])->fetch();
|
])->fetch();
|
||||||
if(!$photo) return NULL;
|
if(!$photo) return NULL;
|
||||||
|
|
||||||
|
@ -37,8 +39,10 @@ class Photos
|
||||||
{
|
{
|
||||||
$perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE;
|
$perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE;
|
||||||
$photos = $this->photos->where([
|
$photos = $this->photos->where([
|
||||||
"owner" => $user->getId(),
|
"owner" => $user->getId(),
|
||||||
"deleted" => 0
|
"deleted" => 0,
|
||||||
|
"system" => 0,
|
||||||
|
"private" => 0,
|
||||||
])->order("id DESC");
|
])->order("id DESC");
|
||||||
|
|
||||||
foreach($photos->limit($limit, $offset) as $photo) {
|
foreach($photos->limit($limit, $offset) as $photo) {
|
||||||
|
@ -49,8 +53,10 @@ class Photos
|
||||||
function getUserPhotosCount(User $user)
|
function getUserPhotosCount(User $user)
|
||||||
{
|
{
|
||||||
$photos = $this->photos->where([
|
$photos = $this->photos->where([
|
||||||
"owner" => $user->getId(),
|
"owner" => $user->getId(),
|
||||||
"deleted" => 0
|
"deleted" => 0,
|
||||||
|
"system" => 0,
|
||||||
|
"private" => 0,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return sizeof($photos);
|
return sizeof($photos);
|
||||||
|
|
|
@ -88,7 +88,7 @@ final class CommentPresenter extends OpenVKPresenter
|
||||||
if(!empty($this->postParam("vertical_attachments"))) {
|
if(!empty($this->postParam("vertical_attachments"))) {
|
||||||
$vertical_attachments_array = array_slice(explode(",", $this->postParam("vertical_attachments")), 0, OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["maxAttachments"]);
|
$vertical_attachments_array = array_slice(explode(",", $this->postParam("vertical_attachments")), 0, OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["maxAttachments"]);
|
||||||
if(sizeof($vertical_attachments_array) > 0) {
|
if(sizeof($vertical_attachments_array) > 0) {
|
||||||
$vertical_attachments = parseAttachments($vertical_attachments_array, ['audio', 'note']);
|
$vertical_attachments = parseAttachments($vertical_attachments_array, ['audio', 'note', 'doc']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
179
Web/Presenters/DocumentsPresenter.php
Normal file
179
Web/Presenters/DocumentsPresenter.php
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Presenters;
|
||||||
|
use openvk\Web\Models\Repositories\{Documents, Clubs};
|
||||||
|
use openvk\Web\Models\Entities\Document;
|
||||||
|
use Nette\InvalidStateException as ISE;
|
||||||
|
|
||||||
|
final class DocumentsPresenter extends OpenVKPresenter
|
||||||
|
{
|
||||||
|
protected $presenterName = "documents";
|
||||||
|
protected $silent = true;
|
||||||
|
|
||||||
|
function renderList(?int $owner_id = NULL): void
|
||||||
|
{
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
|
||||||
|
$this->template->_template = "Documents/List.xml";
|
||||||
|
if($owner_id > 0)
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
|
if($owner_id < 0) {
|
||||||
|
$owner = (new Clubs)->get(abs($owner_id));
|
||||||
|
if(!$owner || $owner->isBanned())
|
||||||
|
$this->notFound();
|
||||||
|
else
|
||||||
|
$this->template->group = $owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$owner_id)
|
||||||
|
$owner_id = $this->user->id;
|
||||||
|
|
||||||
|
$current_tab = (int)($this->queryParam("tab") ?? 0);
|
||||||
|
$current_order = (int)($this->queryParam("order") ?? 0);
|
||||||
|
$page = (int)($this->queryParam("p") ?? 1);
|
||||||
|
$order = in_array($current_order, [0,1,2]) ? $current_order : 0;
|
||||||
|
$tab = in_array($current_tab, [0,1,2,3,4,5,6,7,8]) ? $current_tab : 0;
|
||||||
|
|
||||||
|
$api_request = $this->queryParam("picker") == "1";
|
||||||
|
if($api_request && $_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
|
$ctx_type = $this->postParam("context");
|
||||||
|
$docs = NULL;
|
||||||
|
|
||||||
|
switch($ctx_type) {
|
||||||
|
default:
|
||||||
|
case "list":
|
||||||
|
$docs = (new Documents)->getDocumentsByOwner($owner_id, (int)$order, (int)$tab);
|
||||||
|
break;
|
||||||
|
case "search":
|
||||||
|
$ctx_query = $this->postParam("ctx_query");
|
||||||
|
$docs = (new Documents)->find($ctx_query);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->template->docs = $docs->page($page, OPENVK_DEFAULT_PER_PAGE);
|
||||||
|
$this->template->page = $page;
|
||||||
|
$this->template->count = $docs->size();
|
||||||
|
$this->template->pagesCount = ceil($this->template->count / OPENVK_DEFAULT_PER_PAGE);
|
||||||
|
$this->template->_template = "Documents/ApiGetContext.xml";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$docs = (new Documents)->getDocumentsByOwner($owner_id, (int)$order, (int)$tab);
|
||||||
|
$this->template->tabs = (new Documents)->getTypes($owner_id);
|
||||||
|
$this->template->tags = (new Documents)->getTags($owner_id, (int)$tab);
|
||||||
|
$this->template->current_tab = $tab;
|
||||||
|
$this->template->order = $order;
|
||||||
|
$this->template->count = $docs->size();
|
||||||
|
$this->template->docs = iterator_to_array($docs->page($page, OPENVK_DEFAULT_PER_PAGE));
|
||||||
|
$this->template->locale_string = "you_have_x_documents";
|
||||||
|
if($owner_id < 0) {
|
||||||
|
$this->template->locale_string = "group_has_x_documents";
|
||||||
|
} elseif($current_tab != 0) {
|
||||||
|
$this->template->locale_string = "x_documents_in_tab";
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->template->canUpload = $owner_id == $this->user->id || $this->template->group->canBeModifiedBy($this->user->identity);
|
||||||
|
$this->template->paginatorConf = (object) [
|
||||||
|
"count" => $this->template->count,
|
||||||
|
"page" => $page,
|
||||||
|
"amount" => sizeof($this->template->docs),
|
||||||
|
"perPage" => OPENVK_DEFAULT_PER_PAGE,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderListGroup(?int $gid)
|
||||||
|
{
|
||||||
|
$this->renderList($gid);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderUpload()
|
||||||
|
{
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
$group = NULL;
|
||||||
|
$isAjax = $this->postParam("ajax", false) == 1;
|
||||||
|
$ref = $this->postParam("referrer", false) ?? "user";
|
||||||
|
|
||||||
|
if(!is_null($this->queryParam("gid"))) {
|
||||||
|
$gid = (int) $this->queryParam("gid");
|
||||||
|
$group = (new Clubs)->get($gid);
|
||||||
|
if(!$group || $group->isBanned())
|
||||||
|
$this->flashFail("err", tr("forbidden"), tr("not_enough_permissions_comment"), null, $isAjax);
|
||||||
|
|
||||||
|
if(!$group->canUploadDocs($this->user->identity))
|
||||||
|
$this->flashFail("err", tr("forbidden"), tr("not_enough_permissions_comment"), null, $isAjax);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->template->group = $group;
|
||||||
|
if($_SERVER["REQUEST_METHOD"] !== "POST")
|
||||||
|
return;
|
||||||
|
|
||||||
|
$owner = $this->user->id;
|
||||||
|
if($group) {
|
||||||
|
$owner = $group->getRealId();
|
||||||
|
}
|
||||||
|
|
||||||
|
$upload = $_FILES["blob"];
|
||||||
|
$name = $this->postParam("name");
|
||||||
|
$tags = $this->postParam("tags");
|
||||||
|
$folder = $this->postParam("folder");
|
||||||
|
$owner_hidden = ($this->postParam("owner_hidden") ?? "off") === "on";
|
||||||
|
|
||||||
|
try {
|
||||||
|
$document = new Document;
|
||||||
|
$document->setOwner($owner);
|
||||||
|
$document->setName(ovk_proc_strtr($name, 255));
|
||||||
|
$document->setFolder_id($folder);
|
||||||
|
$document->setTags(empty($tags) ? NULL : $tags);
|
||||||
|
$document->setOwner_hidden($owner_hidden);
|
||||||
|
$document->setFile([
|
||||||
|
"tmp_name" => $upload["tmp_name"],
|
||||||
|
"error" => $upload["error"],
|
||||||
|
"name" => $upload["name"],
|
||||||
|
"size" => $upload["size"],
|
||||||
|
"preview_owner" => $this->user->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$document->save();
|
||||||
|
} catch(\TypeError $e) {
|
||||||
|
$this->flashFail("err", tr("forbidden"), $e->getMessage(), null, $isAjax);
|
||||||
|
} catch(ISE $e) {
|
||||||
|
$this->flashFail("err", tr("forbidden"), tr("error_file_preview"), null, $isAjax);
|
||||||
|
} catch(\ValueError $e) {
|
||||||
|
$this->flashFail("err", tr("forbidden"), $e->getMessage(), null, $isAjax);
|
||||||
|
} catch(\ImagickException $e) {
|
||||||
|
$this->flashFail("err", tr("forbidden"), tr("error_file_preview"), null, $isAjax);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$isAjax) {
|
||||||
|
$this->redirect("/docs" . (isset($group) ? $group->getRealId() : ""));
|
||||||
|
} else {
|
||||||
|
$this->returnJson([
|
||||||
|
"success" => true,
|
||||||
|
"redirect" => "/docs" . (isset($group) ? $group->getRealId() : ""),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPage(int $virtual_id, int $real_id): void
|
||||||
|
{
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
|
||||||
|
$access_key = $this->queryParam("key");
|
||||||
|
$doc = (new Documents)->getDocumentById((int)$virtual_id, (int)$real_id, $access_key);
|
||||||
|
if(!$doc || $doc->isDeleted())
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
|
if(!$doc->checkAccessKey($access_key))
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
|
$this->template->doc = $doc;
|
||||||
|
$this->template->type = $doc->getVKAPIType();
|
||||||
|
$this->template->is_image = $doc->isImage();
|
||||||
|
$this->template->tags = $doc->getTags();
|
||||||
|
$this->template->copied = $doc->isCopiedBy($this->user->identity);
|
||||||
|
$this->template->copyImportance = true;
|
||||||
|
$this->template->modifiable = $doc->canBeModifiedBy($this->user->identity);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ namespace openvk\Web\Presenters;
|
||||||
use openvk\Web\Models\Entities\{Club, Photo, Post};
|
use openvk\Web\Models\Entities\{Club, Photo, Post};
|
||||||
use Nette\InvalidStateException;
|
use Nette\InvalidStateException;
|
||||||
use openvk\Web\Models\Entities\Notifications\ClubModeratorNotification;
|
use openvk\Web\Models\Entities\Notifications\ClubModeratorNotification;
|
||||||
use openvk\Web\Models\Repositories\{Clubs, Users, Albums, Managers, Topics, Audios, Posts};
|
use openvk\Web\Models\Repositories\{Clubs, Users, Albums, Managers, Topics, Audios, Posts, Documents};
|
||||||
use Chandler\Security\Authenticator;
|
use Chandler\Security\Authenticator;
|
||||||
|
|
||||||
final class GroupPresenter extends OpenVKPresenter
|
final class GroupPresenter extends OpenVKPresenter
|
||||||
|
@ -27,12 +27,15 @@ final class GroupPresenter extends OpenVKPresenter
|
||||||
if ($club->isBanned()) {
|
if ($club->isBanned()) {
|
||||||
$this->template->_template = "Group/Banned.xml";
|
$this->template->_template = "Group/Banned.xml";
|
||||||
} else {
|
} else {
|
||||||
|
$docs = (new Documents)->getDocumentsByOwner($club->getRealId());
|
||||||
$this->template->albums = (new Albums)->getClubAlbums($club, 1, 3);
|
$this->template->albums = (new Albums)->getClubAlbums($club, 1, 3);
|
||||||
$this->template->albumsCount = (new Albums)->getClubAlbumsCount($club);
|
$this->template->albumsCount = (new Albums)->getClubAlbumsCount($club);
|
||||||
$this->template->topics = (new Topics)->getLastTopics($club, 3);
|
$this->template->topics = (new Topics)->getLastTopics($club, 3);
|
||||||
$this->template->topicsCount = (new Topics)->getClubTopicsCount($club);
|
$this->template->topicsCount = (new Topics)->getClubTopicsCount($club);
|
||||||
$this->template->audios = (new Audios)->getRandomThreeAudiosByEntityId($club->getRealId());
|
$this->template->audios = (new Audios)->getRandomThreeAudiosByEntityId($club->getRealId());
|
||||||
$this->template->audiosCount = (new Audios)->getClubCollectionSize($club);
|
$this->template->audiosCount = (new Audios)->getClubCollectionSize($club);
|
||||||
|
$this->template->docsCount = $docs->size();
|
||||||
|
$this->template->docs = $docs->offsetLimit(0, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!is_null($this->user->identity) && $club->getWallType() == 2) {
|
if(!is_null($this->user->identity) && $club->getWallType() == 2) {
|
||||||
|
|
|
@ -23,7 +23,7 @@ final class ReportPresenter extends OpenVKPresenter
|
||||||
if ($_SERVER["REQUEST_METHOD"] === "POST")
|
if ($_SERVER["REQUEST_METHOD"] === "POST")
|
||||||
$this->assertNoCSRF();
|
$this->assertNoCSRF();
|
||||||
|
|
||||||
$act = in_array($this->queryParam("act"), ["post", "photo", "video", "group", "comment", "note", "app", "user", "audio"]) ? $this->queryParam("act") : NULL;
|
$act = in_array($this->queryParam("act"), ["post", "photo", "video", "group", "comment", "note", "app", "user", "audio", "doc"]) ? $this->queryParam("act") : NULL;
|
||||||
|
|
||||||
if (!$this->queryParam("orig")) {
|
if (!$this->queryParam("orig")) {
|
||||||
$this->template->reports = $this->reports->getReports(0, (int)($this->queryParam("p") ?? 1), $act, $_SERVER["REQUEST_METHOD"] !== "POST");
|
$this->template->reports = $this->reports->getReports(0, (int)($this->queryParam("p") ?? 1), $act, $_SERVER["REQUEST_METHOD"] !== "POST");
|
||||||
|
@ -93,7 +93,7 @@ final class ReportPresenter extends OpenVKPresenter
|
||||||
if ($this->queryParam("type") === "user" && $id === $this->user->id)
|
if ($this->queryParam("type") === "user" && $id === $this->user->id)
|
||||||
exit(json_encode([ "error" => "You can't report yourself" ]));
|
exit(json_encode([ "error" => "You can't report yourself" ]));
|
||||||
|
|
||||||
if(in_array($this->queryParam("type"), ["post", "photo", "video", "group", "comment", "note", "app", "user", "audio"])) {
|
if(in_array($this->queryParam("type"), ["post", "photo", "video", "group", "comment", "note", "app", "user", "audio", "doc"])) {
|
||||||
if (count(iterator_to_array($this->reports->getDuplicates($this->queryParam("type"), $id, NULL, $this->user->id))) <= 0) {
|
if (count(iterator_to_array($this->reports->getDuplicates($this->queryParam("type"), $id, NULL, $this->user->id))) <= 0) {
|
||||||
$report = new Report;
|
$report = new Report;
|
||||||
$report->setUser_id($this->user->id);
|
$report->setUser_id($this->user->id);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Presenters;
|
namespace openvk\Web\Presenters;
|
||||||
use openvk\Web\Models\Entities\{User, Club};
|
use openvk\Web\Models\Entities\{User, Club};
|
||||||
use openvk\Web\Models\Repositories\{Users, Clubs, Posts, Videos, Applications, Audios};
|
use openvk\Web\Models\Repositories\{Users, Clubs, Posts, Videos, Applications, Audios, Documents};
|
||||||
use Chandler\Database\DatabaseConnection;
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
|
||||||
final class SearchPresenter extends OpenVKPresenter
|
final class SearchPresenter extends OpenVKPresenter
|
||||||
|
@ -12,6 +12,7 @@ final class SearchPresenter extends OpenVKPresenter
|
||||||
private $videos;
|
private $videos;
|
||||||
private $apps;
|
private $apps;
|
||||||
private $audios;
|
private $audios;
|
||||||
|
private $documents;
|
||||||
|
|
||||||
function __construct()
|
function __construct()
|
||||||
{
|
{
|
||||||
|
@ -21,6 +22,7 @@ final class SearchPresenter extends OpenVKPresenter
|
||||||
$this->videos = new Videos;
|
$this->videos = new Videos;
|
||||||
$this->apps = new Applications;
|
$this->apps = new Applications;
|
||||||
$this->audios = new Audios;
|
$this->audios = new Audios;
|
||||||
|
$this->documents = new Documents;
|
||||||
|
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
@ -45,7 +47,8 @@ final class SearchPresenter extends OpenVKPresenter
|
||||||
"videos" => "videos",
|
"videos" => "videos",
|
||||||
"audios" => "audios",
|
"audios" => "audios",
|
||||||
"apps" => "apps",
|
"apps" => "apps",
|
||||||
"audios_playlists" => "audios"
|
"audios_playlists" => "audios",
|
||||||
|
"docs" => "documents"
|
||||||
];
|
];
|
||||||
$parameters = [
|
$parameters = [
|
||||||
"ignore_private" => true,
|
"ignore_private" => true,
|
||||||
|
|
|
@ -611,7 +611,8 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
"menu_novajoj" => "news",
|
"menu_novajoj" => "news",
|
||||||
"menu_ligiloj" => "links",
|
"menu_ligiloj" => "links",
|
||||||
"menu_standardo" => "poster",
|
"menu_standardo" => "poster",
|
||||||
"menu_aplikoj" => "apps"
|
"menu_aplikoj" => "apps",
|
||||||
|
"menu_doxc" => "docs",
|
||||||
];
|
];
|
||||||
foreach($settings as $checkbox => $setting)
|
foreach($settings as $checkbox => $setting)
|
||||||
$user->setLeftMenuItemStatus($setting, $this->checkbox($checkbox));
|
$user->setLeftMenuItemStatus($setting, $this->checkbox($checkbox));
|
||||||
|
|
|
@ -294,7 +294,7 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
if(!empty($this->postParam("vertical_attachments"))) {
|
if(!empty($this->postParam("vertical_attachments"))) {
|
||||||
$vertical_attachments_array = array_slice(explode(",", $this->postParam("vertical_attachments")), 0, OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["maxAttachments"]);
|
$vertical_attachments_array = array_slice(explode(",", $this->postParam("vertical_attachments")), 0, OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["maxAttachments"]);
|
||||||
if(sizeof($vertical_attachments_array) > 0) {
|
if(sizeof($vertical_attachments_array) > 0) {
|
||||||
$vertical_attachments = parseAttachments($vertical_attachments_array, ['audio', 'note']);
|
$vertical_attachments = parseAttachments($vertical_attachments_array, ['audio', 'note', 'doc']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -142,6 +142,7 @@
|
||||||
<option n:attr="selected => $_REQUEST['section'] == 'apps'" value="apps">{_s_by_apps}</option>
|
<option n:attr="selected => $_REQUEST['section'] == 'apps'" value="apps">{_s_by_apps}</option>
|
||||||
<option n:attr="selected => $_REQUEST['section'] == 'audios'" value="audios">{_s_by_audios}</option>
|
<option n:attr="selected => $_REQUEST['section'] == 'audios'" value="audios">{_s_by_audios}</option>
|
||||||
<option n:attr="selected => $_REQUEST['section'] == 'audios_playlists'" value="audios_playlists">{_s_by_audios_playlists}</option>
|
<option n:attr="selected => $_REQUEST['section'] == 'audios_playlists'" value="audios_playlists">{_s_by_audios_playlists}</option>
|
||||||
|
<option n:attr="selected => $_REQUEST['section'] == 'docs'" value="docs">{_s_by_documents}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<button class="search_box_button">
|
<button class="search_box_button">
|
||||||
|
@ -195,9 +196,14 @@
|
||||||
(<b>{$thisUser->getNotificationsCount()}</b>)
|
(<b>{$thisUser->getNotificationsCount()}</b>)
|
||||||
</object>
|
</object>
|
||||||
</a>
|
</a>
|
||||||
<a n:if="$thisUser->getLeftMenuItemStatus('apps')" href="/apps?act=installed" class="link">{_my_apps}</a>
|
|
||||||
<a href="/settings" class="link">{_my_settings}</a>
|
<a href="/settings" class="link">{_my_settings}</a>
|
||||||
|
|
||||||
|
{if $thisUser->getLeftMenuItemStatus('docs') || $thisUser->getLeftMenuItemStatus('apps')}
|
||||||
|
<div class="menu_divider"></div>
|
||||||
|
<a n:if="$thisUser->getLeftMenuItemStatus('apps')" href="/apps?act=installed" class="link">{_apps}</a>
|
||||||
|
<a n:if="$thisUser->getLeftMenuItemStatus('docs')" href="/docs" class="link">{_my_documents}</a>
|
||||||
|
{/if}
|
||||||
|
|
||||||
{var $canAccessAdminPanel = $thisUser->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL)}
|
{var $canAccessAdminPanel = $thisUser->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL)}
|
||||||
{var $canAccessHelpdesk = $thisUser->getChandlerUser()->can("write")->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)}
|
{var $canAccessHelpdesk = $thisUser->getChandlerUser()->can("write")->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)}
|
||||||
{var $menuLinksAvaiable = sizeof(OPENVK_ROOT_CONF['openvk']['preferences']['menu']['links']) > 0 && $thisUser->getLeftMenuItemStatus('links')}
|
{var $menuLinksAvaiable = sizeof(OPENVK_ROOT_CONF['openvk']['preferences']['menu']['links']) > 0 && $thisUser->getLeftMenuItemStatus('links')}
|
||||||
|
@ -397,6 +403,7 @@
|
||||||
{script "js/scroll.js"}
|
{script "js/scroll.js"}
|
||||||
{script "js/player.js"}
|
{script "js/player.js"}
|
||||||
{script "js/al_wall.js"}
|
{script "js/al_wall.js"}
|
||||||
|
{script "js/al_docs.js"}
|
||||||
{script "js/al_api.js"}
|
{script "js/al_api.js"}
|
||||||
{script "js/al_mentions.js"}
|
{script "js/al_mentions.js"}
|
||||||
{script "js/al_polls.js"}
|
{script "js/al_polls.js"}
|
||||||
|
@ -461,6 +468,8 @@
|
||||||
"current_id": {$thisUser ? $thisUser->getId() : 0},
|
"current_id": {$thisUser ? $thisUser->getId() : 0},
|
||||||
"disable_ajax": {$disable_ajax ? $disable_ajax : 0},
|
"disable_ajax": {$disable_ajax ? $disable_ajax : 0},
|
||||||
"max_add_fields": {ovkGetQuirk("users.max-fields")},
|
"max_add_fields": {ovkGetQuirk("users.max-fields")},
|
||||||
|
"docs_max": {\OPENVK_ROOT_CONF["openvk"]["preferences"]["docs"]["maxSize"]},
|
||||||
|
"docs_allowed": {\OPENVK_ROOT_CONF["openvk"]["preferences"]["docs"]["allowedFormats"]},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
9
Web/Presenters/templates/Documents/ApiGetContext.xml
Normal file
9
Web/Presenters/templates/Documents/ApiGetContext.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<input type="hidden" name="count" value="{$count}">
|
||||||
|
<input type="hidden" name="pagesCount" value="{$pagesCount}">
|
||||||
|
<input type="hidden" name="page" value="{$page}">
|
||||||
|
|
||||||
|
{foreach $docs as $doc}
|
||||||
|
<div class='display_flex_row _content' data-attachmentdata="{$doc->getVirtualId()}_{$doc->getId()}_{$doc->getAccessKey()}" data-name='{$doc->getName()}'>
|
||||||
|
{include "components/doc.xml", doc => $doc, hideButtons => true}
|
||||||
|
</div>
|
||||||
|
{/foreach}
|
80
Web/Presenters/templates/Documents/List.xml
Normal file
80
Web/Presenters/templates/Documents/List.xml
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
{extends "../@layout.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{if !isset($group)}
|
||||||
|
{_my_documents_objectively}
|
||||||
|
{else}
|
||||||
|
{_documents_of_group}
|
||||||
|
{/if}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block header}
|
||||||
|
{if !isset($group)}
|
||||||
|
{_my_documents}
|
||||||
|
{else}
|
||||||
|
<a href="{$group->getURL()}">{$group->getCanonicalName()}</a> »
|
||||||
|
{_my_documents}
|
||||||
|
{/if}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
{var $is_gallery = $current_tab == 3 || $current_tab == 4}
|
||||||
|
<div id="docs_page_wrapper">
|
||||||
|
<div class="docs_page_search">
|
||||||
|
<form action="/search" method="get">
|
||||||
|
<input type="hidden" name="section" value="docs">
|
||||||
|
<input type="search" name="q" class="input_with_search_icon" placeholder="{_search_by_documents}">
|
||||||
|
</form>
|
||||||
|
<input n:if="$canUpload" id="upload_entry_point" class="button" type="button" value="{_upload_button}" {if isset($group)}data-gid="{$group->getId()}"{/if}>
|
||||||
|
</div>
|
||||||
|
<div n:if="sizeof($tabs) > 1" class="docs_page_tabs">
|
||||||
|
<div class="mb_tabs">
|
||||||
|
<div class="mb_tab" n:attr="id => $current_tab == 0 ? active">
|
||||||
|
<a href="?tab=0">{_document_type_0}</a>
|
||||||
|
</div>
|
||||||
|
<div n:foreach="$tabs as $tab" class="mb_tab" n:attr="id => $tab['type'] == $current_tab ? active">
|
||||||
|
<a href="?tab={$tab['type']}">
|
||||||
|
{$tab["name"]}
|
||||||
|
<span n:if="$tab['count'] > 1" class="special_counter">{$tab["count"]}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div n:class="docs_page_content, $is_gallery ? docs_page_gallery">
|
||||||
|
<div class="summaryBar display_flex_row display_flex_space_between">
|
||||||
|
<div class="summary">{tr($locale_string, $count)}.</div>
|
||||||
|
|
||||||
|
<select n:if="$count > 3" name="docs_sort">
|
||||||
|
<option n:attr="selected => $order == 0" value="0">{_documents_sort_add}</option>
|
||||||
|
<option n:attr="selected => $order == 1" value="1">{_documents_sort_alphabet}</option>
|
||||||
|
<option n:attr="selected => $order == 2" value="2">{_documents_sort_size}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div n:attr="id => !$is_gallery && sizeof($tags) > 0 ? search_page">
|
||||||
|
<div n:class="container_white, scroll_container, !$is_gallery && sizeof($tags) > 0 ? page_wrap_content_main">
|
||||||
|
{if $count > 0}
|
||||||
|
{foreach $docs as $doc}
|
||||||
|
{if $is_gallery}
|
||||||
|
{include "components/image.xml", doc => $doc, scroll_context => true, club => isset($group) ? $group : NULL}
|
||||||
|
{else}
|
||||||
|
{include "components/doc.xml", doc => $doc, scroll_context => true, club => isset($group) ? $group : NULL}
|
||||||
|
{/if}
|
||||||
|
{/foreach}
|
||||||
|
{else}
|
||||||
|
{include "../components/error.xml", description => tr("there_is_no_documents_alright")}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div n:if="!$is_gallery && sizeof($tags) > 0" class='page_wrap_content_options verticalGrayTabsWrapper'>
|
||||||
|
<div class="page_wrap_content_options_list verticalGrayTabs with_padding">
|
||||||
|
<a id="used">{_documents_all}</a>
|
||||||
|
{foreach $tags as $tag}
|
||||||
|
<a href="/search?section=docs&tags={urlencode($tag)}">{$tag}</a>
|
||||||
|
{/foreach}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{include "../components/paginator.xml", conf => $paginatorConf}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/block}
|
74
Web/Presenters/templates/Documents/Page.xml
Normal file
74
Web/Presenters/templates/Documents/Page.xml
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
{extends "../@layout.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{_document} "{ovk_proc_strtr($doc->getName(), 20)}"
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block header}
|
||||||
|
{$doc->getName()}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<style>
|
||||||
|
.sidebar, .page_header, .page_footer {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page_body {
|
||||||
|
margin-top: -45px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class='media-page-wrapper photo-page-wrapper'>
|
||||||
|
<div class='photo-page-wrapper-photo'>
|
||||||
|
{if $is_image}
|
||||||
|
<img alt="doc image" src="{$doc->getURL()}" />
|
||||||
|
{else}
|
||||||
|
<a href="{$doc->getURL()}" download="{downloadable_name($doc->getName())}">
|
||||||
|
<input class="button" type="button" value="{_download_file}">
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='ovk-photo-details'>
|
||||||
|
<div class='media-page-wrapper-description'>
|
||||||
|
<p n:if='sizeof($tags) > 0'>
|
||||||
|
{foreach $tags as $tag}
|
||||||
|
<a href="/search?section=docs&tags={urlencode($tag)}">
|
||||||
|
{$tag}{if $tag != $tags[sizeof($tags) - 1]},{/if}
|
||||||
|
</a>
|
||||||
|
{/foreach}
|
||||||
|
</p>
|
||||||
|
<div class='upload_time'>
|
||||||
|
{_info_upload_date}: {$doc->getPublicationTime()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
<div class="media-page-wrapper-details">
|
||||||
|
<div class='media-page-wrapper-comments'></div>
|
||||||
|
<div class='media-page-wrapper-actions docMainItem' data-context="page" data-id="{$doc->getPrettiestId()}">
|
||||||
|
{if !$doc->isOwnerHidden()}
|
||||||
|
{var $owner = $doc->getOwner()}
|
||||||
|
<a href="{$owner->getURL()}" class='media-page-author-block'>
|
||||||
|
<img class='cCompactAvatars' src="{$owner->getAvatarURL('miniscule')}">
|
||||||
|
|
||||||
|
<div class='media-page-author-block-name'>
|
||||||
|
<b>{$owner->getCanonicalName()}</b>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{if isset($thisUser)}
|
||||||
|
<a n:if="$modifiable" class="profile_link" style="display:block;width:96%;" id="edit_icon">{_edit}</a>
|
||||||
|
<a n:if="$modifiable" class="profile_link" style="display:block;width:96%;" id="report_icon">{_report}</a>
|
||||||
|
<a n:if="!$copied || $copied && $copyImportance" class="profile_link" style="display:block;width:96%;" id="add_icon">{_add}</a>
|
||||||
|
<a n:if="$copied && !$copyImportance" class="profile_link" style="display:block;width:96%;" id="remove_icon">{_remove}</a>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/block}
|
68
Web/Presenters/templates/Documents/Upload.xml
Normal file
68
Web/Presenters/templates/Documents/Upload.xml
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
{extends "../@layout.xml"}
|
||||||
|
|
||||||
|
{block title}{_document_uploading_in_general}{/block}
|
||||||
|
|
||||||
|
{block header}
|
||||||
|
{if !is_null($group)}
|
||||||
|
<a href="{$group->getURL()}">{$group->getCanonicalName()}</a>
|
||||||
|
»
|
||||||
|
<a href="/docs-{$group->getId()}">{_documents}</a>
|
||||||
|
{else}
|
||||||
|
<a href="{$thisUser->getURL()}">{$thisUser->getCanonicalName()}</a>
|
||||||
|
»
|
||||||
|
<a href="/docs">{_documents}</a>
|
||||||
|
{/if}
|
||||||
|
»
|
||||||
|
Non-AJAX Document upload
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<form method="post" enctype="multipart/form-data">
|
||||||
|
<table width="600">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><span class="nobold">{_name}:</span></td>
|
||||||
|
<td><input type="text" name="name" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><span class="nobold">{_tags}:</span></td>
|
||||||
|
<td><textarea name="tags"></textarea></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><span class="nobold">{_accessbility}:</span></td>
|
||||||
|
<td>
|
||||||
|
<select name="folder">
|
||||||
|
<option value="0">Private file</option>
|
||||||
|
<option value="4">Public file</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="owner_hidden">
|
||||||
|
Owner is hidden
|
||||||
|
</label>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><span class="nobold">{_file}:</span></td>
|
||||||
|
<td>
|
||||||
|
<label class="button" style="">{_browse}
|
||||||
|
<input type="file" id="blob" name="blob" style="display: none;" onchange="filename.innerHTML=blob.files[0].name" />
|
||||||
|
</label>
|
||||||
|
<div id="filename" style="margin-top: 10px;"></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td>
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input type="submit" class="button" name="submit" value="{_upload_button}" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
{/block}
|
37
Web/Presenters/templates/Documents/components/doc.xml
Normal file
37
Web/Presenters/templates/Documents/components/doc.xml
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
{var $preview = $doc->hasPreview() ? $doc->getPreview() : NULL}
|
||||||
|
{var $tags = $doc->getTags()}
|
||||||
|
{var $copied = !isset($club) ? $doc->isCopiedBy($thisUser) : $doc->isCopiedBy($club)}
|
||||||
|
{var $modifiable = $doc->canBeModifiedBy($thisUser)}
|
||||||
|
|
||||||
|
<div n:class="docMainItem, docListViewItem, $scroll_context ? scroll_node" data-id="{$doc->getPrettiestId()}">
|
||||||
|
<a class="viewerOpener" href="/doc{$doc->getPrettyId()}?key={$doc->getAccessKey()}">
|
||||||
|
{if $preview}
|
||||||
|
<img class="doc_icon" alt="document_preview" src="{$preview->getURLBySizeId('tiny')}">
|
||||||
|
{else}
|
||||||
|
<div class="doc_icon no_image">
|
||||||
|
<span>{$doc->getFileExtension()}</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</a>
|
||||||
|
<div class="doc_content noOverflow">
|
||||||
|
<a class="viewerOpener noOverflow" href="/doc{$doc->getPrettyId()}?key={$doc->getAccessKey()}"><b class="noOverflow doc_name">{$doc->getName()}</b></a>
|
||||||
|
|
||||||
|
<div class="doc_content_info">
|
||||||
|
<span>{$doc->getPublicationTime()}</span>,
|
||||||
|
<span>{readable_filesize($doc->getFilesize())}</span>{if sizeof($tags) > 0} -
|
||||||
|
<span n:if="!$noTags" class="doc_tags" style="text-wrap: wrap;">
|
||||||
|
{foreach $tags as $tag}
|
||||||
|
<a href="/search?section=docs&tags={urlencode($tag)}">
|
||||||
|
{$tag}{if $tag != $tags[sizeof($tags) - 1]},{/if}
|
||||||
|
</a>
|
||||||
|
{/foreach}
|
||||||
|
</span>{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="doc_volume" n:if="!$hideButtons && $thisUser">
|
||||||
|
<div n:if="!$modifiable" id="report_icon"></div>
|
||||||
|
<div n:if="$modifiable" id="edit_icon"></div>
|
||||||
|
<div n:if="!$copied || $copied && $copyImportance" id="add_icon"></div>
|
||||||
|
<div n:if="$copied && !$copyImportance" id="remove_icon"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
25
Web/Presenters/templates/Documents/components/image.xml
Normal file
25
Web/Presenters/templates/Documents/components/image.xml
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{var $preview = $doc->hasPreview() ? $doc->getPreview() : NULL}
|
||||||
|
{var $copied = !isset($club) ? $doc->isCopiedBy($thisUser) : $doc->isCopiedBy($club)}
|
||||||
|
{var $modifiable = $doc->canBeModifiedBy($thisUser)}
|
||||||
|
|
||||||
|
<a href="/doc{$doc->getPrettyId()}?key={$doc->getAccessKey()}" n:class="docMainItem, viewerOpener, docGalleryItem, $scroll_context ? scroll_node, $embed ? embeddable" data-id="{$doc->getPrettiestId()}">
|
||||||
|
<img class="docGalleryItem_main_preview" loading="lazy" src="{$preview->getURLBySizeId('medium')}" alt="gallery photo">
|
||||||
|
{if $embed}
|
||||||
|
<div class="play-button">
|
||||||
|
<div class="play-button-ico"></div>
|
||||||
|
</div>
|
||||||
|
<img class="docGalleryItem_gif_preview" loading="lazy" src="{$doc->getURL()}" alt="gif photo view">
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="doc_top_panel doc_shown_by_hover" n:if="$thisUser">
|
||||||
|
<div class="doc_volume_action" n:if="!$modifiable" id="report_icon"></div>
|
||||||
|
<div class="doc_volume_action" n:if="$modifiable" id="edit_icon"></div>
|
||||||
|
<div class="doc_volume_action" n:if="!$copied || $copied && $copyImportance" id="add_icon"></div>
|
||||||
|
<div class="doc_volume_action" n:if="$copied && !$copyImportance" id="remove_icon"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div n:class="doc_bottom_panel, doc_shown_by_hover, doc_content, $showInfo ? info_shown">
|
||||||
|
<span class="doc_bottom_panel_name noOverflow doc_name">{$doc->getName()}</span>
|
||||||
|
<span class="doc_bottom_panel_size">{readable_filesize($doc->getFilesize())}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
|
@ -43,7 +43,7 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div n:if="$club->getFollowersCount() > 0">
|
<div n:if="$thisUser && $club->getFollowersCount() > 0">
|
||||||
{var $followersCount = $club->getFollowersCount()}
|
{var $followersCount = $club->getFollowersCount()}
|
||||||
|
|
||||||
<div class="content_title_expanded" onclick="hidePanel(this, {$followersCount});">
|
<div class="content_title_expanded" onclick="hidePanel(this, {$followersCount});">
|
||||||
|
@ -91,7 +91,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div n:if="$thisUser">
|
||||||
<div class="content_title_expanded" onclick="hidePanel(this, {$audiosCount});">
|
<div class="content_title_expanded" onclick="hidePanel(this, {$audiosCount});">
|
||||||
{_audios}
|
{_audios}
|
||||||
</div>
|
</div>
|
||||||
|
@ -291,6 +291,24 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div n:if="($thisUser && $docsCount > 0 || ($thisUser && $club->canBeModifiedBy($thisUser)))">
|
||||||
|
<div class="content_title_expanded" onclick="hidePanel(this, {$topicsCount});">
|
||||||
|
{_documents}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="content_subtitle">
|
||||||
|
{tr("documents", $docsCount)}
|
||||||
|
<div style="float: right;">
|
||||||
|
<a href="/docs{$club->getRealId()}">{_all_title}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{foreach $docs as $doc}
|
||||||
|
{include "../Documents/components/doc.xml", doc => $doc, hideButtons => true, noTags => true}
|
||||||
|
{/foreach}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/block}
|
{/block}
|
||||||
|
|
|
@ -49,6 +49,9 @@
|
||||||
<div n:attr="id => ($mode === 'audio' ? 'activetabs' : 'ki')" class="tab" mode="audio">
|
<div n:attr="id => ($mode === 'audio' ? 'activetabs' : 'ki')" class="tab" mode="audio">
|
||||||
<a n:attr="id => ($mode === 'audio' ? 'act_tab_a' : 'ki')">{_audios}</a>
|
<a n:attr="id => ($mode === 'audio' ? 'act_tab_a' : 'ki')">{_audios}</a>
|
||||||
</div>
|
</div>
|
||||||
|
<div n:attr="id => ($mode === 'docs' ? 'activetabs' : 'ki')" class="tab" mode="doc">
|
||||||
|
<a n:attr="id => ($mode === 'docs' ? 'act_tab_a' : 'ki')">{_documents}</a>
|
||||||
|
</div>
|
||||||
</center>
|
</center>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
{/if}
|
{/if}
|
||||||
{elseif $type == "audio"}
|
{elseif $type == "audio"}
|
||||||
{include "../Audio/player.xml", audio => $object}
|
{include "../Audio/player.xml", audio => $object}
|
||||||
|
{elseif $type == "doc"}
|
||||||
|
{include "../Documents/components/doc.xml", doc => $object}
|
||||||
{else}
|
{else}
|
||||||
{include "../components/error.xml", description => tr("version_incompatibility")}
|
{include "../components/error.xml", description => tr("version_incompatibility")}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -284,6 +284,18 @@
|
||||||
highlightText({$query}, '.page_wrap_content_main', [".playlistName", ".playlistDesc"])
|
highlightText({$query}, '.page_wrap_content_main', [".playlistName", ".playlistDesc"])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__scrollHook()
|
||||||
|
</script>
|
||||||
|
{elseif $section === 'docs'}
|
||||||
|
<div class='scroll_node search_content' n:foreach="$data as $dat">
|
||||||
|
{include "../Documents/components/doc.xml", doc => $dat, copyImportance => true}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script n:if="$count > 0 && !empty($query)">
|
||||||
|
function __scrollHook(page) {
|
||||||
|
highlightText({$query}, '.page_wrap_content_main', [".doc_content .noOverflow"])
|
||||||
|
}
|
||||||
|
|
||||||
__scrollHook()
|
__scrollHook()
|
||||||
</script>
|
</script>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -300,6 +312,7 @@
|
||||||
<a n:attr="id => $section === 'apps' ? 'used'" href="/search?section=apps&q={urlencode($query)}"> {_s_apps}</a>
|
<a n:attr="id => $section === 'apps' ? 'used'" href="/search?section=apps&q={urlencode($query)}"> {_s_apps}</a>
|
||||||
<a n:attr="id => $section === 'audios' ? 'used'" href="/search?section=audios&q={urlencode($query)}"> {_s_audios}</a>
|
<a n:attr="id => $section === 'audios' ? 'used'" href="/search?section=audios&q={urlencode($query)}"> {_s_audios}</a>
|
||||||
<a n:attr="id => $section === 'audios_playlists' ? 'used'" href="/search?section=audios_playlists&q={urlencode($query)}">{_s_audios_playlists}</a>
|
<a n:attr="id => $section === 'audios_playlists' ? 'used'" href="/search?section=audios_playlists&q={urlencode($query)}">{_s_audios_playlists}</a>
|
||||||
|
<a n:attr="id => $section === 'docs' ? 'used'" href="/search?section=docs&q={urlencode($query)}">{_s_documents}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class='page_search_options'>
|
<div class='page_search_options'>
|
||||||
|
@ -415,6 +428,19 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div n:if="$section == 'docs'" class="search_option">
|
||||||
|
<div class="search_option_name">
|
||||||
|
<div class='search_option_name_ico'></div>
|
||||||
|
{_s_type}
|
||||||
|
</div>
|
||||||
|
<div class="search_option_content">
|
||||||
|
<select name="type" form="search_form" data-default='0'>
|
||||||
|
<option n:foreach="range(0, 8) as $i" value="{$i}" n:attr="selected => $_REQUEST['type'] == $i">
|
||||||
|
{tr("document_type_".$i)}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div n:if="$section == 'audios'" class="search_option">
|
<div n:if="$section == 'audios'" class="search_option">
|
||||||
<div class="search_option_name">
|
<div class="search_option_name">
|
||||||
<div class='search_option_name_ico'></div>
|
<div class='search_option_name_ico'></div>
|
||||||
|
|
|
@ -684,7 +684,19 @@
|
||||||
<td>
|
<td>
|
||||||
<span class="nobold">{_my_apps}</span>
|
<span class="nobold">{_my_apps}</span>
|
||||||
</td>
|
</td>
|
||||||
</tr><tr n:if="sizeof(OPENVK_ROOT_CONF['openvk']['preferences']['menu']['links']) > 0">
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="120" valign="top" align="right" align="right">
|
||||||
|
<input
|
||||||
|
n:attr="checked => $user->getLeftMenuItemStatus('docs')"
|
||||||
|
type="checkbox"
|
||||||
|
name="menu_doxc" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="nobold">{_my_documents}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr n:if="sizeof(OPENVK_ROOT_CONF['openvk']['preferences']['menu']['links']) > 0">
|
||||||
<td width="120" valign="top" align="right" align="right">
|
<td width="120" valign="top" align="right" align="right">
|
||||||
<input
|
<input
|
||||||
n:attr="checked => $user->getLeftMenuItemStatus('links')"
|
n:attr="checked => $user->getLeftMenuItemStatus('links')"
|
||||||
|
|
|
@ -65,6 +65,24 @@
|
||||||
<div style="width:100%;" data-att_type='audio' data-att_id="{$attachment->getPrettyId()}">
|
<div style="width:100%;" data-att_type='audio' data-att_id="{$attachment->getPrettyId()}">
|
||||||
{include "../Audio/player.xml", audio => $attachment}
|
{include "../Audio/player.xml", audio => $attachment}
|
||||||
</div>
|
</div>
|
||||||
|
{elseif $attachment instanceof \openvk\Web\Models\Entities\Document}
|
||||||
|
<div style="width:100%;">
|
||||||
|
<div style="display:none" data-att_type="doc" data-att_id="{$attachment->getPrettiestId()}">
|
||||||
|
<div class="docMainItem attachment_doc attachment_note">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 10"><polygon points="0 0 0 10 8 10 8 4 4 4 4 0 0 0"/><polygon points="5 0 5 3 8 3 5 0"/></svg>
|
||||||
|
|
||||||
|
<div class='attachment_note_content'>
|
||||||
|
<span class="attachment_note_text">{_document}</span>
|
||||||
|
<span class="attachment_note_name"><a href="/doc{$attachment->getPrettyId()}">{ovk_proc_strtr($attachment->getName(), 40)}</a></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{if $attachment->isImage()}
|
||||||
|
{include "../Documents/components/image.xml", doc => $attachment, copyImportance => true, showInfo => true, embed => $attachment->isGif()}
|
||||||
|
{else}
|
||||||
|
{include "../Documents/components/doc.xml", doc => $attachment, copyImportance => true, noTags => true}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
{else}
|
{else}
|
||||||
<span style="color:red;">{_version_incompatibility}</span>
|
<span style="color:red;">{_version_incompatibility}</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -86,6 +86,10 @@
|
||||||
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/audio-ac3.png" />
|
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/audio-ac3.png" />
|
||||||
{_audio}
|
{_audio}
|
||||||
</a>
|
</a>
|
||||||
|
<a n:if="$docs ?? true" id="__documentAttachment" {if !is_null($club ?? NULL) && $club->canBeModifiedBy($thisUser)}data-club="{$club->getRealId()}"{/if}>
|
||||||
|
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/application-octet-stream.png" />
|
||||||
|
{_document}
|
||||||
|
</a>
|
||||||
<a n:if="$notes ?? false" id="__notesAttachment">
|
<a n:if="$notes ?? false" id="__notesAttachment">
|
||||||
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/application-x-srt.png" />
|
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/application-x-srt.png" />
|
||||||
{_note}
|
{_note}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<div class="insertThere" id="postz"></div>
|
<div class="insertThere" id="postz"></div>
|
||||||
<div id="underHeader">
|
<div id="underHeader">
|
||||||
<div n:if="$canPost" class="content_subtitle">
|
<div n:if="$canPost" class="content_subtitle">
|
||||||
{include "../components/textArea.xml", route => "/wall$owner/makePost", graffiti => true, polls => true, notes => true, hasSource => true, geo => true}
|
{include "../components/textArea.xml", route => "/wall$owner/makePost", graffiti => true, polls => true, notes => true, hasSource => true, geo => true, docs => true}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content scroll_container">
|
<div class="content scroll_container">
|
||||||
|
|
|
@ -27,6 +27,7 @@ services:
|
||||||
- openvk\Web\Presenters\VKAPIPresenter
|
- openvk\Web\Presenters\VKAPIPresenter
|
||||||
- openvk\Web\Presenters\PollPresenter
|
- openvk\Web\Presenters\PollPresenter
|
||||||
- openvk\Web\Presenters\BannedLinkPresenter
|
- openvk\Web\Presenters\BannedLinkPresenter
|
||||||
|
- openvk\Web\Presenters\DocumentsPresenter
|
||||||
- openvk\Web\Models\Repositories\Users
|
- openvk\Web\Models\Repositories\Users
|
||||||
- openvk\Web\Models\Repositories\Posts
|
- openvk\Web\Models\Repositories\Posts
|
||||||
- openvk\Web\Models\Repositories\Polls
|
- openvk\Web\Models\Repositories\Polls
|
||||||
|
@ -52,5 +53,6 @@ services:
|
||||||
- openvk\Web\Models\Repositories\Aliases
|
- openvk\Web\Models\Repositories\Aliases
|
||||||
- openvk\Web\Models\Repositories\BannedLinks
|
- openvk\Web\Models\Repositories\BannedLinks
|
||||||
- openvk\Web\Models\Repositories\ChandlerGroups
|
- openvk\Web\Models\Repositories\ChandlerGroups
|
||||||
|
- openvk\Web\Models\Repositories\Documents
|
||||||
- openvk\Web\Presenters\MaintenancePresenter
|
- openvk\Web\Presenters\MaintenancePresenter
|
||||||
- openvk\Web\Presenters\NoSpamPresenter
|
- openvk\Web\Presenters\NoSpamPresenter
|
||||||
|
|
|
@ -311,6 +311,14 @@ routes:
|
||||||
handler: "Poll->view"
|
handler: "Poll->view"
|
||||||
- url: "/poll{num}/voters"
|
- url: "/poll{num}/voters"
|
||||||
handler: "Poll->voters"
|
handler: "Poll->voters"
|
||||||
|
- url: "/docs"
|
||||||
|
handler: "Documents->list"
|
||||||
|
- url: "/docs{num}"
|
||||||
|
handler: "Documents->listGroup"
|
||||||
|
- url: "/docs/upload"
|
||||||
|
handler: "Documents->upload"
|
||||||
|
- url: "/doc{num}_{num}"
|
||||||
|
handler: "Documents->page"
|
||||||
- url: "/admin"
|
- url: "/admin"
|
||||||
handler: "Admin->index"
|
handler: "Admin->index"
|
||||||
- url: "/admin/users"
|
- url: "/admin/users"
|
||||||
|
|
|
@ -55,6 +55,10 @@ h1 {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.display_flex_space_between {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
.layout {
|
.layout {
|
||||||
width: 791px;
|
width: 791px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
@ -2248,6 +2252,12 @@ table td[width="120"] {
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mb_tab .special_counter {
|
||||||
|
color: #595959;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0px 1px;
|
||||||
|
}
|
||||||
|
|
||||||
.mb_tab:hover {
|
.mb_tab:hover {
|
||||||
background: #e2e0e0;
|
background: #e2e0e0;
|
||||||
}
|
}
|
||||||
|
@ -2265,6 +2275,10 @@ table td[width="120"] {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mb_tab#active .special_counter {
|
||||||
|
color: #bdbdbd;
|
||||||
|
}
|
||||||
|
|
||||||
.border-block {
|
.border-block {
|
||||||
box-shadow: inset 0 0 0 1px #b6bfca, inset 0 0 0 10px #d8dfe7;
|
box-shadow: inset 0 0 0 1px #b6bfca, inset 0 0 0 10px #d8dfe7;
|
||||||
width: 300px;
|
width: 300px;
|
||||||
|
@ -2560,7 +2574,7 @@ a.poll-retract-vote {
|
||||||
min-height: 63px;
|
min-height: 63px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-horizontal .upload-item .play-button, .compact_video .play-button {
|
.post-horizontal .upload-item .play-button, .compact_video .play-button, .docGalleryItem .play-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
|
@ -2571,7 +2585,7 @@ a.poll-retract-vote {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-horizontal .upload-item .play-button .play-button-ico, .compact_video .play-button .play-button-ico {
|
.post-horizontal .upload-item .play-button .play-button-ico, .compact_video .play-button .play-button-ico, .docGalleryItem .play-button .play-button-ico {
|
||||||
background: url(/assets/packages/static/openvk/img/wall.png) no-repeat 1px 0;
|
background: url(/assets/packages/static/openvk/img/wall.png) no-repeat 1px 0;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 15px;
|
height: 15px;
|
||||||
|
@ -2694,6 +2708,10 @@ a.poll-retract-vote {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ovk-photo-view .photo_viewer_wrapper.photo_viewer_wrapper_scrollable {
|
||||||
|
height: unset;
|
||||||
|
}
|
||||||
|
|
||||||
.ovk-photo-view #ovk-photo-img {
|
.ovk-photo-view #ovk-photo-img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
max-height: 80vh;
|
max-height: 80vh;
|
||||||
|
@ -2823,7 +2841,7 @@ a.poll-retract-vote {
|
||||||
height: 20px;
|
height: 20px;
|
||||||
background: url('/assets/packages/static/openvk/img/search_icon.png') no-repeat 3px 4px;
|
background: url('/assets/packages/static/openvk/img/search_icon.png') no-repeat 3px 4px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
padding-left: 18px;
|
padding-left: 18px !important;
|
||||||
width: 120px;
|
width: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3964,3 +3982,284 @@ hr {
|
||||||
margin-bottom: -2px;
|
margin-bottom: -2px;
|
||||||
fill: #7d7d7d;
|
fill: #7d7d7d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Documents */
|
||||||
|
|
||||||
|
#docs_page_wrapper {
|
||||||
|
margin-left: -10px;
|
||||||
|
margin-top: -10px;
|
||||||
|
margin-bottom: -10px;
|
||||||
|
display: block;
|
||||||
|
width: 102.8%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#docs_page_wrapper .docs_page_search {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 10px 10px;
|
||||||
|
border-bottom: 1px solid #CCCCCC;
|
||||||
|
background: #F0F0F0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#docs_page_wrapper .docs_page_search input[type="search"], .attachment_selector .attachment_search input {
|
||||||
|
height: 23px;
|
||||||
|
background: url('/assets/packages/static/openvk/img/search_icon.png') no-repeat 3px 5px;
|
||||||
|
background-color: #fff;
|
||||||
|
padding-left: 18px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#docs_page_wrapper .docs_page_search input[type="search"], #docs_page_wrapper .docs_page_search form {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#docs_page_wrapper .container_white {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#docs_page_wrapper .docs_page_content.docs_page_gallery .scroll_container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
|
gap: 10px;
|
||||||
|
padding: 10px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docGalleryItem {
|
||||||
|
height: 200px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
/*width: 200px;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachments .docGalleryItem.embeddable {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
/*background: black;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachments .docGalleryItem.embeddable .docGalleryItem_gif_preview {
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachments .docGalleryItem.embeddable.playing .play-button, .attachments .docGalleryItem.embeddable.playing .doc_bottom_panel, .docGalleryItem .docGalleryItem_gif_preview, .attachments .docGalleryItem.embeddable.playing .docGalleryItem_main_preview {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachments .docGalleryItem.embeddable.playing .docGalleryItem_gif_preview {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docGalleryItem img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docGalleryItem .doc_bottom_panel {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0px;
|
||||||
|
background: rgba(1, 1, 1, 0.7);
|
||||||
|
padding: 6px 6px;
|
||||||
|
width: calc(100% - 12px);
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 0fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docGalleryItem .doc_shown_by_hover {
|
||||||
|
transition: all 100ms ease-in;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docGalleryItem:hover .doc_shown_by_hover, .doc_shown_by_hover.info_shown {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docGalleryItem .doc_bottom_panel span {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docGalleryItem .doc_bottom_panel .doc_bottom_panel_size {
|
||||||
|
max-width: 49px;
|
||||||
|
width: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docGalleryItem .doc_top_panel {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
right: 5px;
|
||||||
|
background: rgba(1, 1, 1, 0.7);
|
||||||
|
padding: 6px 6px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docGalleryItem .doc_top_panel > div {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
background: url('/assets/packages/static/openvk/img/docs_controls.png?v=8');
|
||||||
|
background-size: 57px;
|
||||||
|
background-position-y: -24px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
#docs_page_wrapper select {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docListViewItem {
|
||||||
|
min-height: 38px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 0fr 1fr 0fr;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 7px 7px;
|
||||||
|
border-bottom: 1px solid #EDEDED;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs_page_content .docListViewItem:last-of-type {
|
||||||
|
border-bottom: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docListViewItem:hover, .attachButton:hover {
|
||||||
|
background: #f7f7f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docListViewItem .doc_icon {
|
||||||
|
width: 50px;
|
||||||
|
height: 38px;
|
||||||
|
background: #E6E6E6;
|
||||||
|
user-select: none;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docListViewItem .doc_content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-top: 1px;
|
||||||
|
/*gap: 5px;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.docListViewItem .doc_content b {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docListViewItem .doc_content .doc_content_info, .docListViewItem .doc_content span {
|
||||||
|
color: #6d6d6d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docListViewItem .doc_icon.no_image {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 2px;
|
||||||
|
height: 17px;
|
||||||
|
padding: 2px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docListViewItem .doc_icon.no_image span {
|
||||||
|
color: #6b6b6b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docListViewItem .doc_icon img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docListViewItem .doc_volume {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docListViewItem .doc_volume > div {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background: url('/assets/packages/static/openvk/img/docs_controls.png?v=8');
|
||||||
|
}
|
||||||
|
|
||||||
|
.docListViewItem:hover .doc_volume {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docListViewItem .doc_volume > div:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background-position-y: -21px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docListViewItem .doc_volume #report_icon {
|
||||||
|
background-position-x: -40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docGalleryItem .doc_top_panel #report_icon {
|
||||||
|
background-position-x: -24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docListViewItem .doc_volume #edit_icon {
|
||||||
|
background-position-x: -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docListViewItem .doc_volume #add_icon {
|
||||||
|
background-position-x: -60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docGalleryItem .doc_top_panel #add_icon {
|
||||||
|
background-position-x: -35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docGalleryItem .doc_top_panel #edit_icon {
|
||||||
|
background-position-x: -12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docListViewItem .doc_volume #mark_icon {
|
||||||
|
background-position-x: -80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docGalleryItem .doc_top_panel #mark_icon {
|
||||||
|
background-position-x: -46px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc_viewer_wrapper {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachments .docListViewItem {
|
||||||
|
min-height: 30px;
|
||||||
|
border-bottom: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachments .docGalleryItem {
|
||||||
|
display: block;
|
||||||
|
min-width: 170px;
|
||||||
|
height: 170px;
|
||||||
|
width: 50%;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachButton {
|
||||||
|
width: 12%;
|
||||||
|
height: 55px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment_selector .attachment_search {
|
||||||
|
margin: 6px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment_selector .attachment_search input {
|
||||||
|
height: 30px;
|
||||||
|
background-position-y: 9px;
|
||||||
|
}
|
||||||
|
|
BIN
Web/static/img/docs_controls.png
Normal file
BIN
Web/static/img/docs_controls.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6 KiB |
532
Web/static/js/al_docs.js
Normal file
532
Web/static/js/al_docs.js
Normal file
|
@ -0,0 +1,532 @@
|
||||||
|
function showDocumentUploadDialog(target = null, append_to_url = null, after_upload = null)
|
||||||
|
{
|
||||||
|
let file = null
|
||||||
|
const cmsg = new CMessageBox({
|
||||||
|
title: tr("document_uploading_in_general"),
|
||||||
|
body: `
|
||||||
|
<b>${tr("limits")}</b>
|
||||||
|
<ul style="margin: 5px 0px;padding-left: 20px;">
|
||||||
|
<li>${tr('limitations_file_limit_size', window.openvk.docs_max)}.</li>
|
||||||
|
<li>${tr('limitations_file_allowed_formats')}: ${window.openvk.docs_allowed.sort(() => Math.random() - 0.59).slice(0, 10).join(', ')}.</li>
|
||||||
|
<li>${tr("limitations_file_author_rights")}.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div id="_document_upload_frame" style="text-align:center;margin: 10px 0px 2px 0px;">
|
||||||
|
<input onclick="upload_btn.click()" class="button" type="button" value="${tr("select_file_fp")}">
|
||||||
|
<input id="upload_btn" type="file" accept="${window.openvk.docs_allowed.join(",.")}" style="display:none;">
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
buttons: [tr('close')],
|
||||||
|
callbacks: [Function.noop],
|
||||||
|
unique_name: "doc_upload_dialog",
|
||||||
|
})
|
||||||
|
|
||||||
|
cmsg.getNode().find('.ovk-diag-body').attr('style', "padding:15px;")
|
||||||
|
cmsg.getNode().attr('style', "width: 400px;")
|
||||||
|
cmsg.getNode().find('#upload_btn').on('change', (e) => {
|
||||||
|
file = e.target.files[0]
|
||||||
|
const name = file.name
|
||||||
|
const format = name.split(".")[name.split(".").length - 1]
|
||||||
|
if(window.openvk.docs_allowed.indexOf(format.toLowerCase()) == -1) {
|
||||||
|
makeError(tr("error_file_invalid_format"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if(file.size > window.openvk.docs_max * 1024 * 1024) {
|
||||||
|
makeError(tr("error_file_too_big"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cmsg.close()
|
||||||
|
|
||||||
|
const cmsg_2 = new CMessageBox({
|
||||||
|
title: tr("document_uploading_in_general"),
|
||||||
|
body: `
|
||||||
|
<p><b>${tr("info_name")}</b></p>
|
||||||
|
<input type="text" name="doc_name" value="${name}" placeholder="...">
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<input maxlength="255" value="0" type="radio" name="doc_access" checked>
|
||||||
|
${tr("private_document")}
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
<label>
|
||||||
|
<input value="3" type="radio" name="doc_access">
|
||||||
|
${tr("public_document")}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<p><b>${tr("tags")}</b></p>
|
||||||
|
<input maxlength="256" type="text" name="doc_tags" placeholder="...">
|
||||||
|
<br>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="doc_owner" checked>
|
||||||
|
${tr("owner_is_hidden")}
|
||||||
|
</label>
|
||||||
|
`,
|
||||||
|
buttons: [tr('upload_button'), tr('cancel')],
|
||||||
|
callbacks: [async () => {
|
||||||
|
const fd = new FormData
|
||||||
|
fd.append("name", u(`input[name="doc_name"]`).nodes[0].value)
|
||||||
|
fd.append("tags", u(`input[name="doc_tags"]`).nodes[0].value)
|
||||||
|
fd.append("folder", u(`input[name="doc_access"]:checked`).nodes[0].value)
|
||||||
|
fd.append("owner_hidden", u(`input[name="doc_owner"]`).nodes[0].checked ? "on" : "off")
|
||||||
|
fd.append("blob", file)
|
||||||
|
fd.append("ajax", 1)
|
||||||
|
fd.append("hash", window.router.csrf)
|
||||||
|
|
||||||
|
const endpoint_url = `/docs/upload` + (!isNaN(append_to_url) ? "?gid="+append_to_url : '')
|
||||||
|
const fetcher = await fetch(endpoint_url, {
|
||||||
|
method: 'POST',
|
||||||
|
body: fd,
|
||||||
|
})
|
||||||
|
const json = await fetcher.json()
|
||||||
|
|
||||||
|
if(json.success) {
|
||||||
|
if(target != "search") {
|
||||||
|
window.router.route(location.href)
|
||||||
|
} else {
|
||||||
|
if(after_upload)
|
||||||
|
after_upload()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fastError(escapeHtml(json.flash.message))
|
||||||
|
}
|
||||||
|
}, Function.noop],
|
||||||
|
})
|
||||||
|
cmsg_2.getNode().find('.ovk-diag-body').attr('style', "padding:15px;")
|
||||||
|
cmsg_2.getNode().attr('style', "width: 400px;")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
u(document).on("drop", "#_document_upload_frame", (e) => {
|
||||||
|
e.dataTransfer.dropEffect = 'move';
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
u(`#_document_upload_frame #upload_btn`).nodes[0].files = e.dataTransfer.files
|
||||||
|
u("#_document_upload_frame #upload_btn").trigger("change")
|
||||||
|
})
|
||||||
|
|
||||||
|
u(document).on('click', '.docMainItem #edit_icon', async (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
if(u("#ajloader").hasClass("shown")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = u(e.target).closest("#edit_icon")
|
||||||
|
const item = target.closest('.docMainItem')
|
||||||
|
const id = item.nodes[0].dataset.id
|
||||||
|
|
||||||
|
CMessageBox.toggleLoader()
|
||||||
|
|
||||||
|
const docs = await window.OVKAPI.call('docs.getById', {docs: id, return_tags: 1})
|
||||||
|
const doc = docs[0]
|
||||||
|
if(!doc) {
|
||||||
|
fastError("(")
|
||||||
|
CMessageBox.toggleLoader()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const cmsg_2 = new CMessageBox({
|
||||||
|
unique_name: "document_edit_modal",
|
||||||
|
title: tr("document_editing_in_general"),
|
||||||
|
body: `
|
||||||
|
<p><b>${tr("info_name")}</b></p>
|
||||||
|
<input maxlength="128" type="text" name="doc_name" value="${doc.title}" placeholder="...">
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<input value="0" type="radio" name="doc_access" ${doc.folder_id != 3 ? "checked" : ''}>
|
||||||
|
${tr("private_document")}
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
<label>
|
||||||
|
<input value="3" type="radio" name="doc_access" ${doc.folder_id == 3 ? "checked" : ''}>
|
||||||
|
${tr("public_document")}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<p><b>${tr("tags")}</b></p>
|
||||||
|
<input maxlength="256" type="text" name="doc_tags" value="${doc.tags.join(',')}" placeholder="...">
|
||||||
|
<br>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="doc_owner" ${doc.is_hidden ? "checked" : ''}>
|
||||||
|
${tr("owner_is_hidden")}
|
||||||
|
</label>
|
||||||
|
`,
|
||||||
|
buttons: [tr('save'), tr('cancel')],
|
||||||
|
callbacks: [async () => {
|
||||||
|
const params = {
|
||||||
|
owner_id: id.split('_')[0],
|
||||||
|
doc_id: id.split('_')[1],
|
||||||
|
title: u(`input[name='doc_name']`).nodes[0].value,
|
||||||
|
tags: u(`input[name='doc_tags']`).nodes[0].value,
|
||||||
|
folder_id: u(`input[name="doc_access"]:checked`).nodes[0].value,
|
||||||
|
owner_hidden: u(`input[name="doc_owner"]`).nodes[0].checked ? 1 : 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
const edit = await window.OVKAPI.call('docs.edit', params)
|
||||||
|
if(edit == 1) {
|
||||||
|
item.find('.doc_content .doc_name').html(escapeHtml(params.title))
|
||||||
|
item.find('.doc_content .doc_tags').html(escapeHtml(params.tags))
|
||||||
|
}
|
||||||
|
}, Function.noop],
|
||||||
|
})
|
||||||
|
cmsg_2.getNode().find('.ovk-diag-body').attr('style', "padding:15px;")
|
||||||
|
cmsg_2.getNode().attr('style', "width: 400px;")
|
||||||
|
|
||||||
|
CMessageBox.toggleLoader()
|
||||||
|
})
|
||||||
|
|
||||||
|
u(document).on('click', '#upload_entry_point', (e) => {
|
||||||
|
showDocumentUploadDialog(null, Number(e.target.dataset.gid))
|
||||||
|
})
|
||||||
|
|
||||||
|
u(document).on('change', "#docs_page_wrapper select[name='docs_sort']", (e) => {
|
||||||
|
const new_url = new URL(location.href)
|
||||||
|
new_url.searchParams.set('order', e.target.value)
|
||||||
|
|
||||||
|
window.router.route(new_url.href)
|
||||||
|
})
|
||||||
|
|
||||||
|
u(document).on('click', '.docMainItem #remove_icon', async (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
const target = u(e.target).closest("#remove_icon")
|
||||||
|
const item = target.closest('.docMainItem')
|
||||||
|
const context = item.attr('data-context')
|
||||||
|
const id = item.nodes[0].dataset.id.split("_")
|
||||||
|
|
||||||
|
target.addClass('lagged')
|
||||||
|
const res = await window.OVKAPI.call('docs.delete', {owner_id: id[0], doc_id: id[1]})
|
||||||
|
target.removeClass('lagged')
|
||||||
|
|
||||||
|
if(res == 1) {
|
||||||
|
target.attr('id', 'mark_icon')
|
||||||
|
|
||||||
|
if(context == "page") {
|
||||||
|
target.html('✓')
|
||||||
|
window.router.route('/docs')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
u(document).on('click', '.docMainItem #add_icon', async (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
const target = u(e.target).closest("#add_icon")
|
||||||
|
const item = target.closest('.docMainItem')
|
||||||
|
const id = item.nodes[0].dataset.id.split("_")
|
||||||
|
const context = item.attr('data-context')
|
||||||
|
|
||||||
|
target.addClass('lagged')
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await window.OVKAPI.call('docs.add', {owner_id: id[0], doc_id: id[1], access_key: id[2]})
|
||||||
|
} catch(e) {
|
||||||
|
makeError(tr("error_file_adding_copied"))
|
||||||
|
target.removeClass('lagged')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
target.removeClass('lagged')
|
||||||
|
target.attr('id', 'mark_icon')
|
||||||
|
|
||||||
|
if(context == "page") {
|
||||||
|
target.html('✓')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
u(document).on('click', '.docMainItem #report_icon', (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
const target = u(e.target).closest("#report_icon")
|
||||||
|
const item = target.closest('.docMainItem')
|
||||||
|
const id = item.nodes[0].dataset.id.split("_")
|
||||||
|
|
||||||
|
MessageBox(tr("report_question"), `
|
||||||
|
${tr("going_to_report_doc")}
|
||||||
|
<br/>${tr("report_question_text")}
|
||||||
|
<br/><br/><b> ${tr("report_reason")}</b>: <input type='text' id='uReportMsgInput' placeholder='${tr("reason")}' />`, [tr("confirm_m"), tr("cancel")], [(function() {
|
||||||
|
|
||||||
|
res = document.querySelector("#uReportMsgInput").value;
|
||||||
|
xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("GET", "/report/" + id[1] + "?reason=" + res + "&type=doc", true);
|
||||||
|
xhr.onload = (function() {
|
||||||
|
if(xhr.responseText.indexOf("reason") === -1)
|
||||||
|
MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
|
||||||
|
else
|
||||||
|
MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
|
||||||
|
});
|
||||||
|
xhr.send(null)
|
||||||
|
}),
|
||||||
|
|
||||||
|
Function.noop])
|
||||||
|
})
|
||||||
|
|
||||||
|
u(document).on("click", ".docListViewItem a.viewerOpener, a.docGalleryItem", async (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
if(e.target.closest('.doc_volume_action')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(window.openvk.current_id == 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = u(e.target)
|
||||||
|
const link = target.closest('a')
|
||||||
|
if(target.closest(".embeddable").length > 0) {
|
||||||
|
target.closest(".embeddable").toggleClass("playing")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
CMessageBox.toggleLoader()
|
||||||
|
const url = link.nodes[0].href
|
||||||
|
const request = await fetch(url)
|
||||||
|
const body_html = await request.text()
|
||||||
|
const parser = new DOMParser
|
||||||
|
const body = parser.parseFromString(body_html, "text/html")
|
||||||
|
|
||||||
|
const preview = body.querySelector('.photo-page-wrapper-photo')
|
||||||
|
const details = body.querySelector('.ovk-photo-details')
|
||||||
|
|
||||||
|
u(preview.querySelector('img')).attr('id', 'ovk-photo-img')
|
||||||
|
|
||||||
|
const photo_viewer = new CMessageBox({
|
||||||
|
title: '',
|
||||||
|
custom_template: u(`
|
||||||
|
<div class="ovk-photo-view-dimmer">
|
||||||
|
<div class="ovk-photo-view">
|
||||||
|
<div class="photo_com_title">
|
||||||
|
<text id="photo_com_title_photos">
|
||||||
|
${tr("document")}
|
||||||
|
</text>
|
||||||
|
<div>
|
||||||
|
<a id="ovk-photo-close">${tr("close")}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class='photo_viewer_wrapper doc_viewer_wrapper'>
|
||||||
|
${preview.innerHTML}
|
||||||
|
</div>
|
||||||
|
<div class="ovk-photo-details">
|
||||||
|
${details.innerHTML}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`)
|
||||||
|
})
|
||||||
|
photo_viewer.getNode().find("#ovk-photo-close").on("click", function(e) {
|
||||||
|
photo_viewer.close()
|
||||||
|
});
|
||||||
|
|
||||||
|
CMessageBox.toggleLoader()
|
||||||
|
})
|
||||||
|
|
||||||
|
// ctx > "wall" and maybe "messages" in future
|
||||||
|
// source > "user" || "club" > source_arg
|
||||||
|
async function __docAttachment(form, ctx = "wall", source = "user", source_arg = 0) {
|
||||||
|
const per_page = 10
|
||||||
|
const msg = new CMessageBox({
|
||||||
|
title: tr('select_doc'),
|
||||||
|
custom_template: u(`
|
||||||
|
<div class="ovk-photo-view-dimmer">
|
||||||
|
<div class="ovk-photo-view">
|
||||||
|
<div class="photo_com_title">
|
||||||
|
<text id="photo_com_title_photos">
|
||||||
|
${tr("select_doc")}
|
||||||
|
</text>
|
||||||
|
<span style="display: inline-flex;gap: 7px;">
|
||||||
|
${source != "user" ? `<a id="_doc_picker_go_to_my">${tr("go_to_my_documents")}</a>`: ""}
|
||||||
|
<a id="_doc_picker_upload">${tr("upload_button")}</a>
|
||||||
|
</span>
|
||||||
|
<div>
|
||||||
|
<a id="ovk-photo-close">${tr("close")}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class='photo_viewer_wrapper photo_viewer_wrapper_scrollable doc_viewer_wrapper'>
|
||||||
|
<div class='attachment_selector' style="width: 100%;">
|
||||||
|
<div class="attachment_search">
|
||||||
|
<input type="search" maxlength="100" name="q" class="input_with_search_icon" placeholder="${tr("search_by_documents")}">
|
||||||
|
</div>
|
||||||
|
<div id='_attachment_insert'>
|
||||||
|
<div class="docsInsert"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ovk-photo-details"></div>
|
||||||
|
</div>
|
||||||
|
</div>`),
|
||||||
|
})
|
||||||
|
|
||||||
|
msg.getNode().find(".ovk-photo-view").attr('style', 'width: 400px;min-height:90vh;')
|
||||||
|
msg.getNode().find('.ovk-diag-body').attr('style', 'height:335px;padding:0px;')
|
||||||
|
docs_reciever = new class {
|
||||||
|
ctx = "my"
|
||||||
|
ctx_id = 0
|
||||||
|
stat = {
|
||||||
|
page: 0,
|
||||||
|
pagesCount: 0,
|
||||||
|
count: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
clean() {
|
||||||
|
this.stat = {
|
||||||
|
page: 0,
|
||||||
|
pagesCount: 0,
|
||||||
|
count: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
u('#gif_loader, #_attachment_insert #show_more').remove()
|
||||||
|
u("#_attachment_insert .docsInsert").html("")
|
||||||
|
}
|
||||||
|
|
||||||
|
async page(page = 1, perPage = 10) {
|
||||||
|
u('#_attachment_insert').append(`<div id='gif_loader'></div>`)
|
||||||
|
|
||||||
|
const fd = new FormData
|
||||||
|
fd.append("context", "list")
|
||||||
|
fd.append("hash", window.router.csrf)
|
||||||
|
let url = `/docs${source == "club" ? source_arg : ""}?picker=1&p=${page}`
|
||||||
|
if(this.query) {
|
||||||
|
fd.append("context", "search")
|
||||||
|
fd.append("ctx_query", this.query)
|
||||||
|
}
|
||||||
|
const req = await fetch(url, {
|
||||||
|
method: "POST",
|
||||||
|
body: fd
|
||||||
|
})
|
||||||
|
const res = await req.text()
|
||||||
|
const dom = new DOMParser
|
||||||
|
const pre = dom.parseFromString(res, "text/html")
|
||||||
|
const pagesCount = Number(pre.querySelector("input[name='pagesCount']").value)
|
||||||
|
const count = Number(pre.querySelector("input[name='count']").value)
|
||||||
|
if(count < 1) {
|
||||||
|
u('#_attachment_insert .docsInsert').append(`
|
||||||
|
<div class="information">
|
||||||
|
${tr("no_documents")}.
|
||||||
|
</div>
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
pre.querySelectorAll("._content").forEach(doc => {
|
||||||
|
const res = u(`${doc.outerHTML}`)
|
||||||
|
const id = res.attr("data-attachmentdata")
|
||||||
|
|
||||||
|
res.find(".docMainItem").attr("style", "width: 85%;")
|
||||||
|
res.append(`
|
||||||
|
<div class="attachButton" id='__attach_doc'>
|
||||||
|
${this.isDocAttached(id) ? tr("detach") : tr("attach")}
|
||||||
|
</div>
|
||||||
|
`)
|
||||||
|
u('#_attachment_insert .docsInsert').append(res)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.stat.page = page
|
||||||
|
this.stat.pagesCount = pagesCount
|
||||||
|
this.stat.count = count
|
||||||
|
u('#gif_loader').remove()
|
||||||
|
this.showMore()
|
||||||
|
}
|
||||||
|
|
||||||
|
async search(query_string) {
|
||||||
|
this.clean()
|
||||||
|
if(query_string == "")
|
||||||
|
this.query = null
|
||||||
|
else
|
||||||
|
this.query = query_string
|
||||||
|
|
||||||
|
await this.page(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
showMore() {
|
||||||
|
if(this.stat.page < this.stat.pagesCount) {
|
||||||
|
u('#_attachment_insert').append(`
|
||||||
|
<div id="show_more" data-pagesCount="${this.stat.pagesCount}">
|
||||||
|
<span>${tr('show_more')}</span>
|
||||||
|
</div>
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxAttachmentsCheck() {
|
||||||
|
if(u(form).find(`.upload-item`).length > window.openvk.max_attachments) {
|
||||||
|
makeError(tr('too_many_attachments'), 'Red', 10000, 1)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
attach(dataset, button) {
|
||||||
|
if(this.isDocAttached(dataset.attachmentdata)) {
|
||||||
|
(form.find(`.upload-item[data-type='doc'][data-id='${dataset.attachmentdata}']`)).remove()
|
||||||
|
button.html(tr('attach'))
|
||||||
|
} else {
|
||||||
|
const _url = dataset.attachmentdata.split("_")
|
||||||
|
button.html(tr('detach'))
|
||||||
|
form.find('.post-vertical').append(`
|
||||||
|
<div class="vertical-attachment upload-item" draggable="true" data-type='doc' data-id="${dataset.attachmentdata}">
|
||||||
|
<div class='vertical-attachment-content' draggable="false">
|
||||||
|
<div class="docMainItem attachment_doc attachment_note">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 10"><polygon points="0 0 0 10 8 10 8 4 4 4 4 0 0 0"/><polygon points="5 0 5 3 8 3 5 0"/></svg>
|
||||||
|
|
||||||
|
<div class='attachment_note_content'>
|
||||||
|
<span class="attachment_note_text">${tr("document")}</span>
|
||||||
|
<span class="attachment_note_name"><a href="/doc${_url[0]}_${_url[1]}?key=${_url[2]}">${ovk_proc_strtr(escapeHtml(dataset.name), 50)}</a></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class='vertical-attachment-remove'>
|
||||||
|
<div id='small_remove_button'></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isDocAttached(attachmentdata) {
|
||||||
|
return (form.find(`.upload-item[data-type='doc'][data-id='${attachmentdata}']`)).length > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.getNode().find("#ovk-photo-close").on("click", function(e) {
|
||||||
|
msg.close()
|
||||||
|
})
|
||||||
|
msg.getNode().on("click", "#__attach_doc", async (ev) => {
|
||||||
|
if(docs_reciever.maxAttachmentsCheck() == true) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = u(ev.target).closest('._content')
|
||||||
|
const button = target.find('#__attach_doc')
|
||||||
|
const dataset = target.nodes[0].dataset
|
||||||
|
docs_reciever.attach(dataset, button)
|
||||||
|
})
|
||||||
|
msg.getNode().on("click", "#show_more", async (ev) => {
|
||||||
|
const target = u(ev.target).closest('#show_more')
|
||||||
|
target.addClass('lagged')
|
||||||
|
await docs_reciever.page(docs_reciever.stat.page + 1)
|
||||||
|
target.remove()
|
||||||
|
})
|
||||||
|
msg.getNode().on("click", "#_doc_picker_go_to_my", async (e) => {
|
||||||
|
msg.close()
|
||||||
|
await __docAttachment(form, "wall")
|
||||||
|
})
|
||||||
|
msg.getNode().on("click", "#_doc_picker_upload", async (e) => {
|
||||||
|
showDocumentUploadDialog("search", source_arg >= 0 ? NaN : Math.abs(source_arg), () => {
|
||||||
|
docs_reciever.clean()
|
||||||
|
docs_reciever.page(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
msg.getNode().on("change", ".attachment_search input", async (e) => {
|
||||||
|
await docs_reciever.search(ovk_proc_strtr(e.target.value, 100))
|
||||||
|
})
|
||||||
|
|
||||||
|
await docs_reciever.page(docs_reciever.stat.page + 1)
|
||||||
|
}
|
||||||
|
u(document).on('click', '#__documentAttachment', async (e) => {
|
||||||
|
const form = u(e.target).closest('form')
|
||||||
|
const targ = u(e.target).closest("#__documentAttachment")
|
||||||
|
let entity_source = "user"
|
||||||
|
let entity_id = 0
|
||||||
|
if(targ.attr('data-club') != null) {
|
||||||
|
entity_source = "club"
|
||||||
|
entity_id = Number(targ.attr('data-club'))
|
||||||
|
}
|
||||||
|
|
||||||
|
await __docAttachment(form, "wall", entity_source, entity_id)
|
||||||
|
})
|
|
@ -999,6 +999,10 @@ u(document).on("click", "#editPost", async (e) => {
|
||||||
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/audio-ac3.png" />
|
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/audio-ac3.png" />
|
||||||
${tr('audio')}
|
${tr('audio')}
|
||||||
</a>
|
</a>
|
||||||
|
<a id="__documentAttachment">
|
||||||
|
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/application-octet-stream.png" />
|
||||||
|
${tr('document')}
|
||||||
|
</a>
|
||||||
${type == 'post' ? `<a id="__notesAttachment">
|
${type == 'post' ? `<a id="__notesAttachment">
|
||||||
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/application-x-srt.png" />
|
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/application-x-srt.png" />
|
||||||
${tr('note')}
|
${tr('note')}
|
||||||
|
@ -1035,7 +1039,10 @@ u(document).on("click", "#editPost", async (e) => {
|
||||||
// horizontal attachments
|
// horizontal attachments
|
||||||
api_post.attachments.forEach(att => {
|
api_post.attachments.forEach(att => {
|
||||||
const type = att.type
|
const type = att.type
|
||||||
const aid = att[type].owner_id + '_' + att[type].id
|
let aid = att[type].owner_id + '_' + att[type].id
|
||||||
|
if(att[type] && att[type].access_key) {
|
||||||
|
aid += "_" + att[type].access_key
|
||||||
|
}
|
||||||
|
|
||||||
if(type == 'video' || type == 'photo') {
|
if(type == 'video' || type == 'photo') {
|
||||||
let preview = ''
|
let preview = ''
|
||||||
|
|
|
@ -309,10 +309,6 @@ u(document).on('submit', 'form', async (e) => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if((localStorage.getItem('ux.disable_ajax_routing') ?? 0) == 1 || window.openvk.current_id == 0) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if(window.openvk.disable_ajax == 1) {
|
if(window.openvk.disable_ajax == 1) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -322,6 +318,10 @@ u(document).on('submit', 'form', async (e) => {
|
||||||
collect_attachments_node(target)
|
collect_attachments_node(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if((localStorage.getItem('ux.disable_ajax_routing') ?? 0) == 1 || window.openvk.current_id == 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
u('#ajloader').addClass('shown')
|
u('#ajloader').addClass('shown')
|
||||||
|
|
||||||
const form = e.target
|
const form = e.target
|
||||||
|
|
|
@ -261,6 +261,11 @@ function parseAttachments($attachments, array $allow_types = ['photo', 'video',
|
||||||
'method' => 'get',
|
'method' => 'get',
|
||||||
'onlyId' => true,
|
'onlyId' => true,
|
||||||
],
|
],
|
||||||
|
'doc' => [
|
||||||
|
'repo' => 'openvk\Web\Models\Repositories\Documents',
|
||||||
|
'method' => 'getDocumentById',
|
||||||
|
'withKey' => true,
|
||||||
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach($exploded_attachments as $attachment_string) {
|
foreach($exploded_attachments as $attachment_string) {
|
||||||
|
@ -277,6 +282,14 @@ function parseAttachments($attachments, array $allow_types = ['photo', 'video',
|
||||||
$repository_class = $repositories[$attachment_type]['repo'];
|
$repository_class = $repositories[$attachment_type]['repo'];
|
||||||
if(!$repository_class) continue;
|
if(!$repository_class) continue;
|
||||||
$attachment_model = (new $repository_class)->{$repositories[$attachment_type]['method']}($attachment_id);
|
$attachment_model = (new $repository_class)->{$repositories[$attachment_type]['method']}($attachment_id);
|
||||||
|
$output_attachments[] = $attachment_model;
|
||||||
|
} elseif($repositories[$attachment_type]['withKey']) {
|
||||||
|
[$attachment_owner, $attachment_id, $access_key] = explode('_', $attachment_ids);
|
||||||
|
|
||||||
|
$repository_class = $repositories[$attachment_type]['repo'];
|
||||||
|
if(!$repository_class) continue;
|
||||||
|
$attachment_model = (new $repository_class)->{$repositories[$attachment_type]['method']}((int)$attachment_owner, (int)$attachment_id, $access_key);
|
||||||
|
|
||||||
$output_attachments[] = $attachment_model;
|
$output_attachments[] = $attachment_model;
|
||||||
} else {
|
} else {
|
||||||
[$attachment_owner, $attachment_id] = array_map('intval', explode('_', $attachment_ids));
|
[$attachment_owner, $attachment_id] = array_map('intval', explode('_', $attachment_ids));
|
||||||
|
@ -371,6 +384,23 @@ function escape_html(string $unsafe): string
|
||||||
return htmlspecialchars($unsafe, ENT_DISALLOWED | ENT_XHTML);
|
return htmlspecialchars($unsafe, ENT_DISALLOWED | ENT_XHTML);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readable_filesize($bytes, $precision = 2): string
|
||||||
|
{
|
||||||
|
$units = ['B', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb'];
|
||||||
|
|
||||||
|
$bytes = max($bytes, 0);
|
||||||
|
$power = $bytes > 0 ? floor(log($bytes, 1024)) : 0;
|
||||||
|
$power = min($power, count($units) - 1);
|
||||||
|
$bytes /= pow(1024, $power);
|
||||||
|
|
||||||
|
return round($bytes, $precision) . $units[$power];
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadable_name(string $text): string
|
||||||
|
{
|
||||||
|
return preg_replace('/[\\/:*?"<>|]/', '_', str_replace(' ', '_', $text));
|
||||||
|
}
|
||||||
|
|
||||||
return (function() {
|
return (function() {
|
||||||
_ovk_check_environment();
|
_ovk_check_environment();
|
||||||
require __DIR__ . "/vendor/autoload.php";
|
require __DIR__ . "/vendor/autoload.php";
|
||||||
|
|
28
install/sqls/00054-docs.sql
Normal file
28
install/sqls/00054-docs.sql
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
CREATE TABLE `documents` (
|
||||||
|
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`owner` BIGINT(20) NOT NULL,
|
||||||
|
`virtual_id` BIGINT(20) UNSIGNED NOT NULL,
|
||||||
|
`hash` CHAR(128) NOT NULL,
|
||||||
|
`owner_hidden` TINYINT(1) UNSIGNED NOT NULL DEFAULT '1',
|
||||||
|
`copy_of` BIGINT(20) UNSIGNED NULL DEFAULT NULL,
|
||||||
|
`created` BIGINT(20) UNSIGNED NOT NULL,
|
||||||
|
`edited` BIGINT(20) UNSIGNED NULL DEFAULT NULL,
|
||||||
|
`name` VARCHAR(256) NOT NULL,
|
||||||
|
`original_name` VARCHAR(500) NULL DEFAULT NULL,
|
||||||
|
`access_key` VARCHAR(100) NULL DEFAULT NULL,
|
||||||
|
`format` VARCHAR(20) NOT NULL DEFAULT 'gif',
|
||||||
|
`type` TINYINT(10) UNSIGNED NOT NULL DEFAULT '0',
|
||||||
|
`folder_id` TINYINT(10) UNSIGNED NOT NULL DEFAULT '0',
|
||||||
|
`preview` VARCHAR(200) NULL DEFAULT NULL,
|
||||||
|
`tags` VARCHAR(500) NULL DEFAULT NULL,
|
||||||
|
`filesize` BIGINT(20) UNSIGNED NOT NULL,
|
||||||
|
`deleted` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0',
|
||||||
|
`unlisted` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE = InnoDB COLLATE=utf8mb4_unicode_520_ci;
|
||||||
|
|
||||||
|
ALTER TABLE `documents` ADD INDEX (`deleted`);
|
||||||
|
ALTER TABLE `documents` ADD INDEX (`unlisted`);
|
||||||
|
ALTER TABLE `documents` ADD INDEX `virtual_id_id` (`virtual_id`, `id`);
|
||||||
|
ALTER TABLE `documents` ADD INDEX `folder_id` (`folder_id`);
|
||||||
|
ALTER TABLE `photos` ADD `system` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' AFTER `anonymous`, ADD `private` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' AFTER `system`;
|
|
@ -711,6 +711,7 @@
|
||||||
"search_for_notes" = "Search for notes";
|
"search_for_notes" = "Search for notes";
|
||||||
"search_for_audios" = "Search for music";
|
"search_for_audios" = "Search for music";
|
||||||
"search_for_audios_playlists" = "Search for playlists";
|
"search_for_audios_playlists" = "Search for playlists";
|
||||||
|
"search_for_docs" = "Search for documents";
|
||||||
"search_button" = "Find";
|
"search_button" = "Find";
|
||||||
"search_placeholder" = "Start typing any name, title or word";
|
"search_placeholder" = "Start typing any name, title or word";
|
||||||
"results_zero" = "No results";
|
"results_zero" = "No results";
|
||||||
|
@ -1308,6 +1309,7 @@
|
||||||
"going_to_report_user" = "You are about to report this user.";
|
"going_to_report_user" = "You are about to report this user.";
|
||||||
"going_to_report_video" = "You are about to report this video.";
|
"going_to_report_video" = "You are about to report this video.";
|
||||||
"going_to_report_audio" = "You are about to report this audio.";
|
"going_to_report_audio" = "You are about to report this audio.";
|
||||||
|
"going_to_report_doc" = "You are about to report this document.";
|
||||||
"going_to_report_post" = "You are about to report this post.";
|
"going_to_report_post" = "You are about to report this post.";
|
||||||
"going_to_report_comment" = "You are about to report this comment.";
|
"going_to_report_comment" = "You are about to report this comment.";
|
||||||
|
|
||||||
|
@ -2114,6 +2116,7 @@
|
||||||
"s_videos" = "Videos";
|
"s_videos" = "Videos";
|
||||||
"s_audios" = "Music";
|
"s_audios" = "Music";
|
||||||
"s_audios_playlists" = "Playlists";
|
"s_audios_playlists" = "Playlists";
|
||||||
|
"s_documents" = "Documents";
|
||||||
|
|
||||||
"s_by_people" = "for users";
|
"s_by_people" = "for users";
|
||||||
"s_by_groups" = "for groups";
|
"s_by_groups" = "for groups";
|
||||||
|
@ -2123,6 +2126,7 @@
|
||||||
"s_by_apps" = "for apps";
|
"s_by_apps" = "for apps";
|
||||||
"s_by_audios" = "for audios";
|
"s_by_audios" = "for audios";
|
||||||
"s_by_audios_playlists" = "for playlists";
|
"s_by_audios_playlists" = "for playlists";
|
||||||
|
"s_by_documents" = "for documents";
|
||||||
|
|
||||||
"s_order_by" = "Order by...";
|
"s_order_by" = "Order by...";
|
||||||
|
|
||||||
|
@ -2145,6 +2149,7 @@
|
||||||
"s_date_after" = "After";
|
"s_date_after" = "After";
|
||||||
|
|
||||||
"s_main" = "Main";
|
"s_main" = "Main";
|
||||||
|
"s_type" = "Type";
|
||||||
|
|
||||||
"s_now_on_site" = "now on site";
|
"s_now_on_site" = "now on site";
|
||||||
"s_with_photo" = "with photo";
|
"s_with_photo" = "with photo";
|
||||||
|
@ -2309,3 +2314,74 @@
|
||||||
"upd_in_general" = "Avatar update";
|
"upd_in_general" = "Avatar update";
|
||||||
"on_wall" = "On wall";
|
"on_wall" = "On wall";
|
||||||
"sign_short" = "Sign";
|
"sign_short" = "Sign";
|
||||||
|
|
||||||
|
/* Documents */
|
||||||
|
|
||||||
|
"my_documents" = "Documents";
|
||||||
|
"my_documents_objectively" = "My Documents";
|
||||||
|
"documents_of_group" = "Group's documents";
|
||||||
|
"search_by_documents" = "Search for documents..";
|
||||||
|
"documents" = "Documents";
|
||||||
|
"document_uploading_in_general" = "Upload document";
|
||||||
|
"document_editing_in_general" = "Edit document";
|
||||||
|
"file" = "File";
|
||||||
|
"tags" = "Tags";
|
||||||
|
"owner_is_hidden" = "Hide author";
|
||||||
|
"accessbility" = "Accessbility";
|
||||||
|
"download_file" = "Download file";
|
||||||
|
"remove" = "Remove";
|
||||||
|
|
||||||
|
"document" = "Document";
|
||||||
|
"documents_all" = "All documents";
|
||||||
|
"document_type_0" = "All";
|
||||||
|
"document_type_1" = "Text";
|
||||||
|
"document_type_2" = "Archives";
|
||||||
|
"document_type_3" = "GIF";
|
||||||
|
"document_type_4" = "Images";
|
||||||
|
"document_type_5" = "Audio";
|
||||||
|
"document_type_6" = "Video";
|
||||||
|
"document_type_7" = "Books";
|
||||||
|
"document_type_8" = "Another";
|
||||||
|
|
||||||
|
"documents_one" = "$1 document";
|
||||||
|
"documents_few" = "$1 documents";
|
||||||
|
"documents_many" = "$1 documents";
|
||||||
|
"documents_other" = "$1 documents";
|
||||||
|
"documents_zero" = "$1 documents";
|
||||||
|
|
||||||
|
"you_have_x_documents_one" = "You have $1 document";
|
||||||
|
"you_have_x_documents_few" = "You have $1 documents";
|
||||||
|
"you_have_x_documents_many" = "You have $1 documents";
|
||||||
|
"you_have_x_documents_other" = "You have $1 documents";
|
||||||
|
"you_have_x_documents_zero" = "You have $1 documents";
|
||||||
|
|
||||||
|
"group_has_x_documents_one" = "This group has $1 document";
|
||||||
|
"group_has_x_documents_few" = "This group has $1 documents";
|
||||||
|
"group_has_x_documents_many" = "This group has $1 documents";
|
||||||
|
"group_has_x_documents_other" = "This group has $1 documents";
|
||||||
|
"group_has_x_documents_zero" = "This group has $1 documents";
|
||||||
|
|
||||||
|
"x_documents_in_tab_one" = "$1 document at this tab";
|
||||||
|
"x_documents_in_tab_few" = "$1 documents at this tab";
|
||||||
|
"x_documents_in_tab_many" = "$1 documents at this tab";
|
||||||
|
"x_documents_in_tab_other" = "$1 documents at this tab";
|
||||||
|
"x_documents_in_tab_zero" = "$1 documents at this tab";
|
||||||
|
|
||||||
|
"there_is_no_documents_alright" = "There is no documents.";
|
||||||
|
"limitations_file_limit_size" = "File must not exceed $1 MB";
|
||||||
|
"limitations_file_allowed_formats" = "Allowed formats";
|
||||||
|
"limitations_file_author_rights" = "File must not violate copyright and site rules";
|
||||||
|
"select_file_fp" = "Select file";
|
||||||
|
"error_file_too_big" = "File is too big.";
|
||||||
|
"error_file_invalid_format" = "File format is not allowed.";
|
||||||
|
"error_file_adding_copied" = "File is already added.";
|
||||||
|
"error_file_preview" = "Error when uploading file: weird image.";
|
||||||
|
|
||||||
|
"private_document" = "Private (by link)";
|
||||||
|
"public_document" = "Public";
|
||||||
|
"documents_sort_add" = "By date";
|
||||||
|
"documents_sort_alphabet" = "A-Z";
|
||||||
|
"documents_sort_size" = "By size";
|
||||||
|
"select_doc" = "Attach document";
|
||||||
|
"no_documents" = "No documents found";
|
||||||
|
"go_to_my_documents" = "Go to own documents";
|
||||||
|
|
|
@ -683,6 +683,7 @@
|
||||||
"search_for_notes" = "Поиск записок";
|
"search_for_notes" = "Поиск записок";
|
||||||
"search_for_audios" = "Поиск музыки";
|
"search_for_audios" = "Поиск музыки";
|
||||||
"search_for_audios_playlists" = "Поиск плейлистов";
|
"search_for_audios_playlists" = "Поиск плейлистов";
|
||||||
|
"search_for_docs" = "Поиск документов";
|
||||||
"search_button" = "Найти";
|
"search_button" = "Найти";
|
||||||
"search_placeholder" = "Начните вводить любое имя, название или слово";
|
"search_placeholder" = "Начните вводить любое имя, название или слово";
|
||||||
"results_zero" = "Ни одного результата";
|
"results_zero" = "Ни одного результата";
|
||||||
|
@ -1240,6 +1241,7 @@
|
||||||
"going_to_report_user" = "Вы собираетесь пожаловаться на данного пользователя.";
|
"going_to_report_user" = "Вы собираетесь пожаловаться на данного пользователя.";
|
||||||
"going_to_report_video" = "Вы собираетесь пожаловаться на данную видеозапись.";
|
"going_to_report_video" = "Вы собираетесь пожаловаться на данную видеозапись.";
|
||||||
"going_to_report_audio" = "Вы собираетесь пожаловаться на данную аудиозапись.";
|
"going_to_report_audio" = "Вы собираетесь пожаловаться на данную аудиозапись.";
|
||||||
|
"going_to_report_doc" = "Вы собираетесь пожаловаться на этот документ.";
|
||||||
"going_to_report_post" = "Вы собираетесь пожаловаться на данную запись.";
|
"going_to_report_post" = "Вы собираетесь пожаловаться на данную запись.";
|
||||||
"going_to_report_comment" = "Вы собираетесь пожаловаться на данный комментарий.";
|
"going_to_report_comment" = "Вы собираетесь пожаловаться на данный комментарий.";
|
||||||
|
|
||||||
|
@ -2009,6 +2011,7 @@
|
||||||
"s_videos" = "Видео";
|
"s_videos" = "Видео";
|
||||||
"s_audios" = "Аудио";
|
"s_audios" = "Аудио";
|
||||||
"s_audios_playlists" = "Плейлисты";
|
"s_audios_playlists" = "Плейлисты";
|
||||||
|
"s_documents" = "Документы";
|
||||||
|
|
||||||
"s_by_people" = "по пользователям";
|
"s_by_people" = "по пользователям";
|
||||||
"s_by_groups" = "по группам";
|
"s_by_groups" = "по группам";
|
||||||
|
@ -2018,6 +2021,7 @@
|
||||||
"s_by_apps" = "по приложениям";
|
"s_by_apps" = "по приложениям";
|
||||||
"s_by_audios" = "по аудиозаписям";
|
"s_by_audios" = "по аудиозаписям";
|
||||||
"s_by_audios_playlists" = "по плейлистам";
|
"s_by_audios_playlists" = "по плейлистам";
|
||||||
|
"s_by_documents" = "по документам";
|
||||||
|
|
||||||
"s_order_by" = "Порядок";
|
"s_order_by" = "Порядок";
|
||||||
|
|
||||||
|
@ -2040,6 +2044,7 @@
|
||||||
"s_date_after" = "После";
|
"s_date_after" = "После";
|
||||||
|
|
||||||
"s_main" = "Основное";
|
"s_main" = "Основное";
|
||||||
|
"s_type" = "Тип";
|
||||||
|
|
||||||
"s_now_on_site" = "cейчас на сайте";
|
"s_now_on_site" = "cейчас на сайте";
|
||||||
"s_with_photo" = "с фото";
|
"s_with_photo" = "с фото";
|
||||||
|
@ -2204,3 +2209,74 @@
|
||||||
"upd_in_general" = "Обновление фотографии страницы";
|
"upd_in_general" = "Обновление фотографии страницы";
|
||||||
"on_wall" = "На стене";
|
"on_wall" = "На стене";
|
||||||
"sign_short" = "Подпись";
|
"sign_short" = "Подпись";
|
||||||
|
|
||||||
|
/* Documents */
|
||||||
|
|
||||||
|
"my_documents" = "Документы";
|
||||||
|
"my_documents_objectively" = "Мои Документы";
|
||||||
|
"documents_of_group" = "Документы группы";
|
||||||
|
"search_by_documents" = "Поиск по документам...";
|
||||||
|
"documents" = "Документы";
|
||||||
|
"document_uploading_in_general" = "Загрузка документа";
|
||||||
|
"document_editing_in_general" = "Редактирование документа";
|
||||||
|
"file" = "Файл";
|
||||||
|
"tags" = "Теги";
|
||||||
|
"owner_is_hidden" = "Автор скрыт";
|
||||||
|
"accessbility" = "Доступность";
|
||||||
|
"download_file" = "Скачать файл";
|
||||||
|
"remove" = "Удалить";
|
||||||
|
|
||||||
|
"document" = "Документ";
|
||||||
|
"documents_all" = "Все документы";
|
||||||
|
"document_type_0" = "Все";
|
||||||
|
"document_type_1" = "Текстовые";
|
||||||
|
"document_type_2" = "Архивы";
|
||||||
|
"document_type_3" = "GIF";
|
||||||
|
"document_type_4" = "Изображения";
|
||||||
|
"document_type_5" = "Аудио";
|
||||||
|
"document_type_6" = "Видео";
|
||||||
|
"document_type_7" = "Книги";
|
||||||
|
"document_type_8" = "Остальные";
|
||||||
|
|
||||||
|
"documents_one" = "$1 документ";
|
||||||
|
"documents_few" = "$1 документа";
|
||||||
|
"documents_many" = "$1 документов";
|
||||||
|
"documents_other" = "$1 документов";
|
||||||
|
"documents_zero" = "$1 документов";
|
||||||
|
|
||||||
|
"you_have_x_documents_one" = "У Вас $1 документ";
|
||||||
|
"you_have_x_documents_few" = "У Вас $1 документа";
|
||||||
|
"you_have_x_documents_many" = "У Вас $1 документов";
|
||||||
|
"you_have_x_documents_other" = "У Вас $1 документов";
|
||||||
|
"you_have_x_documents_zero" = "У Вас $1 документов";
|
||||||
|
|
||||||
|
"group_has_x_documents_one" = "У этой группы $1 документ";
|
||||||
|
"group_has_x_documents_few" = "У этой группы $1 документа";
|
||||||
|
"group_has_x_documents_many" = "У этой группы $1 документов";
|
||||||
|
"group_has_x_documents_other" = "У этой группы $1 документов";
|
||||||
|
"group_has_x_documents_zero" = "У этой группы $1 документов";
|
||||||
|
|
||||||
|
"x_documents_in_tab_one" = "В этой вкладке $1 документ";
|
||||||
|
"x_documents_in_tab_few" = "В этой вкладке $1 документа";
|
||||||
|
"x_documents_in_tab_many" = "В этой вкладке $1 документов";
|
||||||
|
"x_documents_in_tab_other" = "В этой вкладке $1 документов";
|
||||||
|
"x_documents_in_tab_zero" = "В этой вкладке $1 документов";
|
||||||
|
|
||||||
|
"there_is_no_documents_alright" = "Здесь нет документов.";
|
||||||
|
"limitations_file_limit_size" = "Файл не должен превышать $1 МБ";
|
||||||
|
"limitations_file_allowed_formats" = "Разрешены следующие типы файлов";
|
||||||
|
"limitations_file_author_rights" = "Файл не должен нарушать авторские права и правила сайта";
|
||||||
|
"select_file_fp" = "Выбрать файл";
|
||||||
|
"error_file_too_big" = "Файл слишком большой.";
|
||||||
|
"error_file_invalid_format" = "Формат файла не разрешён.";
|
||||||
|
"error_file_adding_copied" = "Не удалось добавить файл; он уже добавлен.";
|
||||||
|
"error_file_preview" = "Не удалось загрузить файл: изображение имеет странности.";
|
||||||
|
|
||||||
|
"private_document" = "Приватный (по ссылке)";
|
||||||
|
"public_document" = "Публичный";
|
||||||
|
"documents_sort_add" = "По дате добавления";
|
||||||
|
"documents_sort_alphabet" = "A-Z/А-Я";
|
||||||
|
"documents_sort_size" = "По размеру";
|
||||||
|
"select_doc" = "Выбор документа";
|
||||||
|
"no_documents" = "Документов нет";
|
||||||
|
"go_to_my_documents" = "Перейти к своим документам";
|
||||||
|
|
|
@ -22,6 +22,9 @@ openvk:
|
||||||
photoSaving: "quick"
|
photoSaving: "quick"
|
||||||
videos:
|
videos:
|
||||||
disableUploading: false
|
disableUploading: false
|
||||||
|
docs:
|
||||||
|
maxSize: 10 # in megabytes
|
||||||
|
allowedFormats: ["jpg", "jpeg", "png", "gif", "webp", "psd", "aep", "docx", "doc", "odt", "txt", "md", "pptx", "ppt", "xls", "xlsx", "pdf", "djvu", "fb2", "ps", "apk", "zip", "7z", "mp4", "avi", "mp3", "flac"]
|
||||||
apps:
|
apps:
|
||||||
withdrawTax: 8
|
withdrawTax: 8
|
||||||
security:
|
security:
|
||||||
|
|
|
@ -8,10 +8,14 @@ html {
|
||||||
|
|
||||||
body,
|
body,
|
||||||
#backdropDripper,
|
#backdropDripper,
|
||||||
#standaloneCommentBox,
|
#standaloneCommentBox {
|
||||||
|
background-color: #0e0b1a;
|
||||||
|
color: #c6d2e8;
|
||||||
|
}
|
||||||
|
|
||||||
.ovk-photo-view,
|
.ovk-photo-view,
|
||||||
.articleView {
|
.articleView {
|
||||||
background-color: #0e0b1a;
|
background-color: #100539;
|
||||||
color: #c6d2e8;
|
color: #c6d2e8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -635,3 +639,24 @@ ul {
|
||||||
border: 1px solid #383052;
|
border: 1px solid #383052;
|
||||||
background: #1e1b2a;
|
background: #1e1b2a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#docs_page_wrapper .docs_page_search {
|
||||||
|
background: #1d1a27;
|
||||||
|
border-bottom: #2a2841 solid 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docListViewItem {
|
||||||
|
border-bottom-color: #2a2841;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docListViewItem:hover, .attachButton:hover {
|
||||||
|
background: #271c48;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docListViewItem .doc_icon {
|
||||||
|
background: #33255e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docListViewItem .doc_content b {
|
||||||
|
color: #7c94c5;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue