This commit is contained in:
mrilyew 2024-10-30 11:08:45 +03:00
parent a3d535040d
commit c332510b24
22 changed files with 766 additions and 28 deletions

View file

@ -32,6 +32,7 @@ final class Newsfeed extends VKAPIRequestHandler
->select("id") ->select("id")
->where("wall IN (?)", $ids) ->where("wall IN (?)", $ids)
->where("deleted", 0) ->where("deleted", 0)
->where("suggested", 0)
->where("id < (?)", empty($start_from) ? PHP_INT_MAX : $start_from) ->where("id < (?)", empty($start_from) ? PHP_INT_MAX : $start_from)
->where("? <= created", empty($start_time) ? 0 : $start_time) ->where("? <= created", empty($start_time) ? 0 : $start_time)
->where("? >= created", empty($end_time) ? PHP_INT_MAX : $end_time) ->where("? >= created", empty($end_time) ? PHP_INT_MAX : $end_time)
@ -56,6 +57,15 @@ final class Newsfeed extends VKAPIRequestHandler
if($this->getUser()->getNsfwTolerance() === User::NSFW_INTOLERANT) if($this->getUser()->getNsfwTolerance() === User::NSFW_INTOLERANT)
$queryBase .= " AND `nsfw` = 0"; $queryBase .= " AND `nsfw` = 0";
if($return_banned == 0) {
$ignored_sources_ids = $this->getUser()->getIgnoredSources(0, OPENVK_ROOT_CONF['openvk']['preferences']['newsfeed']['ignoredSourcesLimit'] ?? 50, true);
if(sizeof($ignored_sources_ids) > 0) {
$imploded_ids = implode("', '", $ignored_sources_ids);
$queryBase .= " AND `posts`.`wall` NOT IN ('$imploded_ids')";
}
}
$start_from = empty($start_from) ? PHP_INT_MAX : $start_from; $start_from = empty($start_from) ? PHP_INT_MAX : $start_from;
$start_time = empty($start_time) ? 0 : $start_time; $start_time = empty($start_time) ? 0 : $start_time;
@ -74,4 +84,152 @@ final class Newsfeed extends VKAPIRequestHandler
return $response; return $response;
} }
function getByType(string $feed_type = 'top', string $fields = "", int $start_from = 0, int $start_time = 0, int $end_time = 0, int $offset = 0, int $count = 30, int $extended = 0, int $return_banned = 0)
{
$this->requireUser();
switch($feed_type) {
case 'top':
return $this->getGlobal($fields, $start_from, $start_time, $end_time, $offset, $count, $extended, $return_banned);
break;
default:
return $this->get($fields, $start_from, $start_time, $end_time, $offset, $count, $extended);
break;
}
}
function getBanned(int $extended = 0, string $fields = "", string $name_case = "nom", int $merge = 0): object
{
$this->requireUser();
$offset = 0;
$count = OPENVK_ROOT_CONF['openvk']['preferences']['newsfeed']['ignoredSourcesLimit'] ?? 50;
$banned = $this->getUser()->getIgnoredSources($offset, $count, ($extended != 1));
$return_object = (object) [
'groups' => [],
'members' => [],
];
if($extended == 0) {
foreach($banned as $ban) {
if($ban > 0)
$return_object->members[] = $ban;
else
$return_object->groups[] = $ban;
}
} else {
if($merge == 1) {
$return_object = (object) [
'count' => sizeof($banned),
'items' => [],
];
foreach($banned as $ban) {
$return_object->items[] = $ban->toVkApiStruct($this->getUser(), $fields);
}
} else {
$return_object = (object) [
'groups' => [],
'profiles' => [],
];
foreach($banned as $ban) {
if($ban->getRealId() > 0)
$return_object->profiles[] = $ban->toVkApiStruct($this->getUser(), $fields);
else
$return_object->groups[] = $ban->toVkApiStruct($this->getUser(), $fields);
}
}
}
return $return_object;
}
function addBan(string $user_ids = "", string $group_ids = "")
{
$this->requireUser();
$this->willExecuteWriteAction();
# Formatting input ids
if(!empty($user_ids)) {
$user_ids = array_map(function($el) {
return (int)$el;
}, explode(',', $user_ids));
$user_ids = array_unique($user_ids);
} else
$user_ids = [];
if(!empty($group_ids)) {
$group_ids = array_map(function($el) {
return abs((int)$el) * -1;
}, explode(',', $group_ids));
$group_ids = array_unique($group_ids);
} else
$group_ids = [];
$ids = array_merge($user_ids, $group_ids);
if(sizeof($ids) < 1)
return 0;
if(sizeof($ids) > 10)
$this->fail(-10, "Limit of 'ids' is 10");
$config_limit = OPENVK_ROOT_CONF['openvk']['preferences']['newsfeed']['ignoredSourcesLimit'] ?? 50;
$user_ignores = $this->getUser()->getIgnoredSourcesCount();
if(($user_ignores + sizeof($ids)) > $config_limit) {
$this->fail(-50, "Ignoring limit exceeded");
}
$entities = get_entities($ids);
$successes = 0;
foreach($entities as $entity) {
if(!$entity || $entity->getRealId() === $this->getUser()->getRealId() || $entity->isHideFromGlobalFeedEnabled() || $entity->isIgnoredBy($this->getUser())) continue;
$entity->addIgnore($this->getUser());
$successes += 1;
}
return 1;
}
function deleteBan(string $user_ids = "", string $group_ids = "")
{
$this->requireUser();
$this->willExecuteWriteAction();
if(!empty($user_ids)) {
$user_ids = array_map(function($el) {
return (int)$el;
}, explode(',', $user_ids));
$user_ids = array_unique($user_ids);
} else
$user_ids = [];
if(!empty($group_ids)) {
$group_ids = array_map(function($el) {
return abs((int)$el) * -1;
}, explode(',', $group_ids));
$group_ids = array_unique($group_ids);
} else
$group_ids = [];
$ids = array_merge($user_ids, $group_ids);
if(sizeof($ids) < 1)
return 0;
if(sizeof($ids) > 10)
$this->fail(-10, "Limit of ids is 10");
$entities = get_entities($ids);
$successes = 0;
foreach($entities as $entity) {
if(!$entity || $entity->getRealId() === $this->getUser()->getRealId() || !$entity->isIgnoredBy($this->getUser())) continue;
$entity->removeIgnore($this->getUser());
$successes += 1;
}
return 1;
}
} }

