mirror of
https://github.com/openvk/openvk
synced 2025-04-23 00:23:01 +03:00
Merge branch 'master' into suggests
This commit is contained in:
commit
a0e895824c
117 changed files with 3724 additions and 753 deletions
92
ServiceAPI/Photos.php
Normal file
92
ServiceAPI/Photos.php
Normal file
|
@ -0,0 +1,92 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\ServiceAPI;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use openvk\Web\Models\Repositories\{Photos as PhotosRepo, Albums, Clubs};
|
||||
|
||||
class Photos implements Handler
|
||||
{
|
||||
protected $user;
|
||||
protected $photos;
|
||||
|
||||
function __construct(?User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->photos = new PhotosRepo;
|
||||
}
|
||||
|
||||
function getPhotos(int $page = 1, int $album = 0, callable $resolve, callable $reject)
|
||||
{
|
||||
if($album == 0) {
|
||||
$photos = $this->photos->getEveryUserPhoto($this->user, $page, 24);
|
||||
$count = $this->photos->getUserPhotosCount($this->user);
|
||||
} else {
|
||||
$album = (new Albums)->get($album);
|
||||
|
||||
if(!$album || $album->isDeleted())
|
||||
$reject(55, "Invalid .");
|
||||
|
||||
if($album->getOwner() instanceof User) {
|
||||
if($album->getOwner()->getId() != $this->user->getId())
|
||||
$reject(555, "Access to album denied");
|
||||
} else {
|
||||
if(!$album->getOwner()->canBeModifiedBy($this->user))
|
||||
$reject(555, "Access to album denied");
|
||||
}
|
||||
|
||||
$photos = $album->getPhotos($page, 24);
|
||||
$count = $album->size();
|
||||
}
|
||||
|
||||
$arr = [
|
||||
"count" => $count,
|
||||
"items" => [],
|
||||
];
|
||||
|
||||
foreach($photos as $photo) {
|
||||
$res = json_decode(json_encode($photo->toVkApiStruct()), true);
|
||||
|
||||
$arr["items"][] = $res;
|
||||
}
|
||||
|
||||
$resolve($arr);
|
||||
}
|
||||
|
||||
function getAlbums(int $club, callable $resolve, callable $reject)
|
||||
{
|
||||
$albumsRepo = (new Albums);
|
||||
|
||||
$count = $albumsRepo->getUserAlbumsCount($this->user);
|
||||
$albums = $albumsRepo->getUserAlbums($this->user, 1, $count);
|
||||
|
||||
$arr = [
|
||||
"count" => $count,
|
||||
"items" => [],
|
||||
];
|
||||
|
||||
foreach($albums as $album) {
|
||||
$res = ["id" => $album->getId(), "name" => $album->getName()];
|
||||
|
||||
$arr["items"][] = $res;
|
||||
}
|
||||
|
||||
if($club > 0) {
|
||||
$cluber = (new Clubs)->get($club);
|
||||
|
||||
if(!$cluber || !$cluber->canBeModifiedBy($this->user))
|
||||
$reject(1337, "Invalid (club), or you can't modify him");
|
||||
|
||||
$clubCount = (new Albums)->getClubAlbumsCount($cluber);
|
||||
$clubAlbums = (new Albums)->getClubAlbums($cluber, 1, $clubCount);
|
||||
|
||||
foreach($clubAlbums as $albumr) {
|
||||
$res = ["id" => $albumr->getId(), "name" => $albumr->getName()];
|
||||
|
||||
$arr["items"][] = $res;
|
||||
}
|
||||
|
||||
$arr["count"] = $arr["count"] + $clubCount;
|
||||
}
|
||||
|
||||
$resolve($arr);
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ use openvk\Web\Models\Entities\Post;
|
|||
use openvk\Web\Models\Entities\User;
|
||||
use openvk\Web\Models\Entities\Notifications\PostAcceptedNotification;
|
||||
use openvk\Web\Models\Repositories\{Posts, Notes};
|
||||
use openvk\Web\Models\Repositories\{Posts, Notes, Videos};
|
||||
|
||||
class Wall implements Handler
|
||||
{
|
||||
|
@ -16,6 +17,7 @@ class Wall implements Handler
|
|||
$this->user = $user;
|
||||
$this->posts = new Posts;
|
||||
$this->notes = new Notes;
|
||||
$this->videos = new Videos;
|
||||
}
|
||||
|
||||
function getPost(int $id, callable $resolve, callable $reject): void
|
||||
|
@ -99,7 +101,7 @@ class Wall implements Handler
|
|||
|
||||
$resolve($arr);
|
||||
}
|
||||
|
||||
|
||||
function declinePost(int $id, callable $resolve, callable $reject)
|
||||
{
|
||||
$post = $this->posts->get($id);
|
||||
|
@ -159,4 +161,45 @@ class Wall implements Handler
|
|||
|
||||
$resolve(["id" => $post->getPrettyId(), "new_count" => $this->posts->getSuggestedPostsCount($post->getWallOwner()->getId())]);
|
||||
}
|
||||
|
||||
function getVideos(int $page = 1, callable $resolve, callable $reject)
|
||||
{
|
||||
$videos = $this->videos->getByUser($this->user, $page, 8);
|
||||
$count = $this->videos->getUserVideosCount($this->user);
|
||||
|
||||
$arr = [
|
||||
"count" => $count,
|
||||
"items" => [],
|
||||
];
|
||||
|
||||
foreach($videos as $video) {
|
||||
$res = json_decode(json_encode($video->toVkApiStruct()), true);
|
||||
$res["video"]["author_name"] = $video->getOwner()->getCanonicalName();
|
||||
|
||||
$arr["items"][] = $res;
|
||||
}
|
||||
|
||||
$resolve($arr);
|
||||
}
|
||||
|
||||
function searchVideos(int $page = 1, string $query, callable $resolve, callable $reject)
|
||||
{
|
||||
$dbc = $this->videos->find($query);
|
||||
$videos = $dbc->page($page, 8);
|
||||
$count = $dbc->size();
|
||||
|
||||
$arr = [
|
||||
"count" => $count,
|
||||
"items" => [],
|
||||
];
|
||||
|
||||
foreach($videos as $video) {
|
||||
$res = json_decode(json_encode($video->toVkApiStruct()), true);
|
||||
$res["video"]["author_name"] = $video->getOwner()->getCanonicalName();
|
||||
|
||||
$arr["items"][] = $res;
|
||||
}
|
||||
|
||||
$resolve($arr);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -211,7 +211,7 @@ final class Notes extends VKAPIRequestHandler
|
|||
$items = [];
|
||||
|
||||
$note = (new NotesRepo)->getNoteById((int)$id[0], (int)$id[1]);
|
||||
if($note) {
|
||||
if($note && !$note->isDeleted()) {
|
||||
$nodez->notes[] = $note->toVkApiStruct();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -578,28 +578,25 @@ final class Wall extends VKAPIRequestHandler
|
|||
if($attachmentType == "photo") {
|
||||
$attacc = (new PhotosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Photo does not exists");
|
||||
if($attacc->getOwner()->getId() != $this->getUser()->getId())
|
||||
$this->fail(43, "You do not have access to this photo");
|
||||
$this->fail(100, "Invalid photo");
|
||||
if(!$attacc->getOwner()->getPrivacyPermission('photos.read', $this->getUser()))
|
||||
$this->fail(43, "Access to photo denied");
|
||||
|
||||
$post->attach($attacc);
|
||||
} elseif($attachmentType == "video") {
|
||||
$attacc = (new VideosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Video does not exists");
|
||||
if($attacc->getOwner()->getId() != $this->getUser()->getId())
|
||||
$this->fail(43, "You do not have access to this video");
|
||||
if(!$attacc->getOwner()->getPrivacyPermission('videos.read', $this->getUser()))
|
||||
$this->fail(43, "Access to video denied");
|
||||
|
||||
$post->attach($attacc);
|
||||
} elseif($attachmentType == "note") {
|
||||
$attacc = (new NotesRepo)->getNoteById($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Note does not exist");
|
||||
if($attacc->getOwner()->getId() != $this->getUser()->getId())
|
||||
$this->fail(43, "You do not have access to this note");
|
||||
|
||||
if($attacc->getOwner()->getPrivacySetting("notes.read") < 1)
|
||||
$this->fail(11, "You can't attach note to post, because your notes list is closed. Change it in privacy settings in web-version.");
|
||||
if(!$attacc->getOwner()->getPrivacyPermission('notes.read', $this->getUser()))
|
||||
$this->fail(11, "Access to note denied");
|
||||
|
||||
$post->attach($attacc);
|
||||
}
|
||||
|
@ -809,7 +806,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
return $response;
|
||||
}
|
||||
|
||||
function createComment(int $owner_id, int $post_id, string $message, int $from_group = 0, string $attachments = "") {
|
||||
function createComment(int $owner_id, int $post_id, string $message = "", int $from_group = 0, string $attachments = "") {
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
|
@ -867,16 +864,16 @@ final class Wall extends VKAPIRequestHandler
|
|||
$attacc = (new PhotosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Photo does not exists");
|
||||
if($attacc->getOwner()->getId() != $this->getUser()->getId())
|
||||
$this->fail(43, "You do not have access to this photo");
|
||||
if(!$attacc->getOwner()->getPrivacyPermission('photos.read', $this->getUser()))
|
||||
$this->fail(11, "Access to photo denied");
|
||||
|
||||
$comment->attach($attacc);
|
||||
} elseif($attachmentType == "video") {
|
||||
$attacc = (new VideosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Video does not exists");
|
||||
if($attacc->getOwner()->getId() != $this->getUser()->getId())
|
||||
$this->fail(43, "You do not have access to this video");
|
||||
if(!$attacc->getOwner()->getPrivacyPermission('videos.read', $this->getUser()))
|
||||
$this->fail(11, "Access to video denied");
|
||||
|
||||
$comment->attach($attacc);
|
||||
}
|
||||
|
|
|
@ -241,7 +241,7 @@ class Club extends RowModel
|
|||
"shape" => "spline",
|
||||
"color" => "#597da3",
|
||||
],
|
||||
"name" => $unique ? "Полный охват" : "Все просмотры",
|
||||
"name" => $unique ? tr("full_coverage") : tr("all_views"),
|
||||
],
|
||||
"subs" => [
|
||||
"x" => array_reverse(range(1, 7)),
|
||||
|
@ -252,7 +252,7 @@ class Club extends RowModel
|
|||
"color" => "#b05c91",
|
||||
],
|
||||
"fill" => "tozeroy",
|
||||
"name" => $unique ? "Охват подписчиков" : "Просмотры подписчиков",
|
||||
"name" => $unique ? tr("subs_coverage") : tr("subs_views"),
|
||||
],
|
||||
"viral" => [
|
||||
"x" => array_reverse(range(1, 7)),
|
||||
|
@ -263,7 +263,7 @@ class Club extends RowModel
|
|||
"color" => "#4d9fab",
|
||||
],
|
||||
"fill" => "tozeroy",
|
||||
"name" => $unique ? "Виральный охват" : "Виральные просмотры",
|
||||
"name" => $unique ? tr("viral_coverage") : tr("viral_views"),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ class Comment extends Post
|
|||
|
||||
function getPrettyId(): string
|
||||
{
|
||||
return $this->getRecord()->id;
|
||||
return (string)$this->getRecord()->id;
|
||||
}
|
||||
|
||||
function getVirtualId(): int
|
||||
|
@ -90,4 +90,12 @@ class Comment extends Post
|
|||
{
|
||||
return "/wall" . $this->getTarget()->getPrettyId() . "#_comment" . $this->getId();
|
||||
}
|
||||
|
||||
function canBeEditedBy(?User $user = NULL): bool
|
||||
{
|
||||
if(!$user)
|
||||
return false;
|
||||
|
||||
return $user->getId() == $this->getOwner(false)->getId();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ class IP extends RowModel
|
|||
$this->stateChanges("ip", $ip);
|
||||
}
|
||||
|
||||
function save($log): void
|
||||
function save(?bool $log = false): void
|
||||
{
|
||||
if(is_null($this->getRecord()))
|
||||
$this->stateChanges("first_seen", time());
|
||||
|
|
|
@ -121,14 +121,14 @@ abstract class Media extends Postable
|
|||
$this->stateChanges("hash", $hash);
|
||||
}
|
||||
|
||||
function save(): void
|
||||
function save(?bool $log = false): void
|
||||
{
|
||||
if(!is_null($this->processingPlaceholder) && is_null($this->getRecord())) {
|
||||
$this->stateChanges("processed", 0);
|
||||
$this->stateChanges("last_checked", time());
|
||||
}
|
||||
|
||||
parent::save();
|
||||
parent::save($log);
|
||||
}
|
||||
|
||||
function delete(bool $softly = true): void
|
||||
|
|
|
@ -124,7 +124,7 @@ class Note extends Postable
|
|||
$res = (object) [];
|
||||
|
||||
$res->type = "note";
|
||||
$res->id = $this->getId();
|
||||
$res->id = $this->getVirtualId();
|
||||
$res->owner_id = $this->getOwner()->getId();
|
||||
$res->title = $this->getName();
|
||||
$res->text = $this->getText();
|
||||
|
|
|
@ -248,11 +248,25 @@ class Post extends Postable
|
|||
$this->unwire();
|
||||
$this->save();
|
||||
}
|
||||
|
||||
|
||||
function getSuggestionType()
|
||||
{
|
||||
return $this->getRecord()->suggested;
|
||||
}
|
||||
|
||||
function canBeEditedBy(?User $user = NULL): bool
|
||||
{
|
||||
if(!$user)
|
||||
return false;
|
||||
|
||||
if($this->isDeactivationMessage() || $this->isUpdateAvatarMessage())
|
||||
return false;
|
||||
|
||||
if($this->getTargetWall() > 0)
|
||||
return $this->getPublicationTime()->timestamp() + WEEK > time() && $user->getId() == $this->getOwner(false)->getId();
|
||||
|
||||
return $user->getId() == $this->getOwner(false)->getId();
|
||||
}
|
||||
|
||||
use Traits\TRichText;
|
||||
}
|
||||
|
|
|
@ -152,7 +152,7 @@ abstract class Postable extends Attachable
|
|||
throw new ISE("Setting virtual id manually is forbidden");
|
||||
}
|
||||
|
||||
function save(): void
|
||||
function save(?bool $log = false): void
|
||||
{
|
||||
$vref = $this->upperNodeReferenceColumnName;
|
||||
|
||||
|
@ -167,11 +167,11 @@ abstract class Postable extends Attachable
|
|||
$this->stateChanges("created", time());
|
||||
|
||||
$this->stateChanges("virtual_id", $pCount + 1);
|
||||
} else {
|
||||
} /*else {
|
||||
$this->stateChanges("edited", time());
|
||||
}
|
||||
}*/
|
||||
|
||||
parent::save();
|
||||
parent::save($log);
|
||||
}
|
||||
|
||||
use Traits\TAttachmentHost;
|
||||
|
|
|
@ -3,6 +3,7 @@ namespace openvk\Web\Models\Entities;
|
|||
use openvk\Web\Util\DateTime;
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
use openvk\Web\Models\RowModel;
|
||||
use openvk\Web\Models\Entities\Club;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use openvk\Web\Models\Repositories\{Applications, Comments, Notes, Reports, Users, Posts, Photos, Videos, Clubs};
|
||||
use Chandler\Database\DatabaseConnection as DB;
|
||||
|
@ -96,8 +97,19 @@ class Report extends RowModel
|
|||
{
|
||||
if ($this->getContentType() !== "user") {
|
||||
$pubTime = $this->getContentObject()->getPublicationTime();
|
||||
$name = $this->getContentObject()->getName();
|
||||
$this->getAuthor()->adminNotify("Ваш контент, который вы опубликовали $pubTime ($name) был удалён модераторами инстанса. За повторные или серьёзные нарушения вас могут заблокировать.");
|
||||
if (method_exists($this->getContentObject(), "getName")) {
|
||||
$name = $this->getContentObject()->getName();
|
||||
$placeholder = "$pubTime ($name)";
|
||||
} else {
|
||||
$placeholder = "$pubTime";
|
||||
}
|
||||
|
||||
if ($this->getAuthor() instanceof Club) {
|
||||
$name = $this->getAuthor()->getName();
|
||||
$this->getAuthor()->getOwner()->adminNotify("Ваш контент, который опубликовали $placeholder в созданной вами группе \"$name\" был удалён модераторами инстанса. За повторные или серьёзные нарушения группу могут заблокировать.");
|
||||
} else {
|
||||
$this->getAuthor()->adminNotify("Ваш контент, который вы опубликовали $placeholder был удалён модераторами инстанса. За повторные или серьёзные нарушения вас могут заблокировать.");
|
||||
}
|
||||
$this->getContentObject()->delete($this->getContentType() !== "app");
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Entities\Traits;
|
||||
use openvk\Web\Models\Entities\Attachable;
|
||||
use openvk\Web\Models\Entities\{Attachable, Photo};
|
||||
use openvk\Web\Util\Makima\Makima;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
|
||||
trait TAttachmentHost
|
||||
|
@ -29,6 +30,46 @@ trait TAttachmentHost
|
|||
yield $repo->get($rel->attachable_id);
|
||||
}
|
||||
}
|
||||
|
||||
function getChildrenWithLayout(int $w, int $h = -1): object
|
||||
{
|
||||
if($h < 0)
|
||||
$h = $w;
|
||||
|
||||
$children = $this->getChildren();
|
||||
$skipped = $photos = $result = [];
|
||||
foreach($children as $child) {
|
||||
if($child instanceof Photo) {
|
||||
$photos[] = $child;
|
||||
continue;
|
||||
}
|
||||
|
||||
$skipped[] = $child;
|
||||
}
|
||||
|
||||
$height = "unset";
|
||||
$width = $w;
|
||||
if(sizeof($photos) < 2) {
|
||||
if(isset($photos[0]))
|
||||
$result[] = ["100%", "unset", $photos[0], "unset"];
|
||||
} else {
|
||||
$mak = new Makima($photos);
|
||||
$layout = $mak->computeMasonryLayout($w, $h);
|
||||
$height = $layout->height;
|
||||
$width = $layout->width;
|
||||
for($i = 0; $i < sizeof($photos); $i++) {
|
||||
$tile = $layout->tiles[$i];
|
||||
$result[] = [$tile->width . "px", $tile->height . "px", $photos[$i], "left"];
|
||||
}
|
||||
}
|
||||
|
||||
return (object) [
|
||||
"width" => $width . "px",
|
||||
"height" => $height . "px",
|
||||
"tiles" => $result,
|
||||
"extras" => $skipped,
|
||||
];
|
||||
}
|
||||
|
||||
function attach(Attachable $attachment): void
|
||||
{
|
||||
|
|
|
@ -462,6 +462,7 @@ class User extends RowModel
|
|||
"news",
|
||||
"links",
|
||||
"poster",
|
||||
"apps"
|
||||
],
|
||||
])->get($id);
|
||||
}
|
||||
|
@ -1026,6 +1027,7 @@ class User extends RowModel
|
|||
"news",
|
||||
"links",
|
||||
"poster",
|
||||
"apps"
|
||||
],
|
||||
])->set($id, (int) $status)->toInteger();
|
||||
|
||||
|
|
|
@ -42,4 +42,10 @@ class Gifts
|
|||
foreach($cats as $cat)
|
||||
yield new GiftCategory($cat);
|
||||
}
|
||||
|
||||
function getCategoriesCount(): int
|
||||
{
|
||||
$cats = $this->cats->where("deleted", false);
|
||||
return $cats->count();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,14 +33,26 @@ class Photos
|
|||
return new Photo($photo);
|
||||
}
|
||||
|
||||
function getEveryUserPhoto(User $user): \Traversable
|
||||
function getEveryUserPhoto(User $user, int $page = 1, ?int $perPage = NULL): \Traversable
|
||||
{
|
||||
$perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE;
|
||||
$photos = $this->photos->where([
|
||||
"owner" => $user->getId()
|
||||
]);
|
||||
"owner" => $user->getId(),
|
||||
"deleted" => 0
|
||||
])->order("id DESC");
|
||||
|
||||
foreach($photos as $photo) {
|
||||
foreach($photos->page($page, $perPage) as $photo) {
|
||||
yield new Photo($photo);
|
||||
}
|
||||
}
|
||||
|
||||
function getUserPhotosCount(User $user)
|
||||
{
|
||||
$photos = $this->photos->where([
|
||||
"owner" => $user->getId(),
|
||||
"deleted" => 0
|
||||
]);
|
||||
|
||||
return sizeof($photos);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -283,7 +283,7 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$this->notFound();
|
||||
|
||||
$gift->delete();
|
||||
$this->flashFail("succ", "Gift moved successfully", "This gift will now be in <b>Recycle Bin</b>.");
|
||||
$this->flashFail("succ", tr("admin_gift_moved_successfully"), tr("admin_gift_moved_to_recycle"));
|
||||
break;
|
||||
case "copy":
|
||||
case "move":
|
||||
|
@ -302,7 +302,7 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$catTo->addGift($gift);
|
||||
|
||||
$name = $catTo->getName();
|
||||
$this->flash("succ", "Gift moved successfully", "This gift will now be in <b>$name</b>.");
|
||||
$this->flash("succ", tr("admin_gift_moved_successfully"), "This gift will now be in <b>$name</b>.");
|
||||
$this->redirect("/admin/gifts/" . $catTo->getSlug() . "." . $catTo->getId() . "/");
|
||||
break;
|
||||
default:
|
||||
|
@ -333,10 +333,10 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$gift->setUsages((int) $this->postParam("usages"));
|
||||
if(isset($_FILES["pic"]) && $_FILES["pic"]["error"] === UPLOAD_ERR_OK) {
|
||||
if(!$gift->setImage($_FILES["pic"]["tmp_name"]))
|
||||
$this->flashFail("err", "Не удалось сохранить подарок", "Изображение подарка кривое.");
|
||||
$this->flashFail("err", tr("error_when_saving_gift"), tr("error_when_saving_gift_bad_image"));
|
||||
} else if($gen) {
|
||||
# If there's no gift pic but it's newly created
|
||||
$this->flashFail("err", "Не удалось сохранить подарок", "Пожалуйста, загрузите изображение подарка.");
|
||||
$this->flashFail("err", tr("error_when_saving_gift"), tr("error_when_saving_gift_no_image"));
|
||||
}
|
||||
|
||||
$gift->save();
|
||||
|
@ -363,7 +363,7 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
if (str_contains($this->queryParam("reason"), "*"))
|
||||
exit(json_encode([ "error" => "Incorrect reason" ]));
|
||||
|
||||
$unban_time = strtotime($this->queryParam("date")) ?: NULL;
|
||||
$unban_time = strtotime($this->queryParam("date")) ?: "permanent";
|
||||
|
||||
$user = $this->users->get($id);
|
||||
if(!$user)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\{IP, User, PasswordReset, EmailVerification};
|
||||
use openvk\Web\Models\Repositories\{Bans, IPs, Users, Restores, Verifications, Logs};
|
||||
use openvk\Web\Models\Repositories\{Bans, IPs, Users, Restores, Verifications};
|
||||
use openvk\Web\Models\Exceptions\InvalidUserNameException;
|
||||
use openvk\Web\Util\Validator;
|
||||
use Chandler\Session\Session;
|
||||
|
@ -130,7 +130,6 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
}
|
||||
|
||||
$this->authenticator->authenticate($chUser->getId());
|
||||
(new Logs)->create($user->getId(), "profiles", "openvk\\Web\\Models\\Entities\\User", 0, $user, $user, $_SERVER["REMOTE_ADDR"], $_SERVER["HTTP_USER_AGENT"]);
|
||||
$this->redirect("/id" . $user->getId());
|
||||
$user->save();
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\{Comment, Notifications\MentionNotification, Photo, Video, User, Topic, Post};
|
||||
use openvk\Web\Models\Entities\Notifications\CommentNotification;
|
||||
use openvk\Web\Models\Repositories\{Comments, Clubs};
|
||||
use openvk\Web\Models\Repositories\{Comments, Clubs, Videos, Photos};
|
||||
|
||||
final class CommentPresenter extends OpenVKPresenter
|
||||
{
|
||||
|
@ -54,9 +54,6 @@ final class CommentPresenter extends OpenVKPresenter
|
|||
if ($entity instanceof Post && $entity->getWallOwner()->isBanned())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
if($_FILES["_vid_attachment"] && OPENVK_ROOT_CONF['openvk']['preferences']['videos']['disableUploading'])
|
||||
$this->flashFail("err", tr("error"), "Video uploads are disabled by the system administrator.");
|
||||
|
||||
$flags = 0;
|
||||
if($this->postParam("as_group") === "on" && !is_null($club) && $club->canBeModifiedBy($this->user->identity))
|
||||
$flags |= 0b10000000;
|
||||
|
@ -66,31 +63,49 @@ final class CommentPresenter extends OpenVKPresenter
|
|||
try {
|
||||
$photo = Photo::fastMake($this->user->id, $this->postParam("text"), $_FILES["_pic_attachment"]);
|
||||
} catch(ISE $ex) {
|
||||
$this->flashFail("err", "Не удалось опубликовать пост", "Файл изображения повреждён, слишком велик или одна сторона изображения в разы больше другой.");
|
||||
$this->flashFail("err", tr("error_when_publishing_comment"), tr("error_when_publishing_comment_description"));
|
||||
}
|
||||
}
|
||||
|
||||
# TODO move to trait
|
||||
try {
|
||||
$photo = NULL;
|
||||
$video = NULL;
|
||||
if($_FILES["_pic_attachment"]["error"] === UPLOAD_ERR_OK) {
|
||||
$album = NULL;
|
||||
if($wall > 0 && $wall === $this->user->id)
|
||||
$album = (new Albums)->getUserWallAlbum($wallOwner);
|
||||
|
||||
$photo = Photo::fastMake($this->user->id, $this->postParam("text"), $_FILES["_pic_attachment"], $album);
|
||||
$photos = [];
|
||||
if(!empty($this->postParam("photos"))) {
|
||||
$un = rtrim($this->postParam("photos"), ",");
|
||||
$arr = explode(",", $un);
|
||||
|
||||
if(sizeof($arr) < 11) {
|
||||
foreach($arr as $dat) {
|
||||
$ids = explode("_", $dat);
|
||||
$photo = (new Photos)->getByOwnerAndVID((int)$ids[0], (int)$ids[1]);
|
||||
|
||||
if(!$photo || $photo->isDeleted())
|
||||
continue;
|
||||
|
||||
$photos[] = $photo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$videos = [];
|
||||
|
||||
if(!empty($this->postParam("videos"))) {
|
||||
$un = rtrim($this->postParam("videos"), ",");
|
||||
$arr = explode(",", $un);
|
||||
|
||||
if($_FILES["_vid_attachment"]["error"] === UPLOAD_ERR_OK) {
|
||||
$video = Video::fastMake($this->user->id, $_FILES["_vid_attachment"]["name"], $this->postParam("text"), $_FILES["_vid_attachment"]);
|
||||
if(sizeof($arr) < 11) {
|
||||
foreach($arr as $dat) {
|
||||
$ids = explode("_", $dat);
|
||||
$video = (new Videos)->getByOwnerAndVID((int)$ids[0], (int)$ids[1]);
|
||||
|
||||
if(!$video || $video->isDeleted())
|
||||
continue;
|
||||
|
||||
$videos[] = $video;
|
||||
}
|
||||
}
|
||||
} catch(ISE $ex) {
|
||||
$this->flashFail("err", "Не удалось опубликовать комментарий", "Файл медиаконтента повреждён или слишком велик.");
|
||||
}
|
||||
|
||||
if(empty($this->postParam("text")) && !$photo && !$video)
|
||||
$this->flashFail("err", "Не удалось опубликовать комментарий", "Комментарий пустой или слишком большой.");
|
||||
if(empty($this->postParam("text")) && sizeof($photos) < 1 && sizeof($videos) < 1)
|
||||
$this->flashFail("err", tr("error_when_publishing_comment"), tr("error_comment_empty"));
|
||||
|
||||
try {
|
||||
$comment = new Comment;
|
||||
|
@ -102,14 +117,15 @@ final class CommentPresenter extends OpenVKPresenter
|
|||
$comment->setFlags($flags);
|
||||
$comment->save();
|
||||
} catch (\LengthException $ex) {
|
||||
$this->flashFail("err", "Не удалось опубликовать комментарий", "Комментарий слишком большой.");
|
||||
$this->flashFail("err", tr("error_when_publishing_comment"), tr("error_comment_too_big"));
|
||||
}
|
||||
|
||||
if(!is_null($photo))
|
||||
$comment->attach($photo);
|
||||
foreach($photos as $photo)
|
||||
$comment->attach($photo);
|
||||
|
||||
if(!is_null($video))
|
||||
$comment->attach($video);
|
||||
if(sizeof($videos) > 0)
|
||||
foreach($videos as $vid)
|
||||
$comment->attach($vid);
|
||||
|
||||
if($entity->getOwner()->getId() !== $this->user->identity->getId())
|
||||
if(($owner = $entity->getOwner()) instanceof User)
|
||||
|
@ -124,7 +140,7 @@ final class CommentPresenter extends OpenVKPresenter
|
|||
if($mentionee instanceof User)
|
||||
(new MentionNotification($mentionee, $entity, $comment->getOwner(), strip_tags($comment->getText())))->emit();
|
||||
|
||||
$this->flashFail("succ", "Комментарий добавлен", "Ваш комментарий появится на странице.");
|
||||
$this->flashFail("succ", tr("comment_is_added"), tr("comment_is_added_desc"));
|
||||
}
|
||||
|
||||
function renderDeleteComment(int $id): void
|
||||
|
@ -135,15 +151,15 @@ final class CommentPresenter extends OpenVKPresenter
|
|||
$comment = (new Comments)->get($id);
|
||||
if(!$comment) $this->notFound();
|
||||
if(!$comment->canBeDeletedBy($this->user->identity))
|
||||
$this->throwError(403, "Forbidden", "У вас недостаточно прав чтобы редактировать этот ресурс.");
|
||||
$this->throwError(403, "Forbidden", tr("error_access_denied"));
|
||||
if ($comment->getTarget() instanceof Post && $comment->getTarget()->getWallOwner()->isBanned())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
$comment->delete();
|
||||
$this->flashFail(
|
||||
"succ",
|
||||
"Успешно",
|
||||
"Этот комментарий больше не будет показыватся.<br/><a href='/al_comments/spam?$id'>Отметить как спам</a>?"
|
||||
tr("success"),
|
||||
tr("comment_will_not_appear")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ final class GiftsPresenter extends OpenVKPresenter
|
|||
|
||||
$this->template->user = $user;
|
||||
$this->template->iterator = $cats;
|
||||
$this->template->count = $this->gifts->getCategoriesCount();
|
||||
$this->template->_template = "Gifts/Menu.xml";
|
||||
}
|
||||
|
||||
|
@ -49,7 +50,7 @@ final class GiftsPresenter extends OpenVKPresenter
|
|||
$user = $this->users->get((int) ($this->queryParam("user") ?? 0));
|
||||
$cat = $this->gifts->getCat((int) ($this->queryParam("pack") ?? 0));
|
||||
if(!$user || !$cat)
|
||||
$this->flashFail("err", "Не удалось подарить", "Пользователь или набор не существуют.");
|
||||
$this->flashFail("err", tr("error_when_gifting"), tr("error_user_not_exists"));
|
||||
|
||||
$this->template->page = $page = (int) ($this->queryParam("p") ?? 1);
|
||||
$gifts = $cat->getGifts($page, null, $this->template->count);
|
||||
|
@ -66,14 +67,14 @@ final class GiftsPresenter extends OpenVKPresenter
|
|||
$gift = $this->gifts->get((int) ($this->queryParam("elid") ?? 0));
|
||||
$cat = $this->gifts->getCat((int) ($this->queryParam("pack") ?? 0));
|
||||
if(!$user || !$cat || !$gift || !$cat->hasGift($gift))
|
||||
$this->flashFail("err", "Не удалось подарить", "Не удалось подтвердить права на подарок.");
|
||||
$this->flashFail("err", tr("error_when_gifting"), tr("error_no_rights_gifts"));
|
||||
|
||||
if(!$gift->canUse($this->user->identity))
|
||||
$this->flashFail("err", "Не удалось подарить", "У вас больше не осталось таких подарков.");
|
||||
$this->flashFail("err", tr("error_when_gifting"), tr("error_no_more_gifts"));
|
||||
|
||||
$coinsLeft = $this->user->identity->getCoins() - $gift->getPrice();
|
||||
if($coinsLeft < 0)
|
||||
$this->flashFail("err", "Не удалось подарить", "Ору нищ не пук.");
|
||||
$this->flashFail("err", tr("error_when_gifting"), tr("error_no_money"));
|
||||
|
||||
$this->template->_template = "Gifts/Confirm.xml";
|
||||
if($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||
|
@ -91,7 +92,7 @@ final class GiftsPresenter extends OpenVKPresenter
|
|||
$user->gift($this->user->identity, $gift, $comment, !is_null($this->postParam("anonymous")));
|
||||
$gift->used();
|
||||
|
||||
$this->flash("succ", "Подарок отправлен", "Вы отправили подарок <b>" . $user->getFirstName() . "</b> за " . $gift->getPrice() . " голосов.");
|
||||
$this->flash("succ", tr("gift_sent"), tr("gift_sent_desc", $user->getFirstName(), $gift->getPrice()));
|
||||
$this->redirect($user->getURL());
|
||||
}
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$club->save();
|
||||
} catch(\PDOException $ex) {
|
||||
if($ex->getCode() == 23000)
|
||||
$this->flashFail("err", "Ошибка", "Произошла ошибка на стороне сервера. Обратитесь к системному администратору.");
|
||||
$this->flashFail("err", tr("error"), tr("error_on_server_side"));
|
||||
else
|
||||
throw $ex;
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$club->toggleSubscription($this->user->identity);
|
||||
$this->redirect("/club" . $club->getId());
|
||||
}else{
|
||||
$this->flashFail("err", "Ошибка", "Вы не ввели название группы.");
|
||||
$this->flashFail("err", tr("error"), tr("error_no_group_name"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$this->notFound();
|
||||
|
||||
if(!$club->canBeModifiedBy($this->user->identity ?? NULL))
|
||||
$this->flashFail("err", "Ошибка доступа", "У вас недостаточно прав, чтобы изменять этот ресурс.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
if(!is_null($hidden)) {
|
||||
if($club->getOwner()->getId() == $user->getId()) {
|
||||
|
@ -157,9 +157,9 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
}
|
||||
|
||||
if($hidden) {
|
||||
$this->flashFail("succ", "Операция успешна", "Теперь " . $user->getCanonicalName() . " будет показываться как обычный подписчик всем кроме других администраторов");
|
||||
$this->flashFail("succ", tr("success_action"), tr("x_is_now_hidden", $user->getCanonicalName()));
|
||||
} else {
|
||||
$this->flashFail("succ", "Операция успешна", "Теперь все будут знать про то что " . $user->getCanonicalName() . " - администратор");
|
||||
$this->flashFail("succ", tr("success_action"), tr("x_is_now_showed", $user->getCanonicalName()));
|
||||
}
|
||||
} elseif($removeComment) {
|
||||
if($club->getOwner()->getId() == $user->getId()) {
|
||||
|
@ -171,11 +171,11 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$manager->save();
|
||||
}
|
||||
|
||||
$this->flashFail("succ", "Операция успешна", "Комментарий к администратору удален");
|
||||
$this->flashFail("succ", tr("success_action"), tr("comment_is_deleted"));
|
||||
} elseif($comment) {
|
||||
if(mb_strlen($comment) > 36) {
|
||||
$commentLength = (string) mb_strlen($comment);
|
||||
$this->flashFail("err", "Ошибка", "Комментарий слишком длинный ($commentLength символов вместо 36 символов)");
|
||||
$this->flashFail("err", tr("error"), tr("comment_is_too_long", $commentLength));
|
||||
}
|
||||
|
||||
if($club->getOwner()->getId() == $user->getId()) {
|
||||
|
@ -187,16 +187,16 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$manager->save();
|
||||
}
|
||||
|
||||
$this->flashFail("succ", "Операция успешна", "Комментарий к администратору изменён");
|
||||
$this->flashFail("succ", tr("success_action"), tr("comment_is_changed"));
|
||||
}else{
|
||||
if($club->canBeModifiedBy($user)) {
|
||||
$club->removeManager($user);
|
||||
$this->flashFail("succ", "Операция успешна", $user->getCanonicalName() . " более не администратор.");
|
||||
$this->flashFail("succ", tr("success_action"), tr("x_no_more_admin", $user->getCanonicalName()));
|
||||
} else {
|
||||
$club->addManager($user);
|
||||
|
||||
(new ClubModeratorNotification($user, $club, $this->user->identity))->emit();
|
||||
$this->flashFail("succ", "Операция успешна", $user->getCanonicalName() . " назначен(а) администратором.");
|
||||
$this->flashFail("succ", tr("success_action"), tr("x_is_admin", $user->getCanonicalName()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -257,7 +257,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
(new Albums)->getClubAvatarAlbum($club)->addPhoto($photo);
|
||||
} catch(ISE $ex) {
|
||||
$name = $album->getName();
|
||||
$this->flashFail("err", "Неизвестная ошибка", "Не удалось сохранить фотографию.");
|
||||
$this->flashFail("err", tr("error"), tr("error_when_uploading_photo"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -265,12 +265,12 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$club->save();
|
||||
} catch(\PDOException $ex) {
|
||||
if($ex->getCode() == 23000)
|
||||
$this->flashFail("err", "Ошибка", "Произошла ошибка на стороне сервера. Обратитесь к системному администратору.");
|
||||
$this->flashFail("err", tr("error"), tr("error_on_server_side"));
|
||||
else
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
$this->flash("succ", "Изменения сохранены", "Новые данные появятся в вашей группе.");
|
||||
$this->flash("succ", tr("changes_saved"), tr("new_changes_desc"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -310,7 +310,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
|
||||
} catch(ISE $ex) {
|
||||
$name = $album->getName();
|
||||
$this->flashFail("err", "Неизвестная ошибка", "Не удалось сохранить фотографию.");
|
||||
$this->flashFail("err", tr("error"), tr("error_when_uploading_photo"));
|
||||
}
|
||||
}
|
||||
$this->returnJson([
|
||||
|
@ -362,7 +362,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$this->assertUserLoggedIn();
|
||||
|
||||
if(!eventdb())
|
||||
$this->flashFail("err", "Ошибка подключения", "Не удалось подключится к службе телеметрии.");
|
||||
$this->flashFail("err", tr("connection_error"), tr("connection_error_desc"));
|
||||
|
||||
$club = $this->clubs->get($id);
|
||||
if(!$club->canBeModifiedBy($this->user->identity))
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Repositories\{Posts, Comments};
|
||||
use MessagePack\MessagePack;
|
||||
use Chandler\Session\Session;
|
||||
|
||||
|
@ -95,4 +96,41 @@ final class InternalAPIPresenter extends OpenVKPresenter
|
|||
]);
|
||||
}
|
||||
}
|
||||
|
||||
function renderGetPhotosFromPost(int $owner_id, int $post_id) {
|
||||
if($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||
header("HTTP/1.1 405 Method Not Allowed");
|
||||
exit("иди нахуй заебал");
|
||||
}
|
||||
|
||||
if($this->postParam("parentType", false) == "post") {
|
||||
$post = (new Posts)->getPostById($owner_id, $post_id);
|
||||
} else {
|
||||
$post = (new Comments)->get($post_id);
|
||||
}
|
||||
|
||||
|
||||
if(is_null($post)) {
|
||||
$this->returnJson([
|
||||
"success" => 0
|
||||
]);
|
||||
} else {
|
||||
$response = [];
|
||||
$attachments = $post->getChildren();
|
||||
foreach($attachments as $attachment)
|
||||
{
|
||||
if($attachment instanceof \openvk\Web\Models\Entities\Photo)
|
||||
{
|
||||
$response[] = [
|
||||
"url" => $attachment->getURLBySizeId('normal'),
|
||||
"id" => $attachment->getPrettyId()
|
||||
];
|
||||
}
|
||||
}
|
||||
$this->returnJson([
|
||||
"success" => 1,
|
||||
"body" => $response
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -177,26 +177,25 @@ final class NoSpamPresenter extends OpenVKPresenter
|
|||
if ($conditions) {
|
||||
$logs = $db->query("SELECT * FROM `ChandlerLogs` $whereStart $conditions GROUP BY `object_id`, `object_model`");
|
||||
|
||||
if (!$where) {
|
||||
foreach ($logs as $log) {
|
||||
$log = (new Logs)->get($log->id);
|
||||
$response[] = $log->getObject()->unwrap();
|
||||
}
|
||||
} else {
|
||||
foreach ($logs as $log) {
|
||||
$log = (new Logs)->get($log->id);
|
||||
$object = $log->getObject()->unwrap();
|
||||
foreach ($logs as $log) {
|
||||
$log = (new Logs)->get($log->id);
|
||||
$object = $log->getObject()->unwrap();
|
||||
|
||||
if (!$object) continue;
|
||||
if (!$object) continue;
|
||||
if ($where) {
|
||||
if (str_starts_with($where, " AND")) {
|
||||
$where = substr_replace($where, "", 0, strlen(" AND"));
|
||||
}
|
||||
|
||||
foreach ($db->query("SELECT * FROM `$table` WHERE $where")->fetchAll() as $o) {
|
||||
if ($object->id === $o["id"]) {
|
||||
$a = $db->query("SELECT * FROM `$table` WHERE $where")->fetchAll();
|
||||
foreach ($a as $o) {
|
||||
if ($object->id == $o["id"]) {
|
||||
$response[] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
$response[] = $object;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -206,70 +205,72 @@ final class NoSpamPresenter extends OpenVKPresenter
|
|||
}
|
||||
|
||||
try {
|
||||
$response = [];
|
||||
$processed = 0;
|
||||
$response = [];
|
||||
$processed = 0;
|
||||
|
||||
$where = $this->postParam("where");
|
||||
$ip = $this->postParam("ip");
|
||||
$useragent = $this->postParam("useragent");
|
||||
$searchTerm = $this->postParam("q");
|
||||
$ts = (int)$this->postParam("ts");
|
||||
$te = (int)$this->postParam("te");
|
||||
$user = $this->postParam("user");
|
||||
$where = $this->postParam("where");
|
||||
$ip = addslashes($this->postParam("ip"));
|
||||
$useragent = addslashes($this->postParam("useragent"));
|
||||
$searchTerm = addslashes($this->postParam("q"));
|
||||
$ts = (int)$this->postParam("ts");
|
||||
$te = (int)$this->postParam("te");
|
||||
$user = addslashes($this->postParam("user"));
|
||||
|
||||
if (!$ip && !$useragent && !$searchTerm && !$ts && !$te && !$where && !$searchTerm && !$user)
|
||||
$this->returnJson(["success" => false, "error" => "Нет запроса. Заполните поле \"подстрока\" или введите запрос \"WHERE\" в поле под ним."]);
|
||||
|
||||
$models = explode(",", $this->postParam("models"));
|
||||
|
||||
foreach ($models as $_model) {
|
||||
$model_name = NoSpamPresenter::ENTITIES_NAMESPACE . "\\" . $_model;
|
||||
if (!class_exists($model_name)) {
|
||||
continue;
|
||||
if ($where) {
|
||||
$where = explode(";", $where)[0];
|
||||
}
|
||||
|
||||
$model = new $model_name;
|
||||
if (!$ip && !$useragent && !$searchTerm && !$ts && !$te && !$where && !$searchTerm && !$user)
|
||||
$this->returnJson(["success" => false, "error" => "Нет запроса. Заполните поле \"подстрока\" или введите запрос \"WHERE\" в поле под ним."]);
|
||||
|
||||
$c = new \ReflectionClass($model_name);
|
||||
if ($c->isAbstract() || $c->getName() == NoSpamPresenter::ENTITIES_NAMESPACE . "\\Correspondence") {
|
||||
continue;
|
||||
}
|
||||
$models = explode(",", $this->postParam("models"));
|
||||
|
||||
$db = DatabaseConnection::i()->getContext();
|
||||
$table = $model->getTableName();
|
||||
$columns = $db->getStructure()->getColumns($table);
|
||||
|
||||
if ($searchTerm) {
|
||||
$conditions = [];
|
||||
$need_deleted = false;
|
||||
foreach ($columns as $column) {
|
||||
if ($column["name"] == "deleted") {
|
||||
$need_deleted = true;
|
||||
} else {
|
||||
$conditions[] = "`$column[name]` REGEXP '$searchTerm'";
|
||||
}
|
||||
foreach ($models as $_model) {
|
||||
$model_name = NoSpamPresenter::ENTITIES_NAMESPACE . "\\" . $_model;
|
||||
if (!class_exists($model_name)) {
|
||||
continue;
|
||||
}
|
||||
$conditions = implode(" OR ", $conditions);
|
||||
|
||||
$where = ($this->postParam("where") ? " AND ($conditions)" : "($conditions)");
|
||||
if ($need_deleted) $where .= " AND (`deleted` = 0)";
|
||||
}
|
||||
$model = new $model_name;
|
||||
|
||||
$rows = [];
|
||||
if ($ip || $useragent || $ts || $te || $user) {
|
||||
$rows = searchByAdditionalParams($table, $where, $ip, $useragent, $ts, $te, $user);
|
||||
}
|
||||
$c = new \ReflectionClass($model_name);
|
||||
if ($c->isAbstract() || $c->getName() == NoSpamPresenter::ENTITIES_NAMESPACE . "\\Correspondence") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (count($rows) === 0) {
|
||||
if (!$searchTerm) {
|
||||
if (str_starts_with($where, " AND")) {
|
||||
if ($searchTerm && !$this->postParam("where")) {
|
||||
$where = substr_replace($where, "", 0, strlen(" AND"));
|
||||
$db = DatabaseConnection::i()->getContext();
|
||||
$table = $model->getTableName();
|
||||
$columns = $db->getStructure()->getColumns($table);
|
||||
|
||||
if ($searchTerm) {
|
||||
$conditions = [];
|
||||
$need_deleted = false;
|
||||
foreach ($columns as $column) {
|
||||
if ($column["name"] == "deleted") {
|
||||
$need_deleted = true;
|
||||
} else {
|
||||
$where = "(" . $this->postParam("where") . ")" . $where;
|
||||
$conditions[] = "`$column[name]` REGEXP '$searchTerm'";
|
||||
}
|
||||
}
|
||||
$conditions = implode(" OR ", $conditions);
|
||||
|
||||
$where = ($this->postParam("where") ? " AND ($conditions)" : "($conditions)");
|
||||
if ($need_deleted) $where .= " AND (`deleted` = 0)";
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
|
||||
if (str_starts_with($where, " AND")) {
|
||||
if ($searchTerm && !$this->postParam("where")) {
|
||||
$where = substr_replace($where, "", 0, strlen(" AND"));
|
||||
} else {
|
||||
$where = "(" . $this->postParam("where") . ")" . $where;
|
||||
}
|
||||
}
|
||||
|
||||
if ($ip || $useragent || $ts || $te || $user) {
|
||||
$rows = searchByAdditionalParams($table, $where, $ip, $useragent, $ts, $te, $user);
|
||||
} else {
|
||||
if (!$where) {
|
||||
$rows = [];
|
||||
} else {
|
||||
|
@ -277,99 +278,105 @@ final class NoSpamPresenter extends OpenVKPresenter
|
|||
$rows = $result->fetchAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!in_array((int)$this->postParam("ban"), [1, 2, 3])) {
|
||||
foreach ($rows as $key => $object) {
|
||||
$object = (array)$object;
|
||||
$_obj = [];
|
||||
foreach ($object as $key => $value) {
|
||||
foreach ($columns as $column) {
|
||||
if ($column["name"] === $key && in_array(strtoupper($column["nativetype"]), ["BLOB", "BINARY", "VARBINARY", "TINYBLOB", "MEDIUMBLOB", "LONGBLOB"])) {
|
||||
$value = "[BINARY]";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$_obj[$key] = $value;
|
||||
$_obj["__model_name"] = $_model;
|
||||
}
|
||||
$response[] = $_obj;
|
||||
}
|
||||
} else {
|
||||
$ids = [];
|
||||
|
||||
foreach ($rows as $object) {
|
||||
$object = new $model_name($db->table($table)->get($object->id));
|
||||
if (!$object) continue;
|
||||
$ids[] = $object->getId();
|
||||
}
|
||||
|
||||
$log = new NoSpamLog;
|
||||
$log->setUser($this->user->id);
|
||||
$log->setModel($_model);
|
||||
if ($searchTerm) {
|
||||
$log->setRegex($searchTerm);
|
||||
} else {
|
||||
$log->setRequest($where);
|
||||
}
|
||||
$log->setBan_Type((int)$this->postParam("ban"));
|
||||
$log->setCount(count($rows));
|
||||
$log->setTime(time());
|
||||
$log->setItems(implode(",", $ids));
|
||||
$log->save();
|
||||
|
||||
$banned_ids = [];
|
||||
foreach ($rows as $object) {
|
||||
$object = new $model_name($db->table($table)->get($object->id));
|
||||
if (!$object) continue;
|
||||
|
||||
$owner = NULL;
|
||||
$methods = ["getOwner", "getUser", "getRecipient", "getInitiator"];
|
||||
|
||||
if (method_exists($object, "ban")) {
|
||||
$owner = $object;
|
||||
} else {
|
||||
foreach ($methods as $method) {
|
||||
if (method_exists($object, $method)) {
|
||||
$owner = $object->$method();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($owner instanceof User && $owner->getId() === $this->user->id) {
|
||||
if (count($rows) === 1) {
|
||||
$this->returnJson(["success" => false, "error" => "\"Производственная травма\" — Вы не можете блокировать или удалять свой же контент"]);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array((int)$this->postParam("ban"), [2, 3])) {
|
||||
if ($owner) {
|
||||
$_id = ($owner instanceof Club ? $owner->getId() * -1 : $owner->getId());
|
||||
if (!in_array($_id, $banned_ids)) {
|
||||
if ($owner instanceof User) {
|
||||
$owner->ban("**content-noSpamTemplate-" . $log->getId() . "**", false, time() + $owner->getNewBanTime(), $this->user->id);
|
||||
} else {
|
||||
$owner->ban("Подозрительная активность");
|
||||
if (!in_array((int)$this->postParam("ban"), [1, 2, 3])) {
|
||||
foreach ($rows as $key => $object) {
|
||||
$object = (array)$object;
|
||||
$_obj = [];
|
||||
foreach ($object as $key => $value) {
|
||||
foreach ($columns as $column) {
|
||||
if ($column["name"] === $key && in_array(strtoupper($column["nativetype"]), ["BLOB", "BINARY", "VARBINARY", "TINYBLOB", "MEDIUMBLOB", "LONGBLOB"])) {
|
||||
$value = "[BINARY]";
|
||||
break;
|
||||
}
|
||||
|
||||
$banned_ids[] = $_id;
|
||||
}
|
||||
|
||||
$_obj[$key] = $value;
|
||||
$_obj["__model_name"] = $_model;
|
||||
}
|
||||
$response[] = $_obj;
|
||||
}
|
||||
} else {
|
||||
$ids = [];
|
||||
|
||||
foreach ($rows as $object) {
|
||||
$object = new $model_name($db->table($table)->get($object->id));
|
||||
if (!$object) continue;
|
||||
$ids[] = $object->getId();
|
||||
}
|
||||
|
||||
if (in_array((int)$this->postParam("ban"), [1, 3]))
|
||||
$object->delete();
|
||||
$log = new NoSpamLog;
|
||||
$log->setUser($this->user->id);
|
||||
$log->setModel($_model);
|
||||
if ($searchTerm) {
|
||||
$log->setRegex($searchTerm);
|
||||
} else {
|
||||
$log->setRequest($where);
|
||||
}
|
||||
$log->setBan_Type((int)$this->postParam("ban"));
|
||||
$log->setCount(count($rows));
|
||||
$log->setTime(time());
|
||||
$log->setItems(implode(",", $ids));
|
||||
$log->save();
|
||||
|
||||
$banned_ids = [];
|
||||
foreach ($rows as $object) {
|
||||
$object = new $model_name($db->table($table)->get($object->id));
|
||||
if (!$object) continue;
|
||||
|
||||
$owner = NULL;
|
||||
$methods = ["getOwner", "getUser", "getRecipient", "getInitiator"];
|
||||
|
||||
if (method_exists($object, "ban")) {
|
||||
$owner = $object;
|
||||
} else {
|
||||
foreach ($methods as $method) {
|
||||
if (method_exists($object, $method)) {
|
||||
$owner = $object->$method();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($owner instanceof User && $owner->getId() === $this->user->id) {
|
||||
if (count($rows) === 1) {
|
||||
$this->returnJson(["success" => false, "error" => "\"Производственная травма\" — Вы не можете блокировать или удалять свой же контент"]);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array((int)$this->postParam("ban"), [2, 3])) {
|
||||
$reason = mb_strlen(trim($this->postParam("ban_reason"))) > 0 ? addslashes($this->postParam("ban_reason")) : ("**content-noSpamTemplate-" . $log->getId() . "**");
|
||||
$is_forever = (string)$this->postParam("is_forever") === "true";
|
||||
$unban_time = $is_forever ? 0 : (int)$this->postParam("unban_time") ?? NULL;
|
||||
|
||||
if ($owner) {
|
||||
$_id = ($owner instanceof Club ? $owner->getId() * -1 : $owner->getId());
|
||||
if (!in_array($_id, $banned_ids)) {
|
||||
if ($owner instanceof User) {
|
||||
if (!$unban_time && !$is_forever)
|
||||
$unban_time = time() + $owner->getNewBanTime();
|
||||
|
||||
$owner->ban($reason, false, $unban_time, $this->user->id);
|
||||
} else {
|
||||
$owner->ban("Подозрительная активность");
|
||||
}
|
||||
|
||||
$banned_ids[] = $_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array((int)$this->postParam("ban"), [1, 3]))
|
||||
$object->delete();
|
||||
}
|
||||
|
||||
$processed++;
|
||||
}
|
||||
|
||||
$processed++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->returnJson(["success" => true, "processed" => $processed, "count" => count($response), "list" => $response]);
|
||||
$this->returnJson(["success" => true, "processed" => $processed, "count" => count($response), "list" => $response]);
|
||||
} catch (\Throwable $e) {
|
||||
$this->returnJson(["success" => false, "error" => $e->getMessage()]);
|
||||
}
|
||||
|
|
|
@ -107,7 +107,7 @@ final class NotesPresenter extends OpenVKPresenter
|
|||
if(!$note || $note->getOwner()->getId() !== $owner || $note->isDeleted())
|
||||
$this->notFound();
|
||||
if(is_null($this->user) || !$note->canBeModifiedBy($this->user->identity))
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
$this->template->note = $note;
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
|
@ -135,11 +135,11 @@ final class NotesPresenter extends OpenVKPresenter
|
|||
if(!$note) $this->notFound();
|
||||
if($note->getOwner()->getId() . "_" . $note->getId() !== $owner . "_" . $id || $note->isDeleted()) $this->notFound();
|
||||
if(is_null($this->user) || !$note->canBeModifiedBy($this->user->identity))
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
$name = $note->getName();
|
||||
$note->delete();
|
||||
$this->flash("succ", "Заметка удалена", "Заметка \"$name\" была успешно удалена.");
|
||||
$this->flash("succ", tr("note_is_deleted"), tr("note_x_is_now_deleted", $name));
|
||||
$this->redirect("/notes" . $this->user->id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\{Club, Photo, Album};
|
||||
use openvk\Web\Models\Entities\{Club, Photo, Album, User};
|
||||
use openvk\Web\Models\Repositories\{Photos, Albums, Users, Clubs};
|
||||
use Nette\InvalidStateException as ISE;
|
||||
|
||||
|
@ -27,7 +27,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
if(!$user) $this->notFound();
|
||||
if (!$user->getPrivacyPermission('photos.read', $this->user->identity ?? NULL))
|
||||
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||
$this->template->albums = $this->albums->getUserAlbums($user, $this->queryParam("p") ?? 1);
|
||||
$this->template->albums = $this->albums->getUserAlbums($user, (int)($this->queryParam("p") ?? 1));
|
||||
$this->template->count = $this->albums->getUserAlbumsCount($user);
|
||||
$this->template->owner = $user;
|
||||
$this->template->canEdit = false;
|
||||
|
@ -36,7 +36,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
} else {
|
||||
$club = (new Clubs)->get(abs($owner));
|
||||
if(!$club) $this->notFound();
|
||||
$this->template->albums = $this->albums->getClubAlbums($club, $this->queryParam("p") ?? 1);
|
||||
$this->template->albums = $this->albums->getClubAlbums($club, (int)($this->queryParam("p") ?? 1));
|
||||
$this->template->count = $this->albums->getClubAlbumsCount($club);
|
||||
$this->template->owner = $club;
|
||||
$this->template->canEdit = false;
|
||||
|
@ -46,7 +46,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
|
||||
$this->template->paginatorConf = (object) [
|
||||
"count" => $this->template->count,
|
||||
"page" => $this->queryParam("p") ?? 1,
|
||||
"page" => (int)($this->queryParam("p") ?? 1),
|
||||
"amount" => NULL,
|
||||
"perPage" => OPENVK_DEFAULT_PER_PAGE,
|
||||
];
|
||||
|
@ -94,7 +94,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
if(!$album) $this->notFound();
|
||||
if($album->getPrettyId() !== $owner . "_" . $id || $album->isDeleted()) $this->notFound();
|
||||
if(is_null($this->user) || !$album->canBeModifiedBy($this->user->identity) || $album->isDeleted())
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
$this->template->album = $album;
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
|
@ -106,7 +106,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
$album->setEdited(time());
|
||||
$album->save();
|
||||
|
||||
$this->flash("succ", "Изменения сохранены", "Новые данные приняты.");
|
||||
$this->flash("succ", tr("changes_saved"), tr("new_data_accepted"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,13 +120,13 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
if(!$album) $this->notFound();
|
||||
if($album->getPrettyId() !== $owner . "_" . $id || $album->isDeleted()) $this->notFound();
|
||||
if(is_null($this->user) || !$album->canBeModifiedBy($this->user->identity))
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
$name = $album->getName();
|
||||
$owner = $album->getOwner();
|
||||
$album->delete();
|
||||
|
||||
$this->flash("succ", "Альбом удалён", "Альбом $name был успешно удалён.");
|
||||
$this->flash("succ", tr("album_is_deleted"), tr("album_x_is_deleted", $name));
|
||||
$this->redirect("/albums" . ($owner instanceof Club ? "-" : "") . $owner->getId());
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
$this->template->photos = iterator_to_array( $album->getPhotos( (int) ($this->queryParam("p") ?? 1), 20) );
|
||||
$this->template->paginatorConf = (object) [
|
||||
"count" => $album->getPhotosCount(),
|
||||
"page" => $this->queryParam("p") ?? 1,
|
||||
"page" => (int)($this->queryParam("p") ?? 1),
|
||||
"amount" => sizeof($this->template->photos),
|
||||
"perPage" => 20,
|
||||
"atBottom" => true
|
||||
|
@ -205,13 +205,13 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
$photo = $this->photos->getByOwnerAndVID($ownerId, $photoId);
|
||||
if(!$photo) $this->notFound();
|
||||
if(is_null($this->user) || $this->user->id != $ownerId)
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$photo->setDescription(empty($this->postParam("desc")) ? NULL : $this->postParam("desc"));
|
||||
$photo->save();
|
||||
|
||||
$this->flash("succ", "Изменения сохранены", "Обновлённое описание появится на странице с фоткой.");
|
||||
$this->flash("succ", tr("changes_saved"), tr("new_description_will_appear"));
|
||||
$this->redirect("/photo" . $photo->getPrettyId());
|
||||
}
|
||||
|
||||
|
@ -221,39 +221,82 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
function renderUploadPhoto(): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if(is_null($this->queryParam("album")))
|
||||
$this->flashFail("err", "Неизвестная ошибка", "Не удалось сохранить фотографию в <b>DELETED</b>.");
|
||||
|
||||
[$owner, $id] = explode("_", $this->queryParam("album"));
|
||||
$album = $this->albums->get((int) $id);
|
||||
$this->willExecuteWriteAction(true);
|
||||
|
||||
if(is_null($this->queryParam("album"))) {
|
||||
$album = $this->albums->getUserWallAlbum($this->user->identity);
|
||||
} else {
|
||||
[$owner, $id] = explode("_", $this->queryParam("album"));
|
||||
$album = $this->albums->get((int) $id);
|
||||
}
|
||||
|
||||
if(!$album)
|
||||
$this->flashFail("err", "Неизвестная ошибка", "Не удалось сохранить фотографию в <b>DELETED</b>.");
|
||||
if(is_null($this->user) || !$album->canBeModifiedBy($this->user->identity))
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
$this->flashFail("err", tr("error"), tr("error_adding_to_deleted"), 500, true);
|
||||
|
||||
# Для быстрой загрузки фоток из пикера фотографий нужен альбом, но юзер не может загружать фото
|
||||
# в системные альбомы, так что так.
|
||||
if(is_null($this->user) || !is_null($this->queryParam("album")) && !$album->canBeModifiedBy($this->user->identity))
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"), 500, true);
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
if(!isset($_FILES["blob"]))
|
||||
$this->flashFail("err", "Нету фотографии", "Выберите файл.");
|
||||
|
||||
try {
|
||||
$photo = new Photo;
|
||||
$photo->setOwner($this->user->id);
|
||||
$photo->setDescription($this->postParam("desc"));
|
||||
$photo->setFile($_FILES["blob"]);
|
||||
$photo->setCreated(time());
|
||||
$photo->save();
|
||||
} catch(ISE $ex) {
|
||||
$name = $album->getName();
|
||||
$this->flashFail("err", "Неизвестная ошибка", "Не удалось сохранить фотографию в <b>$name</b>.");
|
||||
}
|
||||
|
||||
$album->addPhoto($photo);
|
||||
$album->setEdited(time());
|
||||
$album->save();
|
||||
if($this->queryParam("act") == "finish") {
|
||||
$result = json_decode($this->postParam("photos"), true);
|
||||
|
||||
foreach($result as $photoId => $description) {
|
||||
$phot = $this->photos->get($photoId);
|
||||
|
||||
$this->redirect("/photo" . $photo->getPrettyId() . "?from=album" . $album->getId());
|
||||
if(!$phot || $phot->isDeleted() || $phot->getOwner()->getId() != $this->user->id)
|
||||
continue;
|
||||
|
||||
if(iconv_strlen($description) > 255)
|
||||
$this->flashFail("err", tr("error"), tr("description_too_long"), 500, true);
|
||||
|
||||
$phot->setDescription($description);
|
||||
$phot->save();
|
||||
|
||||
$album = $phot->getAlbum();
|
||||
}
|
||||
|
||||
$this->returnJson(["success" => true,
|
||||
"album" => $album->getId(),
|
||||
"owner" => $album->getOwner() instanceof User ? $album->getOwner()->getId() : $album->getOwner()->getId() * -1]);
|
||||
}
|
||||
|
||||
if(!isset($_FILES))
|
||||
$this->flashFail("err", tr("no_photo"), tr("select_file"), 500, true);
|
||||
|
||||
$photos = [];
|
||||
if((int)$this->postParam("count") > 10)
|
||||
$this->flashFail("err", tr("no_photo"), "ты еблан", 500, true);
|
||||
|
||||
for($i = 0; $i < $this->postParam("count"); $i++) {
|
||||
try {
|
||||
$photo = new Photo;
|
||||
$photo->setOwner($this->user->id);
|
||||
$photo->setDescription("");
|
||||
$photo->setFile($_FILES["photo_".$i]);
|
||||
$photo->setCreated(time());
|
||||
$photo->save();
|
||||
|
||||
$photos[] = [
|
||||
"url" => $photo->getURLBySizeId("tiny"),
|
||||
"id" => $photo->getId(),
|
||||
"vid" => $photo->getVirtualId(),
|
||||
"owner" => $photo->getOwner()->getId(),
|
||||
"link" => $photo->getURL()
|
||||
];
|
||||
} catch(ISE $ex) {
|
||||
$name = $album->getName();
|
||||
$this->flashFail("err", "Неизвестная ошибка", "Не удалось сохранить фотографию в $name.", 500, true);
|
||||
}
|
||||
|
||||
$album->addPhoto($photo);
|
||||
$album->setEdited(time());
|
||||
$album->save();
|
||||
}
|
||||
|
||||
$this->returnJson(["success" => true,
|
||||
"photos" => $photos]);
|
||||
} else {
|
||||
$this->template->album = $album;
|
||||
}
|
||||
|
@ -269,7 +312,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
if(!$album || !$photo) $this->notFound();
|
||||
if(!$album->hasPhoto($photo)) $this->notFound();
|
||||
if(is_null($this->user) || !$album->canBeModifiedBy($this->user->identity))
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$this->assertNoCSRF();
|
||||
|
@ -277,7 +320,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
$album->setEdited(time());
|
||||
$album->save();
|
||||
|
||||
$this->flash("succ", "Фотография удалена", "Эта фотография была успешно удалена.");
|
||||
$this->flash("succ", tr("photo_is_deleted"), tr("photo_is_deleted_desc"));
|
||||
$this->redirect("/album" . $album->getPrettyId());
|
||||
}
|
||||
}
|
||||
|
@ -285,18 +328,23 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
function renderDeletePhoto(int $ownerId, int $photoId): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
$this->willExecuteWriteAction($_SERVER["REQUEST_METHOD"] === "POST");
|
||||
$this->assertNoCSRF();
|
||||
|
||||
$photo = $this->photos->getByOwnerAndVID($ownerId, $photoId);
|
||||
if(!$photo) $this->notFound();
|
||||
if(is_null($this->user) || $this->user->id != $ownerId)
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
$redirect = $photo->getAlbum()->getOwner() instanceof User ? "/id0" : "/club" . $ownerId;
|
||||
|
||||
$photo->isolate();
|
||||
$photo->delete();
|
||||
|
||||
$this->flash("succ", "Фотография удалена", "Эта фотография была успешно удалена.");
|
||||
$this->redirect("/id0");
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST")
|
||||
$this->returnJson(["success" => true]);
|
||||
|
||||
$this->flash("succ", tr("photo_is_deleted"), tr("photo_is_deleted_desc"));
|
||||
$this->redirect($redirect);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,22 +118,22 @@ final class ReportPresenter extends OpenVKPresenter
|
|||
$report->deleteContent();
|
||||
$report->banUser($this->user->identity->getId());
|
||||
|
||||
$this->flash("suc", "Смэрть...", "Пользователь успешно забанен.");
|
||||
$this->flash("suc", tr("death"), tr("user_successfully_banned"));
|
||||
} else if ($this->postParam("delete")) {
|
||||
$report->deleteContent();
|
||||
|
||||
$this->flash("suc", "Нехай живе!", "Контент удалён, а пользователю прилетело предупреждение.");
|
||||
$this->flash("suc", tr("nehay"), tr("content_is_deleted"));
|
||||
} else if ($this->postParam("ignore")) {
|
||||
$report->delete();
|
||||
|
||||
$this->flash("suc", "Нехай живе!", "Жалоба проигнорирована.");
|
||||
$this->flash("suc", tr("nehay"), tr("report_is_ignored"));
|
||||
} else if ($this->postParam("banClubOwner") || $this->postParam("banClub")) {
|
||||
if ($report->getContentType() !== "group")
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
$club = $report->getContentObject();
|
||||
if (!$club || $club->isBanned())
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
if ($this->postParam("banClubOwner")) {
|
||||
$club->getOwner()->ban("**content-" . $report->getContentType() . "-" . $report->getContentId() . "**", false, $club->getOwner()->getNewBanTime(), $this->user->identity->getId());
|
||||
|
@ -143,7 +143,7 @@ final class ReportPresenter extends OpenVKPresenter
|
|||
|
||||
$report->delete();
|
||||
|
||||
$this->flash("suc", "Смэрть...", ($this->postParam("banClubOwner") ? "Создатель сообщества успешно забанен." : "Сообщество успешно забанено"));
|
||||
$this->flash("suc", tr("death"), ($this->postParam("banClubOwner") ? tr("group_owner_is_banned") : tr("group_is_banned")));
|
||||
}
|
||||
|
||||
$this->redirect("/scumfeed");
|
||||
|
|
|
@ -111,7 +111,7 @@ final class TopicsPresenter extends OpenVKPresenter
|
|||
$video = Video::fastMake($this->user->id, $_FILES["_vid_attachment"]["name"], $this->postParam("text"), $_FILES["_vid_attachment"]);
|
||||
}
|
||||
} catch(ISE $ex) {
|
||||
$this->flash("err", "Не удалось опубликовать комментарий", "Файл медиаконтента повреждён или слишком велик.");
|
||||
$this->flash("err", tr("error_when_publishing_comment"), tr("error_comment_file_too_big"));
|
||||
$this->redirect("/topic" . $topic->getPrettyId());
|
||||
}
|
||||
|
||||
|
@ -126,7 +126,7 @@ final class TopicsPresenter extends OpenVKPresenter
|
|||
$comment->setFlags($flags);
|
||||
$comment->save();
|
||||
} catch (\LengthException $ex) {
|
||||
$this->flash("err", "Не удалось опубликовать комментарий", "Комментарий слишком большой.");
|
||||
$this->flash("err", tr("error_when_publishing_comment"), tr("error_comment_too_big"));
|
||||
$this->redirect("/topic" . $topic->getPrettyId());
|
||||
}
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ final class UserPresenter extends OpenVKPresenter
|
|||
if(!is_null($this->user)) {
|
||||
if($this->template->mode !== "friends" && $this->user->id !== $id) {
|
||||
$name = $user->getFullName();
|
||||
$this->flash("err", "Ошибка доступа", "Вы не можете просматривать полный список подписок $name.");
|
||||
$this->flash("err", tr("error_access_denied_short"), tr("error_viewing_subs", $name));
|
||||
|
||||
$this->redirect($user->getURL());
|
||||
}
|
||||
|
@ -107,11 +107,11 @@ final class UserPresenter extends OpenVKPresenter
|
|||
$this->notFound();
|
||||
|
||||
if(!$club->canBeModifiedBy($this->user->identity ?? NULL))
|
||||
$this->flashFail("err", "Ошибка доступа", "У вас недостаточно прав, чтобы изменять этот ресурс.", NULL, true);
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"), NULL, true);
|
||||
|
||||
$isClubPinned = $this->user->identity->isClubPinned($club);
|
||||
if(!$isClubPinned && $this->user->identity->getPinnedClubCount() > 10)
|
||||
$this->flashFail("err", "Ошибка", "Находится в левом меню могут максимум 10 групп", NULL, true);
|
||||
$this->flashFail("err", tr("error"), tr("error_max_pinned_clubs"), NULL, true);
|
||||
|
||||
if($club->getOwner()->getId() === $this->user->identity->getId()) {
|
||||
$club->setOwner_Club_Pinned(!$isClubPinned);
|
||||
|
@ -237,7 +237,7 @@ final class UserPresenter extends OpenVKPresenter
|
|||
} elseif($_GET['act'] === "status") {
|
||||
if(mb_strlen($this->postParam("status")) > 255) {
|
||||
$statusLength = (string) mb_strlen($this->postParam("status"));
|
||||
$this->flashFail("err", "Ошибка", "Статус слишком длинный ($statusLength символов вместо 255 символов)", NULL, true);
|
||||
$this->flashFail("err", tr("error"), tr("error_status_too_long", $statusLength), NULL, true);
|
||||
}
|
||||
|
||||
$user->setStatus(empty($this->postParam("status")) ? NULL : $this->postParam("status"));
|
||||
|
@ -281,7 +281,7 @@ final class UserPresenter extends OpenVKPresenter
|
|||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
if(!$user->verifyNumber($this->postParam("code") ?? 0))
|
||||
$this->flashFail("err", "Ошибка", "Не удалось подтвердить номер телефона: неверный код.");
|
||||
$this->flashFail("err", tr("error"), tr("invalid_code"));
|
||||
|
||||
$this->flash("succ", tr("changes_saved"), tr("changes_saved_comment"));
|
||||
}
|
||||
|
@ -481,6 +481,7 @@ final class UserPresenter extends OpenVKPresenter
|
|||
"menu_novajoj" => "news",
|
||||
"menu_ligiloj" => "links",
|
||||
"menu_standardo" => "poster",
|
||||
"menu_aplikoj" => "apps"
|
||||
];
|
||||
foreach($settings as $checkbox => $setting)
|
||||
$user->setLeftMenuItemStatus($setting, $this->checkbox($checkbox));
|
||||
|
|
|
@ -58,7 +58,7 @@ final class VideosPresenter extends OpenVKPresenter
|
|||
$this->willExecuteWriteAction();
|
||||
|
||||
if(OPENVK_ROOT_CONF['openvk']['preferences']['videos']['disableUploading'])
|
||||
$this->flashFail("err", tr("error"), "Video uploads are disabled by the system administrator.");
|
||||
$this->flashFail("err", tr("error"), tr("video_uploads_disabled"));
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
if(!empty($this->postParam("name"))) {
|
||||
|
@ -74,18 +74,18 @@ final class VideosPresenter extends OpenVKPresenter
|
|||
else if(!empty($this->postParam("link")))
|
||||
$video->setLink($this->postParam("link"));
|
||||
else
|
||||
$this->flashFail("err", "Нету видеозаписи", "Выберите файл или укажите ссылку.");
|
||||
$this->flashFail("err", tr("no_video"), tr("no_video_desc"));
|
||||
} catch(\DomainException $ex) {
|
||||
$this->flashFail("err", "Произошла ошибка", "Файл повреждён или не содержит видео." );
|
||||
$this->flashFail("err", tr("error_occured"), tr("error_video_damaged_file"));
|
||||
} catch(ISE $ex) {
|
||||
$this->flashFail("err", "Произошла ошибка", "Возможно, ссылка некорректна.");
|
||||
$this->flashFail("err", tr("error_occured"), tr("error_video_incorrect_link"));
|
||||
}
|
||||
|
||||
$video->save();
|
||||
|
||||
$this->redirect("/video" . $video->getPrettyId());
|
||||
} else {
|
||||
$this->flashFail("err", "Произошла ошибка", "Видео не может быть опубликовано без названия.");
|
||||
$this->flashFail("err", tr("error_occured"), tr("error_video_no_title"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -99,14 +99,14 @@ final class VideosPresenter extends OpenVKPresenter
|
|||
if(!$video)
|
||||
$this->notFound();
|
||||
if(is_null($this->user) || $this->user->id !== $owner)
|
||||
$this->flashFail("err", "Ошибка доступа", "Вы не имеете права редактировать этот ресурс.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$video->setName(empty($this->postParam("name")) ? NULL : $this->postParam("name"));
|
||||
$video->setDescription(empty($this->postParam("desc")) ? NULL : $this->postParam("desc"));
|
||||
$video->save();
|
||||
|
||||
$this->flash("succ", "Изменения сохранены", "Обновлённое описание появится на странице с видосиком.");
|
||||
$this->flash("succ", tr("changes_saved"), tr("new_data_video"));
|
||||
$this->redirect("/video" . $video->getPrettyId());
|
||||
}
|
||||
|
||||
|
@ -128,7 +128,7 @@ final class VideosPresenter extends OpenVKPresenter
|
|||
$video->deleteVideo($owner, $vid);
|
||||
}
|
||||
} else {
|
||||
$this->flashFail("err", "Не удалось удалить пост", "Вы не вошли в аккаунт.");
|
||||
$this->flashFail("err", tr("error_deleting_video"), tr("login_please"));
|
||||
}
|
||||
|
||||
$this->redirect("/videos" . $owner);
|
||||
|
|
|
@ -3,7 +3,7 @@ namespace openvk\Web\Presenters;
|
|||
use openvk\Web\Models\Exceptions\TooMuchOptionsException;
|
||||
use openvk\Web\Models\Entities\{Poll, Post, Photo, Video, Club, User};
|
||||
use openvk\Web\Models\Entities\Notifications\{MentionNotification, NewSuggestedPostsNotification, RepostNotification, WallPostNotification};
|
||||
use openvk\Web\Models\Repositories\{Posts, Users, Clubs, Albums, Notes};
|
||||
use openvk\Web\Models\Repositories\{Posts, Users, Clubs, Albums, Notes, Videos, Comments, Photos};
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use Nette\InvalidStateException as ISE;
|
||||
use Bhaktaraz\RSSGenerator\Item;
|
||||
|
@ -253,10 +253,7 @@ final class WallPresenter extends OpenVKPresenter
|
|||
|
||||
if(!$canPost)
|
||||
$this->flashFail("err", tr("not_enough_permissions"), tr("not_enough_permissions_comment"));
|
||||
|
||||
if($_FILES["_vid_attachment"] && OPENVK_ROOT_CONF['openvk']['preferences']['videos']['disableUploading'])
|
||||
$this->flashFail("err", tr("error"), "Video uploads are disabled by the system administrator.");
|
||||
|
||||
|
||||
$anon = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"];
|
||||
if($wallOwner instanceof Club && $this->postParam("as_group") === "on" && $this->postParam("force_sign") !== "on" && $anon) {
|
||||
$manager = $wallOwner->getManager($this->user->identity);
|
||||
|
@ -274,23 +271,23 @@ final class WallPresenter extends OpenVKPresenter
|
|||
if($this->postParam("force_sign") === "on")
|
||||
$flags |= 0b01000000;
|
||||
|
||||
try {
|
||||
$photo = NULL;
|
||||
$video = NULL;
|
||||
if($_FILES["_pic_attachment"]["error"] === UPLOAD_ERR_OK) {
|
||||
$album = NULL;
|
||||
if(!$anon && $wall > 0 && $wall === $this->user->id)
|
||||
$album = (new Albums)->getUserWallAlbum($wallOwner);
|
||||
|
||||
$photo = Photo::fastMake($this->user->id, $this->postParam("text"), $_FILES["_pic_attachment"], $album, $anon);
|
||||
$photos = [];
|
||||
|
||||
if(!empty($this->postParam("photos"))) {
|
||||
$un = rtrim($this->postParam("photos"), ",");
|
||||
$arr = explode(",", $un);
|
||||
|
||||
if(sizeof($arr) < 11) {
|
||||
foreach($arr as $dat) {
|
||||
$ids = explode("_", $dat);
|
||||
$photo = (new Photos)->getByOwnerAndVID((int)$ids[0], (int)$ids[1]);
|
||||
|
||||
if(!$photo || $photo->isDeleted())
|
||||
continue;
|
||||
|
||||
$photos[] = $photo;
|
||||
}
|
||||
}
|
||||
|
||||
if($_FILES["_vid_attachment"]["error"] === UPLOAD_ERR_OK)
|
||||
$video = Video::fastMake($this->user->id, $_FILES["_vid_attachment"]["name"], $this->postParam("text"), $_FILES["_vid_attachment"], $anon);
|
||||
} catch(\DomainException $ex) {
|
||||
$this->flashFail("err", tr("failed_to_publish_post"), tr("media_file_corrupted"));
|
||||
} catch(ISE $ex) {
|
||||
$this->flashFail("err", tr("failed_to_publish_post"), tr("media_file_corrupted_or_too_large"));
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -317,8 +314,27 @@ final class WallPresenter extends OpenVKPresenter
|
|||
$this->flashFail("err", " ");
|
||||
}
|
||||
}
|
||||
|
||||
$videos = [];
|
||||
|
||||
if(!empty($this->postParam("videos"))) {
|
||||
$un = rtrim($this->postParam("videos"), ",");
|
||||
$arr = explode(",", $un);
|
||||
|
||||
if(sizeof($arr) < 11) {
|
||||
foreach($arr as $dat) {
|
||||
$ids = explode("_", $dat);
|
||||
$video = (new Videos)->getByOwnerAndVID((int)$ids[0], (int)$ids[1]);
|
||||
|
||||
if(!$video || $video->isDeleted())
|
||||
continue;
|
||||
|
||||
$videos[] = $video;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(empty($this->postParam("text")) && !$photo && !$video && !$poll && !$note)
|
||||
if(empty($this->postParam("text")) && sizeof($photos) < 1 && sizeof($videos) < 1 && !$poll && !$note)
|
||||
$this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_empty_or_too_big"));
|
||||
|
||||
try {
|
||||
|
@ -339,11 +355,12 @@ final class WallPresenter extends OpenVKPresenter
|
|||
$this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_too_big"));
|
||||
}
|
||||
|
||||
if(!is_null($photo))
|
||||
$post->attach($photo);
|
||||
foreach($photos as $photo)
|
||||
$post->attach($photo);
|
||||
|
||||
if(!is_null($video))
|
||||
$post->attach($video);
|
||||
if(sizeof($videos) > 0)
|
||||
foreach($videos as $vid)
|
||||
$post->attach($vid);
|
||||
|
||||
if(!is_null($poll))
|
||||
$post->attach($poll);
|
||||
|
@ -547,4 +564,64 @@ final class WallPresenter extends OpenVKPresenter
|
|||
# TODO localize message based on language and ?act=(un)pin
|
||||
$this->flashFail("succ", tr("information_-1"), tr("changes_saved_comment"));
|
||||
}
|
||||
|
||||
function renderEdit()
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] !== "POST")
|
||||
$this->redirect("/id0");
|
||||
|
||||
if($this->postParam("type") == "post")
|
||||
$post = $this->posts->get((int)$this->postParam("postid"));
|
||||
else
|
||||
$post = (new Comments)->get((int)$this->postParam("postid"));
|
||||
|
||||
if(!$post || $post->isDeleted())
|
||||
$this->returnJson(["error" => "Invalid post"]);
|
||||
|
||||
if(!$post->canBeEditedBy($this->user->identity))
|
||||
$this->returnJson(["error" => "Access denied"]);
|
||||
|
||||
$attachmentsCount = sizeof(iterator_to_array($post->getChildren()));
|
||||
|
||||
if(empty($this->postParam("newContent")) && $attachmentsCount < 1)
|
||||
$this->returnJson(["error" => "Empty post"]);
|
||||
|
||||
$post->setEdited(time());
|
||||
|
||||
try {
|
||||
$post->setContent($this->postParam("newContent"));
|
||||
} catch(\LengthException $e) {
|
||||
$this->returnJson(["error" => $e->getMessage()]);
|
||||
}
|
||||
|
||||
if($this->postParam("type") === "post") {
|
||||
$post->setNsfw($this->postParam("nsfw") == "true");
|
||||
$flags = 0;
|
||||
|
||||
if($post->getTargetWall() < 0 && $post->getWallOwner()->canBeModifiedBy($this->user->identity)) {
|
||||
if($this->postParam("fromgroup") == "true") {
|
||||
$flags |= 0b10000000;
|
||||
$post->setFlags($flags);
|
||||
} else
|
||||
$post->setFlags($flags);
|
||||
}
|
||||
}
|
||||
|
||||
$post->save(true);
|
||||
|
||||
$this->returnJson(["error" => "no",
|
||||
"new_content" => $post->getText(),
|
||||
"new_edited" => (string)$post->getEditTime(),
|
||||
"nsfw" => $this->postParam("type") === "post" ? (int)$post->isExplicit() : 0,
|
||||
"from_group" => $this->postParam("type") === "post" && $post->getTargetWall() < 0 ?
|
||||
((int)$post->isPostedOnBehalfOfGroup()) : "false",
|
||||
"new_text" => $post->getText(false),
|
||||
"author" => [
|
||||
"name" => $post->getOwner()->getCanonicalName(),
|
||||
"avatar" => $post->getOwner()->getAvatarUrl()
|
||||
]]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,9 +38,8 @@
|
|||
<body>
|
||||
<div id="sudo-banner" n:if="isset($thisUser) && $userTainted">
|
||||
<p>
|
||||
Вы вошли как <b>{$thisUser->getCanonicalName()}</b>. Пожалуйста, уважайте
|
||||
право на тайну переписки других людей и не злоупотребляйте подменой пользователя.
|
||||
Нажмите <a href="/setSID/unset?hash={rawurlencode($csrfToken)}">здесь</a>, чтобы выйти.
|
||||
{_you_entered_as} <b>{$thisUser->getCanonicalName()}</b>. {_please_rights}
|
||||
{_click_on} <a href="/setSID/unset?hash={rawurlencode($csrfToken)}">{_there}</a>, {_to_leave}.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
@ -176,7 +175,7 @@
|
|||
<a href="{$thisUser->getURL()}" class="link" title="{_my_page} [Alt+Shift+.]" accesskey=".">{_my_page}</a>
|
||||
<a href="/friends{$thisUser->getId()}" class="link">{_my_friends}
|
||||
<object type="internal/link" n:if="$thisUser->getFollowersCount() > 0">
|
||||
<a href="/friends{$thisUser->getId()}?act=incoming">
|
||||
<a href="/friends{$thisUser->getId()}?act=incoming" class="linkunderline">
|
||||
(<b>{$thisUser->getFollowersCount()}</b>)
|
||||
</a>
|
||||
</object>
|
||||
|
@ -196,7 +195,7 @@
|
|||
(<b>{$thisUser->getNotificationsCount()}</b>)
|
||||
{/if}
|
||||
</a>
|
||||
<a href="/apps?act=installed" class="link">{_my_apps}</a>
|
||||
<a n:if="$thisUser->getLeftMenuItemStatus('apps')" href="/apps?act=installed" class="link">{_my_apps}</a>
|
||||
<a href="/settings" class="link">{_my_settings}</a>
|
||||
|
||||
{var $canAccessAdminPanel = $thisUser->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL)}
|
||||
|
@ -310,7 +309,7 @@
|
|||
</div>
|
||||
</div>
|
||||
{ifset $thisUser}
|
||||
{if !$thisUser->isBanned()}
|
||||
{if !$thisUser->isBanned() && !$thisUser->isDeleted()}
|
||||
</div>
|
||||
{/if}
|
||||
{/ifset}
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
{extends "../@layout.xml"}
|
||||
{block title}Ваш браузер устарел{/block}
|
||||
{block title}{_deprecated_browser}{/block}
|
||||
|
||||
{block header}
|
||||
Устаревший браузер
|
||||
{_deprecated_browser}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
Для просмотра этого контента вам понадобится Firefox ESR 52+ или
|
||||
эквивалентный по функционалу навигатор по всемирной сети интернет.<br/>
|
||||
Сожалеем об этом.
|
||||
{_deprecated_browser_description}
|
||||
{/block}
|
||||
|
|
|
@ -9,5 +9,5 @@
|
|||
<div id="faqhead">Для кого этот сайт?</div>
|
||||
<div id="faqcontent">Сайт предназначен для поиска друзей и знакомых, а также просмотр данных пользователя. Это как справочник города, с помощью которого люди могут быстро найти актуальную информацию о человеке. Также этот сайт подойдёт для ностальгираторов и тех, кто решил слезть с трубы "ВКонтакте", которого клон и является.<br></div>
|
||||
Я попозже допишу ок ~~ veselcraft - 12.01.2020 - 22:05 GMT+3
|
||||
|
||||
Давай
|
||||
{/block}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{block title}Sandbox{/block}
|
||||
|
||||
{block header}
|
||||
Sandbox для разработчиков
|
||||
{_sandbox_for_developers}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{extends "./@layout.xml"}
|
||||
|
||||
{block title}
|
||||
История блокировок
|
||||
{_bans_history}
|
||||
{/block}
|
||||
|
||||
{block heading}
|
||||
|
@ -13,13 +13,13 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Забаненный</th>
|
||||
<th>Инициатор</th>
|
||||
<th>Начало</th>
|
||||
<th>Конец</th>
|
||||
<th>Время</th>
|
||||
<th>Причина</th>
|
||||
<th>Снята</th>
|
||||
<th>{_bans_history_blocked}</th>
|
||||
<th>{_bans_history_initiator}</th>
|
||||
<th>{_bans_history_start}</th>
|
||||
<th>{_bans_history_end}</th>
|
||||
<th>{_bans_history_time}</th>
|
||||
<th>{_bans_history_reason}</th>
|
||||
<th>{_bans_history_removed}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -77,7 +77,7 @@
|
|||
{_admin_banned}
|
||||
</span>
|
||||
{else}
|
||||
<b style="color: red;">Активная блокировка</b>
|
||||
<b style="color: red;">{_bans_history_active}</b>
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
{extends "@layout.xml"}
|
||||
|
||||
{block title}
|
||||
Логи
|
||||
{_logs}
|
||||
{/block}
|
||||
|
||||
{block heading}
|
||||
Логи
|
||||
{_logs}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
|
@ -18,23 +18,23 @@
|
|||
</style>
|
||||
<form class="aui">
|
||||
<div>
|
||||
<select class="select medium-field" type="number" id="type" name="type" placeholder="Тип изменения">
|
||||
<option value="any" n:attr="selected => !$type">Любое</option>
|
||||
<option value="0" n:attr="selected => $type === 0">Создание</option>
|
||||
<option value="1" n:attr="selected => $type === 1">Редактирование</option>
|
||||
<option value="2" n:attr="selected => $type === 2">Удаление</option>
|
||||
<option value="3" n:attr="selected => $type === 3">Восстановление</option>
|
||||
<select class="select medium-field" type="number" id="type" name="type" placeholder="{_logs_change_type}">
|
||||
<option value="any" n:attr="selected => !$type">{_logs_anything}</option>
|
||||
<option value="0" n:attr="selected => $type === 0">{_logs_adding}</option>
|
||||
<option value="1" n:attr="selected => $type === 1">{_logs_editing}</option>
|
||||
<option value="2" n:attr="selected => $type === 2">{_logs_removing}</option>
|
||||
<option value="3" n:attr="selected => $type === 3">{_logs_restoring}</option>
|
||||
</select>
|
||||
<input class="text medium-field" type="number" id="id" name="id" placeholder="ID записи" n:attr="value => $id"/>
|
||||
<input class="text medium-field" type="text" id="uid" name="uid" placeholder="UUID пользователя" n:attr="value => $user"/>
|
||||
<input class="text medium-field" type="number" id="id" name="id" placeholder="{_logs_id_post}" n:attr="value => $id"/>
|
||||
<input class="text medium-field" type="text" id="uid" name="uid" placeholder="{_logs_uuid_user}" n:attr="value => $user"/>
|
||||
</div>
|
||||
<div style="margin: 8px 0;" />
|
||||
<div>
|
||||
<select class="select medium-field" id="obj_type" name="obj_type" placeholder="Тип объекта">
|
||||
<option value="any" n:attr="selected => !$obj_type">Любой</option>
|
||||
<select class="select medium-field" id="obj_type" name="obj_type" placeholder="{_logs_change_object}">
|
||||
<option value="any" n:attr="selected => !$obj_type">{_logs_anything}</option>
|
||||
<option n:foreach="$object_types as $type" n:attr="selected => $obj_type === $type">{$type}</option>
|
||||
</select>
|
||||
<input class="text medium-field" type="number" id="obj_id" name="obj_id" placeholder="ID объекта" n:attr="value => $obj_id"/>
|
||||
<input class="text medium-field" type="number" id="obj_id" name="obj_id" placeholder="{_logs_id_object}" n:attr="value => $obj_id"/>
|
||||
<input type="submit" class="aui-button aui-button-primary medium-field" value="Поиск" style="width: 165px;"/>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -42,11 +42,11 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Пользователь</th>
|
||||
<th>Объект</th>
|
||||
<th>Тип</th>
|
||||
<th>Изменения</th>
|
||||
<th>Время</th>
|
||||
<th>{_logs_user}</th>
|
||||
<th>{_logs_object}</th>
|
||||
<th>{_logs_type}</th>
|
||||
<th>{_logs_changes}</th>
|
||||
<th>{_logs_time}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
{block header}
|
||||
{$name}
|
||||
<a style="float: right;" onClick="reportApp()" n:if="$canReport ?? false">Пожаловаться</a>
|
||||
<a style="float: right;" onClick="reportApp()" n:if="$canReport ?? false">{_report}</a>
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
|
@ -37,20 +37,20 @@
|
|||
|
||||
<script n:if="$canReport ?? false">
|
||||
function reportApp() {
|
||||
uReportMsgTxt = "Вы собираетесь пожаловаться на данное приложение.";
|
||||
uReportMsgTxt += "<br/>Что именно вам кажется недопустимым в этом материале?";
|
||||
uReportMsgTxt += "<br/><br/><b>Причина жалобы</b>: <input type='text' id='uReportMsgInput' placeholder='Причина' />"
|
||||
uReportMsgTxt = {_going_to_report_app};
|
||||
uReportMsgTxt += "<br/>"+tr("report_question_text");
|
||||
uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"
|
||||
|
||||
MessageBox("Пожаловаться?", uReportMsgTxt, ["Подтвердить", "Отмена"], [
|
||||
MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [
|
||||
(function() {
|
||||
res = document.querySelector("#uReportMsgInput").value;
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", "/report/" + {$id} + "?reason=" + res + "&type=app", true);
|
||||
xhr.onload = (function() {
|
||||
if(xhr.responseText.indexOf("reason") === -1)
|
||||
MessageBox("Ошибка", "Не удалось подать жалобу...", ["OK"], [Function.noop]);
|
||||
MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
|
||||
else
|
||||
MessageBox("Операция успешна", "Скоро её рассмотрят модераторы", ["OK"], [Function.noop]);
|
||||
MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
|
||||
});
|
||||
xhr.send(null);
|
||||
}),
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{extends "../@layout.xml"}
|
||||
|
||||
{block title}Переход по ссылке заблокирован{/block}
|
||||
{block title}{_transition_is_blocked}{/block}
|
||||
|
||||
{block header}
|
||||
Предупреждение
|
||||
{_caution}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
<tbody>
|
||||
<tr>
|
||||
<td width="120" valign="top"><span class="nobold">{_gender}: </span></td>
|
||||
<td>{$user->isFemale() ? "женский" : "мужской"}</td>
|
||||
<td>{$user->isFemale() ? tr("female"): tr("male")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="120" valign="top"><span class="nobold">{_registration_date}: </span></td>
|
||||
|
@ -82,8 +82,6 @@
|
|||
</table>
|
||||
|
||||
<script n:if="$club->getOwner()->getId() != $user->getId() && $manager && $thisUser->getId() == $club->getOwner()->getId()">
|
||||
console.log("gayshit");
|
||||
console.log("сам такой");
|
||||
function changeOwner(club, newOwner) {
|
||||
const action = "/groups/" + club + "/setNewOwner/" + newOwner;
|
||||
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
|
||||
{block content}
|
||||
<div>
|
||||
<h4>Охват</h4>
|
||||
<p>Этот график отображает охват за последние 7 дней.</p>
|
||||
<h4>{_coverage}</h4>
|
||||
<p>{_coverage_this_week}</p>
|
||||
<div id="reachChart" style="width: 100%; height: 280px;"></div>
|
||||
|
||||
<h4>Просмотры</h4>
|
||||
<p>Этот график отображает просмотры постов сообщества за последние 7 дней.</p>
|
||||
<h4>{_views}</h4>
|
||||
<p>{_views_this_week}</p>
|
||||
<div id="viewsChart" style="width: 100%; height: 280px;"></div>
|
||||
|
||||
<style>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<img n:if="$club->isVerified()"
|
||||
class="name-checkmark"
|
||||
src="/assets/packages/static/openvk/img/checkmark.png"
|
||||
alt="Подтверждённая страница"
|
||||
alt="{_verified_page}"
|
||||
/>
|
||||
{/block}
|
||||
|
||||
|
@ -155,24 +155,24 @@
|
|||
{/if}
|
||||
{var $canReport = $thisUser->getId() != $club->getOwner()->getId()}
|
||||
{if $canReport}
|
||||
<a class="profile_link" style="display:block;width:96%;" href="javascript:reportVideo()">{_report}</a>
|
||||
<a class="profile_link" style="display:block;" href="javascript:reportVideo()">{_report}</a>
|
||||
|
||||
<script>
|
||||
function reportVideo() {
|
||||
uReportMsgTxt = "Вы собираетесь пожаловаться на данное сообщество.";
|
||||
uReportMsgTxt += "<br/>Что именно вам кажется недопустимым в этом материале?";
|
||||
uReportMsgTxt += "<br/><br/><b>Причина жалобы</b>: <input type='text' id='uReportMsgInput' placeholder='Причина' />"
|
||||
uReportMsgTxt = tr("going_to_report_club");
|
||||
uReportMsgTxt += "<br/>"+tr("report_question_text");
|
||||
uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"
|
||||
|
||||
MessageBox("Пожаловаться?", uReportMsgTxt, ["Подтвердить", "Отмена"], [
|
||||
MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [
|
||||
(function() {
|
||||
res = document.querySelector("#uReportMsgInput").value;
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", "/report/" + {$club->getId()} + "?reason=" + res + "&type=group", true);
|
||||
xhr.onload = (function() {
|
||||
if(xhr.responseText.indexOf("reason") === -1)
|
||||
MessageBox("Ошибка", "Не удалось подать жалобу...", ["OK"], [Function.noop]);
|
||||
MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
|
||||
else
|
||||
MessageBox("Операция успешна", "Скоро её рассмотрят модераторы", ["OK"], [Function.noop]);
|
||||
MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
|
||||
});
|
||||
xhr.send(null);
|
||||
}),
|
||||
|
|
|
@ -27,13 +27,13 @@
|
|||
<tbody id="models-list">
|
||||
<tr id="0-model">
|
||||
<td width="83px">
|
||||
<span class="nobold">Раздел:</span>
|
||||
<span class="nobold">{_section}:</span>
|
||||
</td>
|
||||
<td>
|
||||
<div style="display: flex; gap: 8px; justify-content: space-between;">
|
||||
<div id="add-model" class="noSpamIcon noSpamIcon-Add" style="display: none;" />
|
||||
<select name="model" id="model" class="model initialModel" style="margin-left: -2px;">
|
||||
<option selected value="none">Не выбрано</option>
|
||||
<option selected value="none">{_relationship_0}</option>
|
||||
<option n:foreach="$models as $model" value="{$model}">{$model}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
@ -47,7 +47,7 @@
|
|||
<tbody>
|
||||
<tr style="width: 129px; border-top: 1px solid #ECECEC;">
|
||||
<td>
|
||||
<span class="nobold">Подстрока:</span>
|
||||
<span class="nobold">{_substring}:</span>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="regex" placeholder="Regex" id="regex">
|
||||
|
@ -55,10 +55,10 @@
|
|||
</tr>
|
||||
<tr style="width: 129px; border-top: 1px solid #ECECEC;">
|
||||
<td>
|
||||
<span class="nobold">Пользователь:</span>
|
||||
<span class="nobold">{_n_user}:</span>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="user" placeholder="Ссылка на страницу" id="user">
|
||||
<input type="text" name="user" placeholder="{_link_to_page}" id="user">
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="width: 129px">
|
||||
|
@ -66,12 +66,12 @@
|
|||
<span class="nobold">IP:</span>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="ip" id="ip" placeholder="или подсеть">
|
||||
<input type="text" name="ip" id="ip" placeholder="{_or_subnet}">
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="width: 129px">
|
||||
<td>
|
||||
<span class="nobold">Юзер-агент:</span>
|
||||
<span class="nobold">User-Agent:</span>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="useragent" id="useragent" placeholder="Mozila 1.0 Blablabla/test">
|
||||
|
@ -79,7 +79,7 @@
|
|||
</tr>
|
||||
<tr style="width: 129px">
|
||||
<td>
|
||||
<span class="nobold">Время раньше, чем:</span>
|
||||
<span class="nobold">{_time_before}:</span>
|
||||
</td>
|
||||
<td>
|
||||
<input type="datetime-local" name="ts" id="ts">
|
||||
|
@ -87,7 +87,7 @@
|
|||
</tr>
|
||||
<tr style="width: 129px">
|
||||
<td>
|
||||
<span class="nobold">Время позже, чем:</span>
|
||||
<span class="nobold">{_time_after}:</span>
|
||||
</td>
|
||||
<td>
|
||||
<input type="datetime-local" name="te" id="te">
|
||||
|
@ -97,29 +97,47 @@
|
|||
</table>
|
||||
<textarea style="resize: vertical; width: calc(100% - 6px)" placeholder='city = "Воскресенск" && id = 1'
|
||||
name="where" id="where"/>
|
||||
<span style="color: grey; font-size: 8px;">WHERE для поиска по разделу</span>
|
||||
<span style="color: grey; font-size: 8px;">{_where_for_search}</span>
|
||||
<div style="border-top: 1px solid #ECECEC; margin: 8px 0;"/>
|
||||
<table cellspacing="7" cellpadding="0" width="100%" border="0">
|
||||
<tbody>
|
||||
<tr style="width: 129px; border-top: 1px solid #ECECEC;">
|
||||
<td>
|
||||
<span class="nobold">Параметры блокировки:</span>
|
||||
<span class="nobold">{_block_params}:</span>
|
||||
</td>
|
||||
<td>
|
||||
<select name="ban_type" id="noSpam-ban-type">
|
||||
<option value="1">Только откат</option>
|
||||
<option value="2">Только блокировка</option>
|
||||
<option value="3">Откат и блокировка</option>
|
||||
<select name="ban_type" id="noSpam-ban-type" style="width: 140px;">
|
||||
<option value="1">{_only_rollback}</option>
|
||||
<option value="2">{_only_block}</option>
|
||||
<option value="3">{_rollback_and_block}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="banSettings" style="width: 129px; border-top: 1px solid #ECECEC; display: none;">
|
||||
<td>
|
||||
<span class="nobold">Причина:</span>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="ban-reason" id="ban-reason" style="width: 140px;" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="banSettings" style="width: 129px; border-top: 1px solid #ECECEC; display: none;">
|
||||
<td>
|
||||
<span class="nobold">До:</span>
|
||||
</td>
|
||||
<td>
|
||||
<input type="datetime-local" name="unban-time" id="unban-time" style="width: 140px;" />
|
||||
<br />
|
||||
<input type="checkbox" name="is_forever" id="is-forever" /> навсегда
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div style="border-top: 1px solid #ECECEC; margin: 8px 0;"/>
|
||||
<center>
|
||||
<div id="noSpam-buttons">
|
||||
<input id="search" type="submit" value="Поиск" class="button"/>
|
||||
<input id="apply" type="submit" value="Применить" class="button" style="display: none;"/>
|
||||
<input id="search" type="submit" value="{_header_search}" class="button"/>
|
||||
<input id="apply" type="submit" value="{_subm}" class="button" style="display: none;"/>
|
||||
</div>
|
||||
<div id="noSpam-loader" style="display: none;">
|
||||
<img src="/assets/packages/static/openvk/img/loading_mini.gif" style="width: 40px;">
|
||||
|
@ -127,7 +145,7 @@
|
|||
</center>
|
||||
</div>
|
||||
<div id="noSpam-model-not-selected">
|
||||
<center id="noSpam-model-not-selected-text" style="padding: 71px 25px;">Выберите раздел для начала работы</center>
|
||||
<center id="noSpam-model-not-selected-text" style="padding: 71px 25px;">{_select_section_for_start}</center>
|
||||
<center id="noSpam-model-not-selected-loader" style="display: none;">
|
||||
<img src="/assets/packages/static/openvk/img/loading_mini.gif" style="width: 40px; margin: 125px 0;">
|
||||
</center>
|
||||
|
@ -137,11 +155,11 @@
|
|||
<center id="noSpam-results-loader" style="display: none;">
|
||||
<img src="/assets/packages/static/openvk/img/loading_mini.gif" style="width: 40px; margin: 125px 0;">
|
||||
</center>
|
||||
<center id="noSpam-results-text" style="margin: 125px 25px;">Здесь будут отображаться результаты поиска</center>
|
||||
<center id="noSpam-results-text" style="margin: 125px 25px;">{_results_will_be_there}</center>
|
||||
<div id="noSpam-results-block" style="display: none;">
|
||||
<h4 style="padding: 8px;">Результаты поиска
|
||||
<h4 style="padding: 8px;">{_search_results}
|
||||
<span style="color: #a2a2a2; font-weight: inherit">
|
||||
(<span id="noSpam-results-count" style="color: #a2a2a2; font-weight: inherit;"></span> шт.)
|
||||
(<span id="noSpam-results-count" style="color: #a2a2a2; font-weight: inherit;"></span> {_cnt}.)
|
||||
</span>
|
||||
</h4>
|
||||
<ul style="padding-inline-start:18px;" id="noSpam-results-list"></ul>
|
||||
|
@ -158,7 +176,6 @@
|
|||
$("#noSpam-results-loader").show();
|
||||
$("#noSpam-loader").show();
|
||||
|
||||
|
||||
let models = [];
|
||||
$(".model").each(function (i) {
|
||||
let name = $(this).val();
|
||||
|
@ -178,6 +195,10 @@
|
|||
let ts = $("#ts").val() ? Math.floor(new Date($("#ts").val()).getTime() / 1000) : null;
|
||||
let te = $("#te").val() ? Math.floor(new Date($("#te").val()).getTime() / 1000) : null;
|
||||
let user = $("#user").val();
|
||||
let ban_reason = $("#ban-reason").val();
|
||||
let unban_time = $("#unban-time").val() ? Math.floor(new Date($("#unban-time").val()).getTime() / 1000) : null;
|
||||
let is_forever = $("#is-forever").prop('checked');
|
||||
console.log(ban_reason, unban_time, is_forever);
|
||||
|
||||
await $.ajax({
|
||||
type: "POST",
|
||||
|
@ -193,6 +214,9 @@
|
|||
ts: ts,
|
||||
te: te,
|
||||
user: user,
|
||||
ban_reason: ban_reason,
|
||||
unban_time: unban_time,
|
||||
is_forever: is_forever,
|
||||
hash: {=$csrfToken}
|
||||
},
|
||||
success: (response) => {
|
||||
|
@ -218,17 +242,17 @@
|
|||
$("#noSpam-results-block").show();
|
||||
$("#apply").show();
|
||||
} else {
|
||||
$("#noSpam-results-text").text(ban ? "Операция завершена успешно" : "Ничего не найдено :(");
|
||||
$("#noSpam-results-text").text(ban ? tr("operation_successfully") : tr("no_found"));
|
||||
$("#noSpam-results-text").show();
|
||||
}
|
||||
} else {
|
||||
$("#noSpam-results-text").text(response?.error ?? "Неизвестная ошибка");
|
||||
$("#noSpam-results-text").text(response?.error ?? tr("unknown_error"));
|
||||
$("#noSpam-results-text").show();
|
||||
}
|
||||
},
|
||||
error: (error) => {
|
||||
console.error("Error while searching noSpam:", error);
|
||||
$("#noSpam-results-text").text("Ошибка при выполнении запроса");
|
||||
$("#noSpam-results-text").text(tr("error_when_searching"));
|
||||
$("#noSpam-results-text").show();
|
||||
}
|
||||
});
|
||||
|
@ -277,6 +301,17 @@
|
|||
selectChange(e.target.value);
|
||||
})
|
||||
|
||||
$("#noSpam-ban-type").change(async (e) => {
|
||||
if (e.target.value > 1) {
|
||||
$(".banSettings").show();
|
||||
} else {
|
||||
$("#ban-reason").val(null);
|
||||
$("#unban-time").val(null);
|
||||
$("#is-forever").prop('checked', false);
|
||||
$(".banSettings").hide();
|
||||
}
|
||||
});
|
||||
|
||||
$("#add-model").on("click", () => {
|
||||
console.log($(".model").length);
|
||||
$("#models-list").append(`
|
||||
|
@ -288,7 +323,7 @@
|
|||
<div style="display: flex; gap: 8px; justify-content: space-between;">
|
||||
<div class="noSpamIcon noSpamIcon-Delete" onClick="deleteModelSelect(${ $('.model').length});"></div>
|
||||
<select name="model" class="model" style="margin-left: -2px;" onChange="selectChange($(this).val())">
|
||||
<option selected value="none">Не выбрано</option>
|
||||
<option selected value="none">{_relationship_0}</option>
|
||||
{foreach $models as $model}
|
||||
<option value={$model}>{$model|noescape}</option>
|
||||
{/foreach}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<div n:attr="id => ($mode === 'form' ? 'activetabs' : 'ki')" class="tab">
|
||||
<a n:attr="id => ($mode === 'form' ? 'act_tab_a' : 'ki')" href="/noSpam">Бан по шаблону</a>
|
||||
<a n:attr="id => ($mode === 'form' ? 'act_tab_a' : 'ki')" href="/noSpam">{_template_ban}</a>
|
||||
</div>
|
||||
<div n:attr="id => ($mode === 'templates' ? 'activetabs' : 'ki')" class="tab">
|
||||
<a n:attr="id => ($mode === 'templates' ? 'act_tab_a' : 'ki')" href="/noSpam?act=templates">Действующие шаблоны</a>
|
||||
<a n:attr="id => ($mode === 'templates' ? 'act_tab_a' : 'ki')" href="/noSpam?act=templates">{_active_templates}</a>
|
||||
</div>
|
||||
<div n:attr="id => ($mode === 'reports' ? 'activetabs' : 'ki')" class="tab">
|
||||
<a n:attr="id => ($mode === 'reports' ? 'act_tab_a' : 'ki')" href="/scumfeed">Жалобы пользователей</a>
|
||||
<a n:attr="id => ($mode === 'reports' ? 'act_tab_a' : 'ki')" href="/scumfeed">{_users_reports}</a>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{extends "../@layout.xml"}
|
||||
|
||||
{block title}Шаблоны{/block}
|
||||
{block title}{_templates}{/block}
|
||||
{block header}{include title}{/block}
|
||||
|
||||
{block content}
|
||||
|
@ -44,14 +44,14 @@
|
|||
<table n:if="count($templates) > 0" cellspacing="0" cellpadding="7" width="100%">
|
||||
<tr>
|
||||
<th style="text-align: center;">ID</th>
|
||||
<th>Пользователь</th>
|
||||
<th style="text-align: center;">Раздел</th>
|
||||
<th>Подстрока</th>
|
||||
<th>{_n_user}</th>
|
||||
<th style="text-align: center;">{_section}</th>
|
||||
<th>{_substring}</th>
|
||||
<th>Where</th>
|
||||
<th style="text-align: center;">Тип</th>
|
||||
<th style="text-align: center;">Количество</th>
|
||||
<th>Время</th>
|
||||
<th style="text-align: center;">Действия</th>
|
||||
<th style="text-align: center;">{_type}</th>
|
||||
<th style="text-align: center;">{_count}</th>
|
||||
<th>{_time}</th>
|
||||
<th style="text-align: center;">{_actions}</th>
|
||||
</tr>
|
||||
<tr n:foreach="$templates as $template">
|
||||
<td id="id-{$template->getId()}" onClick="openTableField('id', {$template->getId()})" style="text-align: center;"><b>{$template->getId()}</b></td>
|
||||
|
@ -75,8 +75,8 @@
|
|||
<div id="noSpam-rollback-loader-{$template->getId()}" style="display: none;">
|
||||
<img src="/assets/packages/static/openvk/img/loading_mini.gif" style="width: 40px;">
|
||||
</div>
|
||||
<a n:if="!$template->isRollbacked()" id="noSpam-rollback-template-link-{$template->getId()}" onClick="rollbackTemplate({$template->getId()})">откатить</a>
|
||||
<span n:attr="style => $template->isRollbacked() ? '' : 'display: none;'" id="noSpam-rollback-template-rollbacked-{$template->getId()}">откачен</span>
|
||||
<a n:if="!$template->isRollbacked()" id="noSpam-rollback-template-link-{$template->getId()}" onClick="rollbackTemplate({$template->getId()})">{_roll_back}</a>
|
||||
<span n:attr="style => $template->isRollbacked() ? '' : 'display: none;'" id="noSpam-rollback-template-rollbacked-{$template->getId()}">{_roll_backed}</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<textarea name="html" style="display:none;"></textarea>
|
||||
<div id="editor" style="width:600px;height:300px;border:1px solid grey"></div>
|
||||
|
||||
<p><i><a href="/kb/notes">Кое-что</a> из (X)HTML поддерживается.</i></p>
|
||||
<p><i><a href="/kb/notes">{_something}</a> {_supports_xhtml}</i></p>
|
||||
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
<button class="button">{_save}</button>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<textarea name="html" style="display:none;"></textarea>
|
||||
<div id="editor" style="width:600px;height:300px;border:1px solid grey"></div>
|
||||
|
||||
<p><i><a href="/kb/notes">Кое-что</a> из (X)HTML поддерживается.</i></p>
|
||||
<p><i><a href="/kb/notes">{_something}</a> {_supports_xhtml}</i></p>
|
||||
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
<button class="button">{_save}</button>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{extends "../@layout.xml"}
|
||||
|
||||
{block title}Альбом {$album->getName()}{/block}
|
||||
{block title}{_album} {$album->getName()}{/block}
|
||||
|
||||
{block header}
|
||||
{var $isClub = ($album->getOwner() instanceof openvk\Web\Models\Entities\Club)}
|
||||
|
@ -18,7 +18,8 @@
|
|||
|
||||
{block content}
|
||||
<a href="/album{$album->getPrettyId()}">
|
||||
<b>{$album->getPhotosCount()} фотографий</b>
|
||||
{* TODO: Добавить склонения *}
|
||||
<b>{$album->getPhotosCount()} {_photos}</b>
|
||||
</a>
|
||||
|
||||
{if !is_null($thisUser) && $album->canBeModifiedBy($thisUser) && !$album->isCreatedBySystem()}
|
||||
|
@ -40,7 +41,7 @@
|
|||
</a>
|
||||
|
||||
<a href="/photo{$photo->getPrettyId()}?from=album{$album->getId()}">
|
||||
<img class="album-photo--image" src="{$photo->getURL()}" alt="{$photo->getDescription()}" />
|
||||
<img class="album-photo--image" src="{$photo->getURLBySizeId('tinier')}" alt="{$photo->getDescription()}" />
|
||||
</a>
|
||||
</div>
|
||||
{/foreach}
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
|
||||
{block description}
|
||||
<span>{$x->getDescription() ?? $x->getName()}</span><br />
|
||||
<span style="color: grey;">{$x->getPhotosCount()} фотографий</span><br />
|
||||
<span style="color: grey;">{$x->getPhotosCount()} {_photos}</span><br />
|
||||
<span style="color: grey;">{tr("updated_at", $x->getEditTime() ?? $x->getCreationTime())}</span><br />
|
||||
<span style="color: grey;">{_created} {$x->getCreationTime()}</span><br />
|
||||
{/block}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{extends "../@layout.xml"}
|
||||
{block title}Изменить альбом{/block}
|
||||
{block title}{_edit_album}{/block}
|
||||
|
||||
{block header}
|
||||
<a href="{$album->getOwner()->getURL()}">{$album->getOwner()->getCanonicalName()}</a>
|
||||
|
@ -14,6 +14,15 @@
|
|||
{/block}
|
||||
|
||||
{block content}
|
||||
<div class="tabs">
|
||||
<div id="activetabs" class="tab">
|
||||
<a id="act_tab_a" href="/album{$album->getPrettyId()}/edit">{_edit_album}</a>
|
||||
</div>
|
||||
<div class="tab">
|
||||
<a href="/photos/upload?album={$album->getPrettyId()}">{_add_photos}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<table cellspacing="6">
|
||||
<tbody>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{extends "../@layout.xml"}
|
||||
{block title}Изменить фотографию{/block}
|
||||
{block title}{_edit_photo}{/block}
|
||||
|
||||
{block header}
|
||||
<a href="{$thisUser->getURL()}">{$thisUser->getCanonicalName()}</a>
|
||||
|
|
|
@ -26,11 +26,11 @@
|
|||
|
||||
<hr/>
|
||||
|
||||
<div style="width: 100%; min-height: 100px;">
|
||||
<div style="float: left; min-height: 100px; width: 70%;">
|
||||
{include "../components/comments.xml", comments => $comments, count => $cCount, page => $cPage, model => "photos", parent => $photo}
|
||||
<div style="width: 100%; min-height: 100px;" class="ovk-photo-details">
|
||||
<div style="float: left; min-height: 100px; width: 68%;margin-left: 3px;">
|
||||
{include "../components/comments.xml", comments => $comments, count => $cCount, page => $cPage, model => "photos", parent => $photo, custom_id => 999}
|
||||
</div>
|
||||
<div style="float: left; min-height: 100px; width: 30%;">
|
||||
<div style="float:right;min-height: 100px;width: 30%;margin-left: 1px;">
|
||||
<div>
|
||||
<h4>{_information}</h4>
|
||||
<span style="color: grey;">{_info_description}:</span>
|
||||
|
@ -42,7 +42,7 @@
|
|||
</div>
|
||||
<br/>
|
||||
<h4>{_actions}</h4>
|
||||
{if $thisUser->getId() != $photo->getOwner()->getId()}
|
||||
{if isset($thisUser) && $thisUser->getId() != $photo->getOwner()->getId()}
|
||||
{var canReport = true}
|
||||
{/if}
|
||||
<div n:if="isset($thisUser) && $thisUser->getId() === $photo->getOwner()->getId()">
|
||||
|
@ -53,20 +53,20 @@
|
|||
<a n:if="$canReport ?? false" class="profile_link" style="display:block;width:96%;" href="javascript:reportPhoto()">{_report}</a>
|
||||
<script n:if="$canReport ?? false">
|
||||
function reportPhoto() {
|
||||
uReportMsgTxt = "Вы собираетесь пожаловаться на данную фотографию.";
|
||||
uReportMsgTxt += "<br/>Что именно вам кажется недопустимым в этом материале?";
|
||||
uReportMsgTxt += "<br/><br/><b>Причина жалобы</b>: <input type='text' id='uReportMsgInput' placeholder='Причина' />"
|
||||
uReportMsgTxt = tr("going_to_report_photo");
|
||||
uReportMsgTxt += "<br/>"+tr("report_question_text");
|
||||
uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"
|
||||
|
||||
MessageBox("Пожаловаться?", uReportMsgTxt, ["Подтвердить", "Отмена"], [
|
||||
MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [
|
||||
(function() {
|
||||
res = document.querySelector("#uReportMsgInput").value;
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", "/report/" + {$photo->getId()} + "?reason=" + res + "&type=photo", true);
|
||||
xhr.onload = (function() {
|
||||
if(xhr.responseText.indexOf("reason") === -1)
|
||||
MessageBox("Ошибка", "Не удалось подать жалобу...", ["OK"], [Function.noop]);
|
||||
MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
|
||||
else
|
||||
MessageBox("Операция успешна", "Скоро её рассмотрят модераторы", ["OK"], [Function.noop]);
|
||||
MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
|
||||
});
|
||||
xhr.send(null);
|
||||
}),
|
||||
|
@ -74,7 +74,6 @@
|
|||
]);
|
||||
}
|
||||
</script>
|
||||
<a href="{$photo->getURL()}" class="profile_link" target="_blank" style="display:block;width:96%;">{_open_original}</a>
|
||||
</div>
|
||||
</div>
|
||||
{/block}
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
{extends "../@layout.xml"}
|
||||
|
||||
{block title}Удалить фотографию?{/block}
|
||||
{block title}{_delete_photo}{/block}
|
||||
|
||||
{block header}
|
||||
Удаление фотографии
|
||||
{_delete_photo}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
Вы уверены что хотите удалить эту фотографию?
|
||||
{_sure_deleting_photo}
|
||||
<br/>
|
||||
<br/>
|
||||
<form method="POST">
|
||||
<input type="hidden" value="{$csrfToken}" name="hash" />
|
||||
|
||||
<a href="{$_SERVER['HTTP_REFERER']}" class="button">Нет</a>
|
||||
<a href="{$_SERVER['HTTP_REFERER']}" class="button">{_no}</a>
|
||||
|
||||
<button class="button">Да</button>
|
||||
<button class="button">{_yes}</button>
|
||||
</form>
|
||||
{/block}
|
||||
|
|
|
@ -2,9 +2,13 @@
|
|||
{block title}{_upload_photo}{/block}
|
||||
|
||||
{block header}
|
||||
<a href="{$thisUser->getURL()}">{$thisUser->getCanonicalName()}</a>
|
||||
<a href="{$album->getOwner()->getURL()}">{$album->getOwner()->getCanonicalName()}</a>
|
||||
»
|
||||
<a href="/albums{$thisUser->getId()}">{_albums}</a>
|
||||
{if $album->getOwner() instanceof openvk\Web\Models\Entities\Club}
|
||||
<a href="/albums{$album->getOwner()->getId() * -1}">{_albums}</a>
|
||||
{else}
|
||||
<a href="/albums{$album->getOwner()->getId()}">{_albums}</a>
|
||||
{/if}
|
||||
»
|
||||
<a href="/album{$album->getPrettyId()}">{$album->getName()}</a>
|
||||
»
|
||||
|
@ -12,32 +16,53 @@
|
|||
{/block}
|
||||
|
||||
{block content}
|
||||
<form action="/photos/upload?album={$album->getPrettyId()}" method="post" enctype="multipart/form-data">
|
||||
<table cellspacing="6">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="120" valign="top"><span class="nobold">{_description}:</span></td>
|
||||
<td><textarea style="margin: 0px; height: 50px; width: 159px; resize: none;" name="desc"></textarea></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="120" valign="top"><span class="nobold">{_photo}:</span></td>
|
||||
<td>
|
||||
<label class="button" style="">{_browse}
|
||||
<input type="file" id="blob" name="blob" style="display: none;" onchange="filename.innerHTML=blob.files[0].name" />
|
||||
</label>
|
||||
<div id="filename" style="margin-top: 10px;"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="120" valign="top"></td>
|
||||
<td>
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
<input type="submit" class="button" name="submit" value="Загрузить" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<input n:ifset="$_GET['album']" type="hidden" name="album" value="{$_GET['album']}" />
|
||||
</form>
|
||||
<div class="tabs">
|
||||
<div class="tab">
|
||||
<a href="/album{$album->getPrettyId()}/edit">{_edit_album}</a>
|
||||
</div>
|
||||
<div id="activetabs" class="tab">
|
||||
<a id="act_tab_a" href="#">{_add_photos}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="file" accept=".jpg,.png,.gif" name="files[]" multiple class="button" id="uploadButton" style="display:none">
|
||||
|
||||
<div class="container_gray" style="min-height: 344px;">
|
||||
<div class="insertThere"></div>
|
||||
<div class="whiteBox" style="display: block;">
|
||||
<div class="boxContent">
|
||||
<h4>{_uploading_photos_from_computer}</h4>
|
||||
|
||||
<div class="limits" style="margin-top:17px">
|
||||
<b style="color:#45688E">{_admin_limits}</b>
|
||||
<ul class="blueList" style="margin-left: -25px;margin-top: 1px;">
|
||||
<li>{_supported_formats}</li>
|
||||
<li>{_max_load_photos}</li>
|
||||
</ul>
|
||||
|
||||
<div style="text-align: center;padding-top: 4px;" class="insertAgain">
|
||||
<input type="button" class="button" id="fakeButton" onclick="uploadButton.click()" value="{_upload_picts}">
|
||||
</div>
|
||||
|
||||
<div class="tipping" style="margin-top: 19px;">
|
||||
<span style="line-height: 15px"><b>{_tip}</b>: {_tip_ctrl}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="insertPhotos" id="photos" style="margin-top: 9px;padding-bottom: 12px;"></div>
|
||||
|
||||
<input type="button" class="button" style="display:none;margin-left: auto;margin-right: auto;" id="endUploading" value="{_end_uploading}">
|
||||
</div>
|
||||
|
||||
<input n:ifset="$_GET['album']" type="hidden" id="album" value="{$_GET['album']}" />
|
||||
|
||||
<script>
|
||||
uploadButton.value = ''
|
||||
</script>
|
||||
{/block}
|
||||
|
||||
{block bodyScripts}
|
||||
{script "js/al_photos.js"}
|
||||
{/block}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
{elseif $type == "group" || $type == "user"}
|
||||
{include "../components/group.xml", group => $object, isUser => $type == "user"}
|
||||
{elseif $type == "comment"}
|
||||
{include "../components/comment.xml", comment => $object, timeOnly => true}
|
||||
{include "../components/comment.xml", comment => $object, timeOnly => true, linkWithPost => true}
|
||||
{elseif $type == "note"}
|
||||
{include "./content/note.xml", note => $object}
|
||||
{elseif $type == "app"}
|
||||
|
|
|
@ -344,9 +344,9 @@
|
|||
<form method="POST" enctype="multipart/form-data">
|
||||
<div id="backdropEditor">
|
||||
<div id="backdropFilePicker">
|
||||
<label class="button" style="">Обзор<input type="file" accept="image/*" name="backdrop1" style="display: none;"></label>
|
||||
<label class="button" style="">{_browse}<input type="file" accept="image/*" name="backdrop1" style="display: none;"></label>
|
||||
<div id="spacer" style="width: 366px;"></div>
|
||||
<label class="button" style="">Обзор<input type="file" accept="image/*" name="backdrop2" style="display: none;"></label>
|
||||
<label class="button" style="">{_browse}<input type="file" accept="image/*" name="backdrop2" style="display: none;"></label>
|
||||
<div id="spacer" style="width: 366px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -190,13 +190,13 @@
|
|||
|
||||
<script>
|
||||
function viewBackupCodes() {
|
||||
MessageBox("Просмотр резервных кодов", `
|
||||
MessageBox(tr("viewing_backup_codes"), `
|
||||
<form id="back-codes-view-form" method="post" action="/settings/2fa">
|
||||
<label for="password">Пароль</label>
|
||||
<input type="password" id="password" name="password" required />
|
||||
<input type="hidden" name="hash" value={$csrfToken} />
|
||||
</form>
|
||||
`, ["Просмотреть", "Отменить"], [
|
||||
`, [tr("viewing"), tr("cancel")], [
|
||||
() => {
|
||||
document.querySelector("#back-codes-view-form").submit();
|
||||
}, Function.noop
|
||||
|
@ -204,13 +204,13 @@
|
|||
}
|
||||
|
||||
function disableTwoFactorAuth() {
|
||||
MessageBox("Отключить 2FA", `
|
||||
MessageBox(tr("disable_2fa"), `
|
||||
<form id="two-factor-auth-disable-form" method="post" action="/settings/2fa/disable">
|
||||
<label for="password">Пароль</label>
|
||||
<input type="password" id="password" name="password" required />
|
||||
<input type="hidden" name="hash" value={$csrfToken} />
|
||||
</form>
|
||||
`, ["Отключить", "Отменить"], [
|
||||
`, [tr("disable"), tr("cancel")], [
|
||||
() => {
|
||||
document.querySelector("#two-factor-auth-disable-form").submit();
|
||||
}, Function.noop
|
||||
|
@ -650,6 +650,16 @@
|
|||
<td>
|
||||
<span class="nobold">{_my_feed}</span>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td width="120" valign="top" align="right" align="right">
|
||||
<input
|
||||
n:attr="checked => $user->getLeftMenuItemStatus('apps')"
|
||||
type="checkbox"
|
||||
name="menu_aplikoj" />
|
||||
</td>
|
||||
<td>
|
||||
<span class="nobold">{_my_apps}</span>
|
||||
</td>
|
||||
</tr><tr n:if="sizeof(OPENVK_ROOT_CONF['openvk']['preferences']['menu']['links']) > 0">
|
||||
<td width="120" valign="top" align="right" align="right">
|
||||
<input
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
{extends "../@layout.xml"}
|
||||
{block title}Подтвердить номер телефона{/block}
|
||||
{block title}{_verify_phone_number}{/block}
|
||||
|
||||
{block header}
|
||||
Подтвердить номер телефона
|
||||
{_verify_phone_number}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
<center>
|
||||
<p>Мы отправили SMS с кодом на номер <b>{substr_replace($change->number, "*****", 5, 5)}</b>, введите его сюда:</p>
|
||||
<p>{_we_sended_first} <b>{substr_replace($change->number, "*****", 5, 5)}</b>, {_we_sended_end}:</p>
|
||||
|
||||
<form method="POST">
|
||||
<input type="text" name="code" placeholder="34156, например" required />
|
||||
|
|
|
@ -119,10 +119,10 @@
|
|||
{_warn_user_action}
|
||||
</a>
|
||||
<a href="/admin/user{$user->getId()}/bans" class="profile_link">
|
||||
Блокировки
|
||||
{_blocks}
|
||||
</a>
|
||||
<a href="/admin/logs?uid={$user->getId()}" class="profile_link" style="width: 194px;">
|
||||
Последние действия
|
||||
{_last_actions}
|
||||
</a>
|
||||
{/if}
|
||||
|
||||
|
@ -173,20 +173,20 @@
|
|||
<a class="profile_link" style="display:block;width:96%;" href="javascript:reportUser()">{_report}</a>
|
||||
<script>
|
||||
function reportUser() {
|
||||
uReportMsgTxt = "Вы собираетесь пожаловаться на данного пользователя.";
|
||||
uReportMsgTxt += "<br/>Что именно вам кажется недопустимым в этом материале?";
|
||||
uReportMsgTxt += "<br/><br/><b>Причина жалобы</b>: <input type='text' id='uReportMsgInput' placeholder='Причина' />"
|
||||
uReportMsgTxt = tr("going_to_report_user");
|
||||
uReportMsgTxt += "<br/>"+tr("report_question_text");
|
||||
uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"
|
||||
|
||||
MessageBox("Пожаловаться?", uReportMsgTxt, ["Подтвердить", "Отмена"], [
|
||||
MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [
|
||||
(function() {
|
||||
res = document.querySelector("#uReportMsgInput").value;
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", "/report/" + {$user->getId()} + "?reason=" + res + "&type=user", true);
|
||||
xhr.onload = (function() {
|
||||
if(xhr.responseText.indexOf("reason") === -1)
|
||||
MessageBox("Ошибка", "Не удалось подать жалобу...", ["OK"], [Function.noop]);
|
||||
MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
|
||||
else
|
||||
MessageBox("Операция успешна", "Скоро её рассмотрят модераторы", ["OK"], [Function.noop]);
|
||||
MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
|
||||
});
|
||||
xhr.send(null);
|
||||
}),
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
<p>
|
||||
{tr("user_banned", htmlentities($user->getFirstName()))|noescape}<br/>
|
||||
{_user_banned_comment} <b>{$user->getBanReason()}</b>.<br/>
|
||||
Пользователь заблокирован
|
||||
<span n:if="$user->getUnbanTime() !== NULL">до: <b>{$user->getUnbanTime()}</b></span>
|
||||
<span n:if="$user->getUnbanTime() === NULL"><b>навсегда</b></span>
|
||||
{_user_is_blocked}
|
||||
<span n:if="$user->getUnbanTime() !== NULL">{_before}: <b>{$user->getUnbanTime()}</b></span>
|
||||
<span n:if="$user->getUnbanTime() === NULL"><b>{_forever}</b></span>
|
||||
</p>
|
||||
{if isset($thisUser)}
|
||||
<p n:if="$thisUser->getChandlerUser()->can('access')->model('admin')->whichBelongsTo(NULL) || $thisUser->getChandlerUser()->can('write')->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{extends "../@layout.xml"}
|
||||
{block title}Изменить видеозапись{/block}
|
||||
{block title}{_change_video}{/block}
|
||||
|
||||
{block header}
|
||||
<a href="{$thisUser->getURL()}">{$thisUser->getCanonicalName()}</a>
|
||||
|
@ -8,12 +8,12 @@
|
|||
»
|
||||
<a href="/video{$video->getPrettyId()}">{_video}</a>
|
||||
»
|
||||
Изменить видеозапись
|
||||
{_change_video}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
<div class="container_gray">
|
||||
<h4>Изменить видеозапись</h4>
|
||||
<h4>{_change_video}</h4>
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
|
||||
<tbody>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
{else}
|
||||
{var $driver = $video->getVideoDriver()}
|
||||
{if !$driver}
|
||||
Эта видеозапись не поддерживается в вашей версии OpenVK.
|
||||
{_unknown_video}
|
||||
{else}
|
||||
{$driver->getEmbed()|noescape}
|
||||
{/if}
|
||||
|
@ -70,20 +70,20 @@
|
|||
|
||||
<script n:if="$canReport ?? false">
|
||||
function reportVideo() {
|
||||
uReportMsgTxt = "Вы собираетесь пожаловаться на данную видеозапись.";
|
||||
uReportMsgTxt += "<br/>Что именно вам кажется недопустимым в этом материале?";
|
||||
uReportMsgTxt += "<br/><br/><b>Причина жалобы</b>: <input type='text' id='uReportMsgInput' placeholder='Причина' />"
|
||||
uReportMsgTxt = tr("going_to_report_video");
|
||||
uReportMsgTxt += "<br/>"+tr("report_question_text");
|
||||
uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"
|
||||
|
||||
MessageBox("Пожаловаться?", uReportMsgTxt, ["Подтвердить", "Отмена"], [
|
||||
MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [
|
||||
(function() {
|
||||
res = document.querySelector("#uReportMsgInput").value;
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", "/report/" + {$video->getId()} + "?reason=" + res + "&type=video", true);
|
||||
xhr.onload = (function() {
|
||||
if(xhr.responseText.indexOf("reason") === -1)
|
||||
MessageBox("Ошибка", "Не удалось подать жалобу...", ["OK"], [Function.noop]);
|
||||
MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
|
||||
else
|
||||
MessageBox("Операция успешна", "Скоро её рассмотрят модераторы", ["OK"], [Function.noop]);
|
||||
MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
|
||||
});
|
||||
xhr.send(null);
|
||||
}),
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
{/block}
|
||||
|
||||
{block content}
|
||||
{php $GLOBALS["_bigWall"] = 1}
|
||||
|
||||
<div class="tabs">
|
||||
<div n:attr="id => (isset($globalFeed) ? 'ki' : 'activetabs')" class="tab">
|
||||
<a n:attr="id => (isset($globalFeed) ? 'ki' : 'act_tab_a')" href="/feed">{_my_news}</a>
|
||||
|
|
|
@ -48,24 +48,32 @@
|
|||
{/if}
|
||||
|
||||
<a n:if="$canDelete ?? false" class="profile_link" style="display:block;width:96%;" href="/wall{$post->getPrettyId()}/delete">{_delete}</a>
|
||||
<a
|
||||
n:if="isset($thisUser) && $thisUser->getChandlerUser()->can('access')->model('admin')->whichBelongsTo(NULL) AND $post->getEditTime()"
|
||||
style="display:block;width:96%;"
|
||||
class="profile_link"
|
||||
href="/admin/logs?type=1&obj_type=Post&obj_id={$post->getId()}"
|
||||
>
|
||||
{_changes_history}
|
||||
</a>
|
||||
<a n:if="$canReport ?? false" class="profile_link" style="display:block;width:96%;" href="javascript:reportPost()">{_report}</a>
|
||||
</div>
|
||||
<script n:if="$canReport ?? false">
|
||||
function reportPost() {
|
||||
uReportMsgTxt = "Вы собираетесь пожаловаться на данную запись.";
|
||||
uReportMsgTxt += "<br/>Что именно вам кажется недопустимым в этом материале?";
|
||||
uReportMsgTxt += "<br/><br/><b>Причина жалобы</b>: <input type='text' id='uReportMsgInput' placeholder='Причина' />"
|
||||
uReportMsgTxt = tr("going_to_report_post");
|
||||
uReportMsgTxt += "<br/>"+tr("report_question_text");
|
||||
uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"
|
||||
|
||||
MessageBox("Пожаловаться?", uReportMsgTxt, ["Подтвердить", "Отмена"], [
|
||||
MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [
|
||||
(function() {
|
||||
res = document.querySelector("#uReportMsgInput").value;
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", "/report/" + {$post->getId()} + "?reason=" + res + "&type=post", true);
|
||||
xhr.onload = (function() {
|
||||
if(xhr.responseText.indexOf("reason") === -1)
|
||||
MessageBox("Ошибка", "Не удалось подать жалобу...", ["OK"], [Function.noop]);
|
||||
MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
|
||||
else
|
||||
MessageBox("Операция успешна", "Скоро её рассмотрят модераторы", ["OK"], [Function.noop]);
|
||||
MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
|
||||
});
|
||||
xhr.send(null);
|
||||
}),
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
{if $attachment instanceof \openvk\Web\Models\Entities\Photo}
|
||||
{if !$attachment->isDeleted()}
|
||||
<a href="{$attachment->getPageUrl()}">
|
||||
<img class="media" src="{$attachment->getURLBySizeId('normal')}" alt="{$attachment->getDescription()}" />
|
||||
{var $link = "/photo" . ($attachment->isAnonymous() ? ("s/" . base_convert((string) $attachment->getId(), 10, 32)) : $attachment->getPrettyId())}
|
||||
<a href="{$link}" onclick="OpenMiniature(event, {$attachment->getURLBySizeId('normal')}, {$parent->getPrettyId()}, {$attachment->getPrettyId()}, {$parentType})">
|
||||
<img class="media media_makima" src="{$attachment->getURLBySizeId('normal')}" alt="{$attachment->getDescription()}" />
|
||||
</a>
|
||||
{else}
|
||||
<a href="javascript:alert('{_attach_no_longer_available}');">
|
||||
|
|
|
@ -8,37 +8,49 @@
|
|||
<tr>
|
||||
<td width="30" valign="top">
|
||||
<a href="{$author->getURL()}">
|
||||
<img src="{$author->getAvatarURL('miniscule')}" width="30" class="cCompactAvatars" />
|
||||
<img src="{$author->getAvatarURL('miniscule')}" width="30" class="cCompactAvatars post-avatar" />
|
||||
</a>
|
||||
</td>
|
||||
<td width="100%" valign="top">
|
||||
<div class="post-author">
|
||||
<a href="{$author->getURL()}"><b>
|
||||
<a href="{$author->getURL()}"><b class="post-author-name">
|
||||
{$author->getCanonicalName()}
|
||||
</b></a>
|
||||
<img n:if="$author->isVerified()" class="name-checkmark" src="/assets/packages/static/openvk/img/checkmark.png"><br/>
|
||||
</div>
|
||||
<div class="post-content" id="{$comment->getId()}">
|
||||
<div class="text" id="text{$comment->getId()}">
|
||||
{$comment->getText()|noescape}
|
||||
<span data-text="{$comment->getText(false)}" class="really_text">{$comment->getText()|noescape}</span>
|
||||
|
||||
<div n:ifcontent class="attachments_b">
|
||||
<div class="attachment" n:foreach="$comment->getChildren() as $attachment" data-localized-nsfw-text="{_nsfw_warning}">
|
||||
{include "attachment.xml", attachment => $attachment}
|
||||
{var $attachmentsLayout = $comment->getChildrenWithLayout(288)}
|
||||
<div n:ifcontent class="attachments attachments_b" style="height: {$attachmentsLayout->height|noescape}; width: {$attachmentsLayout->width|noescape};">
|
||||
<div class="attachment" n:foreach="$attachmentsLayout->tiles as $attachment" style="float: {$attachment[3]|noescape}; width: {$attachment[0]|noescape}; height: {$attachment[1]|noescape};" data-localized-nsfw-text="{_nsfw_warning}">
|
||||
{include "attachment.xml", attachment => $attachment[2], parent => $comment, parentType => "comment"}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div n:ifcontent class="attachments attachments_m">
|
||||
<div class="attachment" n:foreach="$attachmentsLayout->extras as $attachment">
|
||||
{include "attachment.xml", attachment => $attachment, post => $comment}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div n:if="isset($thisUser) &&! ($compact ?? false)" class="post-menu">
|
||||
<a href="#_comment{$comment->getId()}" class="date">{$comment->getPublicationTime()}</a>
|
||||
<a href="#_comment{$comment->getId()}" class="date">{$comment->getPublicationTime()}
|
||||
<span n:if="$comment->getEditTime()" class="edited editedMark">({_edited_short})</span>
|
||||
</a>
|
||||
{if !$timeOnly}
|
||||
|
|
||||
{if $comment->canBeDeletedBy($thisUser)}
|
||||
<a href="/comment{$comment->getId()}/delete">{_delete}</a> |
|
||||
{/if}
|
||||
{if $comment->canBeEditedBy($thisUser)}
|
||||
<a id="editPost" data-id="{$comment->getId()}">{_edit}</a> |
|
||||
{/if}
|
||||
<a class="comment-reply">{_reply}</a>
|
||||
{if $thisUser->getId() != $comment->getOwner()->getId()}
|
||||
{var $canReport = true}
|
||||
| <a href="javascript:reportComment()">Пожаловаться</a>
|
||||
| <a href="javascript:reportComment()">{_report}</a>
|
||||
{/if}
|
||||
<div style="float: right; font-size: .7rem;">
|
||||
<a class="post-like-button" href="/comment{$comment->getId()}/like?hash={rawurlencode($csrfToken)}">
|
||||
|
@ -82,20 +94,20 @@
|
|||
</table>
|
||||
<script n:if="$canReport ?? false">
|
||||
function reportComment() {
|
||||
uReportMsgTxt = "Вы собираетесь пожаловаться на данный комментарий.";
|
||||
uReportMsgTxt += "<br/>Что именно вам кажется недопустимым в этом материале?";
|
||||
uReportMsgTxt += "<br/><br/><b>Причина жалобы</b>: <input type='text' id='uReportMsgInput' placeholder='Причина' />"
|
||||
uReportMsgTxt = tr("going_to_report_comment");
|
||||
uReportMsgTxt += "<br/>"+tr("report_question_text");
|
||||
uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"
|
||||
|
||||
MessageBox("Пожаловаться?", uReportMsgTxt, ["Подтвердить", "Отмена"], [
|
||||
MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [
|
||||
(function() {
|
||||
res = document.querySelector("#uReportMsgInput").value;
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", "/report/" + {$comment->getId()} + "?reason=" + res + "&type=comment", true);
|
||||
xhr.onload = (function() {
|
||||
if(xhr.responseText.indexOf("reason") === -1)
|
||||
MessageBox("Ошибка", "Не удалось подать жалобу...", ["OK"], [Function.noop]);
|
||||
MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
|
||||
else
|
||||
MessageBox("Операция успешна", "Скоро её рассмотрят модераторы", ["OK"], [Function.noop]);
|
||||
MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
|
||||
});
|
||||
xhr.send(null);
|
||||
}),
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
{var $commentsURL = "/al_comments/create/$model/" . $parent->getId()}
|
||||
{var $club = $parent instanceof \openvk\Web\Models\Entities\Post && $parent->getTargetWall() < 0 ? (new openvk\Web\Models\Repositories\Clubs)->get(abs($parent->getTargetWall())) : $club}
|
||||
{if !$readOnly}
|
||||
{include "textArea.xml", route => $commentsURL, postOpts => false, graffiti => (bool) ovkGetQuirk("comments.allow-graffiti"), club => $club}
|
||||
{include "textArea.xml", route => $commentsURL, postOpts => false, graffiti => (bool) ovkGetQuirk("comments.allow-graffiti"), club => $club, custom_id => $custom_id}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{var $gift = $notification->getModel(0)}
|
||||
{var $sender = $notification->getModel(1)}
|
||||
|
||||
<a href="{$sender->getURL()}"><b>{$sender->getCanonicalName()}</b></a> отправил вам подарок.
|
||||
<a href="{$sender->getURL()}"><b>{$sender->getCanonicalName()}</b></a> {_nt_sent_gift}.
|
||||
<div class="nobold">
|
||||
{$notification->getDateTime()}
|
||||
</div>
|
|
@ -18,13 +18,13 @@
|
|||
<tr>
|
||||
<td width="54" valign="top">
|
||||
<a href="{$author->getURL()}">
|
||||
<img src="{$author->getAvatarURL('miniscule')}" width="{if $compact}25{else}50{/if}" {if $compact}class="cCompactAvatars"{/if} />
|
||||
<img src="{$author->getAvatarURL('miniscule')}" width="{if $compact}25{else}50{/if}" class="post-avatar {if $compact}cCompactAvatars{/if}" />
|
||||
<span n:if="!$post->isPostedOnBehalfOfGroup() && !$compact && $author->isOnline()" class="post-online">{_online}</span>
|
||||
</a>
|
||||
</td>
|
||||
<td width="100%" valign="top">
|
||||
<div class="post-author">
|
||||
<a href="{$author->getURL()}"><b>{$author->getCanonicalName()}</b></a>
|
||||
<a href="{$author->getURL()}"><b class="post-author-name">{$author->getCanonicalName()}</b></a>
|
||||
<img n:if="$author->isVerified()" class="name-checkmark" src="/assets/packages/static/openvk/img/checkmark.png">
|
||||
{$post->isDeactivationMessage() ? ($author->isFemale() ? tr($deac . "_f") : tr($deac . "_m"))}
|
||||
{$post->isUpdateAvatarMessage() && !$post->isPostedOnBehalfOfGroup() ? ($author->isFemale() ? tr("upd_f") : tr("upd_m"))}
|
||||
|
@ -62,20 +62,38 @@
|
|||
<a class="pin" href="/wall{$post->getPrettyId()}/pin?act=pin&hash={rawurlencode($csrfToken)}"></a>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{if $post->canBeEditedBy($thisUser) && !($forceNoEditLink ?? false) && $compact == false}
|
||||
<a class="edit" id="editPost"
|
||||
data-id="{$post->getId()}"
|
||||
data-nsfw="{(int)$post->isExplicit()}"
|
||||
{if $post->getTargetWall() < 0 && $post->getWallOwner()->canBeModifiedBy($thisUser)}data-fromgroup="{(int)$post->isPostedOnBehalfOfGroup()}"{/if}></a>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="post-content" id="{$post->getPrettyId()}">
|
||||
<div class="text" id="text{$post->getPrettyId()}">
|
||||
<span class="really_text">{$post->getText()|noescape}</span>
|
||||
<span data-text="{$post->getText(false)}" class="really_text">{$post->getText()|noescape}</span>
|
||||
|
||||
<div n:ifcontent class="attachments_b">
|
||||
<div class="attachment" n:foreach="$post->getChildren() as $attachment" data-localized-nsfw-text="{_nsfw_warning}">
|
||||
{include "../attachment.xml", attachment => $attachment}
|
||||
{var $width = ($GLOBALS["_bigWall"] ?? false) ? 550 : 320}
|
||||
{if isset($GLOBALS["_nesAttGloCou"])}
|
||||
{var $width = $width - 70 * $GLOBALS["_nesAttGloCou"]}
|
||||
{/if}
|
||||
{var $attachmentsLayout = $post->getChildrenWithLayout($width)}
|
||||
<div n:ifcontent class="attachments attachments_b" style="height: {$attachmentsLayout->height|noescape}; width: {$attachmentsLayout->width|noescape};">
|
||||
<div class="attachment" n:foreach="$attachmentsLayout->tiles as $attachment" style="float: {$attachment[3]|noescape}; width: {$attachment[0]|noescape}; height: {$attachment[1]|noescape};" data-localized-nsfw-text="{_nsfw_warning}">
|
||||
{include "../attachment.xml", attachment => $attachment[2], parent => $post, parentType => "post"}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div n:ifcontent class="attachments attachments_m">
|
||||
<div class="attachment" n:foreach="$attachmentsLayout->extras as $attachment">
|
||||
{include "../attachment.xml", attachment => $attachment, post => $post}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div n:if="$post->isAd()" style="color:grey;">
|
||||
<br/>
|
||||
! Этот пост был размещён за взятку.
|
||||
! {_post_is_ad}
|
||||
</div>
|
||||
<div n:if="$post->isSigned()" class="post-signature">
|
||||
{var $actualAuthor = $post->getOwner(false)}
|
||||
|
@ -88,13 +106,16 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="post-menu" n:if="$compact == false">
|
||||
<a href="{if !$suggestion}/wall{$post->getPrettyId()}{else}javascript:void(0){/if}" class="date">{$post->getPublicationTime()}</a>
|
||||
<a href="{if !$suggestion}/wall{$post->getPrettyId()}{else}javascript:void(0){/if}" class="date">{$post->getPublicationTime()}
|
||||
<span n:if="$post->getEditTime()" class="edited editedMark">({_edited_short})</span>
|
||||
</a>
|
||||
|
||||
<a n:if="!empty($platform)" class="client_app" data-app-tag="{$platform}" data-app-name="{$platformDetails['name']}" data-app-url="{$platformDetails['url']}" data-app-img="{$platformDetails['img']}">
|
||||
<img src="/assets/packages/static/openvk/img/app_icons_mini/{$post->getPlatform(this)}.svg">
|
||||
</a>
|
||||
{if isset($thisUser)}
|
||||
|
||||
|
||||
|
||||
<a n:if="!($forceNoCommentsLink ?? false) && $commentsCount == 0" href="javascript:expand_comment_textarea({$commentTextAreaId})">{_comment}</a>
|
||||
|
||||
<div class="like_wrap">
|
||||
|
|
|
@ -7,18 +7,20 @@
|
|||
{var $deac = "post_deact_silent"}
|
||||
{/if}
|
||||
|
||||
|
||||
|
||||
<table border="0" style="font-size: 11px;" n:class="post, $post->isExplicit() ? post-nsfw">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="54" valign="top">
|
||||
<a href="{$author->getURL()}">
|
||||
<img src="{$author->getAvatarURL('miniscule')}" width="50" />
|
||||
<img src="{$author->getAvatarURL('miniscule')}" class="post-avatar" width="50" />
|
||||
<span n:if="!$post->isPostedOnBehalfOfGroup() && !($compact ?? false) && $author->isOnline()" class="post-online">{_online}</span>
|
||||
</a>
|
||||
</td>
|
||||
<td width="100%" valign="top">
|
||||
<div class="post-author">
|
||||
<a href="{$author->getURL()}"><b>{$author->getCanonicalName()}</b></a>
|
||||
<a href="{$author->getURL()}"><b class="post-author-name">{$author->getCanonicalName()}</b></a>
|
||||
<img n:if="$author->isVerified()" class="name-checkmark" src="/assets/packages/static/openvk/img/checkmark.png">
|
||||
{if $post->isDeactivationMessage()}
|
||||
{$author->isFemale() ? tr($deac . "_f") : tr($deac . "_m")}
|
||||
|
@ -51,7 +53,8 @@
|
|||
{/if}
|
||||
<br/>
|
||||
<a href="{if !$suggestion}/wall{$post->getPrettyId()}{else}javascript:void(0){/if}" class="date">
|
||||
{$post->getPublicationTime()}{if $post->isPinned()}, {_pinned}{/if}
|
||||
{$post->getPublicationTime()} <span n:if="$post->getEditTime()" class="editedMark">({_edited_short})</span>{if $post->isPinned()}, {_pinned}{/if}
|
||||
|
||||
<a n:if="!empty($platform)" class="client_app" data-app-tag="{$platform}" data-app-name="{$platformDetails['name']}" data-app-url="{$platformDetails['url']}" data-app-img="{$platformDetails['img']}">
|
||||
<img src="/assets/packages/static/openvk/img/app_icons_mini/{$post->getPlatform(this)}.svg">
|
||||
</a>
|
||||
|
@ -59,10 +62,23 @@
|
|||
</div>
|
||||
<div class="post-content" id="{$post->getPrettyId()}">
|
||||
<div class="text" id="text{$post->getPrettyId()}">
|
||||
<span class="really_text">{$post->getText()|noescape}</span>
|
||||
|
||||
<div n:ifcontent class="attachments_b">
|
||||
<div class="attachment" n:foreach="$post->getChildren() as $attachment" data-localized-nsfw-text="{_nsfw_warning}">
|
||||
{var $owner = $author->getId()}
|
||||
|
||||
<span data-text="{$post->getText(false)}" class="really_text">{$post->getText()|noescape}</span>
|
||||
|
||||
{var $width = ($GLOBALS["_bigWall"] ?? false) ? 550 : 320}
|
||||
{if isset($GLOBALS["_nesAttGloCou"])}
|
||||
{var $width = $width - 70 * $GLOBALS["_nesAttGloCou"]}
|
||||
{/if}
|
||||
{var $attachmentsLayout = $post->getChildrenWithLayout($width)}
|
||||
<div n:ifcontent class="attachments attachments_b" style="height: {$attachmentsLayout->height|noescape}; width: {$attachmentsLayout->width|noescape};">
|
||||
<div class="attachment" n:foreach="$attachmentsLayout->tiles as $attachment" style="float: {$attachment[3]|noescape}; width: {$attachment[0]|noescape}; height: {$attachment[1]|noescape};" data-localized-nsfw-text="{_nsfw_warning}">
|
||||
{include "../attachment.xml", attachment => $attachment[2], parent => $post, parentType => "post"}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div n:ifcontent class="attachments attachments_m">
|
||||
<div class="attachment" n:foreach="$attachmentsLayout->extras as $attachment">
|
||||
{include "../attachment.xml", attachment => $attachment}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -73,7 +89,7 @@
|
|||
</div>
|
||||
<div n:if="$post->isAd()" style="color:grey;">
|
||||
<br/>
|
||||
! Этот пост был размещён за взятку.
|
||||
! {_post_is_ad}
|
||||
</div>
|
||||
<div n:if="$post->isSigned()" class="post-signature">
|
||||
{var $actualAuthor = $post->getOwner(false)}
|
||||
|
@ -91,6 +107,13 @@
|
|||
{var $forceNoPinLink = true}
|
||||
{/if}
|
||||
|
||||
{if !($forceNoEditLink ?? false) && $post->canBeEditedBy($thisUser)}
|
||||
<a id="editPost"
|
||||
data-id="{$post->getId()}"
|
||||
data-nsfw="{(int)$post->isExplicit()}"
|
||||
{if $post->getTargetWall() < 0 && $post->getWallOwner()->canBeModifiedBy($thisUser)}data-fromgroup="{(int)$post->isPostedOnBehalfOfGroup()}"{/if}>{_edit}</a> |
|
||||
{/if}
|
||||
|
||||
{if !($forceNoDeleteLink ?? false) && $post->canBeDeletedBy($thisUser)}
|
||||
<a href="/wall{$post->getPrettyId()}/delete">{_delete}</a> |
|
||||
{/if}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{php if(!isset($GLOBALS["textAreaCtr"])) $GLOBALS["textAreaCtr"] = 10;}
|
||||
{var $textAreaId = ($post ?? NULL) === NULL ? (++$GLOBALS["textAreaCtr"]) : $post->getId()}
|
||||
{var $textAreaId = ($custom_id ?? NULL) === NULL ? $textAreaId : $custom_id}
|
||||
|
||||
<div id="write" style="padding: 5px 0;" onfocusin="expand_wall_textarea({$textAreaId});">
|
||||
<form action="{$route}" method="post" enctype="multipart/form-data" style="margin:0;">
|
||||
|
@ -8,8 +9,11 @@
|
|||
<!-- padding to fix <br/> bug -->
|
||||
</div>
|
||||
<div id="post-buttons{$textAreaId}" style="display: none;">
|
||||
<div class="upload">
|
||||
|
||||
</div>
|
||||
<div class="post-upload">
|
||||
{_attachment}: <span>(unknown)</span>
|
||||
<span style="color: inherit;"></span>
|
||||
</div>
|
||||
<div class="post-has-poll">
|
||||
{_poll}
|
||||
|
@ -17,6 +21,8 @@
|
|||
<div class="post-has-note">
|
||||
|
||||
</div>
|
||||
<div class="post-has-videos"></div>
|
||||
|
||||
<div n:if="$postOpts ?? true" class="post-opts">
|
||||
{var $anonEnabled = OPENVK_ROOT_CONF['openvk']['preferences']['wall']['anonymousPosting']['enable']}
|
||||
{if !is_null($thisUser) && !is_null($club ?? NULL) && $owner < 0}
|
||||
|
@ -54,8 +60,8 @@
|
|||
<input type="checkbox" name="as_group" /> {_comment_as_group}
|
||||
</label>
|
||||
</div>
|
||||
<input type="file" class="postFileSel" id="postFilePic" name="_pic_attachment" accept="image/*" style="display:none;" />
|
||||
<input n:if="!OPENVK_ROOT_CONF['openvk']['preferences']['videos']['disableUploading']" type="file" class="postFileSel" id="postFileVid" name="_vid_attachment" accept="video/*" style="display:none;" />
|
||||
<input type="hidden" name="photos" value="" />
|
||||
<input type="hidden" name="videos" value="" />
|
||||
<input type="hidden" name="poll" value="none" />
|
||||
<input type="hidden" id="note" name="note" value="none" />
|
||||
<input type="hidden" name="type" value="1" />
|
||||
|
@ -71,11 +77,11 @@
|
|||
<a class="header" href="javascript:toggleMenu({$textAreaId});">
|
||||
{_attach}
|
||||
</a>
|
||||
<a href="javascript:void(document.querySelector('#post-buttons{$textAreaId} input[name=_pic_attachment]').click());">
|
||||
<a id="photosAttachments" {if !is_null($club ?? NULL) && $club->canBeModifiedBy($thisUser)}data-club="{$club->getId()}"{/if}>
|
||||
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/application-x-egon.png" />
|
||||
{_photo}
|
||||
</a>
|
||||
<a n:if="!OPENVK_ROOT_CONF['openvk']['preferences']['videos']['disableUploading']" href="javascript:void(document.querySelector('#post-buttons{$textAreaId} input[name=_vid_attachment]').click());">
|
||||
<a id="videoAttachment">
|
||||
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/application-vnd.rn-realmedia.png" />
|
||||
{_video}
|
||||
</a>
|
||||
|
@ -99,12 +105,11 @@
|
|||
|
||||
<script>
|
||||
$(document).ready(() => {
|
||||
u("#post-buttons{$textAreaId} .postFileSel").on("change", function() {
|
||||
handleUpload.bind(this, {$textAreaId})();
|
||||
});
|
||||
|
||||
setupWallPostInputHandlers({$textAreaId});
|
||||
});
|
||||
|
||||
u("#post-buttons{$textAreaId} input[name='videos']")["nodes"].at(0).value = ""
|
||||
u("#post-buttons{$textAreaId} input[name='photos']")["nodes"].at(0).value = ""
|
||||
</script>
|
||||
|
||||
{if $graffiti}
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
<td valign="top">
|
||||
<div class="video-preview">
|
||||
<a href="/video{$video->getPrettyId()}">
|
||||
<img src="{$video->getThumbnailURL()}"
|
||||
style="max-width: 170px; max-height: 127px; margin: auto;" >
|
||||
<div class="video-preview">
|
||||
<img src="{$video->getThumbnailURL()}"
|
||||
style="max-width: 170px; max-height: 127px; margin: auto;" >
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
|
@ -33,5 +35,5 @@
|
|||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table
|
||||
</table>
|
||||
{/block}
|
||||
|
|
305
Web/Util/Makima/Makima.php
Normal file
305
Web/Util/Makima/Makima.php
Normal file
|
@ -0,0 +1,305 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Util\Makima;
|
||||
use openvk\Web\Models\Entities\Photo;
|
||||
|
||||
class Makima
|
||||
{
|
||||
private $photos;
|
||||
|
||||
const ORIENT_WIDE = 0;
|
||||
const ORIENT_REGULAR = 1;
|
||||
const ORIENT_SLIM = 2;
|
||||
|
||||
function __construct(array $photos)
|
||||
{
|
||||
if(sizeof($photos) < 2)
|
||||
throw new \LogicException("Minimum attachment count for tiled layout is 2");
|
||||
|
||||
$this->photos = $photos;
|
||||
}
|
||||
|
||||
private function getOrientation(Photo $photo, &$ratio): int
|
||||
{
|
||||
[$width, $height] = $photo->getDimensions();
|
||||
$ratio = $width / $height;
|
||||
if($ratio >= 1.2)
|
||||
return Makima::ORIENT_WIDE;
|
||||
else if($ratio >= 0.8)
|
||||
return Makima::ORIENT_REGULAR;
|
||||
else
|
||||
return Makima::ORIENT_SLIM;
|
||||
}
|
||||
|
||||
private function calculateMultiThumbsHeight(array $ratios, float $w, float $m): float
|
||||
{
|
||||
return ($w - (sizeof($ratios) - 1) * $m) / array_sum($ratios);
|
||||
}
|
||||
|
||||
private function extractSubArr(array $arr, int $from, int $to): array
|
||||
{
|
||||
return array_slice($arr, $from, sizeof($arr) - $from - (sizeof($arr) - $to));
|
||||
}
|
||||
|
||||
function computeMasonryLayout(float $maxWidth, float $maxHeight): MasonryLayout
|
||||
{
|
||||
$orients = [];
|
||||
$ratios = [];
|
||||
$count = sizeof($this->photos);
|
||||
$result = new MasonryLayout;
|
||||
|
||||
foreach($this->photos as $photo) {
|
||||
$orients[] = $this->getOrientation($photo, $ratio);
|
||||
$ratios[] = $ratio;
|
||||
}
|
||||
|
||||
$avgRatio = array_sum($ratios) / sizeof($ratios);
|
||||
if($maxWidth < 0)
|
||||
$maxWidth = $maxHeight = 510;
|
||||
|
||||
$maxRatio = $maxWidth / $maxHeight;
|
||||
$marginWidth = $marginHeight = 2;
|
||||
|
||||
switch($count) {
|
||||
case 2:
|
||||
if(
|
||||
$orients == [Makima::ORIENT_WIDE, Makima::ORIENT_WIDE] # two wide pics
|
||||
&& $avgRatio > (1.4 * $maxRatio) && abs($ratios[0] - $ratios[1]) < 0.2 # that can be positioned on top of each other
|
||||
) {
|
||||
$computedHeight = ceil( min( $maxWidth / $ratios[0], min( $maxWidth / $ratios[1], ($maxHeight - $marginHeight) / 2 ) ) );
|
||||
|
||||
$result->colSizes = [1];
|
||||
$result->rowSizes = [1, 1];
|
||||
$result->width = ceil($maxWidth);
|
||||
$result->height = $computedHeight;
|
||||
$result->tiles = [new ThumbTile(1, 1, $maxWidth, $computedHeight), new ThumbTile(1, 1, $maxWidth, $computedHeight)];
|
||||
} else if(
|
||||
$orients == [Makima::ORIENT_WIDE, Makima::ORIENT_WIDE]
|
||||
|| $orients == [Makima::ORIENT_REGULAR, Makima::ORIENT_REGULAR] # two normal pics of same ratio
|
||||
) {
|
||||
$computedWidth = ($maxWidth - $marginWidth) / 2;
|
||||
$height = min( $computedWidth / $ratios[0], min( $computedWidth / $ratios[1], $maxHeight ) );
|
||||
|
||||
$result->colSizes = [1, 1];
|
||||
$result->rowSizes = [1];
|
||||
$result->width = ceil($maxWidth);
|
||||
$result->height = ceil($height);
|
||||
$result->tiles = [new ThumbTile(1, 1, $computedWidth, $height), new ThumbTile(1, 1, $computedWidth, $height)];
|
||||
} else /* next to each other, different ratios */ {
|
||||
$w0 = (
|
||||
($maxWidth - $marginWidth) / $ratios[1] / ( (1 / $ratios[0]) + (1 / $ratios[1]) )
|
||||
);
|
||||
$w1 = $maxWidth - $w0 - $marginWidth;
|
||||
$h = min($maxHeight, min($w0 / $ratios[0], $w1 / $ratios[1]));
|
||||
|
||||
$result->colSizes = [ceil($w0), ceil($w1)];
|
||||
$result->rowSizes = [1];
|
||||
$result->width = ceil($w0 + $w1 + $marginWidth);
|
||||
$result->height = ceil($h);
|
||||
$result->tiles = [new ThumbTile(1, 1, $w0, $h), new ThumbTile(1, 1, $w1, $h)];
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
# Three wide photos, we will put two of them below and one on top
|
||||
if($orients == [Makima::ORIENT_WIDE, Makima::ORIENT_WIDE, Makima::ORIENT_WIDE]) {
|
||||
$hCover = min($maxWidth / $ratios[0], ($maxHeight - $marginHeight) * (2 / 3));
|
||||
$w2 = ($maxWidth - $marginWidth) / 2;
|
||||
$h = min($maxHeight - $hCover - $marginHeight, min($w2 / $ratios[1], $w2 / $ratios[2]));
|
||||
|
||||
$result->colSizes = [1, 1];
|
||||
$result->rowSizes = [ceil($hCover), ceil($h)];
|
||||
$result->width = ceil($maxWidth);
|
||||
$result->height = ceil($marginHeight + $hCover + $h);
|
||||
$result->tiles = [
|
||||
new ThumbTile(2, 1, $maxWidth, $hCover),
|
||||
new ThumbTile(1, 1, $w2, $h), new ThumbTile(1, 1, $w2, $h),
|
||||
];
|
||||
} else /* Photos have different sizes or are not wide, so we will put one to left and two to the right */ {
|
||||
$wCover = min($maxHeight * $ratios[0], ($maxWidth - $marginWidth) * (3 / 4));
|
||||
$h1 = ($ratios[1] * ($maxHeight - $marginHeight) / ($ratios[2] + $ratios[1]));
|
||||
$h0 = $maxHeight - $marginHeight - $h1;
|
||||
$w = min($maxWidth - $marginWidth - $wCover, min($h1 * $ratios[2], $h0 * $ratios[1]));
|
||||
|
||||
$result->colSizes = [ceil($wCover), ceil($w)];
|
||||
$result->rowSizes = [ceil($h0), ceil($h1)];
|
||||
$result->width = ceil($w + $wCover + $marginWidth);
|
||||
$result->height = ceil($maxHeight);
|
||||
$result->tiles = [
|
||||
new ThumbTile(1, 2, $wCover, $maxHeight), new ThumbTile(1, 1, $w, $h0),
|
||||
new ThumbTile(1, 1, $w, $h1),
|
||||
];
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
# Four wide photos, we will put one to the top and rest below
|
||||
if($orients == [Makima::ORIENT_WIDE, Makima::ORIENT_WIDE, Makima::ORIENT_WIDE, Makima::ORIENT_WIDE]) {
|
||||
$hCover = min($maxWidth / $ratios[0], ($maxHeight - $marginHeight) / (2 / 3));
|
||||
$h = ($maxWidth - 2 * $marginWidth) / (array_sum($ratios) - $ratios[0]);
|
||||
$w0 = $h * $ratios[1];
|
||||
$w1 = $h * $ratios[2];
|
||||
$w2 = $h * $ratios[3];
|
||||
$h = min($maxHeight - $marginHeight - $hCover, $h);
|
||||
|
||||
$result->colSizes = [ceil($w0), ceil($w1), ceil($w2)];
|
||||
$result->rowSizes = [ceil($hCover), ceil($h)];
|
||||
$result->width = ceil($maxWidth);
|
||||
$result->height = ceil($hCover + $marginHeight + $h);
|
||||
$result->tiles = [
|
||||
new ThumbTile(3, 1, $maxWidth, $hCover),
|
||||
new ThumbTile(1, 1, $w0, $h), new ThumbTile(1, 1, $w1, $h), new ThumbTile(1, 1, $w2, $h),
|
||||
];
|
||||
} else /* Four photos, we will put one to the left and rest to the right */ {
|
||||
$wCover = min($maxHeight * $ratios[0], ($maxWidth - $marginWidth) * (2 / 3));
|
||||
$w = ($maxHeight - 2 * $marginHeight) / (1 / $ratios[1] + 1 / $ratios[2] + 1 / $ratios[3]);
|
||||
$h0 = $w / $ratios[1];
|
||||
$h1 = $w / $ratios[2];
|
||||
$h2 = $w / $ratios[3] + $marginHeight;
|
||||
$w = min($w, $maxWidth - $marginWidth - $wCover);
|
||||
|
||||
$result->colSizes = [ceil($wCover), ceil($w)];
|
||||
$result->rowSizes = [ceil($h0), ceil($h1), ceil($h2)];
|
||||
$result->width = ceil($wCover + $marginWidth + $w);
|
||||
$result->height = ceil($maxHeight);
|
||||
$result->tiles = [
|
||||
new ThumbTile(1, 3, $wCover, $maxHeight), new ThumbTile(1, 1, $w, $h0),
|
||||
new ThumbTile(1, 1, $w, $h1),
|
||||
new ThumbTile(1, 1, $w, $h1),
|
||||
];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// как лопать пузырики
|
||||
$ratiosCropped = [];
|
||||
if($avgRatio > 1.1) {
|
||||
foreach($ratios as $ratio)
|
||||
$ratiosCropped[] = max($ratio, 1.0);
|
||||
} else {
|
||||
foreach($ratios as $ratio)
|
||||
$ratiosCropped[] = min($ratio, 1.0);
|
||||
}
|
||||
|
||||
$tries = [];
|
||||
|
||||
$firstLine;
|
||||
$secondLine;
|
||||
$thirdLine;
|
||||
|
||||
# Try one line:
|
||||
$tries[$firstLine = $count] = [$this->calculateMultiThumbsHeight($ratiosCropped, $maxWidth, $marginWidth)];
|
||||
|
||||
# Try two lines:
|
||||
for($firstLine = 1; $firstLine < ($count - 1); $firstLine++) {
|
||||
$secondLine = $count - $firstLine;
|
||||
$key = "$firstLine&$secondLine";
|
||||
$tries[$key] = [
|
||||
$this->calculateMultiThumbsHeight(array_slice($ratiosCropped, 0, $firstLine), $maxWidth, $marginWidth),
|
||||
$this->calculateMultiThumbsHeight(array_slice($ratiosCropped, $firstLine), $maxWidth, $marginWidth),
|
||||
];
|
||||
}
|
||||
|
||||
# Try three lines:
|
||||
for($firstLine = 1; $firstLine < ($count - 2); $firstLine++) {
|
||||
for($secondLine = 1; $secondLine < ($count - $firstLine - 1); $secondLine++) {
|
||||
$thirdLine = $count - $firstLine - $secondLine;
|
||||
$key = "$firstLine&$secondLine&$thirdLine";
|
||||
$tries[$key] = [
|
||||
$this->calculateMultiThumbsHeight(array_slice($ratiosCropped, 0, $firstLine), $maxWidth, $marginWidth),
|
||||
$this->calculateMultiThumbsHeight($this->extractSubArr($ratiosCropped, $firstLine, $firstLine + $secondLine), $maxWidth, $marginWidth),
|
||||
$this->calculateMultiThumbsHeight($this->extractSubArr($ratiosCropped, $firstLine + $secondLine, sizeof($ratiosCropped)), $maxWidth, $marginWidth),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
# Now let's find the most optimal configuration:
|
||||
$optimalConfiguration = $optimalDifference = NULL;
|
||||
foreach($tries as $config => $heights) {
|
||||
$config = explode('&', (string) $config); # да да стринговые ключи пхп даже со стриктайпами автокастует к инту (см. 187)
|
||||
$confH = $marginHeight * (sizeof($heights) - 1);
|
||||
foreach($heights as $h)
|
||||
$confH += $h;
|
||||
|
||||
$confDiff = abs($confH - $maxHeight);
|
||||
if(sizeof($config) > 1)
|
||||
if($config[0] > $config[1] || sizeof($config) >= 2 && $config[1] > $config[2])
|
||||
$confDiff *= 1.1;
|
||||
|
||||
if(!$optimalConfiguration || $confDigff < $optimalDifference) {
|
||||
$optimalConfiguration = $config;
|
||||
$optimalDifference = $confDiff;
|
||||
}
|
||||
}
|
||||
|
||||
$thumbsRemain = $this->photos;
|
||||
$ratiosRemain = $ratiosCropped;
|
||||
$optHeights = $tries[implode('&', $optimalConfiguration)];
|
||||
$k = 0;
|
||||
|
||||
$result->width = ceil($maxWidth);
|
||||
$result->rowSizes = [sizeof($optHeights)];
|
||||
$result->tiles = [];
|
||||
|
||||
$totalHeight = 0.0;
|
||||
$gridLineOffsets = [];
|
||||
$rowTiles = []; // vector<vector<ThumbTile>>
|
||||
|
||||
for($i = 0; $i < sizeof($optimalConfiguration); $i++) {
|
||||
$lineChunksNum = $optimalConfiguration[$i];
|
||||
$lineThumbs = [];
|
||||
for($j = 0; $j < $lineChunksNum; $j++)
|
||||
$lineThumbs[] = array_shift($thumbsRemain);
|
||||
|
||||
$lineHeight = $optHeights[$i];
|
||||
$totalHeight += $lineHeight;
|
||||
|
||||
$result->rowSizes[$i] = ceil($lineHeight);
|
||||
|
||||
$totalWidth = 0;
|
||||
$row = [];
|
||||
for($j = 0; $j < sizeof($lineThumbs); $j++) {
|
||||
$thumbRatio = array_shift($ratiosRemain);
|
||||
if($j == sizeof($lineThumbs) - 1)
|
||||
$w = $maxWidth - $totalWidth;
|
||||
else
|
||||
$w = $thumbRatio * $lineHeight;
|
||||
|
||||
$totalWidth += ceil($w);
|
||||
if($j < (sizeof($lineThumbs) - 1) && !in_array($totalWidth, $gridLineOffsets))
|
||||
$gridLineOffsets[] = $totalWidth;
|
||||
|
||||
$tile = new ThumbTile(1, 1, $w, $lineHeight);
|
||||
$result->tiles[$k++] = $row[] = $tile;
|
||||
}
|
||||
|
||||
$rowTiles[] = $row;
|
||||
}
|
||||
|
||||
sort($gridLineOffsets, SORT_NUMERIC);
|
||||
$gridLineOffsets[] = $maxWidth;
|
||||
|
||||
$result->colSizes = [$gridLineOffsets[0]];
|
||||
for($i = sizeof($gridLineOffsets) - 1; $i > 0; $i--)
|
||||
$result->colSizes[$i] = $gridLineOffsets[$i] - $gridLineOffsets[$i - 1];
|
||||
|
||||
foreach($rowTiles as $row) {
|
||||
$columnOffset = 0;
|
||||
foreach($row as $tile) {
|
||||
$startColumn = $columnOffset;
|
||||
$width = 0;
|
||||
$tile->colSpan = 0;
|
||||
for($i = $startColumn; $i < sizeof($result->colSizes); $i++) {
|
||||
$width += $result->colSizes[$i];
|
||||
$tile->colSpan++;
|
||||
if($width == $tile->width)
|
||||
break;
|
||||
}
|
||||
|
||||
$columnOffset += $tile->colSpan;
|
||||
}
|
||||
}
|
||||
|
||||
$result->height = ceil($totalHeight + $marginHeight * (sizeof($optHeights) - 1));
|
||||
break;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
10
Web/Util/Makima/MasonryLayout.php
Normal file
10
Web/Util/Makima/MasonryLayout.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Util\Makima;
|
||||
|
||||
class MasonryLayout {
|
||||
public $colSizes;
|
||||
public $rowSizes;
|
||||
public $tiles;
|
||||
public $width;
|
||||
public $height;
|
||||
}
|
14
Web/Util/Makima/ThumbTile.php
Normal file
14
Web/Util/Makima/ThumbTile.php
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Util\Makima;
|
||||
|
||||
class ThumbTile {
|
||||
public $width;
|
||||
public $height;
|
||||
public $rowSpan;
|
||||
public $colSpan;
|
||||
|
||||
function __construct(int $rs, int $cs, float $w, float $h)
|
||||
{
|
||||
[$this->width, $this->height, $this->rowSpan, $this->colSpan] = [ceil($w), ceil($h), $rs, $cs];
|
||||
}
|
||||
}
|
|
@ -129,6 +129,8 @@ routes:
|
|||
handler: "Wall->rss"
|
||||
- url: "/wall{num}/makePost"
|
||||
handler: "Wall->makePost"
|
||||
- url: "/wall/edit"
|
||||
handler: "Wall->edit"
|
||||
- url: "/wall{num}_{num}"
|
||||
handler: "Wall->post"
|
||||
- url: "/wall{num}_{num}/like"
|
||||
|
@ -371,6 +373,8 @@ routes:
|
|||
handler: "About->humansTxt"
|
||||
- url: "/dev"
|
||||
handler: "About->dev"
|
||||
- url: "/iapi/getPhotosFromPost/{num}_{num}"
|
||||
handler: "InternalAPI->getPhotosFromPost"
|
||||
- url: "/tour"
|
||||
handler: "About->tour"
|
||||
- url: "/{?shortCode}"
|
||||
|
|
|
@ -29,6 +29,10 @@ a {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.linkunderline:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
@ -740,10 +744,14 @@ h4 {
|
|||
line-height: 130%;
|
||||
}
|
||||
|
||||
.post-content .attachments_b {
|
||||
.post-content .attachments:first-of-type {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.post-content .attachments_m .attachment {
|
||||
width: 98%;
|
||||
}
|
||||
|
||||
.attachment .post {
|
||||
width: 102%;
|
||||
}
|
||||
|
@ -753,6 +761,12 @@ h4 {
|
|||
image-rendering: -webkit-optimize-contrast;
|
||||
}
|
||||
|
||||
.post-content .media_makima {
|
||||
width: calc(100% - 4px);
|
||||
height: calc(100% - 4px);
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.post-signature {
|
||||
margin: 4px;
|
||||
margin-bottom: 2px;
|
||||
|
@ -1462,6 +1476,12 @@ body.scrolled .toTop:hover {
|
|||
display: none;
|
||||
}
|
||||
|
||||
.post-has-videos {
|
||||
margin-top: 11px;
|
||||
margin-left: 3px;
|
||||
color: #3c3c3c;
|
||||
}
|
||||
|
||||
.post-upload::before, .post-has-poll::before, .post-has-note::before {
|
||||
content: " ";
|
||||
width: 8px;
|
||||
|
@ -1473,6 +1493,28 @@ body.scrolled .toTop:hover {
|
|||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.post-has-video {
|
||||
padding-bottom: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.post-has-video:hover span {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.post-has-video::before {
|
||||
content: " ";
|
||||
width: 14px;
|
||||
height: 15px;
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
background-image: url("/assets/packages/static/openvk/img/video.png");
|
||||
background-repeat: no-repeat;
|
||||
margin: 3px;
|
||||
margin-left: 2px;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
.post-opts {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
@ -2247,6 +2289,124 @@ a.poll-retract-vote {
|
|||
border-radius: 1px;
|
||||
}
|
||||
|
||||
.progress {
|
||||
border: 1px solid #eee;
|
||||
height: 15px;
|
||||
background: linear-gradient(to bottom, #fefefe, #fafafa);
|
||||
}
|
||||
|
||||
.progress .progress-bar {
|
||||
background: url('progress.png');
|
||||
background-repeat: repeat-x;
|
||||
height: 15px;
|
||||
animation-name: progress;
|
||||
animation-duration: 1s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
@keyframes progress {
|
||||
from {
|
||||
background-position: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
background-position: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.upload {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.upload .upload-item {
|
||||
width: 75px;
|
||||
height: 60px;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.upload-item .upload-delete {
|
||||
position: absolute;
|
||||
background: rgba(0,0,0,0.5);
|
||||
padding: 2px 5px;
|
||||
text-decoration: none;
|
||||
color: #fff;
|
||||
font-size: 11px;
|
||||
margin-left: 57px; /* мне лень переделывать :DDDD */
|
||||
opacity: 0;
|
||||
transition: 0.25s;
|
||||
}
|
||||
|
||||
.upload-item:hover > .upload-delete {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.upload-item img {
|
||||
width: 100%;
|
||||
max-height: 60px;
|
||||
object-fit: cover;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
/* https://imgur.com/a/ihB3JZ4 */
|
||||
|
||||
.ovk-photo-view-dimmer {
|
||||
position: fixed;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
overflow: auto;
|
||||
padding-bottom: 20px;
|
||||
z-index: 300;
|
||||
}
|
||||
|
||||
.ovk-photo-view {
|
||||
position: relative;
|
||||
z-index: 999;
|
||||
background: #fff;
|
||||
width: 610px;
|
||||
padding: 20px;
|
||||
padding-top: 15px;
|
||||
padding-bottom: 10px;
|
||||
box-shadow: 0px 0px 3px 1px #222;
|
||||
margin: 15px auto 0 auto;
|
||||
}
|
||||
|
||||
.ovk-photo-details {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.photo_com_title {
|
||||
font-weight: bold;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.photo_com_title div {
|
||||
float: right;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.ovk-photo-slide-left {
|
||||
left: 0;
|
||||
width: 35%;
|
||||
height: 100%;
|
||||
max-height: 60vh;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ovk-photo-slide-right {
|
||||
right: 0;
|
||||
width: 35%;
|
||||
height: 100%;
|
||||
max-height: 60vh;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.client_app > img {
|
||||
top: 3px;
|
||||
position: relative;
|
||||
|
@ -2750,3 +2910,119 @@ body.article .floating_sidebar, body.article .page_content {
|
|||
font-weight: normal !important;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.topGrayBlock {
|
||||
background: #F0F0F0;
|
||||
height: 37px;
|
||||
border-bottom: 1px solid #C7C7C7;
|
||||
}
|
||||
|
||||
.edited {
|
||||
color: #9b9b9b;
|
||||
}
|
||||
|
||||
.uploadedImage img {
|
||||
max-height: 76px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.lagged {
|
||||
filter: opacity(0.5);
|
||||
cursor: progress;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.editMenu.loading {
|
||||
filter: opacity(0.5);
|
||||
cursor: progress;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.editMenu.loading * {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.lagged * {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.button.dragged {
|
||||
background: #c4c4c4 !important;
|
||||
border-color: #c4c4c4 !important;
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
.whiteBox {
|
||||
background: white;
|
||||
width: 421px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border: 1px solid #E8E8E8;
|
||||
margin-top: 7%;
|
||||
height: 231px;
|
||||
}
|
||||
|
||||
.boxContent {
|
||||
padding: 24px 38px;
|
||||
}
|
||||
|
||||
.blueList {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.blueList li {
|
||||
color: black;
|
||||
font-size: 11px;
|
||||
padding-top: 7px;
|
||||
}
|
||||
|
||||
.blueList li::before {
|
||||
content: " ";
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
background-color: #73889C;
|
||||
margin: 3px;
|
||||
margin-left: 2px;
|
||||
margin-right: 7px;
|
||||
}
|
||||
|
||||
.insertedPhoto {
|
||||
background: white;
|
||||
border: 1px solid #E8E7EA;
|
||||
padding: 10px;
|
||||
height: 100px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.uploadedImage {
|
||||
float: right;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.uploadedImageDescription {
|
||||
width: 449px;
|
||||
}
|
||||
|
||||
.uploadedImageDescription textarea {
|
||||
width: 84%;
|
||||
height: 86px;
|
||||
}
|
||||
|
||||
.smallFrame {
|
||||
border: 1px solid #E1E3E5;
|
||||
background: #F0F0F0;
|
||||
height: 33px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.smallFrame .smallBtn {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.smallFrame:hover {
|
||||
background: #E9F0F1 !important;
|
||||
}
|
||||
|
|
|
@ -110,10 +110,24 @@
|
|||
transition-duration: 0.3s;
|
||||
}
|
||||
|
||||
.post-author .edit {
|
||||
float: right;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
overflow: auto;
|
||||
background: url("/assets/packages/static/openvk/img/edit.png") no-repeat 0 0;
|
||||
opacity: 0.1;
|
||||
transition-duration: 0.3s;
|
||||
}
|
||||
|
||||
.post-author .pin:hover {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.post-author .edit:hover {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.expand_button {
|
||||
background-color: #eee;
|
||||
width: 100%;
|
||||
|
|
BIN
Web/static/img/edit.png
Normal file
BIN
Web/static/img/edit.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 571 B |
BIN
Web/static/img/video.png
Normal file
BIN
Web/static/img/video.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 510 B |
198
Web/static/js/al_photos.js
Normal file
198
Web/static/js/al_photos.js
Normal file
|
@ -0,0 +1,198 @@
|
|||
$(document).on("change", "#uploadButton", (e) => {
|
||||
let iterator = 0
|
||||
|
||||
if(e.currentTarget.files.length > 10) {
|
||||
MessageBox(tr("error"), tr("too_many_pictures"), [tr("ok")], [() => {Function.noop}])
|
||||
return;
|
||||
}
|
||||
|
||||
for(const file of e.currentTarget.files) {
|
||||
if(!file.type.startsWith('image/')) {
|
||||
MessageBox(tr("error"), tr("only_images_accepted", escapeHtml(file.name)), [tr("ok")], [() => {Function.noop}])
|
||||
return;
|
||||
}
|
||||
|
||||
if(file.size > 5 * 1024 * 1024) {
|
||||
MessageBox(tr("error"), tr("max_filesize", 5), [tr("ok")], [() => {Function.noop}])
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(document.querySelector(".whiteBox").style.display == "block") {
|
||||
document.querySelector(".whiteBox").style.display = "none"
|
||||
document.querySelector(".insertThere").append(document.getElementById("fakeButton"));
|
||||
}
|
||||
|
||||
let photos = new FormData()
|
||||
for(file of e.currentTarget.files) {
|
||||
photos.append("photo_"+iterator, file)
|
||||
iterator += 1
|
||||
}
|
||||
|
||||
photos.append("count", e.currentTarget.files.length)
|
||||
photos.append("hash", u("meta[name=csrf]").attr("value"))
|
||||
|
||||
let xhr = new XMLHttpRequest()
|
||||
xhr.open("POST", "/photos/upload?album="+document.getElementById("album").value)
|
||||
|
||||
xhr.onloadstart = () => {
|
||||
document.querySelector(".insertPhotos").insertAdjacentHTML("beforeend", `<img id="loader" src="/assets/packages/static/openvk/img/loading_mini.gif">`)
|
||||
}
|
||||
|
||||
xhr.onload = () => {
|
||||
let result = JSON.parse(xhr.responseText)
|
||||
|
||||
if(result.success) {
|
||||
u("#loader").remove()
|
||||
let photosArr = result.photos
|
||||
|
||||
for(photo of photosArr) {
|
||||
let table = document.querySelector(".insertPhotos")
|
||||
|
||||
table.insertAdjacentHTML("beforeend", `
|
||||
<div id="photo" class="insertedPhoto" data-id="${photo.id}">
|
||||
<div class="uploadedImageDescription" style="float: left;">
|
||||
<span style="color: #464646;position: absolute;">${tr("description")}:</span>
|
||||
<textarea style="margin-left: 62px; resize: none;" maxlength="255"></textarea>
|
||||
</div>
|
||||
<div class="uploadedImage">
|
||||
<a href="${photo.link}" target="_blank"><img width="125" src="${photo.url}"></a>
|
||||
<a class="profile_link" style="width: 125px;" id="deletePhoto" data-id="${photo.vid}" data-owner="${photo.owner}">${tr("delete")}</a>
|
||||
<!--<div class="smallFrame" style="margin-top: 6px;">
|
||||
<div class="smallBtn">${tr("album_poster")}</div>
|
||||
</div>-->
|
||||
</div>
|
||||
</div>
|
||||
`)
|
||||
}
|
||||
|
||||
document.getElementById("endUploading").style.display = "block"
|
||||
} else {
|
||||
u("#loader").remove()
|
||||
MessageBox(tr("error"), escapeHtml(result.flash.message) ?? tr("error_uploading_photo"), [tr("ok")], [() => {Function.noop}])
|
||||
}
|
||||
}
|
||||
|
||||
xhr.send(photos)
|
||||
})
|
||||
|
||||
$(document).on("click", "#endUploading", (e) => {
|
||||
let table = document.querySelector("#photos")
|
||||
let data = new FormData()
|
||||
let arr = new Map();
|
||||
for(el of table.querySelectorAll("div#photo")) {
|
||||
arr.set(el.dataset.id, el.querySelector("textarea").value)
|
||||
}
|
||||
|
||||
data.append("photos", JSON.stringify(Object.fromEntries(arr)))
|
||||
data.append("hash", u("meta[name=csrf]").attr("value"))
|
||||
|
||||
let xhr = new XMLHttpRequest()
|
||||
// в самом вк на каждое изменение описания отправляется свой запрос, но тут мы экономим запросы
|
||||
xhr.open("POST", "/photos/upload?act=finish&album="+document.getElementById("album").value)
|
||||
|
||||
xhr.onloadstart = () => {
|
||||
e.currentTarget.setAttribute("disabled", "disabled")
|
||||
}
|
||||
|
||||
xhr.onerror = () => {
|
||||
MessageBox(tr("error"), tr("error_uploading_photo"), [tr("ok")], [() => {Function.noop}])
|
||||
}
|
||||
|
||||
xhr.onload = () => {
|
||||
let result = JSON.parse(xhr.responseText)
|
||||
|
||||
if(!result.success) {
|
||||
MessageBox(tr("error"), escapeHtml(result.flash.message), [tr("ok")], [() => {Function.noop}])
|
||||
} else {
|
||||
document.querySelector(".page_content .insertPhotos").innerHTML = ""
|
||||
document.getElementById("endUploading").style.display = "none"
|
||||
|
||||
NewNotification(tr("photos_successfully_uploaded"), tr("click_to_go_to_album"), null, () => {window.location.assign(`/album${result.owner}_${result.album}`)})
|
||||
|
||||
document.querySelector(".whiteBox").style.display = "block"
|
||||
document.querySelector(".insertAgain").append(document.getElementById("fakeButton"))
|
||||
}
|
||||
|
||||
e.currentTarget.removeAttribute("disabled")
|
||||
}
|
||||
|
||||
xhr.send(data)
|
||||
})
|
||||
|
||||
$(document).on("click", "#deletePhoto", (e) => {
|
||||
let data = new FormData()
|
||||
data.append("hash", u("meta[name=csrf]").attr("value"))
|
||||
|
||||
let xhr = new XMLHttpRequest()
|
||||
xhr.open("POST", `/photo${e.currentTarget.dataset.owner}_${e.currentTarget.dataset.id}/delete`)
|
||||
|
||||
xhr.onloadstart = () => {
|
||||
e.currentTarget.closest("div#photo").classList.add("lagged")
|
||||
}
|
||||
|
||||
xhr.onerror = () => {
|
||||
MessageBox(tr("error"), tr("unknown_error"), [tr("ok")], [() => {Function.noop}])
|
||||
}
|
||||
|
||||
xhr.onload = () => {
|
||||
u(e.currentTarget.closest("div#photo")).remove()
|
||||
|
||||
if(document.querySelectorAll("div#photo").length < 1) {
|
||||
document.getElementById("endUploading").style.display = "none"
|
||||
document.querySelector(".whiteBox").style.display = "block"
|
||||
document.querySelector(".insertAgain").append(document.getElementById("fakeButton"))
|
||||
}
|
||||
}
|
||||
|
||||
xhr.send(data)
|
||||
})
|
||||
|
||||
$(document).on("dragover drop", (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
return false;
|
||||
})
|
||||
|
||||
$(".container_gray").on("dragover", (e) => {
|
||||
e.preventDefault()
|
||||
document.querySelector("#fakeButton").classList.add("dragged")
|
||||
document.querySelector("#fakeButton").value = tr("drag_files_here")
|
||||
})
|
||||
|
||||
$(".container_gray").on("dragleave", (e) => {
|
||||
e.preventDefault()
|
||||
document.querySelector("#fakeButton").classList.remove("dragged")
|
||||
document.querySelector("#fakeButton").value = tr("upload_picts")
|
||||
})
|
||||
|
||||
$(".container_gray").on("drop", (e) => {
|
||||
e.originalEvent.dataTransfer.dropEffect = 'move';
|
||||
e.preventDefault()
|
||||
|
||||
$(".container_gray").trigger("dragleave")
|
||||
|
||||
let files = e.originalEvent.dataTransfer.files
|
||||
|
||||
for(const file of files) {
|
||||
if(!file.type.startsWith('image/')) {
|
||||
MessageBox(tr("error"), tr("only_images_accepted", escapeHtml(file.name)), [tr("ok")], [() => {Function.noop}])
|
||||
return;
|
||||
}
|
||||
|
||||
if(file.size > 5 * 1024 * 1024) {
|
||||
MessageBox(tr("error"), tr("max_filesize", 5), [tr("ok")], [() => {Function.noop}])
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById("uploadButton").files = files
|
||||
u("#uploadButton").trigger("change")
|
||||
})
|
||||
|
||||
u(".container_gray").on("paste", (e) => {
|
||||
if(e.clipboardData.files.length > 0 && e.clipboardData.files.length < 10) {
|
||||
document.getElementById("uploadButton").files = e.clipboardData.files;
|
||||
u("#uploadButton").trigger("change")
|
||||
}
|
||||
})
|
|
@ -22,37 +22,14 @@ function trim(string) {
|
|||
return newStr;
|
||||
}
|
||||
|
||||
function handleUpload(id) {
|
||||
console.warn("блять...");
|
||||
|
||||
u("#post-buttons" + id + " .postFileSel").not("#" + this.id).each(input => input.value = null);
|
||||
|
||||
var indicator = u("#post-buttons" + id + " .post-upload");
|
||||
var file = this.files[0];
|
||||
if(typeof file === "undefined") {
|
||||
indicator.attr("style", "display: none;");
|
||||
} else {
|
||||
u("span", indicator.nodes[0]).text(trim(file.name) + " (" + humanFileSize(file.size, false) + ")");
|
||||
indicator.attr("style", "display: block;");
|
||||
}
|
||||
|
||||
document.querySelector("#post-buttons" + id + " #wallAttachmentMenu").classList.add("hidden");
|
||||
}
|
||||
|
||||
function initGraffiti(id) {
|
||||
let canvas = null;
|
||||
let msgbox = MessageBox(tr("draw_graffiti"), "<div id='ovkDraw'></div>", [tr("save"), tr("cancel")], [function() {
|
||||
canvas.getImage({includeWatermark: false}).toBlob(blob => {
|
||||
let fName = "Graffiti-" + Math.ceil(performance.now()).toString() + ".jpeg";
|
||||
let image = new File([blob], fName, {type: "image/jpeg", lastModified: new Date().getTime()});
|
||||
let trans = new DataTransfer();
|
||||
trans.items.add(image);
|
||||
|
||||
let fileSelect = document.querySelector("#post-buttons" + id + " input[name='_pic_attachment']");
|
||||
fileSelect.files = trans.files;
|
||||
|
||||
u(fileSelect).trigger("change");
|
||||
u("#post-buttons" + id + " #write textarea").trigger("focusin");
|
||||
fastUploadImage(id, image)
|
||||
}, "image/jpeg", 0.92);
|
||||
|
||||
canvas.teardown();
|
||||
|
@ -75,6 +52,79 @@ function initGraffiti(id) {
|
|||
});
|
||||
}
|
||||
|
||||
function fastUploadImage(textareaId, file) {
|
||||
// uploading images
|
||||
|
||||
if(!file.type.startsWith('image/')) {
|
||||
MessageBox(tr("error"), tr("only_images_accepted", escapeHtml(file.name)), [tr("ok")], [() => {Function.noop}])
|
||||
return;
|
||||
}
|
||||
|
||||
// 🤓🤓🤓
|
||||
if(file.size > 5 * 1024 * 1024) {
|
||||
MessageBox(tr("error"), tr("max_filesize", 5), [tr("ok")], [() => {Function.noop}])
|
||||
return;
|
||||
}
|
||||
|
||||
let imagesCount = document.querySelector("#post-buttons" + textareaId + " input[name='photos']").value.split(",").length
|
||||
|
||||
if(imagesCount > 10) {
|
||||
MessageBox(tr("error"), tr("too_many_photos"), [tr("ok")], [() => {Function.noop}])
|
||||
return
|
||||
}
|
||||
|
||||
let xhr = new XMLHttpRequest
|
||||
let data = new FormData
|
||||
|
||||
data.append("photo_0", file)
|
||||
data.append("count", 1)
|
||||
data.append("hash", u("meta[name=csrf]").attr("value"))
|
||||
|
||||
xhr.open("POST", "/photos/upload")
|
||||
|
||||
xhr.onloadstart = () => {
|
||||
document.querySelector("#post-buttons"+textareaId+" .upload").insertAdjacentHTML("beforeend", `<img id="loader" src="/assets/packages/static/openvk/img/loading_mini.gif">`)
|
||||
}
|
||||
|
||||
xhr.onload = () => {
|
||||
let response = JSON.parse(xhr.responseText)
|
||||
|
||||
appendImage(response, textareaId)
|
||||
}
|
||||
|
||||
xhr.send(data)
|
||||
}
|
||||
|
||||
// append image after uploading via /photos/upload
|
||||
function appendImage(response, textareaId) {
|
||||
if(!response.success) {
|
||||
MessageBox(tr("error"), (tr("error_uploading_photo") + response.flash.message), [tr("ok")], [() => {Function.noop}])
|
||||
} else {
|
||||
let form = document.querySelector("#post-buttons"+textareaId)
|
||||
let photosInput = form.querySelector("input[name='photos']")
|
||||
let photosIndicator = form.querySelector(".upload")
|
||||
|
||||
for(const phot of response.photos) {
|
||||
let id = phot.owner + "_" + phot.vid
|
||||
|
||||
photosInput.value += (id + ",")
|
||||
|
||||
u(photosIndicator).append(u(`
|
||||
<div class="upload-item" id="aP" data-id="${id}">
|
||||
<a class="upload-delete">×</a>
|
||||
<img src="${phot.url}">
|
||||
</div>
|
||||
`))
|
||||
|
||||
u(photosIndicator.querySelector(`.upload #aP[data-id='${id}'] .upload-delete`)).on("click", () => {
|
||||
photosInput.value = photosInput.value.replace(id + ",", "")
|
||||
u(form.querySelector(`.upload #aP[data-id='${id}']`)).remove()
|
||||
})
|
||||
}
|
||||
}
|
||||
u(`#post-buttons${textareaId} .upload #loader`).remove()
|
||||
}
|
||||
|
||||
u(".post-like-button").on("click", function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
|
@ -97,11 +147,12 @@ u(".post-like-button").on("click", function(e) {
|
|||
|
||||
function setupWallPostInputHandlers(id) {
|
||||
u("#wall-post-input" + id).on("paste", function(e) {
|
||||
// Если вы находитесь на странице с постом с id 11, то копирование произойдёт джва раза.
|
||||
// Оч ржачный баг, но вот как его исправить, я, если честно, не знаю.
|
||||
|
||||
if(e.clipboardData.files.length === 1) {
|
||||
var input = u("#post-buttons" + id + " input[name=_pic_attachment]").nodes[0];
|
||||
input.files = e.clipboardData.files;
|
||||
|
||||
u(input).trigger("change");
|
||||
fastUploadImage(id, e.clipboardData.files[0])
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -116,6 +167,183 @@ function setupWallPostInputHandlers(id) {
|
|||
// revert to original size if it is larger (possibly changed by user)
|
||||
// textArea.style.height = (newHeight > originalHeight ? (newHeight + boost) : originalHeight) + "px";
|
||||
});
|
||||
|
||||
u("#wall-post-input" + id).on("dragover", function(e) {
|
||||
e.preventDefault()
|
||||
|
||||
// todo add animation
|
||||
return;
|
||||
});
|
||||
|
||||
$("#wall-post-input" + id).on("drop", function(e) {
|
||||
e.originalEvent.dataTransfer.dropEffect = 'move';
|
||||
fastUploadImage(id, e.originalEvent.dataTransfer.files[0])
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
function OpenMiniature(e, photo, post, photo_id, type = "post") {
|
||||
/*
|
||||
костыли но смешные однако
|
||||
*/
|
||||
e.preventDefault();
|
||||
|
||||
if(u(".ovk-photo-view").length > 0) u(".ovk-photo-view-dimmer").remove();
|
||||
|
||||
// Значения для переключения фоток
|
||||
|
||||
let json;
|
||||
|
||||
let imagesCount = 0;
|
||||
let imagesIndex = 0;
|
||||
|
||||
let tempDetailsSection = [];
|
||||
|
||||
let dialog = u(
|
||||
`<div class="ovk-photo-view-dimmer">
|
||||
<div class="ovk-photo-view">
|
||||
<div class="photo_com_title">
|
||||
<text id="photo_com_title_photos">
|
||||
<img src="/assets/packages/static/openvk/img/loading_mini.gif">
|
||||
</text>
|
||||
<div>
|
||||
<a id="ovk-photo-close">${tr("close")}</a>
|
||||
</div>
|
||||
</div>
|
||||
<center style="margin-bottom: 8pt;">
|
||||
<div class="ovk-photo-slide-left"></div>
|
||||
<div class="ovk-photo-slide-right"></div>
|
||||
<img src="${photo}" style="max-width: 100%; max-height: 60vh; user-select:none;" id="ovk-photo-img">
|
||||
</center>
|
||||
<div class="ovk-photo-details">
|
||||
<img src="/assets/packages/static/openvk/img/loading_mini.gif">
|
||||
</div>
|
||||
</div>
|
||||
</div>`);
|
||||
u("body").addClass("dimmed").append(dialog);
|
||||
document.querySelector("html").style.overflowY = "hidden"
|
||||
|
||||
let button = u("#ovk-photo-close");
|
||||
|
||||
button.on("click", function(e) {
|
||||
let __closeDialog = () => {
|
||||
u("body").removeClass("dimmed");
|
||||
u(".ovk-photo-view-dimmer").remove();
|
||||
document.querySelector("html").style.overflowY = "scroll"
|
||||
};
|
||||
|
||||
__closeDialog();
|
||||
});
|
||||
|
||||
function __reloadTitleBar() {
|
||||
u("#photo_com_title_photos").last().innerHTML = imagesCount > 1 ? tr("photo_x_from_y", imagesIndex, imagesCount) : tr("photo");
|
||||
}
|
||||
|
||||
function __loadDetails(photo_id, index) {
|
||||
if(tempDetailsSection[index] == null) {
|
||||
u(".ovk-photo-details").last().innerHTML = '<img src="/assets/packages/static/openvk/img/loading_mini.gif">';
|
||||
ky("/photo" + photo_id, {
|
||||
hooks: {
|
||||
afterResponse: [
|
||||
async (_request, _options, response) => {
|
||||
let parser = new DOMParser();
|
||||
let body = parser.parseFromString(await response.text(), "text/html");
|
||||
|
||||
let element = u(body.getElementsByClassName("ovk-photo-details")).last();
|
||||
|
||||
tempDetailsSection[index] = element.innerHTML;
|
||||
|
||||
if(index == imagesIndex) {
|
||||
u(".ovk-photo-details").last().innerHTML = element.innerHTML;
|
||||
}
|
||||
|
||||
document.querySelectorAll(".ovk-photo-details .bsdn").forEach(bsdnInitElement)
|
||||
document.querySelectorAll(".ovk-photo-details script").forEach(scr => {
|
||||
// stolen from #953
|
||||
let newScr = document.createElement('script')
|
||||
|
||||
if(scr.src) {
|
||||
newScr.src = scr.src
|
||||
} else {
|
||||
newScr.textContent = scr.textContent
|
||||
}
|
||||
|
||||
document.querySelector(".ovk-photo-details").appendChild(newScr);
|
||||
})
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
} else {
|
||||
u(".ovk-photo-details").last().innerHTML = tempDetailsSection[index];
|
||||
}
|
||||
}
|
||||
|
||||
function __slidePhoto(direction) {
|
||||
/* direction = 1 - right
|
||||
direction = 0 - left */
|
||||
if(json == undefined) {
|
||||
console.log("Да подожди ты. Куда торопишься?");
|
||||
} else {
|
||||
if(imagesIndex >= imagesCount && direction == 1) {
|
||||
imagesIndex = 1;
|
||||
} else if(imagesIndex <= 1 && direction == 0) {
|
||||
imagesIndex = imagesCount;
|
||||
} else if(direction == 1) {
|
||||
imagesIndex++;
|
||||
} else if(direction == 0) {
|
||||
imagesIndex--;
|
||||
}
|
||||
|
||||
let photoURL = json.body[imagesIndex - 1].url;
|
||||
|
||||
u("#ovk-photo-img").last().src = photoURL;
|
||||
__reloadTitleBar();
|
||||
__loadDetails(json.body[imagesIndex - 1].id, imagesIndex);
|
||||
}
|
||||
}
|
||||
|
||||
let slideLeft = u(".ovk-photo-slide-left");
|
||||
|
||||
slideLeft.on("click", (e) => {
|
||||
__slidePhoto(0);
|
||||
});
|
||||
|
||||
let slideRight = u(".ovk-photo-slide-right");
|
||||
|
||||
slideRight.on("click", (e) => {
|
||||
__slidePhoto(1);
|
||||
});
|
||||
|
||||
let data = new FormData()
|
||||
data.append('parentType', type);
|
||||
ky.post("/iapi/getPhotosFromPost/" + (type == "post" ? post : "1_"+post), {
|
||||
hooks: {
|
||||
afterResponse: [
|
||||
async (_request, _options, response) => {
|
||||
json = await response.json();
|
||||
|
||||
imagesCount = json.body.length;
|
||||
imagesIndex = 0;
|
||||
// Это всё придётся правда на 1 прибавлять
|
||||
|
||||
json.body.every(element => {
|
||||
imagesIndex++;
|
||||
if(element.id == photo_id) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
__reloadTitleBar();
|
||||
__loadDetails(json.body[imagesIndex - 1].id, imagesIndex); }
|
||||
]
|
||||
},
|
||||
body: data
|
||||
});
|
||||
|
||||
return u(".ovk-photo-view-dimmer");
|
||||
}
|
||||
|
||||
u("#write > form").on("keydown", function(event) {
|
||||
|
@ -210,6 +438,7 @@ function addNote(textareaId, nid)
|
|||
|
||||
u("body").removeClass("dimmed");
|
||||
u(".ovk-diag-cont").remove();
|
||||
document.querySelector("html").style.overflowY = "scroll"
|
||||
}
|
||||
|
||||
async function attachNote(id)
|
||||
|
@ -263,3 +492,472 @@ async function showArticle(note_id) {
|
|||
u("body").removeClass("dimmed");
|
||||
u("body").addClass("article");
|
||||
}
|
||||
|
||||
$(document).on("click", "#videoAttachment", async (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
let body = `
|
||||
<div class="topGrayBlock">
|
||||
<div style="padding-top: 11px;padding-left: 12px;">
|
||||
<a href="/videos/upload">${tr("upload_new_video")}</a>
|
||||
<input type="text" id="vquery" maxlength="20" placeholder="${tr("header_search")}" style="float: right;width: 160px;margin-right: 17px;margin-top: -2px;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="videosInsert" style="padding: 5px;height: 287px;overflow-y: scroll;"></div>
|
||||
`
|
||||
|
||||
let form = e.currentTarget.closest("form")
|
||||
|
||||
MessageBox(tr("selecting_video"), body, [tr("close")], [Function.noop]);
|
||||
|
||||
// styles for messageboxx
|
||||
document.querySelector(".ovk-diag-body").style.padding = "0"
|
||||
document.querySelector(".ovk-diag-cont").style.width = "580px"
|
||||
document.querySelector(".ovk-diag-body").style.height = "335px"
|
||||
|
||||
async function insertVideos(page, query = "") {
|
||||
document.querySelector(".videosInsert").insertAdjacentHTML("beforeend", `<img id="loader" src="/assets/packages/static/openvk/img/loading_mini.gif">`)
|
||||
|
||||
let vidoses
|
||||
let noVideosText = tr("no_videos")
|
||||
if(query == "") {
|
||||
vidoses = await API.Wall.getVideos(page)
|
||||
} else {
|
||||
vidoses = await API.Wall.searchVideos(page, query)
|
||||
noVideosText = tr("no_videos_results")
|
||||
}
|
||||
|
||||
if(vidoses.count < 1) {
|
||||
document.querySelector(".videosInsert").innerHTML = `<span>${noVideosText}</span>`
|
||||
}
|
||||
|
||||
let pagesCount = Math.ceil(Number(vidoses.count) / 8)
|
||||
u("#loader").remove()
|
||||
let insert = document.querySelector(".videosInsert")
|
||||
|
||||
for(const vid of vidoses.items) {
|
||||
let isAttached = (form.querySelector("input[name='videos']").value.includes(`${vid.video.owner_id}_${vid.video.id},`))
|
||||
|
||||
insert.insertAdjacentHTML("beforeend", `
|
||||
<div class="content" style="padding: unset;">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<a href="/video${vid.video.owner_id}_${vid.video.id}">
|
||||
<div class="video-preview" style="height: 75px;width: 133px;overflow: hidden;">
|
||||
<img src="${vid.video.image[0].url}" alt="${escapeHtml(vid.video.title)}" style="max-width: 133px; height: 75px; margin: auto;">
|
||||
</div>
|
||||
</a>
|
||||
</td>
|
||||
<td valign="top" style="width: 100%">
|
||||
<a href="/video${vid.video.owner_id}_${vid.video.id}">
|
||||
<b>
|
||||
${ovk_proc_strtr(escapeHtml(vid.video.title), 30)}
|
||||
</b>
|
||||
</a>
|
||||
<br>
|
||||
<p>
|
||||
<span>${ovk_proc_strtr(escapeHtml(vid.video.description ?? ""), 140)}</span>
|
||||
</p>
|
||||
<span><a href="/id${vid.video.owner_id}" target="_blank">${escapeHtml(vid.video.author_name ?? "")}</a></span>
|
||||
</td>
|
||||
<td valign="top" class="action_links" style="width: 150px;">
|
||||
<a class="profile_link" id="attachvid" data-name="${escapeHtml(vid.video.title)}" data-attachmentData="${vid.video.owner_id}_${vid.video.id}">${!isAttached ? tr("attach") : tr("detach")}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
`)
|
||||
}
|
||||
|
||||
if(page < pagesCount) {
|
||||
document.querySelector(".videosInsert").insertAdjacentHTML("beforeend", `
|
||||
<div id="showMoreVideos" data-pagesCount="${pagesCount}" data-page="${page + 1}" style="width: 100%;text-align: center;background: #d5d5d5;height: 22px;padding-top: 9px;cursor:pointer;">
|
||||
<span>more...</span>
|
||||
</div>`)
|
||||
}
|
||||
}
|
||||
|
||||
$(".videosInsert").on("click", "#showMoreVideos", (e) => {
|
||||
u(e.currentTarget).remove()
|
||||
insertVideos(Number(e.currentTarget.dataset.page), document.querySelector(".topGrayBlock #vquery").value)
|
||||
})
|
||||
|
||||
$(".topGrayBlock #vquery").on("change", async (e) => {
|
||||
await new Promise(r => setTimeout(r, 1000));
|
||||
|
||||
if(e.currentTarget.value === document.querySelector(".topGrayBlock #vquery").value) {
|
||||
document.querySelector(".videosInsert").innerHTML = ""
|
||||
insertVideos(1, e.currentTarget.value)
|
||||
return;
|
||||
} else {
|
||||
console.info("skipping")
|
||||
}
|
||||
})
|
||||
|
||||
insertVideos(1)
|
||||
|
||||
function insertAttachment(id) {
|
||||
let videos = form.querySelector("input[name='videos']")
|
||||
|
||||
if(!videos.value.includes(id + ",")) {
|
||||
if(videos.value.split(",").length > 10) {
|
||||
NewNotification(tr("error"), tr("max_attached_videos"))
|
||||
return false
|
||||
}
|
||||
|
||||
form.querySelector("input[name='videos']").value += (id + ",")
|
||||
|
||||
console.info(id + " attached")
|
||||
return true
|
||||
} else {
|
||||
form.querySelector("input[name='videos']").value = form.querySelector("input[name='videos']").value.replace(id + ",", "")
|
||||
|
||||
console.info(id + " detached")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
$(".videosInsert").on("click", "#attachvid", (ev) => {
|
||||
// откреплено от псто
|
||||
if(!insertAttachment(ev.currentTarget.dataset.attachmentdata)) {
|
||||
u(`.post-has-videos .post-has-video[data-id='${ev.currentTarget.dataset.attachmentdata}']`).remove()
|
||||
ev.currentTarget.innerHTML = tr("attach")
|
||||
} else {
|
||||
ev.currentTarget.innerHTML = tr("detach")
|
||||
|
||||
form.querySelector(".post-has-videos").insertAdjacentHTML("beforeend", `
|
||||
<div class="post-has-video" id="unattachVideo" data-id="${ev.currentTarget.dataset.attachmentdata}">
|
||||
<span>${tr("video")} <b>"${ovk_proc_strtr(escapeHtml(ev.currentTarget.dataset.name), 20)}"</b></span>
|
||||
</div>
|
||||
`)
|
||||
|
||||
u(`#unattachVideo[data-id='${ev.currentTarget.dataset.attachmentdata}']`).on("click", (e) => {
|
||||
let id = ev.currentTarget.dataset.attachmentdata
|
||||
form.querySelector("input[name='videos']").value = form.querySelector("input[name='videos']").value.replace(id + ",", "")
|
||||
|
||||
console.info(id + " detached")
|
||||
|
||||
u(e.currentTarget).remove()
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
$(document).on("click", "#editPost", (e) => {
|
||||
let post = e.currentTarget.closest("table")
|
||||
let content = post.querySelector(".text")
|
||||
let text = content.querySelector(".really_text")
|
||||
|
||||
if(content.querySelector("textarea") == null) {
|
||||
content.insertAdjacentHTML("afterbegin", `
|
||||
<div class="editMenu">
|
||||
<div id="wall-post-input999">
|
||||
<textarea id="new_content">${text.dataset.text}</textarea>
|
||||
<input type="button" class="button" value="${tr("save")}" id="endEditing">
|
||||
<input type="button" class="button" value="${tr("cancel")}" id="cancelEditing">
|
||||
</div>
|
||||
${e.currentTarget.dataset.nsfw != null ? `
|
||||
<div class="postOptions">
|
||||
<label><input type="checkbox" id="nswfw" ${e.currentTarget.dataset.nsfw == 1 ? `checked` : ``}>${tr("contains_nsfw")}</label>
|
||||
</div>
|
||||
` : ``}
|
||||
${e.currentTarget.dataset.fromgroup != null ? `
|
||||
<div class="postOptions">
|
||||
<label><input type="checkbox" id="fromgroup" ${e.currentTarget.dataset.fromgroup == 1 ? `checked` : ``}>${tr("post_as_group")}</label>
|
||||
</div>
|
||||
` : ``}
|
||||
</div>
|
||||
`)
|
||||
|
||||
u(content.querySelector("#cancelEditing")).on("click", () => {post.querySelector("#editPost").click()})
|
||||
u(content.querySelector("#endEditing")).on("click", () => {
|
||||
let nwcntnt = content.querySelector("#new_content").value
|
||||
let type = "post"
|
||||
|
||||
if(post.classList.contains("comment")) {
|
||||
type = "comment"
|
||||
}
|
||||
|
||||
let xhr = new XMLHttpRequest()
|
||||
xhr.open("POST", "/wall/edit")
|
||||
|
||||
xhr.onloadstart = () => {
|
||||
content.querySelector(".editMenu").classList.add("loading")
|
||||
}
|
||||
|
||||
xhr.onerror = () => {
|
||||
MessageBox(tr("error"), "unknown error occured", [tr("ok")], [() => {Function.noop}])
|
||||
}
|
||||
|
||||
xhr.ontimeout = () => {
|
||||
MessageBox(tr("error"), "Try to refresh page", [tr("ok")], [() => {Function.noop}])
|
||||
}
|
||||
|
||||
xhr.onload = () => {
|
||||
let result = JSON.parse(xhr.responseText)
|
||||
|
||||
if(result.error == "no") {
|
||||
post.querySelector("#editPost").click()
|
||||
content.querySelector(".really_text").innerHTML = result.new_content
|
||||
|
||||
if(post.querySelector(".editedMark") == null) {
|
||||
post.querySelector(".date").insertAdjacentHTML("beforeend", `
|
||||
<span class="edited editedMark">(${tr("edited_short")})</span>
|
||||
`)
|
||||
}
|
||||
|
||||
if(e.currentTarget.dataset.nsfw != null) {
|
||||
e.currentTarget.setAttribute("data-nsfw", result.nsfw)
|
||||
|
||||
if(result.nsfw == 0) {
|
||||
post.classList.remove("post-nsfw")
|
||||
} else {
|
||||
post.classList.add("post-nsfw")
|
||||
}
|
||||
}
|
||||
|
||||
if(e.currentTarget.dataset.fromgroup != null) {
|
||||
e.currentTarget.setAttribute("data-fromgroup", result.from_group)
|
||||
}
|
||||
|
||||
post.querySelector(".post-avatar").setAttribute("src", result.author.avatar)
|
||||
post.querySelector(".post-author-name").innerHTML = result.author.name
|
||||
post.querySelector(".really_text").setAttribute("data-text", result.new_text)
|
||||
} else {
|
||||
MessageBox(tr("error"), result.error, [tr("ok")], [Function.noop])
|
||||
post.querySelector("#editPost").click()
|
||||
}
|
||||
}
|
||||
|
||||
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||
xhr.send("postid="+e.currentTarget.dataset.id+
|
||||
"&newContent="+nwcntnt+
|
||||
"&hash="+encodeURIComponent(u("meta[name=csrf]").attr("value"))+
|
||||
"&type="+type+
|
||||
"&nsfw="+(content.querySelector("#nswfw") != null ? content.querySelector("#nswfw").checked : 0)+
|
||||
"&fromgroup="+(content.querySelector("#fromgroup") != null ? content.querySelector("#fromgroup").checked : 0))
|
||||
})
|
||||
|
||||
u(".editMenu").on("keydown", (e) => {
|
||||
if(e.ctrlKey && e.keyCode === 13)
|
||||
content.querySelector("#endEditing").click()
|
||||
});
|
||||
|
||||
text.style.display = "none"
|
||||
setupWallPostInputHandlers(999)
|
||||
} else {
|
||||
u(content.querySelector(".editMenu")).remove()
|
||||
text.style.display = "block"
|
||||
}
|
||||
})
|
||||
|
||||
// copypaste from videos picker
|
||||
$(document).on("click", "#photosAttachments", async (e) => {
|
||||
let body = `
|
||||
<div class="topGrayBlock">
|
||||
<div style="padding-top: 7px;padding-left: 12px;">
|
||||
${tr("upload_new_photo")}:
|
||||
<input type="file" multiple accept="image/*" id="fastFotosUplod" style="display:none">
|
||||
<input type="button" class="button" value="${tr("upload_button")}" onclick="fastFotosUplod.click()">
|
||||
<select id="albumSelect" style="width: 154px;float: right;margin-right: 17px;">
|
||||
<option value="0">${tr("all_photos")}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="photosInsert" style="padding: 5px;height: 287px;overflow-y: scroll;">
|
||||
<div style="position: fixed;z-index: 1007;width: 92%;background: white;margin-top: -5px;padding-top: 6px;"><h4>${tr("is_x_photos", 0)}</h4></div>
|
||||
<div class="photosList album-flex" style="margin-top: 20px;"></div>
|
||||
</div>
|
||||
`
|
||||
|
||||
let form = e.currentTarget.closest("form")
|
||||
|
||||
MessageBox(tr("select_photo"), body, [tr("close")], [Function.noop]);
|
||||
|
||||
document.querySelector(".ovk-diag-body").style.padding = "0"
|
||||
document.querySelector(".ovk-diag-cont").style.width = "630px"
|
||||
document.querySelector(".ovk-diag-body").style.height = "335px"
|
||||
|
||||
async function insertPhotos(page, album = 0) {
|
||||
u("#loader").remove()
|
||||
|
||||
let insertPlace = document.querySelector(".photosInsert .photosList")
|
||||
document.querySelector(".photosInsert").insertAdjacentHTML("beforeend", `<img id="loader" style="max-height: 8px;max-width: 36px;" src="/assets/packages/static/openvk/img/loading_mini.gif">`)
|
||||
|
||||
let photos;
|
||||
|
||||
try {
|
||||
photos = await API.Photos.getPhotos(page, Number(album))
|
||||
} catch(e) {
|
||||
document.querySelector(".photosInsert h4").innerHTML = tr("is_x_photos", -1)
|
||||
insertPlace.innerHTML = "Invalid album"
|
||||
console.error(e)
|
||||
u("#loader").remove()
|
||||
return;
|
||||
}
|
||||
|
||||
document.querySelector(".photosInsert h4").innerHTML = tr("is_x_photos", photos.count)
|
||||
|
||||
let pagesCount = Math.ceil(Number(photos.count) / 24)
|
||||
u("#loader").remove()
|
||||
|
||||
for(const photo of photos.items) {
|
||||
let isAttached = (form.querySelector("input[name='photos']").value.includes(`${photo.owner_id}_${photo.id},`))
|
||||
|
||||
insertPlace.insertAdjacentHTML("beforeend", `
|
||||
<div style="width: 14%;margin-bottom: 7px;margin-left: 13px;" class="album-photo" data-attachmentdata="${photo.owner_id}_${photo.id}" data-preview="${photo.photo_130}">
|
||||
<a href="/photo${photo.owner_id}_${photo.id}">
|
||||
<img class="album-photo--image" src="${photo.photo_130}" alt="..." style="${isAttached ? "background-color: #646464" : ""}">
|
||||
</a>
|
||||
</div>
|
||||
`)
|
||||
}
|
||||
|
||||
if(page < pagesCount) {
|
||||
insertPlace.insertAdjacentHTML("beforeend", `
|
||||
<div id="showMorePhotos" data-pagesCount="${pagesCount}" data-page="${page + 1}" style="width: 100%;text-align: center;background: #f0f0f0;height: 22px;padding-top: 9px;cursor:pointer;">
|
||||
<span>more...</span>
|
||||
</div>`)
|
||||
}
|
||||
}
|
||||
|
||||
insertPhotos(1)
|
||||
|
||||
let albums = await API.Photos.getAlbums(Number(e.currentTarget.dataset.club ?? 0))
|
||||
|
||||
for(const alb of albums.items) {
|
||||
let sel = document.querySelector(".ovk-diag-body #albumSelect")
|
||||
|
||||
sel.insertAdjacentHTML("beforeend", `<option value="${alb.id}">${ovk_proc_strtr(escapeHtml(alb.name), 20)}</option>`)
|
||||
}
|
||||
|
||||
$(".photosInsert").on("click", "#showMorePhotos", (e) => {
|
||||
u(e.currentTarget).remove()
|
||||
insertPhotos(Number(e.currentTarget.dataset.page), document.querySelector(".topGrayBlock #albumSelect").value)
|
||||
})
|
||||
|
||||
$(".topGrayBlock #albumSelect").on("change", (evv) => {
|
||||
document.querySelector(".photosInsert .photosList").innerHTML = ""
|
||||
|
||||
insertPhotos(1, evv.currentTarget.value)
|
||||
})
|
||||
|
||||
function insertAttachment(id) {
|
||||
let photos = form.querySelector("input[name='photos']")
|
||||
|
||||
if(!photos.value.includes(id + ",")) {
|
||||
if(photos.value.split(",").length > 10) {
|
||||
NewNotification(tr("error"), tr("max_attached_photos"))
|
||||
return false
|
||||
}
|
||||
|
||||
form.querySelector("input[name='photos']").value += (id + ",")
|
||||
|
||||
console.info(id + " attached")
|
||||
return true
|
||||
} else {
|
||||
form.querySelector("input[name='photos']").value = form.querySelector("input[name='photos']").value.replace(id + ",", "")
|
||||
|
||||
console.info(id + " detached")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
$(".photosList").on("click", ".album-photo", (ev) => {
|
||||
ev.preventDefault()
|
||||
|
||||
if(!insertAttachment(ev.currentTarget.dataset.attachmentdata)) {
|
||||
u(form.querySelector(`.upload #aP[data-id='${ev.currentTarget.dataset.attachmentdata}']`)).remove()
|
||||
ev.currentTarget.querySelector("img").style.backgroundColor = "white"
|
||||
} else {
|
||||
ev.currentTarget.querySelector("img").style.backgroundColor = "#646464"
|
||||
let id = ev.currentTarget.dataset.attachmentdata
|
||||
|
||||
u(form.querySelector(`.upload`)).append(u(`
|
||||
<div class="upload-item" id="aP" data-id="${ev.currentTarget.dataset.attachmentdata}">
|
||||
<a class="upload-delete">×</a>
|
||||
<img src="${ev.currentTarget.dataset.preview}">
|
||||
</div>
|
||||
`));
|
||||
|
||||
u(`.upload #aP[data-id='${ev.currentTarget.dataset.attachmentdata}'] .upload-delete`).on("click", () => {
|
||||
form.querySelector("input[name='photos']").value = form.querySelector("input[name='photos']").value.replace(id + ",", "")
|
||||
u(form.querySelector(`.upload #aP[data-id='${ev.currentTarget.dataset.attachmentdata}']`)).remove()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
u("#fastFotosUplod").on("change", (evn) => {
|
||||
let xhr = new XMLHttpRequest()
|
||||
xhr.open("POST", "/photos/upload")
|
||||
|
||||
let formdata = new FormData()
|
||||
let iterator = 0
|
||||
|
||||
for(const fille of evn.currentTarget.files) {
|
||||
if(!fille.type.startsWith('image/')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(fille.size > 5 * 1024 * 1024) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(evn.currentTarget.files.length >= 10) {
|
||||
NewNotification(tr("error"), tr("max_attached_photos"))
|
||||
return;
|
||||
}
|
||||
|
||||
formdata.append("photo_"+iterator, fille)
|
||||
iterator += 1
|
||||
}
|
||||
|
||||
xhr.onloadstart = () => {
|
||||
evn.currentTarget.parentNode.insertAdjacentHTML("beforeend", `<img id="loader" style="max-height: 8px;max-width: 36px;" src="/assets/packages/static/openvk/img/loading_mini.gif">`)
|
||||
}
|
||||
|
||||
xhr.onload = () => {
|
||||
let result = JSON.parse(xhr.responseText)
|
||||
|
||||
u("#loader").remove()
|
||||
if(result.success) {
|
||||
for(const pht of result.photos) {
|
||||
let id = pht.owner + "_" + pht.vid
|
||||
|
||||
if(!insertAttachment(id)) {
|
||||
return
|
||||
}
|
||||
|
||||
u(form.querySelector(`.upload`)).append(u(`
|
||||
<div class="upload-item" id="aP" data-id="${pht.owner + "_" + pht.vid}">
|
||||
<a class="upload-delete">×</a>
|
||||
<img src="${pht.url}">
|
||||
</div>
|
||||
`));
|
||||
|
||||
u(`.upload #aP[data-id='${pht.owner + "_" + pht.vid}'] .upload-delete`).on("click", () => {
|
||||
form.querySelector("input[name='photos']").value = form.querySelector("input[name='photos']").value.replace(id + ",", "")
|
||||
u(form.querySelector(`.upload #aP[data-id='${id}']`)).remove()
|
||||
})
|
||||
}
|
||||
|
||||
u("body").removeClass("dimmed");
|
||||
u(".ovk-diag-cont").remove();
|
||||
document.querySelector("html").style.overflowY = "scroll"
|
||||
} else {
|
||||
// todo: https://vk.com/wall-32295218_78593
|
||||
alert(result.flash.message)
|
||||
}
|
||||
}
|
||||
|
||||
formdata.append("hash", u("meta[name=csrf]").attr("value"))
|
||||
formdata.append("count", iterator)
|
||||
|
||||
xhr.send(formdata)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -3,6 +3,7 @@ Function.noop = () => {};
|
|||
function MessageBox(title, body, buttons, callbacks) {
|
||||
if(u(".ovk-diag-cont").length > 0) return false;
|
||||
|
||||
document.querySelector("html").style.overflowY = "hidden"
|
||||
let dialog = u(
|
||||
`<div class="ovk-diag-cont">
|
||||
<div class="ovk-diag">
|
||||
|
@ -19,7 +20,11 @@ function MessageBox(title, body, buttons, callbacks) {
|
|||
|
||||
button.on("click", function(e) {
|
||||
let __closeDialog = () => {
|
||||
u("body").removeClass("dimmed");
|
||||
if(document.querySelector(".ovk-photo-view-dimmer") == null) {
|
||||
u("body").removeClass("dimmed");
|
||||
document.querySelector("html").style.overflowY = "scroll"
|
||||
}
|
||||
|
||||
u(".ovk-diag-cont").remove();
|
||||
};
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ function toggleMenu(id) {
|
|||
}
|
||||
document.addEventListener("DOMContentLoaded", function() { //BEGIN
|
||||
|
||||
u("#_photoDelete").on("click", function(e) {
|
||||
$(document).on("click", "#_photoDelete", function(e) {
|
||||
var formHtml = "<form id='tmpPhDelF' action='" + u(this).attr("href") + "' >";
|
||||
formHtml += "<input type='hidden' name='hash' value='" + u("meta[name=csrf]").attr("value") + "' />";
|
||||
formHtml += "</form>";
|
||||
|
|
|
@ -8,6 +8,5 @@
|
|||
Require all granted
|
||||
</Directory>
|
||||
|
||||
ErrorLog /var/log/openvk/error.log
|
||||
CustomLog /var/log/openvk/access.log combinedio
|
||||
LogFormat combinedio
|
||||
</VirtualHost>
|
||||
|
|
|
@ -8,7 +8,6 @@ do
|
|||
chown -R 33:33 /opt/chandler/extensions/available/openvk/tmp/api-storage/audios
|
||||
chown -R 33:33 /opt/chandler/extensions/available/openvk/tmp/api-storage/photos
|
||||
chown -R 33:33 /opt/chandler/extensions/available/openvk/tmp/api-storage/videos
|
||||
chown -R 33:33 /var/log/openvk
|
||||
|
||||
sleep 600
|
||||
done
|
|
@ -18,5 +18,6 @@ RUN apt update; \
|
|||
yaml \
|
||||
pdo_mysql \
|
||||
rdkafka \
|
||||
imagick \
|
||||
&& \
|
||||
rm -rf /var/lib/apt/lists/*
|
|
@ -12,7 +12,6 @@ services:
|
|||
- openvk-audios:/opt/chandler/extensions/available/openvk/tmp/api-storage/audios
|
||||
- openvk-photos:/opt/chandler/extensions/available/openvk/tmp/api-storage/photos
|
||||
- openvk-videos:/opt/chandler/extensions/available/openvk/tmp/api-storage/videos
|
||||
- openvk-logs:/var/log/openvk
|
||||
- ./openvk.yml:/opt/chandler/extensions/available/openvk/openvk.yml:ro
|
||||
- ./chandler.yml:/opt/chandler/chandler.yml:ro
|
||||
depends_on:
|
||||
|
@ -32,7 +31,6 @@ services:
|
|||
- openvk-audios:/opt/chandler/extensions/available/openvk/tmp/api-storage/audios
|
||||
- openvk-photos:/opt/chandler/extensions/available/openvk/tmp/api-storage/photos
|
||||
- openvk-videos:/opt/chandler/extensions/available/openvk/tmp/api-storage/videos
|
||||
- openvk-logs:/var/log/openvk
|
||||
- ./acl_handler.sh:/bin/acl_handler.sh:ro
|
||||
|
||||
mariadb-primary:
|
||||
|
@ -76,6 +74,7 @@ services:
|
|||
- KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092
|
||||
- KAFKA_BROKER_ID=1
|
||||
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=1@127.0.0.1:9093
|
||||
- KAFKA_CFG_NODE_ID=1
|
||||
|
||||
phpmyadmin:
|
||||
image: docker.io/phpmyadmin:5
|
||||
|
|
|
@ -48,7 +48,6 @@ RUN ln -s /opt/chandler/extensions/available/commitcaptcha/ /opt/chandler/extens
|
|||
ln -s /opt/chandler/extensions/available/openvk/install/automated/common/10-openvk.conf /etc/apache2/sites-enabled/10-openvk.conf && \
|
||||
a2enmod rewrite
|
||||
|
||||
VOLUME [ "/var/log/openvk" ]
|
||||
VOLUME [ "/opt/chandler/extensions/available/openvk/storage" ]
|
||||
VOLUME [ "/opt/chandler/extensions/available/openvk/tmp/api-storage/audios" ]
|
||||
VOLUME [ "/opt/chandler/extensions/available/openvk/tmp/api-storage/photos" ]
|
||||
|
|
|
@ -38,13 +38,10 @@ items:
|
|||
chown -R 33:33 /opt/chandler/extensions/available/openvk/tmp/api-storage/audios
|
||||
chown -R 33:33 /opt/chandler/extensions/available/openvk/tmp/api-storage/photos
|
||||
chown -R 33:33 /opt/chandler/extensions/available/openvk/tmp/api-storage/videos
|
||||
chown -R 33:33 /var/log/openvk
|
||||
|
||||
sleep 600
|
||||
done
|
||||
volumeMounts:
|
||||
- mountPath: /var/log/openvk
|
||||
name: openvk-logs
|
||||
- mountPath: /opt/chandler/extensions/available/openvk/storage
|
||||
name: openvk-storage
|
||||
- mountPath: /opt/chandler/extensions/available/openvk/tmp/api-storage/audios
|
||||
|
@ -66,8 +63,6 @@ items:
|
|||
cpu: 100m
|
||||
memory: 512Mi
|
||||
volumeMounts:
|
||||
- mountPath: /var/log/openvk
|
||||
name: openvk-logs
|
||||
- mountPath: /opt/chandler/extensions/available/openvk/openvk.yml
|
||||
name: openvk-config
|
||||
subPath: openvk.yml
|
||||
|
|
|
@ -1 +1,7 @@
|
|||
CREATE TABLE `support_names` ( `agent` BIGINT UNSIGNED NOT NULL , `name` VARCHAR(512) NOT NULL , `icon` VARCHAR(1024) NULL DEFAULT NULL , `numerate` BOOLEAN NOT NULL DEFAULT FALSE , PRIMARY KEY (`agent`)) ENGINE = InnoDB;
|
||||
CREATE TABLE `support_names` (
|
||||
`agent` BIGINT UNSIGNED NOT NULL,
|
||||
`name` VARCHAR(512) NOT NULL,
|
||||
`icon` VARCHAR(1024) NULL DEFAULT NULL,
|
||||
`numerate` BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
PRIMARY KEY (`agent`)
|
||||
) ENGINE = InnoDB;
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue