mirror of
https://github.com/openvk/openvk
synced 2025-07-07 08:19:49 +03:00
Compare commits
3 commits
53fa14d89e
...
3d90eb89ec
Author | SHA1 | Date | |
---|---|---|---|
|
3d90eb89ec | ||
|
59554172d6 | ||
|
2fed1d9fef |
11 changed files with 234 additions and 14 deletions
76
ServiceAPI/Search.php
Normal file
76
ServiceAPI/Search.php
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\ServiceAPI;
|
||||||
|
use openvk\Web\Models\Entities\{User, Club};
|
||||||
|
use openvk\Web\Models\Repositories\{Users, Clubs, Videos};
|
||||||
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
|
||||||
|
class Search implements Handler
|
||||||
|
{
|
||||||
|
protected $user;
|
||||||
|
private $users;
|
||||||
|
private $clubs;
|
||||||
|
private $videos;
|
||||||
|
|
||||||
|
function __construct(?User $user)
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
$this->users = new Users;
|
||||||
|
$this->clubs = new Clubs;
|
||||||
|
$this->videos = new Videos;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fastSearch(string $query, string $type = "users", callable $resolve, callable $reject)
|
||||||
|
{
|
||||||
|
if($query == "" || strlen($query) < 3)
|
||||||
|
$reject(12, "No input or input < 3");
|
||||||
|
|
||||||
|
$repo;
|
||||||
|
$sort;
|
||||||
|
|
||||||
|
switch($type) {
|
||||||
|
default:
|
||||||
|
case "users":
|
||||||
|
$repo = (new Users);
|
||||||
|
$sort = "rating DESC";
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "groups":
|
||||||
|
$repo = (new Clubs);
|
||||||
|
$sort = "id ASC";
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "videos":
|
||||||
|
$repo = (new Videos);
|
||||||
|
$sort = "created ASC";
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = $repo->find($query, ["doNotSearchMe" => $this->user->getId()], $sort);
|
||||||
|
|
||||||
|
$results = array_slice(iterator_to_array($res), 0, 5);
|
||||||
|
|
||||||
|
$count = sizeof($results);
|
||||||
|
|
||||||
|
$arr = [
|
||||||
|
"count" => $count,
|
||||||
|
"items" => []
|
||||||
|
];
|
||||||
|
|
||||||
|
if(sizeof($results) < 1) {
|
||||||
|
$reject(2, "No results");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($results as $res) {
|
||||||
|
$arr["items"][] = [
|
||||||
|
"id" => $res->getId(),
|
||||||
|
"name" => $type == "users" ? $res->getCanonicalName() : $res->getName(),
|
||||||
|
"avatar" => $type != "videos" ? $res->getAvatarUrl() : $res->getThumbnailURL(),
|
||||||
|
"url" => $type != "videos" ? $res->getUrl() : "/video".$res->getPrettyId(),
|
||||||
|
"description" => ovk_proc_strtr($res->getDescription() ?? "...", 40)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$resolve($arr);
|
||||||
|
}
|
||||||
|
}
|
|
@ -291,7 +291,7 @@ final class Board extends VKAPIRequestHandler
|
||||||
|
|
||||||
$topic = (new TopicsRepo)->getTopicById($group_id, $topic_id);
|
$topic = (new TopicsRepo)->getTopicById($group_id, $topic_id);
|
||||||
|
|
||||||
if(!$topic || !$topic->getClub() || $topic->isDeleted() || !$topic->getClub()->canBeModifiedBy($this->getUser())) {
|
if(!$topic || !$topic->getClub() || $topic->isDeleted()) {
|
||||||
$this->fail(5, "Invalid topic");
|
$this->fail(5, "Invalid topic");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,6 +325,7 @@ final class Board extends VKAPIRequestHandler
|
||||||
|
|
||||||
$arr = [];
|
$arr = [];
|
||||||
$club = (new ClubsRepo)->get($group_id);
|
$club = (new ClubsRepo)->get($group_id);
|
||||||
|
|
||||||
$topics = array_slice(iterator_to_array((new TopicsRepo)->getClubTopics($club, 1, $count + $offset)), $offset);
|
$topics = array_slice(iterator_to_array((new TopicsRepo)->getClubTopics($club, 1, $count + $offset)), $offset);
|
||||||
$arr["count"] = (new TopicsRepo)->getClubTopicsCount($club);
|
$arr["count"] = (new TopicsRepo)->getClubTopicsCount($club);
|
||||||
$arr["items"] = [];
|
$arr["items"] = [];
|
||||||
|
@ -335,7 +336,7 @@ final class Board extends VKAPIRequestHandler
|
||||||
if(empty($topic_ids)) {
|
if(empty($topic_ids)) {
|
||||||
foreach($topics as $topic) {
|
foreach($topics as $topic) {
|
||||||
if($topic->isDeleted()) continue;
|
if($topic->isDeleted()) continue;
|
||||||
$arr["items"][] = $topic->toVkApiStruct($preview, $preview_length);
|
$arr["items"][] = $topic->toVkApiStruct($preview, $preview_length > 1 ? $preview_length : 90);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$topics = explode(',', $topic_ids);
|
$topics = explode(',', $topic_ids);
|
||||||
|
@ -343,8 +344,9 @@ final class Board extends VKAPIRequestHandler
|
||||||
foreach($topics as $topic) {
|
foreach($topics as $topic) {
|
||||||
$id = explode("_", $topic);
|
$id = explode("_", $topic);
|
||||||
$topicy = (new TopicsRepo)->getTopicById((int)$id[0], (int)$id[1]);
|
$topicy = (new TopicsRepo)->getTopicById((int)$id[0], (int)$id[1]);
|
||||||
if($topicy) {
|
|
||||||
$arr["items"] = $topicy->toVkApiStruct();
|
if($topicy && !$topicy->isDeleted()) {
|
||||||
|
$arr["items"][] = $topicy->toVkApiStruct($preview, $preview_length > 1 ? $preview_length : 90);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -406,7 +408,7 @@ final class Board extends VKAPIRequestHandler
|
||||||
$res->id = $comment->getId();
|
$res->id = $comment->getId();
|
||||||
$res->from_id = $comment->getOwner()->getId();
|
$res->from_id = $comment->getOwner()->getId();
|
||||||
$res->date = $comment->getPublicationTime()->timestamp();
|
$res->date = $comment->getPublicationTime()->timestamp();
|
||||||
$res->text = $comment->getText();
|
$res->text = $comment->getText(false);
|
||||||
$res->attachments = [];
|
$res->attachments = [];
|
||||||
$res->likes = [];
|
$res->likes = [];
|
||||||
if($need_likes) {
|
if($need_likes) {
|
||||||
|
|
|
@ -61,7 +61,7 @@ class Comment extends Post
|
||||||
$res->id = $this->getId();
|
$res->id = $this->getId();
|
||||||
$res->from_id = $this->getOwner()->getId();
|
$res->from_id = $this->getOwner()->getId();
|
||||||
$res->date = $this->getPublicationTime()->timestamp();
|
$res->date = $this->getPublicationTime()->timestamp();
|
||||||
$res->text = $this->getText();
|
$res->text = $this->getText(false);
|
||||||
$res->attachments = [];
|
$res->attachments = [];
|
||||||
$res->parents_stack = [];
|
$res->parents_stack = [];
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,12 @@ class Topic extends Postable
|
||||||
return isset($array[0]) ? $array[0] : NULL;
|
return isset($array[0]) ? $array[0] : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getFirstComment(): ?Comment
|
||||||
|
{
|
||||||
|
$array = iterator_to_array($this->getComments(1));
|
||||||
|
return $array[0] ?? NULL;
|
||||||
|
}
|
||||||
|
|
||||||
function getUpdateTime(): DateTime
|
function getUpdateTime(): DateTime
|
||||||
{
|
{
|
||||||
$lastComment = $this->getLastComment();
|
$lastComment = $this->getLastComment();
|
||||||
|
@ -83,4 +89,40 @@ class Topic extends Postable
|
||||||
$this->unwire();
|
$this->unwire();
|
||||||
$this->save();
|
$this->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toVkApiStruct(int $preview = 0, int $preview_length = 90): object
|
||||||
|
{
|
||||||
|
$res = (object)[];
|
||||||
|
|
||||||
|
$res->id = $this->getId();
|
||||||
|
$res->title = $this->getTitle();
|
||||||
|
$res->created = $this->getPublicationTime()->timestamp();
|
||||||
|
|
||||||
|
if($this->getOwner() instanceof User) {
|
||||||
|
$res->created_by = $this->getOwner()->getId();
|
||||||
|
} else {
|
||||||
|
$res->created_by = $this->getOwner()->getId() * -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$res->updated = $this->getUpdateTime()->timestamp();
|
||||||
|
|
||||||
|
if($this->getLastComment()) {
|
||||||
|
if($this->getLastComment()->getOwner() instanceof User) {
|
||||||
|
$res->updated_by = $this->getLastComment()->getOwner()->getId();
|
||||||
|
} else {
|
||||||
|
$res->updated_by = $this->getLastComment()->getOwner()->getId() * -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$res->is_closed = (int)$this->isClosed();
|
||||||
|
$res->is_fixed = (int)$this->isPinned();
|
||||||
|
$res->comments = $this->getCommentsCount();
|
||||||
|
|
||||||
|
if($preview == 1) {
|
||||||
|
$res->first_comment = $this->getFirstComment() ? ovk_proc_strtr($this->getFirstComment()->getText(false), $preview_length) : NULL;
|
||||||
|
$res->last_comment = $this->getLastComment() ? ovk_proc_strtr($this->getLastComment()->getText(false), $preview_length) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,6 +117,7 @@ class Video extends Media
|
||||||
|
|
||||||
function getApiStructure(): object
|
function getApiStructure(): object
|
||||||
{
|
{
|
||||||
|
$fromYoutube = $this->getType() == Video::TYPE_EMBED;
|
||||||
return (object)[
|
return (object)[
|
||||||
"type" => "video",
|
"type" => "video",
|
||||||
"video" => [
|
"video" => [
|
||||||
|
@ -145,10 +146,11 @@ class Video extends Media
|
||||||
"user_id" => $this->getOwner()->getId(),
|
"user_id" => $this->getOwner()->getId(),
|
||||||
"title" => $this->getName(),
|
"title" => $this->getName(),
|
||||||
"is_favorite" => false,
|
"is_favorite" => false,
|
||||||
"player" => $this->getURL(),
|
"player" => !$fromYoutube ? $this->getURL() : $this->getVideoDriver()->getURL(),
|
||||||
"files" => [
|
"files" => !$fromYoutube ? [
|
||||||
"mp4_480" => $this->getURL()
|
"mp4_480" => $this->getURL()
|
||||||
],
|
] : NULL,
|
||||||
|
"platform" => $fromYoutube ? "youtube" : NULL,
|
||||||
"added" => 0,
|
"added" => 0,
|
||||||
"repeat" => 0,
|
"repeat" => 0,
|
||||||
"type" => "video",
|
"type" => "video",
|
||||||
|
|
|
@ -58,7 +58,7 @@ class Users
|
||||||
$nnparamsCount = 0;
|
$nnparamsCount = 0;
|
||||||
|
|
||||||
foreach($pars as $paramName => $paramValue)
|
foreach($pars as $paramName => $paramValue)
|
||||||
if($paramName != "before" && $paramName != "after" && $paramName != "gender" && $paramName != "maritalstatus" && $paramName != "politViews")
|
if($paramName != "before" && $paramName != "after" && $paramName != "gender" && $paramName != "maritalstatus" && $paramName != "politViews" && $paramName != "doNotSearchMe")
|
||||||
$paramValue != NULL ? $notNullParams += ["$paramName" => "%$paramValue%"] : NULL;
|
$paramValue != NULL ? $notNullParams += ["$paramName" => "%$paramValue%"] : NULL;
|
||||||
else
|
else
|
||||||
$paramValue != NULL ? $notNullParams += ["$paramName" => "$paramValue"] : NULL;
|
$paramValue != NULL ? $notNullParams += ["$paramName" => "$paramValue"] : NULL;
|
||||||
|
@ -125,6 +125,9 @@ class Users
|
||||||
case "gender":
|
case "gender":
|
||||||
$result->where("sex ?", $paramValue);
|
$result->where("sex ?", $paramValue);
|
||||||
break;
|
break;
|
||||||
|
case "doNotSearchMe":
|
||||||
|
$result->where("id !=", $paramValue);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,8 +93,8 @@
|
||||||
|
|
||||||
{if !$atSearch}
|
{if !$atSearch}
|
||||||
<form action="/search" method="get" id="searcher" style="position:relative;">
|
<form action="/search" method="get" id="searcher" style="position:relative;">
|
||||||
<input id="searchInput" onfocus="expandSearch()" onblur="decreaseSearch()" class="sr" type="search" name="query" placeholder="{_header_search}" style="height: 20px;background: url('/assets/packages/static/openvk/img/search_icon.png') no-repeat 3px 4px; background-color: #fff; padding-left: 18px;width: 120px;" title="{_header_search} [Alt+Shift+F]" accesskey="f" />
|
<input autocomplete="off" id="searchInput" oninput="checkSearchTips()" onfocus="expandSearch()" onblur="decreaseSearch()" class="sr" type="search" name="query" placeholder="{_header_search}" style="height: 20px;background: url('/assets/packages/static/openvk/img/search_icon.png') no-repeat 3px 4px; background-color: #fff; padding-left: 18px;width: 120px;" title="{_header_search} [Alt+Shift+F]" accesskey="f" />
|
||||||
<select name="type" class="whatFind" style="display:none;top: 0px;">
|
<select onchange="checkSearchTips()" id="typer" name="type" class="whatFind" style="display:none;top: 0px;">
|
||||||
<option value="users">{_s_by_people}</option>
|
<option value="users">{_s_by_people}</option>
|
||||||
<option value="groups">{_s_by_groups}</option>
|
<option value="groups">{_s_by_groups}</option>
|
||||||
<option value="posts">{_s_by_posts}</option>
|
<option value="posts">{_s_by_posts}</option>
|
||||||
|
@ -103,6 +103,12 @@
|
||||||
<option value="apps">{_s_by_apps}</option>
|
<option value="apps">{_s_by_apps}</option>
|
||||||
</select>
|
</select>
|
||||||
</form>
|
</form>
|
||||||
|
<div class="searchTips" id="srcht" hidden>
|
||||||
|
<table style="border:none;border-spacing: 0;">
|
||||||
|
<tbody id="srchrr">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
{else}
|
{else}
|
||||||
<form action="/search" method="get" id="searcher" style="margin-top: -1px;position:relative;">
|
<form action="/search" method="get" id="searcher" style="margin-top: -1px;position:relative;">
|
||||||
<input id="searchInput" value="{$_GET['query'] ?? ''}" type="search" class="sr" name="query" placeholder="{_header_search}" style="height: 20px; background-color: #fff; padding-left: 6px;width: 555px;" title="{_header_search} [Alt+Shift+F]" accesskey="f" />
|
<input id="searchInput" value="{$_GET['query'] ?? ''}" type="search" class="sr" name="query" placeholder="{_header_search}" style="height: 20px; background-color: #fff; padding-left: 6px;width: 555px;" title="{_header_search} [Alt+Shift+F]" accesskey="f" />
|
||||||
|
|
|
@ -1118,7 +1118,7 @@ table.User {
|
||||||
}
|
}
|
||||||
|
|
||||||
.messenger-app--messages {
|
.messenger-app--messages {
|
||||||
height: 360px;
|
height: 570px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 10px 60px;
|
padding: 10px 60px;
|
||||||
}
|
}
|
||||||
|
@ -2487,3 +2487,49 @@ a.poll-retract-vote {
|
||||||
.page_content.overscrolled div[class$="_big_block"] {
|
.page_content.overscrolled div[class$="_big_block"] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.searchTips
|
||||||
|
{
|
||||||
|
position: absolute;
|
||||||
|
background: white;
|
||||||
|
padding-left: 6px;
|
||||||
|
padding-right: 6px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
border: 1px solid #C0CAD5;
|
||||||
|
border-top: 0px;
|
||||||
|
width: fit-content;
|
||||||
|
z-index: 666666;
|
||||||
|
margin-top: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchTips td
|
||||||
|
{
|
||||||
|
color: black;
|
||||||
|
padding: 3px;
|
||||||
|
font-weight: lighter;
|
||||||
|
position:relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchTips td .desq
|
||||||
|
{
|
||||||
|
margin-top: -9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.restip td a:hover
|
||||||
|
{
|
||||||
|
color: black;
|
||||||
|
font-weight: lighter;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchTips .restip
|
||||||
|
{
|
||||||
|
padding-right: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchTips .restip:hover
|
||||||
|
{
|
||||||
|
background: rgb(236, 235, 235);
|
||||||
|
}
|
||||||
|
|
|
@ -590,9 +590,11 @@ async function decreaseSearch()
|
||||||
{
|
{
|
||||||
// чтобы люди успели выбрать что искать и поиск не скрывался сразу
|
// чтобы люди успели выбрать что искать и поиск не скрывался сразу
|
||||||
await new Promise(r => setTimeout(r, 4000));
|
await new Promise(r => setTimeout(r, 4000));
|
||||||
|
|
||||||
// console.log("search decreased")
|
// console.log("search decreased")
|
||||||
if(document.activeElement !== searchInput)
|
if(document.activeElement !== searchInput && document.activeElement !== typer)
|
||||||
{
|
{
|
||||||
|
srcht.setAttribute("hidden", "hidden")
|
||||||
document.getElementById("searchInput").style.background = "url('/assets/packages/static/openvk/img/search_icon.png') no-repeat 3px 4px";
|
document.getElementById("searchInput").style.background = "url('/assets/packages/static/openvk/img/search_icon.png') no-repeat 3px 4px";
|
||||||
document.getElementById("searchInput").style.backgroundColor = "#fff";
|
document.getElementById("searchInput").style.backgroundColor = "#fff";
|
||||||
document.getElementById("searchInput").style.paddingLeft = "18px";
|
document.getElementById("searchInput").style.paddingLeft = "18px";
|
||||||
|
@ -642,6 +644,43 @@ function resetSearch()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function checkSearchTips()
|
||||||
|
{
|
||||||
|
let query = searchInput.value;
|
||||||
|
|
||||||
|
await new Promise(r => setTimeout(r, 1000));
|
||||||
|
|
||||||
|
let type = typer.value;
|
||||||
|
let smt = type == "users" || type == "groups" || type == "videos";
|
||||||
|
|
||||||
|
if(query.length > 3 && query == searchInput.value && smt) {
|
||||||
|
srcht.removeAttribute("hidden")
|
||||||
|
let etype = type
|
||||||
|
|
||||||
|
try {
|
||||||
|
let results = await API.Search.fastSearch(escapeHtml(query), etype)
|
||||||
|
|
||||||
|
srchrr.innerHTML = ""
|
||||||
|
|
||||||
|
for(const el of results["items"]) {
|
||||||
|
srchrr.insertAdjacentHTML("beforeend", `
|
||||||
|
<tr class="restip" onmouseup="if (event.which === 2) { window.open('${el.url}', '_blank'); } else {location.href='${el.url}'}">
|
||||||
|
<td>
|
||||||
|
<img src="${el.avatar}" width="30">
|
||||||
|
</td>
|
||||||
|
<td valign="top">
|
||||||
|
<p class="nameq" style="margin-top: -2px;text-transform:none;">${escapeHtml(el.name)}</p>
|
||||||
|
<p class="desq" style="text-transform:none;">${escapeHtml(el.description)}</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
} catch(rejection) {
|
||||||
|
srchrr.innerHTML = tr("no_results")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$(document).on("scroll", () => {
|
$(document).on("scroll", () => {
|
||||||
if($(document).scrollTop() > $(".sidebar").height() + 50) {
|
if($(document).scrollTop() > $(".sidebar").height() + 50) {
|
||||||
$(".floating_sidebar")[0].classList.add("show");
|
$(".floating_sidebar")[0].classList.add("show");
|
||||||
|
|
|
@ -1507,6 +1507,8 @@
|
||||||
"closed_group_post" = "This is a post from private group";
|
"closed_group_post" = "This is a post from private group";
|
||||||
"deleted_target_comment" = "This comment belongs to deleted post";
|
"deleted_target_comment" = "This comment belongs to deleted post";
|
||||||
|
|
||||||
|
"no_results" = "No results";
|
||||||
|
|
||||||
/* Mobile */
|
/* Mobile */
|
||||||
"mobile_friends" = "Friends";
|
"mobile_friends" = "Friends";
|
||||||
"mobile_photos" = "Photos";
|
"mobile_photos" = "Photos";
|
||||||
|
|
|
@ -1400,6 +1400,8 @@
|
||||||
"closed_group_post" = "Эта запись из закрытой группы";
|
"closed_group_post" = "Эта запись из закрытой группы";
|
||||||
"deleted_target_comment" = "Этот комментарий принадлежит к удалённой записи";
|
"deleted_target_comment" = "Этот комментарий принадлежит к удалённой записи";
|
||||||
|
|
||||||
|
"no_results" = "Результатов нет";
|
||||||
|
|
||||||
/* Mobile */
|
/* Mobile */
|
||||||
"mobile_friends" = "Друзья";
|
"mobile_friends" = "Друзья";
|
||||||
"mobile_photos" = "Фотографии";
|
"mobile_photos" = "Фотографии";
|
||||||
|
|
Loading…
Reference in a new issue