View file

@ -42,10 +42,11 @@ class Club extends RowModel
return iterator_to_array($avPhotos)[0] ?? NULL; return iterator_to_array($avPhotos)[0] ?? NULL;
} }
function getAvatarUrl(string $size = "miniscule"): string function getAvatarUrl(string $size = "miniscule", $avPhoto = NULL): string
{ {
$serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"]; $serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
$avPhoto = $this->getAvatarPhoto(); if(!$avPhoto)
$avPhoto = $this->getAvatarPhoto();
return is_null($avPhoto) ? "$serverUrl/assets/packages/static/openvk/img/camera_200.png" : $avPhoto->getURLBySizeId($size); return is_null($avPhoto) ? "$serverUrl/assets/packages/static/openvk/img/camera_200.png" : $avPhoto->getURLBySizeId($size);
} }
@ -443,30 +444,57 @@ class Club extends RowModel
$res->id = $this->getId(); $res->id = $this->getId();
$res->name = $this->getName(); $res->name = $this->getName();
$res->screen_name = $this->getShortCode(); $res->screen_name = $this->getShortCode() ?? "club".$this->getId();
$res->is_closed = 0; $res->is_closed = false;
$res->type = 'group';
$res->is_member = $user ? (int)$this->getSubscriptionStatus($user) : 0;
$res->deactivated = NULL; $res->deactivated = NULL;
$res->is_admin = $user && $this->canBeModifiedBy($user); $res->can_access_closed = true;
if($user && $this->canBeModifiedBy($user)) { if(!is_array($fields))
$res->admin_level = 3; $fields = explode(',', $fields);
$avatar_photo = $this->getAvatarPhoto();
foreach($fields as $field) {
switch($field) {
case 'verified':
$res->verified = (int)$this->isVerified();
break;
case 'site':
$res->site = $this->getWebsite();
break;
case 'description':
$res->description = $this->getDescription();
break;
case 'background':
$res->background = $this->getBackDropPictureURLs();
break;
case 'photo_50':
$res->photo_50 = $this->getAvatarUrl('miniscule', $avatar_photo);
break;
case 'photo_100':
$res->photo_100 = $this->getAvatarUrl('tiny', $avatar_photo);
break;
case 'photo_200':
$res->photo_200 = $this->getAvatarUrl('normal', $avatar_photo);
break;
case 'photo_max':
$res->photo_max = $this->getAvatarUrl('original', $avatar_photo);
break;
case 'members_count':
$res->members_count = $this->getFollowersCount();
break;
case 'real_id':
$res->real_id = $this->getRealId();
break;
}
} }
$res->is_member = $user && $this->getSubscriptionStatus($user) ? 1 : 0;
$res->type = "group";
$res->photo_50 = $this->getAvatarUrl("miniscule");
$res->photo_100 = $this->getAvatarUrl("tiny");
$res->photo_200 = $this->getAvatarUrl("normal");
$res->can_create_topic = $user && $this->canBeModifiedBy($user) ? 1 : ($this->isEveryoneCanCreateTopics() ? 1 : 0);
$res->can_post = $user && $this->canBeModifiedBy($user) ? 1 : ($this->canPost() ? 1 : 0);
return $res; return $res;
} }
use Traits\TBackDrops; use Traits\TBackDrops;
use Traits\TSubscribable; use Traits\TSubscribable;
use Traits\TAudioStatuses; use Traits\TAudioStatuses;
use Traits\TIgnorable;
} }

View file

@ -0,0 +1,52 @@
<?php declare(strict_types=1);
namespace openvk\Web\Models\Entities\Traits;
use Chandler\Database\DatabaseConnection;
use openvk\Web\Models\Entities\User;
trait TIgnorable
{
function isIgnoredBy(User $user): bool
{
$ctx = DatabaseConnection::i()->getContext();
$data = [
"owner" => $user->getId(),
"source" => $this->getRealId(),
];
$sub = $ctx->table("ignored_sources")->where($data);
return $sub->count() > 0;
}
function addIgnore(User $for_user): bool
{
DatabaseConnection::i()->getContext()->table("ignored_sources")->insert([
"owner" => $for_user->getId(),
"source" => $this->getRealId(),
]);
return true;
}
function removeIgnore(User $for_user): bool
{
DatabaseConnection::i()->getContext()->table("ignored_sources")->where([
"owner" => $for_user->getId(),
"source" => $this->getRealId(),
])->delete();
return true;
}
function toggleIgnore(User $for_user): bool
{
if($this->isIgnoredBy($for_user)) {
$this->removeIgnore($for_user);
return false;
} else {
$this->addIgnore($for_user);
return true;
}
}
}

View file

@ -112,7 +112,7 @@ class User extends RowModel
return "/id" . $this->getId(); return "/id" . $this->getId();
} }
function getAvatarUrl(string $size = "miniscule"): string function getAvatarUrl(string $size = "miniscule", $avPhoto = NULL): string
{ {
$serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"]; $serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
@ -121,7 +121,9 @@ class User extends RowModel
else if($this->isBanned()) else if($this->isBanned())
return "$serverUrl/assets/packages/static/openvk/img/banned.jpg"; return "$serverUrl/assets/packages/static/openvk/img/banned.jpg";
$avPhoto = $this->getAvatarPhoto(); if(!$avPhoto)
$avPhoto = $this->getAvatarPhoto();
if(is_null($avPhoto)) if(is_null($avPhoto))
return "$serverUrl/assets/packages/static/openvk/img/camera_200.png"; return "$serverUrl/assets/packages/static/openvk/img/camera_200.png";
else else
@ -1344,11 +1346,6 @@ class User extends RowModel
$res->first_name = $this->getFirstName(); $res->first_name = $this->getFirstName();
$res->last_name = $this->getLastName(); $res->last_name = $this->getLastName();
$res->deactivated = $this->isDeactivated(); $res->deactivated = $this->isDeactivated();
$res->photo_50 = $this->getAvatarURL();
$res->photo_100 = $this->getAvatarURL("tiny");
$res->photo_200 = $this->getAvatarURL("normal");
$res->photo_id = !is_null($this->getAvatarPhoto()) ? $this->getAvatarPhoto()->getPrettyId() : NULL;
$res->is_closed = $this->isClosed(); $res->is_closed = $this->isClosed();
if(!is_null($user)) if(!is_null($user))
@ -1357,17 +1354,60 @@ class User extends RowModel
if(!is_array($fields)) if(!is_array($fields))
$fields = explode(',', $fields); $fields = explode(',', $fields);
$avatar_photo = $this->getAvatarPhoto();
foreach($fields as $field) { foreach($fields as $field) {
switch($field) { switch($field) {
case 'is_dead': case 'is_dead':
$res->is_dead = $user->isDead(); $res->is_dead = $this->isDead();
break;
case 'verified':
$res->verified = (int)$this->isVerified();
break;
case 'sex':
$res->sex = $this->isFemale() ? 1 : ($this->isNeutral() ? 0 : 2);
break;
case 'photo_50':
$res->photo_50 = $this->getAvatarUrl('miniscule', $avatar_photo);
break;
case 'photo_100':
$res->photo_100 = $this->getAvatarUrl('tiny', $avatar_photo);
break;
case 'photo_200':
$res->photo_200 = $this->getAvatarUrl('normal', $avatar_photo);
break;
case 'photo_max':
$res->photo_max = $this->getAvatarUrl('original', $avatar_photo);
break;
case 'photo_id':
$res->photo_id = $avatar_photo ? $avatar_photo->getPrettyId() : NULL;
break;
case 'background':
$res->background = $this->getBackDropPictureURLs();
break;
case 'reg_date':
$res->reg_date = $this->getRegistrationTime()->timestamp();
break;
case 'nickname':
$res->nickname = $this->getPseudo();
break;
case 'rating':
$res->rating = $this->getRating();
break;
case 'status':
$res->status = $this->getStatus();
break;
case 'screen_name':
$res->screen_name = $this->getShortCode() ?? "id".$this->getId();
break;
case 'real_id':
$res->real_id = $this->getRealId();
break; break;
} }
} }
return $res; return $res;
} }
function getAudiosCollectionSize() function getAudiosCollectionSize()
{ {
return (new \openvk\Web\Models\Repositories\Audios)->getUserCollectionSize($this); return (new \openvk\Web\Models\Repositories\Audios)->getUserCollectionSize($this);
@ -1407,7 +1447,40 @@ class User extends RowModel
return $returnArr; return $returnArr;
} }
function getIgnoredSources(int $offset = 0, int $limit = 10, bool $onlyIds = false)
{
$sources = DatabaseConnection::i()->getContext()->table("ignored_sources")->where("owner", $this->getId())->limit($limit, $offset)->order('id DESC');
$output_array = [];
foreach($sources as $source) {
if($onlyIds) {
$output_array[] = (int)$source->source;
} else {
$ignored_source_model = NULL;
$ignored_source_id = (int)$source->source;
if($ignored_source_id > 0)
$ignored_source_model = (new Users)->get($ignored_source_id);
else
$ignored_source_model = (new Clubs)->get(abs($ignored_source_id));
if(!$ignored_source_model)
continue;
$output_array[] = $ignored_source_model;
}
}
return $output_array;
}
function getIgnoredSourcesCount()
{
return DatabaseConnection::i()->getContext()->table("ignored_sources")->where("owner", $this->getId())->count();
}
use Traits\TBackDrops; use Traits\TBackDrops;
use Traits\TSubscribable; use Traits\TSubscribable;
use Traits\TAudioStatuses; use Traits\TAudioStatuses;
use Traits\TIgnorable;
} }

