From a205f50f92f0de6d28392beef59055d30ca53851 Mon Sep 17 00:00:00 2001 From: Ruslan Isaev Date: Tue, 3 Feb 2026 23:53:27 +0300 Subject: [PATCH 01/22] start preparing to add messenger attachments --- VKAPI/Handlers/Messages.php | 1 - Web/Models/Entities/Message.php | 39 +++++++++++++++-- Web/Presenters/MessengerPresenter.php | 16 +++++++ Web/Presenters/templates/Messenger/App.latte | 44 ++++++++++++++++++++ 4 files changed, 96 insertions(+), 4 deletions(-) diff --git a/VKAPI/Handlers/Messages.php b/VKAPI/Handlers/Messages.php index b3c4c1b83..df654ccb1 100644 --- a/VKAPI/Handlers/Messages.php +++ b/VKAPI/Handlers/Messages.php @@ -141,7 +141,6 @@ public function send( } elseif (!empty($attachment)) { $attachs = parseAttachments($attachment); - # Работают только фотки, остальное просто не будет отображаться. if (sizeof($attachs) >= 10) { $this->fail(15, "Too many attachments"); } diff --git a/Web/Models/Entities/Message.php b/Web/Models/Entities/Message.php index 1d3364c5b..0314d5802 100644 --- a/Web/Models/Entities/Message.php +++ b/Web/Models/Entities/Message.php @@ -7,7 +7,7 @@ use Chandler\Database\DatabaseConnection; use openvk\Web\Models\Repositories\Clubs; use openvk\Web\Models\Repositories\Users; -use openvk\Web\Models\Entities\Photo; +use openvk\Web\Models\Entities\{Photo, Video, Audio, Note, Document}; use openvk\Web\Models\RowModel; use openvk\Web\Util\DateTime; @@ -136,12 +136,45 @@ public function simplify(): array "caption" => $attachment->getDescription(), ], ]; + } elseif ($attachment instanceof Video) { + $attachments[] = [ + "type" => "video", + "link" => "/video" . $attachment->getPrettyId(), + "video" => [ + "url" => $attachment->getURL(), + "name" => $attachment->getName(), + "length" => $attachment->getLength(), + ], + ]; + } elseif ($attachment instanceof Audio) { + $attachments[] = [ + "type" => "audio", + "link" => "/audio" . $attachment->getPrettyId(), + "audio" => [ + "name" => $attachment->getName(), + "artist" => $attachment->getPerformer(), + ], + ]; + } elseif ($attachment instanceof Note) { + $attachments[] = [ + "type" => "note", + "link" => "/note" . $attachment->getId(), + "note" => [ + "name" => $attachment->getName(), + ], + ]; + } elseif ($attachment instanceof Document) { + $attachments[] = [ + "type" => "doc", + "link" => "/doc" . $attachment->getPrettyId(), + "document" => [ + "name" => $attachment->getName(), + ], + ]; } else { $attachments[] = [ "type" => "unknown", ]; - - # throw new \Exception("Unknown attachment type: " . get_class($attachment)); } } diff --git a/Web/Presenters/MessengerPresenter.php b/Web/Presenters/MessengerPresenter.php index 0de178c0b..bee41650b 100644 --- a/Web/Presenters/MessengerPresenter.php +++ b/Web/Presenters/MessengerPresenter.php @@ -166,11 +166,27 @@ public function renderApiWriteMessage(int $sel): void exit(); } + $attachments = []; + if (!empty($this->postParam("attachments"))) { + $attachments_array = array_slice(explode(",", $this->postParam("attachments")), 0, OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["maxAttachments"]); + if (sizeof($attachments_array) > 0) { + $attachments = parseAttachments($attachments_array, ['photo', 'video', 'audio', 'note', 'doc']); + } + } + $cor = new Correspondence($this->user->identity, $sel); $msg = new Message(); $msg->setContent($this->postParam("content")); $cor->sendMessage($msg); + foreach ($attachments as $attachment) { + if (!$attachment || $attachment->isDeleted() || !$attachment->canBeViewedBy($this->user->identity)) { + continue; + } + + $msg->attach($attachment); + } + header("HTTP/1.1 202 Accepted"); header("Content-Type: application/json"); exit(json_encode($msg->simplify())); diff --git a/Web/Presenters/templates/Messenger/App.latte b/Web/Presenters/templates/Messenger/App.latte index 9cac5ff5f..a1693d34e 100644 --- a/Web/Presenters/templates/Messenger/App.latte +++ b/Web/Presenters/templates/Messenger/App.latte @@ -33,6 +33,34 @@ +
+ +
+
+ +
+
+
+
+
+ + +
+
+
+ +
+ +
+
+
+
+ +
+ +
+
+
@@ -238,6 +266,22 @@ msgData.set("content", content); msgData.set("hash", {$csrfToken}); + let attachmentElements = document.querySelectorAll("[data-att-id]"); + let attachmentIds = []; + if(attachmentElements && attachmentElements.length > 0) { + attachmentElements.forEach(el => { + let type = el.getAttribute("data-att-type"); + let id = el.getAttribute("data-att-id"); + if(type && id) { + attachmentIds.push(type + id); + } + }); + } + + if(attachmentIds.length > 0) { + msgData.set("attachments", attachmentIds.join(",")); + } + let that = this; let xhr = new XMLHttpRequest(); xhr.open("POST", "/im/api/messages" + {$correspondent->getId()} + "/create.json", true); From dbb9b1f4bdf640933aa5989267fa424e5491a753 Mon Sep 17 00:00:00 2001 From: Ruslan Isaev Date: Wed, 4 Feb 2026 14:20:19 +0300 Subject: [PATCH 02/22] try to fix video player in messages --- Web/Models/Entities/Message.php | 9 ++++++--- Web/Presenters/templates/Messenger/App.latte | 14 +++++++++----- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/Web/Models/Entities/Message.php b/Web/Models/Entities/Message.php index 0314d5802..df140cda4 100644 --- a/Web/Models/Entities/Message.php +++ b/Web/Models/Entities/Message.php @@ -141,9 +141,12 @@ public function simplify(): array "type" => "video", "link" => "/video" . $attachment->getPrettyId(), "video" => [ - "url" => $attachment->getURL(), - "name" => $attachment->getName(), - "length" => $attachment->getLength(), + "url" => $attachment->getURL(), + "name" => $attachment->getName(), + "length" => $attachment->getLength(), + "formatted_length" => $attachment->getFormattedLength(), + "thumbnail" => $attachment->getThumbnailURL(), + "author" => $attachment->getOwner()->getCanonicalName(), ], ]; } elseif ($attachment instanceof Audio) { diff --git a/Web/Presenters/templates/Messenger/App.latte b/Web/Presenters/templates/Messenger/App.latte index a1693d34e..308c8e6e4 100644 --- a/Web/Presenters/templates/Messenger/App.latte +++ b/Web/Presenters/templates/Messenger/App.latte @@ -34,12 +34,16 @@
- -
-
- +
+
+
-
+
+
+
+ + +
From bee3dd54b1887d7e41a2245042e8b4b22acd434a Mon Sep 17 00:00:00 2001 From: Ruslan Isaev Date: Wed, 4 Feb 2026 16:00:50 +0300 Subject: [PATCH 03/22] fix attachment width in messenger --- Web/static/css/main.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Web/static/css/main.css b/Web/static/css/main.css index d8d455b05..5a870984f 100644 --- a/Web/static/css/main.css +++ b/Web/static/css/main.css @@ -1334,7 +1334,7 @@ table.User { } .messenger-app--messages---message ._content .attachments>.msg-attach-j * { - max-width: 86%; + max-width: 100%; } .messenger-app--messages---message ._content .attachments:not(:empty) { From f0aa83a4dac627baf0285d54251e5345aaf39e44 Mon Sep 17 00:00:00 2001 From: Ruslan Isaev Date: Wed, 4 Feb 2026 18:17:42 +0300 Subject: [PATCH 04/22] hopefully fix notes and documents --- Web/Models/Entities/Message.php | 2 ++ Web/Presenters/templates/Messenger/App.latte | 33 +++++++++++++++----- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/Web/Models/Entities/Message.php b/Web/Models/Entities/Message.php index df140cda4..e88b92bd1 100644 --- a/Web/Models/Entities/Message.php +++ b/Web/Models/Entities/Message.php @@ -162,6 +162,7 @@ public function simplify(): array $attachments[] = [ "type" => "note", "link" => "/note" . $attachment->getId(), + "id" => $attachment->getId(), "note" => [ "name" => $attachment->getName(), ], @@ -170,6 +171,7 @@ public function simplify(): array $attachments[] = [ "type" => "doc", "link" => "/doc" . $attachment->getPrettyId(), + "id" => $attachment->getPrettyId(), "document" => [ "name" => $attachment->getName(), ], diff --git a/Web/Presenters/templates/Messenger/App.latte b/Web/Presenters/templates/Messenger/App.latte index 308c8e6e4..99707d14f 100644 --- a/Web/Presenters/templates/Messenger/App.latte +++ b/Web/Presenters/templates/Messenger/App.latte @@ -52,18 +52,35 @@
From e61cb63c3c4e95c8947a941b8571c48875dde9a5 Mon Sep 17 00:00:00 2001 From: Ruslan Isaev Date: Wed, 4 Feb 2026 18:42:14 +0300 Subject: [PATCH 05/22] make attachments modal --- Web/Models/Entities/Message.php | 2 + Web/Presenters/templates/Messenger/App.latte | 48 ++++++++++++++------ 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/Web/Models/Entities/Message.php b/Web/Models/Entities/Message.php index e88b92bd1..76158e2c3 100644 --- a/Web/Models/Entities/Message.php +++ b/Web/Models/Entities/Message.php @@ -131,6 +131,7 @@ public function simplify(): array $attachments[] = [ "type" => "photo", "link" => "/photo" . $attachment->getPrettyId(), + "id" => $attachment->getPrettyId(), "photo" => [ "url" => $attachment->getURL(), "caption" => $attachment->getDescription(), @@ -140,6 +141,7 @@ public function simplify(): array $attachments[] = [ "type" => "video", "link" => "/video" . $attachment->getPrettyId(), + "id" => $attachment->getOwner()->getId() . "_" . $attachment->getId(), "video" => [ "url" => $attachment->getURL(), "name" => $attachment->getName(), diff --git a/Web/Presenters/templates/Messenger/App.latte b/Web/Presenters/templates/Messenger/App.latte index 99707d14f..f85b52076 100644 --- a/Web/Presenters/templates/Messenger/App.latte +++ b/Web/Presenters/templates/Messenger/App.latte @@ -29,8 +29,8 @@
@@ -41,7 +41,7 @@
- +
@@ -57,29 +57,23 @@
Note - +
-
- -
- -
- Document - -
-
+
@@ -338,5 +332,29 @@ } window.Msg = new Messenger([]); + + document.querySelector(".messenger-app--messages").addEventListener("click", (e) => { + if(e.target.closest(".photoMessengerOpen")) { + e.preventDefault(); + e.stopPropagation(); + const photoLink = e.target.closest(".photoMessengerOpen"); + const photoId = photoLink.getAttribute("data-photo-id"); + const photoImg = photoLink.querySelector("img"); + if(photoImg && photoId) { + OpenMiniature(e, photoImg.src, null, photoId, null); + } + } + + if(e.target.closest(".videoMessengerOpen")) { + e.preventDefault(); + e.stopPropagation(); + const videoLink = e.target.closest(".videoMessengerOpen"); + const videoId = videoLink.getAttribute("data-video-id"); + if(videoId && videoId.indexOf("_") !== -1) { + const parts = videoId.split("_"); + OpenVideo(parts); + } + } + }); {/block} From 4715c24ab0b6cc5120ab844cacccbc8de7b9f39d Mon Sep 17 00:00:00 2001 From: Ruslan Isaev Date: Wed, 4 Feb 2026 19:22:41 +0300 Subject: [PATCH 06/22] fix not found error for videos and fix note modal displaying --- Web/Models/Entities/Message.php | 2 +- Web/Presenters/templates/Messenger/App.latte | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Web/Models/Entities/Message.php b/Web/Models/Entities/Message.php index 76158e2c3..27478adb5 100644 --- a/Web/Models/Entities/Message.php +++ b/Web/Models/Entities/Message.php @@ -141,7 +141,7 @@ public function simplify(): array $attachments[] = [ "type" => "video", "link" => "/video" . $attachment->getPrettyId(), - "id" => $attachment->getOwner()->getId() . "_" . $attachment->getId(), + "id" => $attachment->getOwner()->getId() . "_" . $attachment->getVirtualId(), "video" => [ "url" => $attachment->getURL(), "name" => $attachment->getName(), diff --git a/Web/Presenters/templates/Messenger/App.latte b/Web/Presenters/templates/Messenger/App.latte index f85b52076..f42f2a8bc 100644 --- a/Web/Presenters/templates/Messenger/App.latte +++ b/Web/Presenters/templates/Messenger/App.latte @@ -57,7 +57,7 @@
Note - +
From c00ca88de795b8a2b1dbe82a16954b94ba954976 Mon Sep 17 00:00:00 2001 From: Ruslan Isaev Date: Thu, 5 Feb 2026 13:04:03 +0300 Subject: [PATCH 07/22] fix notes and document localization and fix note modal view --- Web/Presenters/templates/Messenger/App.latte | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Web/Presenters/templates/Messenger/App.latte b/Web/Presenters/templates/Messenger/App.latte index f42f2a8bc..799e9d64b 100644 --- a/Web/Presenters/templates/Messenger/App.latte +++ b/Web/Presenters/templates/Messenger/App.latte @@ -56,8 +56,10 @@
- Note - + {_note} + + +
@@ -69,7 +71,7 @@
- Document + {_document}
From ff0152fc753090e18617dfe9a53c2d743774ef92 Mon Sep 17 00:00:00 2001 From: Ruslan Isaev Date: Thu, 5 Feb 2026 13:04:35 +0300 Subject: [PATCH 08/22] start adding attachment menu in messenger --- Web/Presenters/templates/Messenger/App.latte | 31 ++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/Web/Presenters/templates/Messenger/App.latte b/Web/Presenters/templates/Messenger/App.latte index 799e9d64b..aaa1490b4 100644 --- a/Web/Presenters/templates/Messenger/App.latte +++ b/Web/Presenters/templates/Messenger/App.latte @@ -95,6 +95,37 @@ data-bind="value: messageContent, event: { keydown: onTextareaKeyPress }" name="message" placeholder="{_enter_message}"> + + + {$correspondent->getCanonicalName()} From 4b81bef4e7f366269cfc3ba09d840e2988e77290 Mon Sep 17 00:00:00 2001 From: Ruslan Isaev Date: Wed, 4 Feb 2026 22:27:33 +0300 Subject: [PATCH 09/22] fix document rendering and previews --- Web/Models/Entities/Message.php | 12 ++ Web/Presenters/templates/Messenger/App.latte | 196 ++++++++++++++----- 2 files changed, 162 insertions(+), 46 deletions(-) diff --git a/Web/Models/Entities/Message.php b/Web/Models/Entities/Message.php index 27478adb5..d7068e3cd 100644 --- a/Web/Models/Entities/Message.php +++ b/Web/Models/Entities/Message.php @@ -170,12 +170,24 @@ public function simplify(): array ], ]; } elseif ($attachment instanceof Document) { + $previewData = null; + if ($attachment->hasPreview()) { + $previewData = [ + "tiny" => $attachment->getPreview()->getURLBySizeId('tiny'), + "medium" => $attachment->getPreview()->getURLBySizeId('medium'), + ]; + } + $attachments[] = [ "type" => "doc", "link" => "/doc" . $attachment->getPrettyId(), "id" => $attachment->getPrettyId(), "document" => [ "name" => $attachment->getName(), + "ext" => $attachment->getFileExtension(), + "size_str" => readable_filesize($attachment->getFilesize()), + "preview" => $previewData, + "pub_time" => (string) $attachment->getPublicationTime(), ], ]; } else { diff --git a/Web/Presenters/templates/Messenger/App.latte b/Web/Presenters/templates/Messenger/App.latte index aaa1490b4..d6cc3b052 100644 --- a/Web/Presenters/templates/Messenger/App.latte +++ b/Web/Presenters/templates/Messenger/App.latte @@ -52,30 +52,61 @@
-
+
-
+
{_note} - +
-
+ @@ -87,46 +118,61 @@
+
{if $correspondent->getId() === $thisUser->getId() || $correspondent->getPrivacyPermission('messages.write', $thisUser)} {$thisUser->getCanonicalName()} {$correspondent->getCanonicalName()} {else} @@ -314,7 +360,8 @@ msgData.set("content", content); msgData.set("hash", {$csrfToken}); - let attachmentElements = document.querySelectorAll("[data-att-id]"); + const writeBox = document.querySelector('.messenger-app--input #write'); + let attachmentElements = writeBox ? writeBox.querySelectorAll("[data-att-id]") : document.querySelectorAll("[data-att-id]"); let attachmentIds = []; if(attachmentElements && attachmentElements.length > 0) { attachmentElements.forEach(el => { @@ -358,6 +405,19 @@ console.debug("Message sent, updating view."); that._patchMessage(tempId, JSON.parse(xhr.responseText)); + + const writeBox = document.querySelector('#write'); + if(writeBox) { + writeBox.querySelector('.post-horizontal').innerHTML = ''; + writeBox.querySelector('.post-vertical').innerHTML = ''; + } + + const previewBox = document.querySelector('#messenger-attachment-preview'); + if(previewBox) { + previewBox.querySelector('.post-horizontal').innerHTML = ''; + previewBox.querySelector('.post-vertical').innerHTML = ''; + previewBox.style.display = 'none'; + } }); xhr.send(msgData); console.debug("Message sent, awaiting response."); @@ -389,5 +449,49 @@ } } }); + + (function() { + function normalizeUploadItem(el) { + if(!el) return; + const items = el.querySelectorAll('.upload-item'); + items.forEach(a => { + const type = a.getAttribute('data-type') || a.getAttribute('data-att-type'); + const id = a.getAttribute('data-id') || a.getAttribute('data-att-id') || a.getAttribute('data-prettiest-id') || a.getAttribute('data-pretty-id'); + if(type && id) { + a.setAttribute('data-att-type', type); + a.setAttribute('data-att-id', id); + + const previewBox = document.querySelector('#messenger-attachment-preview'); + if(previewBox) { + const container = type === 'doc' || type === 'note' ? + previewBox.querySelector('.post-vertical') : + previewBox.querySelector('.post-horizontal'); + if(container && !container.querySelector(`[data-att-id="${ id }"]`)) { + container.appendChild(a.cloneNode(true)); + previewBox.style.display = 'block'; + } + } + } + }); + } + const write = document.querySelector('#write'); + if(write) normalizeUploadItem(write); + const observer = new MutationObserver(mutations => { + mutations.forEach(m => { + if(m.addedNodes && m.addedNodes.length) { + m.addedNodes.forEach(node => { + if(node.nodeType === 1) { + if(node.matches && node.matches('.upload-item')) { + normalizeUploadItem(node.parentNode || node); + } else { + normalizeUploadItem(node); + } + } + }); + } + }); + }); + if(write) observer.observe(write, { childList: true, subtree: true }); + })(); {/block} From e3f59e4c4b4ce55d256dce7b56f5e3ba49102ca8 Mon Sep 17 00:00:00 2001 From: Ruslan Isaev Date: Thu, 5 Feb 2026 14:36:10 +0300 Subject: [PATCH 10/22] fix removing preview items --- Web/Presenters/templates/Messenger/App.latte | 23 ++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Web/Presenters/templates/Messenger/App.latte b/Web/Presenters/templates/Messenger/App.latte index d6cc3b052..c06761d87 100644 --- a/Web/Presenters/templates/Messenger/App.latte +++ b/Web/Presenters/templates/Messenger/App.latte @@ -489,6 +489,29 @@ } }); } + + if(m.removedNodes && m.removedNodes.length) { + const previewBox = document.querySelector('#messenger-attachment-preview'); + if (!previewBox) return; + + m.removedNodes.forEach(node => { + if(node.nodeType === 1 && node.matches && node.matches('.upload-item')) { + const type = node.getAttribute('data-att-type') || node.getAttribute('data-type'); + const id = node.getAttribute('data-att-id') || node.getAttribute('data-id'); + + if(type && id) { + const itemToRemove = previewBox.querySelector(`[data-att-type="${ type }"][data-att-id="${ id }"], [data-type="${ type }"][data-id="${ id }"]`); + if(itemToRemove) { + itemToRemove.remove(); + } + } + } + }); + + if(previewBox.querySelectorAll('.upload-item').length === 0) { + previewBox.style.display = 'none'; + } + } }); }); if(write) observer.observe(write, { childList: true, subtree: true }); From ec5da08e58dd50421daab8ec50b992a932c7adaf Mon Sep 17 00:00:00 2001 From: Ruslan Isaev Date: Fri, 6 Feb 2026 17:38:21 +0300 Subject: [PATCH 11/22] fix video player population with controls --- Web/Presenters/templates/Messenger/App.latte | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Web/Presenters/templates/Messenger/App.latte b/Web/Presenters/templates/Messenger/App.latte index c06761d87..1369ca0bb 100644 --- a/Web/Presenters/templates/Messenger/App.latte +++ b/Web/Presenters/templates/Messenger/App.latte @@ -196,6 +196,12 @@ }; this.loadHistory = _ => { window.Msg._loadHistory(); + document.body.querySelectorAll(".bsdn").forEach(el => { + if(!el.querySelector(".bdsn-hydrated")) { + bsdnInitElement(el); + } + } + ); }; this.onMessagesScroll = (model, e) => { From 875e79bfbd8880c5394f085c124c5a1dd37e1c32 Mon Sep 17 00:00:00 2001 From: Ruslan Isaev Date: Sat, 7 Feb 2026 16:48:00 +0300 Subject: [PATCH 12/22] implement audio attaching --- Web/Presenters/MessengerPresenter.php | 37 ++++++++++++++++++-- Web/Presenters/templates/Messenger/App.latte | 5 ++- Web/static/css/main.css | 5 +++ 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/Web/Presenters/MessengerPresenter.php b/Web/Presenters/MessengerPresenter.php index bee41650b..443155d1b 100644 --- a/Web/Presenters/MessengerPresenter.php +++ b/Web/Presenters/MessengerPresenter.php @@ -143,7 +143,9 @@ public function renderApiGetMessages(int $sel, int $lastMsg): void $messages = []; $correspondence = new Correspondence($this->user->identity, $correspondent); foreach ($correspondence->getMessages(1, $lastMsg === 0 ? null : $lastMsg, null, 0) as $message) { - $messages[] = $message->simplify(); + $simple = $message->simplify(); + $this->enrichAttachmentsWithHTML($message, $simple); + $messages[] = $simple; } header("Content-Type: application/json"); @@ -189,6 +191,37 @@ public function renderApiWriteMessage(int $sel): void header("HTTP/1.1 202 Accepted"); header("Content-Type: application/json"); - exit(json_encode($msg->simplify())); + $simple = $msg->simplify(); + $this->enrichAttachmentsWithHTML($msg, $simple); + exit(json_encode($simple)); + } + + private function enrichAttachmentsWithHTML(Message $messageObj, array &$simplifiedArray): void + { + $children = iterator_to_array($messageObj->getChildren()); + + foreach ($simplifiedArray['attachments'] as $index => &$attachmentData) { + if (!isset($children[$index])) continue; + + $originalObj = $children[$index]; + $html = ""; + + if ($attachmentData['type'] === 'audio') { + $html = $this->getTemplatingEngine()->renderToString( + # костыль жоский + dirname(__FILE__) . '/templates/Audio/player.latte', + [ + 'audio' => $originalObj, + 'thisUser' => $this->user->identity, + 'hideButtons' => false, + 'club' => null + ] + ); + } + + if ($html !== "") { + $attachmentData['html'] = $html; + } + } } } diff --git a/Web/Presenters/templates/Messenger/App.latte b/Web/Presenters/templates/Messenger/App.latte index 1369ca0bb..1568bf5de 100644 --- a/Web/Presenters/templates/Messenger/App.latte +++ b/Web/Presenters/templates/Messenger/App.latte @@ -46,9 +46,8 @@
-
- - +
+
diff --git a/Web/static/css/main.css b/Web/static/css/main.css index 5a870984f..62b0fbc53 100644 --- a/Web/static/css/main.css +++ b/Web/static/css/main.css @@ -1323,6 +1323,11 @@ table.User { display: block; } +.messenger-app--messages---message ._content .audioEmbed .title { + display: inline; + width: auto; +} + .messenger-app--messages---message ._content span { width: 100%; height: fit-content; From d05b0b310daa974c9c7d48b2a620644da2fe8b0b Mon Sep 17 00:00:00 2001 From: Ruslan Isaev Date: Wed, 11 Feb 2026 19:55:42 +0300 Subject: [PATCH 13/22] fix messenger video hydration and note displaying --- Web/Presenters/templates/Messenger/App.latte | 62 ++++++++++++-------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/Web/Presenters/templates/Messenger/App.latte b/Web/Presenters/templates/Messenger/App.latte index 1568bf5de..54cdf4662 100644 --- a/Web/Presenters/templates/Messenger/App.latte +++ b/Web/Presenters/templates/Messenger/App.latte @@ -119,7 +119,7 @@
{if $correspondent->getId() === $thisUser->getId() || $correspondent->getPrivacyPermission('messages.write', $thisUser)} @@ -411,6 +411,13 @@ console.debug("Message sent, updating view."); that._patchMessage(tempId, JSON.parse(xhr.responseText)); + document.body.querySelectorAll(".bsdn").forEach(el => { + if(!el.querySelector(".bdsn-hydrated")) { + bsdnInitElement(el); + } + } + ); + const writeBox = document.querySelector('#write'); if(writeBox) { writeBox.querySelector('.post-horizontal').innerHTML = ''; @@ -456,29 +463,38 @@ }); (function() { + function syncItemToPreview(item) { + if(!item) return; + + const type = item.getAttribute('data-type') || item.getAttribute('data-att-type'); + const id = item.getAttribute('data-id') || item.getAttribute('data-att-id') || item.getAttribute('data-prettiest-id') || item.getAttribute('data-pretty-id'); + + if(type && id) { + item.setAttribute('data-att-type', type); + item.setAttribute('data-att-id', id); + + const previewBox = document.querySelector('#messenger-attachment-preview'); + if(previewBox) { + const isVertical = type === 'doc' || type === 'note' || type === 'audio'; + const container = isVertical ? + previewBox.querySelector('.post-vertical') : + previewBox.querySelector('.post-horizontal'); + if(container && !container.querySelector(`[data-att-id="${ id }"]`)) { + container.appendChild(item.cloneNode(true)); + previewBox.style.display = 'block'; + } + } + } + } + function normalizeUploadItem(el) { if(!el) return; - const items = el.querySelectorAll('.upload-item'); + const items = el.querySelectorAll('.upload-item, .vertical-attachment'); items.forEach(a => { - const type = a.getAttribute('data-type') || a.getAttribute('data-att-type'); - const id = a.getAttribute('data-id') || a.getAttribute('data-att-id') || a.getAttribute('data-prettiest-id') || a.getAttribute('data-pretty-id'); - if(type && id) { - a.setAttribute('data-att-type', type); - a.setAttribute('data-att-id', id); - - const previewBox = document.querySelector('#messenger-attachment-preview'); - if(previewBox) { - const container = type === 'doc' || type === 'note' ? - previewBox.querySelector('.post-vertical') : - previewBox.querySelector('.post-horizontal'); - if(container && !container.querySelector(`[data-att-id="${ id }"]`)) { - container.appendChild(a.cloneNode(true)); - previewBox.style.display = 'block'; - } - } - } + syncItemToPreview(a); }); } + const write = document.querySelector('#write'); if(write) normalizeUploadItem(write); const observer = new MutationObserver(mutations => { @@ -486,8 +502,8 @@ if(m.addedNodes && m.addedNodes.length) { m.addedNodes.forEach(node => { if(node.nodeType === 1) { - if(node.matches && node.matches('.upload-item')) { - normalizeUploadItem(node.parentNode || node); + if(node.matches && (node.matches('.upload-item') || node.matches('.vertical-attachment'))) { + syncItemToPreview(node); } else { normalizeUploadItem(node); } @@ -500,7 +516,7 @@ if (!previewBox) return; m.removedNodes.forEach(node => { - if(node.nodeType === 1 && node.matches && node.matches('.upload-item')) { + if(node.nodeType === 1 && node.matches && (node.matches('.upload-item') || node.matches('.vertical-attachment'))) { const type = node.getAttribute('data-att-type') || node.getAttribute('data-type'); const id = node.getAttribute('data-att-id') || node.getAttribute('data-id'); @@ -513,7 +529,7 @@ } }); - if(previewBox.querySelectorAll('.upload-item').length === 0) { + if(previewBox.querySelectorAll('.upload-item, .vertical-attachment').length === 0) { previewBox.style.display = 'none'; } } From 79bf697699cd3cdd2491db840cd229a445b2f70f Mon Sep 17 00:00:00 2001 From: Ruslan Isaev Date: Thu, 12 Feb 2026 18:28:41 +0300 Subject: [PATCH 14/22] fix attachment menu toggler position --- Web/Presenters/templates/Messenger/App.latte | 54 ++++++++++---------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/Web/Presenters/templates/Messenger/App.latte b/Web/Presenters/templates/Messenger/App.latte index 54cdf4662..b01d29025 100644 --- a/Web/Presenters/templates/Messenger/App.latte +++ b/Web/Presenters/templates/Messenger/App.latte @@ -141,34 +141,36 @@ - - {_attach} - -
From 3d8f237917b23722e2ef1eeec48f712a0b6f2d78 Mon Sep 17 00:00:00 2001 From: Ruslan Isaev Date: Thu, 12 Feb 2026 19:58:21 +0300 Subject: [PATCH 15/22] fix removing attachments from the hidden form --- Web/Presenters/templates/Messenger/App.latte | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Web/Presenters/templates/Messenger/App.latte b/Web/Presenters/templates/Messenger/App.latte index b01d29025..80518244d 100644 --- a/Web/Presenters/templates/Messenger/App.latte +++ b/Web/Presenters/templates/Messenger/App.latte @@ -538,6 +538,31 @@ }); }); if(write) observer.observe(write, { childList: true, subtree: true }); + + const previewBox = document.querySelector('#messenger-attachment-preview'); + if(previewBox) { + previewBox.addEventListener('click', function(e) { + const target = e.target; + const deleteBtn = target.closest('.upload-delete') || target.closest('#small_remove_button') || target.closest('.vertical-attachment-remove'); + + if(deleteBtn) { + e.preventDefault(); + e.stopPropagation(); + + const item = deleteBtn.closest('.upload-item, .vertical-attachment'); + if(item) { + const type = item.getAttribute('data-att-type') || item.getAttribute('data-type'); + const id = item.getAttribute('data-att-id') || item.getAttribute('data-id'); + + if(type && id && write) { + const sourceItem = write.querySelector(`[data-att-type="${ type }"][data-att-id="${ id }"], [data-type="${ type }"][data-id="${ id }"]`); + if(sourceItem) + sourceItem.remove(); + } + } + } + }); + } })(); {/block} From ceef6e78dcbbf1eae6ad452fcebe99b86e66b2d0 Mon Sep 17 00:00:00 2001 From: Ruslan Isaev Date: Sat, 14 Feb 2026 18:24:23 +0300 Subject: [PATCH 16/22] make attachment preview include type attribute --- Web/Presenters/templates/Messenger/App.latte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Web/Presenters/templates/Messenger/App.latte b/Web/Presenters/templates/Messenger/App.latte index 80518244d..fc62c5ab0 100644 --- a/Web/Presenters/templates/Messenger/App.latte +++ b/Web/Presenters/templates/Messenger/App.latte @@ -481,7 +481,7 @@ const container = isVertical ? previewBox.querySelector('.post-vertical') : previewBox.querySelector('.post-horizontal'); - if(container && !container.querySelector(`[data-att-id="${ id }"]`)) { + if(container && !container.querySelector(`[data-att-type="${ type }"][data-att-id="${ id }"]`)) { container.appendChild(item.cloneNode(true)); previewBox.style.display = 'block'; } From acdca6dc8ca5c60898f49452d99ef719e7e7a0de Mon Sep 17 00:00:00 2001 From: Ruslan Isaev Date: Sat, 14 Feb 2026 19:36:57 +0300 Subject: [PATCH 17/22] =?UTF-8?q?=D1=83=D0=B1=D0=BB=D0=B0=D0=B6=D0=B0?= =?UTF-8?q?=D1=8E=20=D0=BB=D0=B8=D0=BD=D1=82=D0=B5=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Web/Presenters/MessengerPresenter.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Web/Presenters/MessengerPresenter.php b/Web/Presenters/MessengerPresenter.php index 443155d1b..8c3ed094e 100644 --- a/Web/Presenters/MessengerPresenter.php +++ b/Web/Presenters/MessengerPresenter.php @@ -201,7 +201,9 @@ private function enrichAttachmentsWithHTML(Message $messageObj, array &$simplifi $children = iterator_to_array($messageObj->getChildren()); foreach ($simplifiedArray['attachments'] as $index => &$attachmentData) { - if (!isset($children[$index])) continue; + if (!isset($children[$index])) { + continue; + } $originalObj = $children[$index]; $html = ""; @@ -214,7 +216,7 @@ private function enrichAttachmentsWithHTML(Message $messageObj, array &$simplifi 'audio' => $originalObj, 'thisUser' => $this->user->identity, 'hideButtons' => false, - 'club' => null + 'club' => null, ] ); } From 6cc09b85791f0f88c91aced8819f6c9a8806f125 Mon Sep 17 00:00:00 2001 From: Ruslan Isaev Date: Sat, 14 Feb 2026 19:38:36 +0300 Subject: [PATCH 18/22] =?UTF-8?q?=D0=B4=D0=B0=20=D0=BA=D0=B0=D0=BF=D0=B5?= =?UTF-8?q?=D1=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Web/Presenters/MessengerPresenter.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Web/Presenters/MessengerPresenter.php b/Web/Presenters/MessengerPresenter.php index 8c3ed094e..6017c3a74 100644 --- a/Web/Presenters/MessengerPresenter.php +++ b/Web/Presenters/MessengerPresenter.php @@ -199,12 +199,12 @@ public function renderApiWriteMessage(int $sel): void private function enrichAttachmentsWithHTML(Message $messageObj, array &$simplifiedArray): void { $children = iterator_to_array($messageObj->getChildren()); - + foreach ($simplifiedArray['attachments'] as $index => &$attachmentData) { if (!isset($children[$index])) { continue; } - + $originalObj = $children[$index]; $html = ""; From e1c13f27b25de3f045e4ef031e47f8d14007ed51 Mon Sep 17 00:00:00 2001 From: Ruslan Isaev Date: Sat, 25 Apr 2026 14:58:50 +0300 Subject: [PATCH 19/22] prevent message attachments from being publicly accessible --- Web/Models/Entities/Photo.php | 18 ++++++++ .../Entities/Traits/TMessageAttachment.php | 43 +++++++++++++++++++ Web/Models/Entities/Video.php | 18 ++++++++ Web/Models/Repositories/Photos.php | 2 + Web/Models/Repositories/Videos.php | 10 ++--- Web/Presenters/PhotosPresenter.php | 12 ++++-- Web/Presenters/VideosPresenter.php | 6 ++- Web/Presenters/templates/Messenger/App.latte | 2 +- Web/static/js/al_wall.js | 18 +++++--- .../00060-message-attachments-privacy.sql | 2 + 10 files changed, 114 insertions(+), 17 deletions(-) create mode 100644 Web/Models/Entities/Traits/TMessageAttachment.php create mode 100644 install/sqls/00060-message-attachments-privacy.sql diff --git a/Web/Models/Entities/Photo.php b/Web/Models/Entities/Photo.php index b0ee2e83e..9ef394b7d 100644 --- a/Web/Models/Entities/Photo.php +++ b/Web/Models/Entities/Photo.php @@ -9,12 +9,15 @@ use Nette\Utils\UnknownImageFileException; use openvk\Web\Models\Entities\Album; use openvk\Web\Models\Repositories\Albums; +use openvk\Web\Models\Entities\Traits\TMessageAttachment; use Chandler\Database\DatabaseConnection as DB; use Nette\InvalidStateException as ISE; use Nette\Utils\Image; class Photo extends Media { + use TMessageAttachment; + protected $tableName = "photos"; protected $fileExtension = "jpeg"; @@ -381,12 +384,27 @@ public function toVkApiStruct(bool $photo_sizes = true, bool $extended = false): return $res; } + public function isMessagePhoto(): bool + { + return (bool) $this->getRecord()->is_message_photo; + } + public function canBeViewedBy(?User $user = null): bool { if ($this->isDeleted() || $this->getOwner()->isDeleted()) { return false; } + if ($this->isMessagePhoto()) { + if (!$user) { + return false; + } + if ($user->getId() === $this->getOwner()->getId()) { + return true; + } + return $this->isViewableByMessageParticipant($user); + } + if (!is_null($this->getAlbum())) { return $this->getAlbum()->canBeViewedBy($user); } else { diff --git a/Web/Models/Entities/Traits/TMessageAttachment.php b/Web/Models/Entities/Traits/TMessageAttachment.php new file mode 100644 index 000000000..7331b1123 --- /dev/null +++ b/Web/Models/Entities/Traits/TMessageAttachment.php @@ -0,0 +1,43 @@ +getContext(); + $msgIds = []; + + foreach ($db->table("attachments") + ->select("target_id") + ->where("attachable_type", get_class($this)) + ->where("attachable_id", $this->getId()) + ->where("target_type", "openvk\\Web\\Models\\Entities\\Message") as $row) { + $msgIds[] = $row->target_id; + } + + if (empty($msgIds)) { + return false; + } + + return (bool) $db->table("messages") + ->where("id", $msgIds) + ->where("deleted", 0) + ->where( + "(sender_type = ? AND sender_id = ?) OR (recipient_type = ? AND recipient_id = ?)", + "openvk\\Web\\Models\\Entities\\User", $user->getId(), + "openvk\\Web\\Models\\Entities\\User", $user->getId() + ) + ->count("*"); + } +} diff --git a/Web/Models/Entities/Video.php b/Web/Models/Entities/Video.php index 8dbfb7a16..cb4aadb65 100644 --- a/Web/Models/Entities/Video.php +++ b/Web/Models/Entities/Video.php @@ -7,12 +7,15 @@ use openvk\Web\Util\Shell\Shell; use openvk\Web\Util\Shell\Exceptions\{ShellUnavailableException, UnknownCommandException}; use openvk\Web\Models\VideoDrivers\VideoDriver; +use openvk\Web\Models\Entities\Traits\TMessageAttachment; use Nette\InvalidStateException as ISE; define("VIDEOS_FRIENDLY_ERROR", "Uploads are disabled on this instance :<"); class Video extends Media { + use TMessageAttachment; + public const TYPE_DIRECT = 0; public const TYPE_EMBED = 1; public const TYPE_UNKNOWN = -1; @@ -339,12 +342,27 @@ public function getPageURL(): string return "/video" . $this->getPrettyId(); } + public function isMessageVideo(): bool + { + return (bool) $this->getRecord()->is_message_video; + } + public function canBeViewedBy(?User $user = null): bool { if ($this->isDeleted() || $this->getOwner()->isDeleted()) { return false; } + if ($this->isMessageVideo()) { + if (!$user) { + return false; + } + if ($user->getId() === $this->getOwner()->getId()) { + return true; + } + return $this->isViewableByMessageParticipant($user); + } + if (get_class($this->getOwner()) == "openvk\\Web\\Models\\Entities\\User") { return $this->getOwner()->canBeViewedBy($user) && $this->getOwner()->getPrivacyPermission('videos.read', $user); } else { diff --git a/Web/Models/Repositories/Photos.php b/Web/Models/Repositories/Photos.php index 4de620257..c4626e52d 100644 --- a/Web/Models/Repositories/Photos.php +++ b/Web/Models/Repositories/Photos.php @@ -52,6 +52,7 @@ public function getEveryUserPhoto(User $user, int $offset = 0, int $limit = 10): "system" => 0, "private" => 0, "anonymous" => 0, + "is_message_photo" => 0, ])->order("id DESC"); foreach ($photos->limit($limit, $offset) as $photo) { @@ -67,6 +68,7 @@ public function getUserPhotosCount(User $user) "system" => 0, "private" => 0, "anonymous" => 0, + "is_message_photo" => 0, ]); return sizeof($photos); diff --git a/Web/Models/Repositories/Videos.php b/Web/Models/Repositories/Videos.php index de8e3d95b..c7ec9e343 100644 --- a/Web/Models/Repositories/Videos.php +++ b/Web/Models/Repositories/Videos.php @@ -45,7 +45,7 @@ public function getByOwnerAndVID(int $owner, int $vId): ?Video public function getByUser(User $user, int $page = 1, ?int $perPage = null): \Traversable { $perPage ??= OPENVK_DEFAULT_PER_PAGE; - foreach ($this->videos->where("owner", $user->getId())->where(["deleted" => 0, "unlisted" => 0])->page($page, $perPage)->order("created DESC") as $video) { + foreach ($this->videos->where("owner", $user->getId())->where(["deleted" => 0, "unlisted" => 0, "is_message_video" => 0])->page($page, $perPage)->order("created DESC") as $video) { yield new Video($video); } } @@ -53,20 +53,20 @@ public function getByUser(User $user, int $page = 1, ?int $perPage = null): \Tra public function getByUserLimit(User $user, int $offset = 0, int $limit = 10): \Traversable { $perPage ??= OPENVK_DEFAULT_PER_PAGE; - foreach ($this->videos->where("owner", $user->getId())->where(["deleted" => 0, "unlisted" => 0])->limit($limit, $offset)->order("created DESC") as $video) { + foreach ($this->videos->where("owner", $user->getId())->where(["deleted" => 0, "unlisted" => 0, "is_message_video" => 0])->limit($limit, $offset)->order("created DESC") as $video) { yield new Video($video); } } public function getUserVideosCount(User $user): int { - return sizeof($this->videos->where("owner", $user->getId())->where(["deleted" => 0, "unlisted" => 0])); + return sizeof($this->videos->where("owner", $user->getId())->where(["deleted" => 0, "unlisted" => 0, "is_message_video" => 0])); } public function find(string $query = "", array $params = [], array $order = ['type' => 'id', 'invert' => false]): Util\EntityStream { $query = "%$query%"; - $result = $this->videos->where("CONCAT_WS(' ', name, description) LIKE ?", $query)->where("deleted", 0)->where("unlisted", 0); + $result = $this->videos->where("CONCAT_WS(' ', name, description) LIKE ?", $query)->where("deleted", 0)->where("unlisted", 0)->where("is_message_video", 0); $order_str = 'id'; switch ($order['type']) { @@ -101,7 +101,7 @@ public function find(string $query = "", array $params = [], array $order = ['ty public function getLastVideo(User $user) { - $video = $this->videos->where("owner", $user->getId())->where(["deleted" => 0, "unlisted" => 0])->order("id DESC")->fetch(); + $video = $this->videos->where("owner", $user->getId())->where(["deleted" => 0, "unlisted" => 0, "is_message_video" => 0])->order("id DESC")->fetch(); return new Video($video); } diff --git a/Web/Presenters/PhotosPresenter.php b/Web/Presenters/PhotosPresenter.php index 63abc53d5..b145ed539 100644 --- a/Web/Presenters/PhotosPresenter.php +++ b/Web/Presenters/PhotosPresenter.php @@ -277,9 +277,11 @@ public function renderUploadPhoto(): void $this->willExecuteWriteAction(true); $upload_context = $this->queryParam("upload_context"); + $isMessageUpload = $upload_context === "messages"; + $album = null; if (is_null($this->queryParam("album"))) { - if ((int) $upload_context == $this->user->id) { + if (!$isMessageUpload && (int) $upload_context == $this->user->id) { $album = $this->albums->getUserWallAlbum($this->user->identity); } } else { @@ -339,6 +341,9 @@ public function renderUploadPhoto(): void $photo->setDescription(""); $photo->setFile($_FILES["photo_" . $i]); $photo->setCreated(time()); + if ($isMessageUpload) { + $photo->setIs_message_photo(1); + } $photo->save(); $photos[] = [ @@ -350,11 +355,10 @@ public function renderUploadPhoto(): void "pretty_id" => $photo->getPrettyId(), ]; } catch (ISE $ex) { - $name = $album->getName(); - $this->flashFail("err", "Неизвестная ошибка", "Не удалось сохранить фотографию в $name.", 500, true); + $this->flashFail("err", "Неизвестная ошибка", "Не удалось сохранить фотографию" . ($album ? " в " . $album->getName() : "") . ".", 500, true); } - if ($album != null) { + if (!$isMessageUpload && $album != null) { $album->addPhoto($photo); $album->setEdited(time()); $album->save(); diff --git a/Web/Presenters/VideosPresenter.php b/Web/Presenters/VideosPresenter.php index 8bad65c6f..581be392a 100644 --- a/Web/Presenters/VideosPresenter.php +++ b/Web/Presenters/VideosPresenter.php @@ -56,7 +56,7 @@ public function renderView(int $owner, int $vId): void if (!$video || $video->isDeleted()) { $this->notFound(); } - if (!$user->getPrivacyPermission('videos.read', $this->user->identity ?? null)) { + if (!$video->canBeViewedBy($this->user->identity)) { $this->flashFail("err", tr("forbidden"), tr("forbidden_comment")); } @@ -103,6 +103,10 @@ public function renderUpload(): void $video->setUnlisted(true); } + if ($this->queryParam("upload_context") === "messages") { + $video->setIs_message_video(1); + } + $video->save(); if ($is_ajax) { diff --git a/Web/Presenters/templates/Messenger/App.latte b/Web/Presenters/templates/Messenger/App.latte index fc62c5ab0..b7bc15da0 100644 --- a/Web/Presenters/templates/Messenger/App.latte +++ b/Web/Presenters/templates/Messenger/App.latte @@ -125,7 +125,7 @@ {if $correspondent->getId() === $thisUser->getId() || $correspondent->getPrivacyPermission('messages.write', $thisUser)} {$thisUser->getCanonicalName()}
-
+
diff --git a/Web/static/js/al_wall.js b/Web/static/js/al_wall.js index 22c0465bb..c2f72b449 100644 --- a/Web/static/js/al_wall.js +++ b/Web/static/js/al_wall.js @@ -1224,7 +1224,9 @@ async function __uploadToTextarea(file, textareaNode) { const rand = random_int(0, 1000) textareaNode.find('.post-horizontal').append(``) - const res = await fetch(`/photos/upload?upload_context=${textareaNode.nodes[0].dataset.id}`, { + const writeEl = textareaNode.nodes[0] + const photoUploadContext = writeEl.dataset.context === 'messages' ? 'messages' : writeEl.dataset.id + const res = await fetch(`/photos/upload?upload_context=${photoUploadContext}`, { method: 'POST', body: form_data }) @@ -1467,7 +1469,7 @@ u(document).on("click", "#__photoAttachment", async (e) => { u(".ovk-diag-body #__pickerQuickUpload").on('change', (ev) => { for(file of ev.target.files) { try { - __uploadToTextarea(file, form) + __uploadToTextarea(file, form.closest('#write')) } catch(e) { makeError(e.message) return @@ -1488,13 +1490,15 @@ u(document).on("click", "#__photoAttachment", async (e) => { u(document).on('click', '#__videoAttachment', async (e) => { const per_page = 10 - const form = u(e.target).closest('form') + const form = u(e.target).closest('form') + const isMessageContext = form.nodes[0]?.closest('#write')?.dataset?.context === 'messages' + const videoUploadUrl = isMessageContext ? '/videos/upload?upload_context=messages' : '/videos/upload' const msg = new CMessageBox({ title: tr('selecting_video'), body: `
- +
@@ -1776,6 +1780,8 @@ u(document).on('click', '#__notesAttachment', async (e) => { }) function showFastVideoUpload(node) { + const isMessageContext = node.nodes[0]?.closest('#write')?.dataset?.context === 'messages' + const uploadUrl = isMessageContext ? '/videos/upload?upload_context=messages' : '/videos/upload' let current_tab = 'file' const msg = new CMessageBox({ title: tr('upload_video'), @@ -1829,7 +1835,7 @@ function showFastVideoUpload(node) { form_data.append("hash", u("meta[name=csrf]").attr("value")) window.messagebox_stack[1].getNode().find('.ovk-diag-action button').nodes[1].classList.add('lagged') - const fetcher = await fetch(`/videos/upload`, { + const fetcher = await fetch(uploadUrl, { method: 'POST', body: form_data }) @@ -1853,7 +1859,7 @@ function showFastVideoUpload(node) { form_data.append("hash", u("meta[name=csrf]").attr("value")) window.messagebox_stack[1].getNode().find('.ovk-diag-action button').nodes[1].classList.add('lagged') - const fetcher_yt = await fetch(`/videos/upload`, { + const fetcher_yt = await fetch(uploadUrl, { method: 'POST', body: form_data }) diff --git a/install/sqls/00060-message-attachments-privacy.sql b/install/sqls/00060-message-attachments-privacy.sql new file mode 100644 index 000000000..cd7651588 --- /dev/null +++ b/install/sqls/00060-message-attachments-privacy.sql @@ -0,0 +1,2 @@ +ALTER TABLE `photos` ADD COLUMN `is_message_photo` TINYINT(1) NOT NULL DEFAULT 0; +ALTER TABLE `videos` ADD COLUMN `is_message_video` TINYINT(1) NOT NULL DEFAULT 0; From 093991a6a9e7a67d7e3cdf374311b9cb79d92207 Mon Sep 17 00:00:00 2001 From: Ruslan Isaev Date: Sat, 25 Apr 2026 19:41:01 +0300 Subject: [PATCH 20/22] =?UTF-8?q?=D0=BB=D0=B8=D0=BD=D1=82=D0=B5=D1=80=20?= =?UTF-8?q?=D0=BE=D0=BF=D1=8F=D1=82=D1=8C=20=D1=8D=D1=82=D0=BE=20=D1=81?= =?UTF-8?q?=D0=B0=D0=BC=D0=BE=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Web/Models/Entities/Traits/TMessageAttachment.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Web/Models/Entities/Traits/TMessageAttachment.php b/Web/Models/Entities/Traits/TMessageAttachment.php index 7331b1123..fa0f31514 100644 --- a/Web/Models/Entities/Traits/TMessageAttachment.php +++ b/Web/Models/Entities/Traits/TMessageAttachment.php @@ -35,8 +35,10 @@ private function isViewableByMessageParticipant(?User $user): bool ->where("deleted", 0) ->where( "(sender_type = ? AND sender_id = ?) OR (recipient_type = ? AND recipient_id = ?)", - "openvk\\Web\\Models\\Entities\\User", $user->getId(), - "openvk\\Web\\Models\\Entities\\User", $user->getId() + "openvk\\Web\\Models\\Entities\\User", + $user->getId(), + "openvk\\Web\\Models\\Entities\\User", + $user->getId() ) ->count("*"); } From 1c8ae5137de76c3e213017b25f2d163b91aae516 Mon Sep 17 00:00:00 2001 From: Ruslan Isaev Date: Sat, 25 Apr 2026 20:03:44 +0300 Subject: [PATCH 21/22] fix migration number --- ...achments-privacy.sql => 00061-message-attachments-privacy.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename install/sqls/{00060-message-attachments-privacy.sql => 00061-message-attachments-privacy.sql} (100%) diff --git a/install/sqls/00060-message-attachments-privacy.sql b/install/sqls/00061-message-attachments-privacy.sql similarity index 100% rename from install/sqls/00060-message-attachments-privacy.sql rename to install/sqls/00061-message-attachments-privacy.sql From b9e1ffb03fd2d3e2a280e01de12b6f1e96b9d5e6 Mon Sep 17 00:00:00 2001 From: Ruslan Isaev Date: Fri, 1 May 2026 23:24:17 +0300 Subject: [PATCH 22/22] fix youtube video rendering in messenger --- Web/Models/Entities/Message.php | 10 +++++++++- Web/Presenters/templates/Messenger/App.latte | 7 ++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Web/Models/Entities/Message.php b/Web/Models/Entities/Message.php index d7068e3cd..b435fc691 100644 --- a/Web/Models/Entities/Message.php +++ b/Web/Models/Entities/Message.php @@ -138,12 +138,20 @@ public function simplify(): array ], ]; } elseif ($attachment instanceof Video) { + if ($attachment->getType() != 1) { + $videoUrl = $attachment->getURL(); + $embedHtml = null; + } else { + $videoUrl = $attachment->getVideoDriver()->getURL(); + $embedHtml = $attachment->getVideoDriver()->getEmbed("100%"); + } $attachments[] = [ "type" => "video", "link" => "/video" . $attachment->getPrettyId(), "id" => $attachment->getOwner()->getId() . "_" . $attachment->getVirtualId(), "video" => [ - "url" => $attachment->getURL(), + "url" => $videoUrl, + "embed_html" => $embedHtml, "name" => $attachment->getName(), "length" => $attachment->getLength(), "formatted_length" => $attachment->getFormattedLength(), diff --git a/Web/Presenters/templates/Messenger/App.latte b/Web/Presenters/templates/Messenger/App.latte index b7bc15da0..21a3d14ad 100644 --- a/Web/Presenters/templates/Messenger/App.latte +++ b/Web/Presenters/templates/Messenger/App.latte @@ -35,14 +35,19 @@
+
+ + +
+
- +