View file

@ -43,6 +43,18 @@ class Clubs
return $this->toClub($this->clubs->get($id)); return $this->toClub($this->clubs->get($id));
} }
function getByIds(array $ids = []): array
{
$clubs = $this->clubs->select('*')->where('id IN (?)', $ids);
$clubs_array = [];
foreach($clubs as $club) {
$clubs_array[] = $this->toClub($club);
}
return $clubs_array;
}
function find(string $query, array $params = [], array $order = ['type' => 'id', 'invert' => false], int $page = 1, ?int $perPage = NULL): \Traversable function find(string $query, array $params = [], array $order = ['type' => 'id', 'invert' => false], int $page = 1, ?int $perPage = NULL): \Traversable
{ {
$query = "%$query%"; $query = "%$query%";

View file

@ -28,6 +28,18 @@ class Users
{ {
return $this->toUser($this->users->get($id)); return $this->toUser($this->users->get($id));
} }
function getByIds(array $ids = []): array
{
$users = $this->users->select('*')->where('id IN (?)', $ids);
$users_array = [];
foreach($users as $user) {
$users_array[] = $this->toUser($user);
}
return $users_array;
}
function getByShortURL(string $url): ?User function getByShortURL(string $url): ?User
{ {

View file

@ -43,6 +43,7 @@ final class GroupPresenter extends OpenVKPresenter
} }
$this->template->club = $club; $this->template->club = $club;
$this->template->ignore_status = $club->isIgnoredBy($this->user->identity);
} }
} }

View file

@ -54,6 +54,10 @@ final class UserPresenter extends OpenVKPresenter
$this->template->audioStatus = $user->getCurrentAudioStatus(); $this->template->audioStatus = $user->getCurrentAudioStatus();
$this->template->user = $user; $this->template->user = $user;
if($id !== $this->user->id) {
$this->template->ignore_status = $user->isIgnoredBy($this->user->identity);
}
} }
} }

View file

@ -197,6 +197,16 @@ final class WallPresenter extends OpenVKPresenter
if($this->user->identity->getNsfwTolerance() === User::NSFW_INTOLERANT) if($this->user->identity->getNsfwTolerance() === User::NSFW_INTOLERANT)
$queryBase .= " AND `nsfw` = 0"; $queryBase .= " AND `nsfw` = 0";
if(((int)$this->queryParam('return_banned')) == 0) {
$ignored_sources_ids = $this->user->identity->getIgnoredSources(0, OPENVK_ROOT_CONF['openvk']['preferences']['newsfeed']['ignoredSourcesLimit'] ?? 50, true);
if(sizeof($ignored_sources_ids) > 0) {
$imploded_ids = implode("', '", $ignored_sources_ids);
$queryBase .= " AND `posts`.`wall` NOT IN ('$imploded_ids')";
}
}
$posts = DatabaseConnection::i()->getConnection()->query("SELECT `posts`.`id` " . $queryBase . " ORDER BY `created` DESC LIMIT " . $pPage . " OFFSET " . ($page - 1) * $pPage); $posts = DatabaseConnection::i()->getConnection()->query("SELECT `posts`.`id` " . $queryBase . " ORDER BY `created` DESC LIMIT " . $pPage . " OFFSET " . ($page - 1) * $pPage);
$count = DatabaseConnection::i()->getConnection()->query("SELECT COUNT(*) " . $queryBase)->fetch()->{"COUNT(*)"}; $count = DatabaseConnection::i()->getConnection()->query("SELECT COUNT(*) " . $queryBase)->fetch()->{"COUNT(*)"};

View file

@ -382,6 +382,7 @@
{ifset $thisUser} {ifset $thisUser}
{script "js/al_notifs.js"} {script "js/al_notifs.js"}
{script "js/al_feed.js"}
{/ifset} {/ifset}
<script>bsdnHydrate();</script> <script>bsdnHydrate();</script>

View file

@ -194,6 +194,9 @@
} }
</script> </script>
{/if} {/if}
<a n:if="!$club->isHideFromGlobalFeedEnabled()" class="profile_link" style="display:block;" id="__ignoreSomeone" data-val='{!$ignore_status ? 1 : 0}' data-id="{$club->getRealId()}">
{if !$ignore_status}{_ignore_club}{else}{_unignore_club}{/if}
</a>
</div> </div>
<div> <div>
<div class="content_title_expanded" onclick="hidePanel(this);"> <div class="content_title_expanded" onclick="hidePanel(this);">

View file

@ -166,6 +166,9 @@
{/if} {/if}
<a class="profile_link" style="display:block;width:96%;" href="javascript:reportUser()">{_report}</a> <a class="profile_link" style="display:block;width:96%;" href="javascript:reportUser()">{_report}</a>
<a n:if="!$user->isHideFromGlobalFeedEnabled()" class="profile_link" style="display:block;width:96%;" id="__ignoreSomeone" data-val='{!$ignore_status ? 1 : 0}' data-id="{$user->getId()}">
{if !$ignore_status}{_ignore_user}{else}{_unignore_user}{/if}
</a>
<script> <script>
function reportUser() { function reportUser() {
uReportMsgTxt = tr("going_to_report_user"); uReportMsgTxt = tr("going_to_report_user");

View file

@ -15,6 +15,8 @@
<div n:attr="id => (isset($globalFeed) ? 'activetabs' : 'ki')" class="tab"> <div n:attr="id => (isset($globalFeed) ? 'activetabs' : 'ki')" class="tab">
<a n:attr="id => (isset($globalFeed) ? 'act_tab_a' : 'ki')" href="/feed/all">{_all_news}</a> <a n:attr="id => (isset($globalFeed) ? 'act_tab_a' : 'ki')" href="/feed/all">{_all_news}</a>
</div> </div>
<a href='#' id="__feed_settings_link" data-pagescount='{ceil($paginatorConf->count / $paginatorConf->perPage)}'>{_feed_settings}</a>
</div> </div>
<div n:class="postFeedWrapper, $thisUser->hasMicroblogEnabled() ? postFeedWrapperMicroblog"> <div n:class="postFeedWrapper, $thisUser->hasMicroblogEnabled() ? postFeedWrapperMicroblog">

View file

@ -40,8 +40,8 @@
<span n:if="$comment->getEditTime()" class="edited editedMark">({_edited_short})</span> <span n:if="$comment->getEditTime()" class="edited editedMark">({_edited_short})</span>
</a> </a>
{if !$timeOnly} {if !$timeOnly}
&nbsp;|
{if $comment->canBeDeletedBy($thisUser)} {if $comment->canBeDeletedBy($thisUser)}
|
<a href="/comment{$comment->getId()}/delete">{_delete}</a> <a href="/comment{$comment->getId()}/delete">{_delete}</a>
{/if} {/if}
{if $comment->canBeEditedBy($thisUser)} {if $comment->canBeEditedBy($thisUser)}

View file

@ -88,3 +88,7 @@ div.ovk-video > div > img
height: 31px; height: 31px;
min-width: 30px; min-width: 30px;
} }
.entity_vertical_list .entity_vertical_list_item img {
border-radius: 20px;
}

View file

@ -621,6 +621,16 @@ input[type=checkbox]:checked:disabled {
background-position: 0 -42px; background-position: 0 -42px;
} }
input[type="number"] {
border: 1px solid #C0CAD5;
padding: 3px;
font-size: 11px;
font-family: tahoma, verdana, arial, sans-serif;
width: 100%;
box-sizing: border-box;
outline: none;
}
#auth { #auth {
padding: 10px; padding: 10px;
} }
@ -2102,6 +2112,10 @@ table td[width="120"] {
border-radius: 2px; border-radius: 2px;
} }
.mb_tab:hover {
background: #e2e0e0;
}
.mb_tab div, .mb_tab > a { .mb_tab div, .mb_tab > a {
padding: 5px 9px; padding: 5px 9px;
display: block; display: block;
@ -3262,3 +3276,44 @@ hr {
width: 30px; width: 30px;
height: 7px; height: 7px;
} }
#__feed_settings_link {
float: right;
margin-right: 3px;
margin-top: 2px;
}
#__feed_settings_link:hover {
text-decoration: underline;
}
#_feed_settings_container {
height: 100%;
}
#_feed_settings_container #__content {
display: flex;
flex-direction: column;
gap: 4px;
padding: 5px;
height: 100%;
}
#_feed_settings_container #__content .settings_item {
display: flex;
flex-direction: column;
}
#_feed_settings_container #__content .final_settings_item {
align-self: end;
margin-top: 74px;
}
#_feed_settings_container #pageNumber {
width: 70px;
}
#_feed_settings_container .entity_vertical_list {
height: 206px;
overflow-x: hidden;
}

243
Web/static/js/al_feed.js Normal file
View file

@ -0,0 +1,243 @@
//u('.postFeedPageSelect').attr('style', 'display:none')
// Source ignoring
u(document).on("click", "#__ignoreSomeone", async (e) => {
e.preventDefault()
const TARGET = u(e.target)
const ENTITY_ID = Number(e.target.dataset.id)
const VAL = Number(e.target.dataset.val)
const ACT = VAL == 1 ? 'ignore' : 'unignore'
const METHOD_NAME = ACT == 'ignore' ? 'addBan' : 'deleteBan'
const PARAM_NAME = ENTITY_ID < 0 ? 'group_ids' : 'user_ids'
const ENTITY_NAME = ENTITY_ID < 0 ? 'club' : 'user'
const URL = `/method/newsfeed.${METHOD_NAME}?auth_mechanism=roaming&${PARAM_NAME}=${Math.abs(ENTITY_ID)}`
TARGET.addClass('lagged')
const REQ = await fetch(URL)
const RES = await REQ.json()
TARGET.removeClass('lagged')
if(RES.error_code) {
switch(RES.error_code) {
case -10:
fastError(';/')
break
case -50:
fastError(tr('ignored_sources_limit'))
break
default:
fastError(res.error_msg)
break
}
return
}
if(RES.response == 1) {
if(ACT == 'unignore') {
TARGET.attr('data-val', '1')
TARGET.html(tr(`ignore_${ENTITY_NAME}`))
} else {
TARGET.attr('data-val', '0')
TARGET.html(tr(`unignore_${ENTITY_NAME}`))
}
}
})
u(document).on('click', '#__feed_settings_link', (e) => {
e.preventDefault()
let current_tab = 'main';
const body = `
<div id='_feed_settings_container'>
<div id='_tabs'>
<div class="mb_tabs">
<div class="mb_tab" data-name='main'>
<a>
${tr('main')}
</a>
</div>
<div class="mb_tab" data-name='ignored'>
<a>
${tr('ignored_sources')}
</a>
</div>
</div>
</div>
<div id='__content'></div>
</div>
`
MessageBox(tr("feed_settings"), body, [tr("close")], [Function.noop])
u('.ovk-diag-body').attr('style', 'padding:0px;height: 255px;')
async function __switchTab(tab)
{
current_tab = tab
u(`#_feed_settings_container .mb_tab`).attr('id', 'ki')
u(`#_feed_settings_container .mb_tab[data-name='${tab}']`).attr('id', 'active')
u(`#_feed_settings_container .mb_tabs input`).remove()
switch(current_tab) {
case 'main':
const __temp_url = new URL(location.href)
const PAGES_COUNT = Number(e.target.dataset.pagescount ?? '10')
const CURRENT_PERPAGE = Number(__temp_url.searchParams.get('posts') ?? 10)
const CURRENT_PAGE = Number(__temp_url.searchParams.get('p') ?? 1)
const CURRENT_RETURN_BANNED = Number(__temp_url.searchParams.get('return_banned') ?? 0)
const COUNT = [1, 5, 10, 20, 30, 40, 50]
u('#_feed_settings_container #__content').html(`
<div class='settings_item'>
${tr('posts_per_page')}:
<select id="pageSelect"></select>
</div>
<div class='settings_item'>
${tr('start_from_page')}
<input type='number' min='1' max='${PAGES_COUNT}' id='pageNumber' value='${CURRENT_PAGE}' placeholder='${CURRENT_PAGE}'>
</div>
<div class='settings_item'>
<label>
<input type='checkbox' id="showIgnored" ${CURRENT_RETURN_BANNED == 1 ? 'checked' : ''}>
${tr('show_ignored_sources')}
</label>
</div>
<div class='final_settings_item'>
<input class='button' type='button' value='${tr('apply')}'>
</div>
`)
u('#_feed_settings_container').on('click', '.final_settings_item input', (e) => {
const INPUT_PAGES_COUNT = parseInt(u('#_feed_settings_container #pageSelect').nodes[0].selectedOptions[0].value ?? '10')
const INPUT_PAGE = parseInt(u('#pageNumber').nodes[0].value ?? '1')
const INPUT_IGNORED = Number(u('#showIgnored').nodes[0].checked ?? false)
const FINAL_URL = new URL(location.href)
if(CURRENT_PERPAGE != INPUT_PAGES_COUNT) {
FINAL_URL.searchParams.set('posts', INPUT_PAGES_COUNT)
}
if(CURRENT_PAGE != INPUT_PAGE && INPUT_PAGE <= PAGES_COUNT) {
FINAL_URL.searchParams.set('p', Math.max(1, INPUT_PAGE))
}
if(INPUT_IGNORED == 1) {
FINAL_URL.searchParams.set('return_banned', 1)
} else {
FINAL_URL.searchParams.delete('return_banned')
}
window.location.assign(FINAL_URL.href)
})
COUNT.forEach(item => {
u('#_feed_settings_container #pageSelect').append(`
<option value="${item}" ${item == CURRENT_PERPAGE ? 'selected' : ''}>${item}</option>
`)
})
break
case 'ignored':
u('#_feed_settings_container #__content').html(`
<div id='gif_loader'></div>
`)
if(!window.openvk.ignored_list) {
const IGNORED_RES = await fetch('/method/newsfeed.getBanned?auth_mechanism=roaming&extended=1&fields=real_id,screen_name,photo_50&merge=1')
const IGNORED_LIST = await IGNORED_RES.json()
window.openvk.ignored_list = IGNORED_LIST
}
u('#_feed_settings_container #__content').html(`
<div class='entity_vertical_list mini'></div>
`)
u('#_feed_settings_container .mb_tabs').append(`
<input class='button lagged' id='_remove_ignores' type='button' value='${tr('stop_ignore')}'>
`)
if(window.openvk.ignored_list.error_code) {
fastError(IGNORED_LIST.error_msg)
return
}
if(window.openvk.ignored_list.response.items.length < 1) {
u('#_feed_settings_container #__content').html(tr('no_ignores_count'))
u('#_remove_ignores').remove()
}
window.openvk.ignored_list.response.items.forEach(ignore_item => {
let name = ignore_item.name
if(!name) {
name = ignore_item.first_name + ' ' + ignore_item.last_name
}
u('#_feed_settings_container #__content .entity_vertical_list').append(`
<label class='entity_vertical_list_item with_third_column' data-id='${ignore_item.real_id}'>
<div class='first_column'>
<a href='/${ignore_item.screen_name}' class='avatar'>
<img src='${ignore_item.photo_50}'>
</a>
<div class='info'>
<b class='noOverflow'>${ovk_proc_strtr(escapeHtml(name), 100)}</b>
</div>
</div>
<div class='third_column'>
<input type='checkbox' name='remove_me'>
</div>
</label>
`)
})
u("#_feed_settings_container").on("click", "input[name='remove_me']", async (e) => {
const checks_count = u(`input[name='remove_me']:checked`).length
if(checks_count > 0) {
u('.mb_tabs #_remove_ignores').removeClass('lagged')
} else {
u('.mb_tabs #_remove_ignores').addClass('lagged')
}
if(checks_count > 10) {
e.preventDefault()
}
})
u('#_feed_settings_container').on('click', '#_remove_ignores', async (e) => {
e.target.classList.add('lagged')
const ids = []
u('#__content .entity_vertical_list label').nodes.forEach(item => {
const _checkbox = item.querySelector(`input[type='checkbox'][name='remove_me']`)
if(_checkbox.checked) {
ids.push(item.dataset.id)
}
})
const user_ids = []
const group_ids = []
ids.forEach(id => {
id > 0 ? user_ids.push(id) : group_ids.push(Math.abs(id))
})
const res = await fetch(`/method/newsfeed.deleteBan?auth_mechanism=roaming&user_ids=${user_ids.join(',')}&group_ids=${group_ids.join(',')}`)
const resp = await res.json()
if(resp.error_code) {
console.error(resp.error_msg)
return
}
window.openvk.ignored_list = null
__switchTab('ignored')
})
break
}
}
u("#_feed_settings_container").on("click", ".mb_tab a", async (e) => {
await __switchTab(u(e.target).closest('.mb_tab').attr('data-name'))
})
__switchTab('main')
})

View file

@ -277,6 +277,45 @@ function parseAttachments(string $attachments): array
return $returnArr; return $returnArr;
} }
function get_entity_by_id(int $id)
{
if($id > 0)
return (new openvk\Web\Models\Repositories\Users)->get($id);
return (new openvk\Web\Models\Repositories\Clubs)->get(abs($id));
}
function get_entities(array $ids = []): array
{
$main_result = [];
$users = [];
$clubs = [];
foreach($ids as $id) {
$id = (int)$id;
if($id < 0)
$clubs[] = abs($id);
if($id > 0)
$users[] = $id;
}
if(sizeof($users) > 0) {
$users_tmp = (new openvk\Web\Models\Repositories\Users)->getByIds($users);
foreach($users_tmp as $user) {
$main_result[] = $user;
}
}
if(sizeof($clubs) > 0) {
$clubs_tmp = (new openvk\Web\Models\Repositories\Clubs)->getByIds($clubs);
foreach($clubs_tmp as $club) {
$main_result[] = $club;
}
}
return $main_result;
}
function ovk_scheme(bool $with_slashes = false): string function ovk_scheme(bool $with_slashes = false): string
{ {
$scheme = ovk_is_ssl() ? "https" : "http"; $scheme = ovk_is_ssl() ? "https" : "http";

View file

@ -0,0 +1,2 @@
CREATE TABLE `openvk`.`ignored_sources` (`id` BIGINT(20) UNSIGNED NULL AUTO_INCREMENT , `owner` BIGINT(20) UNSIGNED NOT NULL , `source` BIGINT(20) NOT NULL , PRIMARY KEY (`id`), INDEX (`owner`)) ENGINE = InnoDB;
ALTER TABLE `openvk`.`ignored_sources` ADD INDEX `owner_source` (`owner`, `source`);

View file

@ -219,6 +219,7 @@
"my_news" = "My news"; "my_news" = "My news";
"all_news" = "All news"; "all_news" = "All news";
"posts_per_page" = "Number of posts per page"; "posts_per_page" = "Number of posts per page";
"show_ignored_sources" = "Show ignored sources";
"attachment" = "Attachment"; "attachment" = "Attachment";
"post_as_group" = "Post as group"; "post_as_group" = "Post as group";
@ -245,6 +246,17 @@
"edited_short" = "edited"; "edited_short" = "edited";
"feed_settings" = "Feed settings";
"ignored_sources" = "Ignored sources";
"ignore_user" = "Ignore user";
"unignore_user" = "Unignore user";
"ignore_club" = "Ignore club";
"unignore_club" = "Unignore club";
"no_ignores_count" = "No ignored sources.";
"stop_ignore" = "Unignore";
"start_from_page" = "Start from page";
"all_posts" = "All posts"; "all_posts" = "All posts";
"users_posts" = "Posts by $1"; "users_posts" = "Posts by $1";
"clubs_posts" = "Group's posts"; "clubs_posts" = "Group's posts";
@ -1535,6 +1547,9 @@
"group_is_banned" = "Group was successfully banned"; "group_is_banned" = "Group was successfully banned";
"description_too_long" = "Description is too long."; "description_too_long" = "Description is too long.";
"invalid_club" = "This group does not exists.";
"invalid_user" = "This user does not exists.";
"ignored_sources_limit" = "Limit of ignored sources has exceed";
"invalid_audio" = "Invalid audio."; "invalid_audio" = "Invalid audio.";
"do_not_have_audio" = "You don't have this audio."; "do_not_have_audio" = "You don't have this audio.";
@ -1749,6 +1764,7 @@
"question_confirm" = "This action can't be undone. Do you really wanna do it?"; "question_confirm" = "This action can't be undone. Do you really wanna do it?";
"confirm_m" = "Confirm"; "confirm_m" = "Confirm";
"action_successfully" = "Success"; "action_successfully" = "Success";
"apply" = "Apply";
/* User Alerts */ /* User Alerts */

View file

@ -204,6 +204,7 @@
"my_news" = "Мои новости"; "my_news" = "Мои новости";
"all_news" = "Все новости"; "all_news" = "Все новости";
"posts_per_page" = "Количество записей на странице"; "posts_per_page" = "Количество записей на странице";
"show_ignored_sources" = "Показывать игнорируемые источники";
"attachment" = "Вложение"; "attachment" = "Вложение";
"post_as_group" = "От имени сообщества"; "post_as_group" = "От имени сообщества";
"comment_as_group" = "От имени сообщества"; "comment_as_group" = "От имени сообщества";
@ -225,6 +226,17 @@
"post_is_ad" = "Этот пост был размещён за взятку."; "post_is_ad" = "Этот пост был размещён за взятку.";
"edited_short" = "ред."; "edited_short" = "ред.";
"feed_settings" = "Настройки ленты";
"ignored_sources" = "Игнорируемые источники";
"ignore_user" = "Игнорировать пользователя";
"unignore_user" = "Не игнорировать пользователя";
"ignore_club" = "Игнорировать группу";
"unignore_club" = "Не игнорировать группу";
"no_ignores_count" = "Игнорируемых источников нет.";
"stop_ignore" = "Не игнорировать";
"start_from_page" = "Начинать со страницы";
"all_posts" = "Все записи"; "all_posts" = "Все записи";
"users_posts" = "Записи $1"; "users_posts" = "Записи $1";
"clubs_posts" = "Записи сообщества"; "clubs_posts" = "Записи сообщества";
@ -1438,6 +1450,11 @@
"group_owner_is_banned" = "Создатель сообщества успешно забанен."; "group_owner_is_banned" = "Создатель сообщества успешно забанен.";
"group_is_banned" = "Сообщество успешно забанено"; "group_is_banned" = "Сообщество успешно забанено";
"description_too_long" = "Описание слишком длинное."; "description_too_long" = "Описание слишком длинное.";
"invalid_club" = "Такой группы не существует.";
"invalid_user" = "Такого пользователя не существует.";
"ignored_sources_limit" = "Превышен лимит игнорируемых источников.";
"invalid_audio" = "Такой аудиозаписи не существует."; "invalid_audio" = "Такой аудиозаписи не существует.";
"do_not_have_audio" = "У вас нет этой аудиозаписи."; "do_not_have_audio" = "У вас нет этой аудиозаписи.";
"do_have_audio" = "У вас уже есть эта аудиозапись."; "do_have_audio" = "У вас уже есть эта аудиозапись.";
@ -1641,6 +1658,7 @@
"question_confirm" = "Это действие нельзя отменить. Вы действительно уверены в том что хотите сделать?"; "question_confirm" = "Это действие нельзя отменить. Вы действительно уверены в том что хотите сделать?";
"confirm_m" = "Подтвердить"; "confirm_m" = "Подтвердить";
"action_successfully" = "Операция успешна"; "action_successfully" = "Операция успешна";
"apply" = "Применить";
/* User alerts */ /* User alerts */

View file

@ -52,6 +52,8 @@ openvk:
strict: false strict: false
music: music:
exposeOriginalURLs: true exposeOriginalURLs: true
newsfeed:
ignoredSourcesLimit: 50
wall: wall:
christian: false christian: false
anonymousPosting: anonymousPosting: