Compare commits

...

7 commits

Author SHA1 Message Date
koke228666
92d5df3ab1
Merge 225f031cfc into c8a97f8b8d 2025-07-05 16:21:50 +03:00
ZAZiOs
c8a97f8b8d
fix(API): users bdate field, can edit/delete comments (#1359)
И парочка фиксов или фич которые позволяют работать OpenVK Native так
как надо.

1. add openvk native support - Просто добавил определение натива как
android/ios клиент
2. Добавил /.well-known/assetlinks.json - По этому адресу автоматически
стучится андроид чтобы проверить что приложению можно открывать ссылки с
этого адреса по умолчанию.
3. Добавил поле bdate в users
4. Добавил инфу о том может ли редачить или удалять комментарий
пользователь.
2025-06-29 17:12:55 +03:00
mr❤️🤢
93b1202a13
feat(rate_limits) (#1353)
Добавляет возможность ограничить такие действия как отправка подарка,
заявка в друзья, джойн в группу, создание группы на время, чтобы можно
было создать только 5 групп за день итд. Находится в ветке конфига
preferences>security>rateLimits>eventsLimit. На момент написания этого
текста регулирование постинга и отправки заявки в друзья не было
продублировано в презентеры, мб так и оставить.
2025-06-29 17:11:33 +03:00
Mikita Wiśniewski
4f126d1b05
update(openvk_modern): expand & actualize stylesheet, add minor fixes (#1341)
* сайдбар больше не становится прозрачным и не прячется за странными
элементами когда ему не хватает места
* новые стили для всплывающего окна лайкнувших
* новые стили для кнопки поиска в шапке
* чуть меньше бордеров на разных элементах
* beautified ™️
2025-06-29 16:21:56 +03:00
eversiege
27fb923178
chore(comments): gramrrmar mistake (#1367) 2025-06-29 16:15:46 +03:00
Artemka
79e2446184
chore(login): 2fa type number (#1322)
Что это дают? 
У поля ввода указано что там должны быть цифры (Полезно когда клавиатура
виртуальная)
Отключено автозаполнение
2025-06-29 16:15:16 +03:00
koke228666
225f031cfc /dev 2025-05-22 16:34:05 +03:00
49 changed files with 1730 additions and 332 deletions

View file

@ -98,6 +98,10 @@ final class Friends extends VKAPIRequestHandler
switch ($user->getSubscriptionStatus($this->getUser())) { switch ($user->getSubscriptionStatus($this->getUser())) {
case 0: case 0:
if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->getUser(), "friends.outgoing_sub")) {
$this->failTooOften();
}
$user->toggleSubscription($this->getUser()); $user->toggleSubscription($this->getUser());
return 1; return 1;

View file

@ -61,6 +61,10 @@ final class Gifts extends VKAPIRequestHandler
$this->fail(-105, "Commerce is disabled on this instance"); $this->fail(-105, "Commerce is disabled on this instance");
} }
if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->getUser(), "gifts.send", false)) {
$this->failTooOften();
}
$user = (new UsersRepo())->get((int) $user_ids); # FAKE прогноз погоды (в данном случае user_ids) $user = (new UsersRepo())->get((int) $user_ids); # FAKE прогноз погоды (в данном случае user_ids)
if (!$user || $user->isDeleted()) { if (!$user || $user->isDeleted()) {

View file

@ -312,6 +312,10 @@ final class Groups extends VKAPIRequestHandler
$isMember = !is_null($this->getUser()) ? (int) $club->getSubscriptionStatus($this->getUser()) : 0; $isMember = !is_null($this->getUser()) ? (int) $club->getSubscriptionStatus($this->getUser()) : 0;
if ($isMember == 0) { if ($isMember == 0) {
if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->getUser(), "groups.sub")) {
$this->failTooOften();
}
$club->toggleSubscription($this->getUser()); $club->toggleSubscription($this->getUser());
} }

View file

@ -317,6 +317,32 @@ final class Users extends VKAPIRequestHandler
$response[$i]->custom_fields = $append_array; $response[$i]->custom_fields = $append_array;
break; break;
case "bdate":
if (!$canView) {
$response[$i]->bdate = "01.01.1970";
break;
}
$visibility = $usr->getBirthdayPrivacy();
$response[$i]->bdate_visibility = $visibility;
$birthday = $usr->getBirthday();
if ($birthday) {
switch ($visibility) {
case 1:
$response[$i]->bdate = $birthday->format('%d.%m');
break;
case 2:
$response[$i]->bdate = $birthday->format('%d.%m.%Y');
break;
case 0:
default:
$response[$i]->bdate = null;
break;
}
} else {
$response[$i]->bdate = null;
}
break;
} }
} }

View file

@ -25,6 +25,11 @@ abstract class VKAPIRequestHandler
throw new APIErrorException($message, $code); throw new APIErrorException($message, $code);
} }
protected function failTooOften(): never
{
$this->fail(9, "Rate limited");
}
protected function getUser(): ?User protected function getUser(): ?User
{ {
return $this->user; return $this->user;

View file

@ -713,6 +713,10 @@ final class Wall extends VKAPIRequestHandler
$post->setSuggested(1); $post->setSuggested(1);
} }
if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->getUser(), "wall.post")) {
$this->failTooOften();
}
$post->save(); $post->save();
} catch (\LogicException $ex) { } catch (\LogicException $ex) {
$this->fail(100, "One of the parameters specified was missing or invalid"); $this->fail(100, "One of the parameters specified was missing or invalid");
@ -723,7 +727,7 @@ final class Wall extends VKAPIRequestHandler
} }
if ($owner_id > 0 && $owner_id !== $this->getUser()->getId()) { if ($owner_id > 0 && $owner_id !== $this->getUser()->getId()) {
(new WallPostNotification($wallOwner, $post, $this->user->identity))->emit(); (new WallPostNotification($wallOwner, $post, $this->getUser()))->emit();
} }
return (object) ["post_id" => $post->getVirtualId()]; return (object) ["post_id" => $post->getVirtualId()];
@ -873,6 +877,8 @@ final class Wall extends VKAPIRequestHandler
"id" => $comment->getId(), "id" => $comment->getId(),
"from_id" => $oid, "from_id" => $oid,
"date" => $comment->getPublicationTime()->timestamp(), "date" => $comment->getPublicationTime()->timestamp(),
"can_edit" => $post->canBeEditedBy($this->getUser()),
"can_delete" => $post->canBeDeletedBy($this->getUser()),
"text" => $comment->getText(false), "text" => $comment->getText(false),
"post_id" => $post->getVirtualId(), "post_id" => $post->getVirtualId(),
"owner_id" => method_exists($post, 'isPostedOnBehalfOfGroup') && $post->isPostedOnBehalfOfGroup() ? $post->getOwner()->getId() * -1 : $post->getOwner()->getId(), "owner_id" => method_exists($post, 'isPostedOnBehalfOfGroup') && $post->isPostedOnBehalfOfGroup() ? $post->getOwner()->getId() * -1 : $post->getOwner()->getId(),

View file

@ -176,11 +176,13 @@ class Post extends Postable
$platform = $this->getRecord()->api_source_name; $platform = $this->getRecord()->api_source_name;
if ($forAPI) { if ($forAPI) {
switch ($platform) { switch ($platform) {
case 'openvk_native':
case 'openvk_refresh_android': case 'openvk_refresh_android':
case 'openvk_legacy_android': case 'openvk_legacy_android':
return 'android'; return 'android';
break; break;
case 'openvk_native_ios':
case 'openvk_ios': case 'openvk_ios':
case 'openvk_legacy_ios': case 'openvk_legacy_ios':
return 'iphone'; return 'iphone';

View file

@ -34,9 +34,9 @@ trait TSubscribable
"target" => $this->getId(), "target" => $this->getId(),
]; ];
$sub = $ctx->table("subscriptions")->where($data); $sub = $ctx->table("subscriptions")->where($data);
if (!($sub->fetch())) { if (!($sub->fetch())) {
$ctx->table("subscriptions")->insert($data); $ctx->table("subscriptions")->insert($data);
return true; return true;
} }

View file

@ -971,11 +971,13 @@ class User extends RowModel
$platform = $this->getRecord()->client_name; $platform = $this->getRecord()->client_name;
if ($forAPI) { if ($forAPI) {
switch ($platform) { switch ($platform) {
case 'openvk_native':
case 'openvk_refresh_android': case 'openvk_refresh_android':
case 'openvk_legacy_android': case 'openvk_legacy_android':
return 'android'; return 'android';
break; break;
case 'openvk_native_ios':
case 'openvk_ios': case 'openvk_ios':
case 'openvk_legacy_ios': case 'openvk_legacy_ios':
return 'iphone'; return 'iphone';
@ -1738,4 +1740,52 @@ class User extends RowModel
{ {
return DatabaseConnection::i()->getContext()->table("blacklist_relations")->where("author", $this->getId())->count(); return DatabaseConnection::i()->getContext()->table("blacklist_relations")->where("author", $this->getId())->count();
} }
public function getEventCounters(array $list): array
{
$count_of_keys = sizeof(array_keys($list));
$ev_str = $this->getRecord()->events_counters;
$counters = [];
if (!$ev_str) {
for ($i = 0; $i < sizeof(array_keys($list)); $i++) {
$counters[] = 0;
}
} else {
$counters = unpack("S" . $count_of_keys, base64_decode($ev_str, true));
}
return [
'counters' => array_combine(array_keys($list), $counters),
'refresh_time' => $this->getRecord()->events_refresh_time,
];
}
public function stateEvents(array $state_list): void
{
$pack_str = "";
foreach ($state_list as $item => $id) {
$pack_str .= "S";
}
$this->stateChanges("events_counters", base64_encode(pack($pack_str, ...array_values($state_list))));
if (!$this->getRecord()->events_refresh_time) {
$this->stateChanges("events_refresh_time", time());
}
}
public function resetEvents(array $list): void
{
$values = [];
foreach ($list as $key => $val) {
$values[$key] = 0;
}
$this->stateEvents($values);
$this->stateChanges("events_refresh_time", time());
$this->save();
}
} }

View file

@ -147,6 +147,29 @@ final class AboutPresenter extends OpenVKPresenter
$this->redirect("https://github.com/openvk/openvk#readme"); $this->redirect("https://github.com/openvk/openvk#readme");
} }
public function renderAssetLinksJSON(): void
{
# Необходимо любому андроид приложению для автоматического разрешения принимать ссылки с этого сайта.
# Не шарю как писать норм на php поэтому тут чутка на вайбкодил - искренне ваш, ZAZiOs.
header("Content-Type: application/json");
$data = [
[
"relation" => ["delegate_permission/common.handle_all_urls"],
"target" => [
"namespace" => "android_app",
"package_name" => "oss.OpenVK.Native",
"sha256_cert_fingerprints" => [
"79:67:14:23:DC:6E:FA:49:64:1F:F1:81:0E:B0:A3:AE:6E:88:AB:0D:CF:BC:02:96:F3:6D:76:6B:82:94:D6:9C",
],
],
],
];
echo json_encode($data, JSON_UNESCAPED_SLASHES);
exit;
}
public function renderDev(): void public function renderDev(): void
{ {
$this->redirect("https://docs.ovk.to/"); $this->redirect("https://docs.ovk.to/");

View file

@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
namespace openvk\Web\Presenters;
use Chandler\Session\Session;
use Parsedown;
final class DevelopersPresenter extends OpenVKPresenter
{
public function renderMain(): void
{
$this->template->_template = "Developers/Main.xml";
$this->template->responseTime = round((microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000, 2);
}
public function renderStandalone(): void
{
$this->template->_template = "Developers/Standalone.xml";
}
public function noPage(): void
{
$this->template->_template = "Developers/NoPage.xml";
}
public function parseMarkdown(string $path)
{
if (!file_exists("$path")) {
$this->noPage();
return [];
}
$dataArray = array();
$lines = file($path);
if (!preg_match("%^OpenVK-KB-Heading: (.+)$%", $lines[0], $matches)) {
$heading = "Article $name";
} else {
$heading = $matches[1];
array_shift($lines);
}
$content = implode($lines);
$parser = new Parsedown();
$dataArray['heading'] = $heading;
$dataArray['content'] = $parser->text($content);
return $dataArray;
}
public function renderDevelopersBaseArticle(string $name): void
{
$lang = Session::i()->get("lang", "ru");
$base = OPENVK_ROOT . "/data/developers";
if (file_exists("$base/$name.$lang.md")) {
$file = "$base/$name.$lang.md";
} elseif (file_exists("$base/$name.md")) {
$file = "$base/$name.md";
} else {
$this->noPage();
return;
}
$parsedMd = $this->parseMarkdown($file);
$this->template->articlename = $name;
$this->template->heading = $parsedMd['heading'];
$this->template->content = $parsedMd['content'];
}
}

View file

@ -106,6 +106,10 @@ final class GiftsPresenter extends OpenVKPresenter
return; return;
} }
if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->user->identity, "gifts.send")) {
$this->flashFail("err", tr("error"), tr("limit_exceed_exception"));
}
$comment = empty($c = $this->postParam("comment")) ? null : $c; $comment = empty($c = $this->postParam("comment")) ? null : $c;
$notification = new GiftNotification($user, $this->user->identity, $gift, $comment); $notification = new GiftNotification($user, $this->user->identity, $gift, $comment);
$notification->emit(); $notification->emit();

View file

@ -68,6 +68,10 @@ final class GroupPresenter extends OpenVKPresenter
$club->setAbout(empty($this->postParam("about")) ? null : $this->postParam("about")); $club->setAbout(empty($this->postParam("about")) ? null : $this->postParam("about"));
$club->setOwner($this->user->id); $club->setOwner($this->user->id);
if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->user->identity, "groups.create")) {
$this->flashFail("err", tr("error"), tr("limit_exceed_exception"));
}
try { try {
$club->save(); $club->save();
} catch (\PDOException $ex) { } catch (\PDOException $ex) {
@ -79,6 +83,7 @@ final class GroupPresenter extends OpenVKPresenter
} }
$club->toggleSubscription($this->user->identity); $club->toggleSubscription($this->user->identity);
$this->redirect("/club" . $club->getId()); $this->redirect("/club" . $club->getId());
} else { } else {
$this->flashFail("err", tr("error"), tr("error_no_group_name")); $this->flashFail("err", tr("error"), tr("error_no_group_name"));
@ -103,6 +108,12 @@ final class GroupPresenter extends OpenVKPresenter
$this->flashFail("err", tr("error"), tr("forbidden")); $this->flashFail("err", tr("error"), tr("forbidden"));
} }
if (!$club->getSubscriptionStatus($this->user->identity)) {
if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->user->identity, "groups.sub")) {
$this->flashFail("err", tr("error"), tr("limit_exceed_exception"));
}
}
$club->toggleSubscription($this->user->identity); $club->toggleSubscription($this->user->identity);
$this->redirect($club->getURL()); $this->redirect($club->getURL());

View file

@ -103,6 +103,10 @@ final class ReportPresenter extends OpenVKPresenter
exit(json_encode([ "error" => "You can't report yourself" ])); exit(json_encode([ "error" => "You can't report yourself" ]));
} }
if ($this->user->identity->isBannedInSupport()) {
exit(json_encode([ "reason" => $this->queryParam("reason") ]));
}
if (in_array($this->queryParam("type"), ["post", "photo", "video", "group", "comment", "note", "app", "user", "audio", "doc"])) { if (in_array($this->queryParam("type"), ["post", "photo", "video", "group", "comment", "note", "app", "user", "audio", "doc"])) {
if (count(iterator_to_array($this->reports->getDuplicates($this->queryParam("type"), $id, null, $this->user->id))) <= 0) { if (count(iterator_to_array($this->reports->getDuplicates($this->queryParam("type"), $id, null, $this->user->id))) <= 0) {
$report = new Report(); $report = new Report();

View file

@ -418,6 +418,12 @@ final class UserPresenter extends OpenVKPresenter
if ($this->postParam("act") == "rej") { if ($this->postParam("act") == "rej") {
$user->changeFlags($this->user->identity, 0b10000000, true); $user->changeFlags($this->user->identity, 0b10000000, true);
} else { } else {
if ($user->getSubscriptionStatus($this->user->identity) == \openvk\Web\Models\Entities\User::SUBSCRIPTION_ABSENT) {
if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->user->identity, "friends.outgoing_sub")) {
$this->flashFail("err", tr("error"), tr("limit_exceed_exception"));
}
}
$user->toggleSubscription($this->user->identity); $user->toggleSubscription($this->user->identity);
} }

View file

@ -356,6 +356,10 @@ final class WallPresenter extends OpenVKPresenter
$this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_empty_or_too_big")); $this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_empty_or_too_big"));
} }
if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->user->identity, "wall.post")) {
$this->flashFail("err", tr("error"), tr("limit_exceed_exception"));
}
$should_be_suggested = $wall < 0 && !$wallOwner->canBeModifiedBy($this->user->identity) && $wallOwner->getWallType() == 2; $should_be_suggested = $wall < 0 && !$wallOwner->canBeModifiedBy($this->user->identity) && $wallOwner->getWallType() == 2;
try { try {
$post = new Post(); $post = new Post();

View file

@ -365,7 +365,7 @@
<a href="/terms" class="link">{_footer_rules}</a> <a href="/terms" class="link">{_footer_rules}</a>
<a href="/blog" class="link">{_footer_blog}</a> <a href="/blog" class="link">{_footer_blog}</a>
<a href="/support" class="link">{_footer_help}</a> <a href="/support" class="link">{_footer_help}</a>
<a href="/dev" target="_blank" class="link">{_footer_developers}</a> <a href="/dev" class="link">{_footer_developers}</a>
<a href="/privacy" class="link">{_footer_privacy}</a> <a href="/privacy" class="link">{_footer_privacy}</a>
</div> </div>
<p> <p>

View file

@ -37,7 +37,7 @@
<span class="nobold">{_"2fa_code_2"}: </span> <span class="nobold">{_"2fa_code_2"}: </span>
</td> </td>
<td class="regform-right"> <td class="regform-right">
<input id="password" type="password" name="password" required /> <input id="password" type="number" autocomplete="off" name="password" required />
</td> </td>
</tr> </tr>
{/if} {/if}

View file

@ -25,7 +25,7 @@
<span class="nobold">{_code}: </span> <span class="nobold">{_code}: </span>
</td> </td>
<td> <td>
<input type="text" name="code" autocomplete="off" required autofocus /> <input type="number" name="code" autocomplete="off" required autofocus />
</td> </td>
</tr> </tr>
<tr> <tr>

View file

@ -0,0 +1,57 @@
{extends "../@layout.xml"}
{block title}
{_developers_title}
{/block}
{block header}
{_developers_title}
{/block}
{block content}
{css "css/dev.css"}
<div class="knowledgeBaseArticle">{$content|noescape}</div>
<div id="dev_sidebar" class="dev_sidebar fl_r">
<div id="dev_mlist_cont">
<div id="dev_page_narrow">
<a
href="/dev/main"
class="dev_section_menu {if $articlename === 'main'} dev_mlist_sel{/if}"
>Документация</a>
<a
href="/dev/native"
class="dev_section_submenu {if $articlename === 'native'} dev_mlist_sel{/if}"
>Приложения и игры</a>
<a
href="/dev/standalone"
class="dev_section_submenu"
>Standalone/Mobile</a>
<a
href="/dev/sites"
class="dev_section_submenu {if $articlename === 'sites'} dev_mlist_sel{/if}"
>Сайты и виджеты</a>
<a
href="/dev/methods"
class="dev_section_menu {if $articlename === 'methods'} dev_mlist_sel{/if}"
>Список методов</a>
<a
href="/dev/datatypes"
class="dev_section_submenu {if $articlename === 'datatypes'} dev_mlist_sel{/if}"
>Типы данных</a>
<a
href="/dev/help"
class="dev_section_menu {if $articlename === 'help'} dev_mlist_sel{/if}"
>Поддержка</a>
<a
href="/dev/rules"
class="dev_section_submenu {if $articlename === 'rules'} dev_mlist_sel{/if}"
>Правила платформы</a>
</div>
</div>
</div>
{/block}

View file

@ -0,0 +1,92 @@
{extends "../@layout.xml"}
{block title}
{_developers_title}
{/block}
{block header}
{_developers_title}
{/block}
{block content}
{css "css/dev.css"}
<div class="dev_head">
<div class="dev_main_header">Разработка приложений</div>
<div class="dev_main_narrow">
<a
class="button"
href="/editapp?act=create"
>&nbsp;&nbsp;Создать приложение&nbsp;&nbsp;</a>
<br>
<br>
<a href="/apps?act=dev">Список моих приложений »</a>
</div>
<div class="dev_main_desc">
<b>оупэн В контакте</b> самый быстрорастущий сайт европейского интернета. Его аудитория удваивается каждые несколько месяцев, и сейчас на оупэн В контакте приходится более половины русскоязычного трафика. Если Вы хотите заниматься разработкой приложений в интернете, Вы можете приобщиться к этому успеху и получить доступ к аудитории свыше
<b>25000 (!!!)</b> пользователей.
</div>
</div>
<div class="dev_cont">
<div class="dev_main_sections">
<a href="/dev/native" class="dev_sect dev_sect_games">
<div class="dev_sect_icon"></div>
Игры и приложения ВКонтакте
</a>
<a href="/dev/standalone" class="dev_sect dev_sect_standalone">
<div class="dev_sect_icon"></div>
Mobile/Standalone приложения
</a>
<a href="/dev/sites" class="dev_sect dev_sect_widgets">
<div class="dev_sect_icon"></div>
Авторизация и виджеты для сайтов
</a>
</div>
<div class="dev_parts_list fl_l">
<a href="/dev/main" class="dev_part">
<div class="dev_part_name">Документация платформы</div>
<div class="dev_part_desc">Описание возможностей платформы ВКонтакте: процедура авторизации приложения, серверные и клиентские методы API, создание приложений и подключение сайтов.</div>
</a>
<a href="/dev/help" class="dev_part">
<div class="dev_part_name">Поддержка</div>
<div class="dev_part_desc">Разрешение вопросов, связанных с платформой: инструменты для связи со службой поддержки, попадание в каталог приложений, статус работы платформы.</div>
</a>
<a href="/dev/rules" class="dev_part">
<div class="dev_part_name">Правила платформы</div>
<div class="dev_part_desc">Правила публикации приложений на площадке ВКонтакте, размещение рекламы в приложениях, прием платежей от пользователей.</div>
</a>
<a href="/bagosi" class="dev_part">
<div class="dev_part_name">Баг-трекер</div>
<div class="dev_part_desc">Инструмент для информирования разработчиков о проблемах в API и документации платформы.</div>
</a>
</div>
<div class="dev_right_col fl_l">
<a class="dev_part dev_platform_info" href="/dev/health">
<div class="dev_info_header">Статус платформы</div>
Время ответа — <span class="dev_api_status_good">{$responseTime} ms</span><br>
Uptime — <span class="dev_api_status_good">целестора сделай%</span><br>
</a>
<div class="dev_last_news">
<div class="dev_info_header">Последние новости</div>
<div class="dev_main_widget_cont">
<ul class="notes_titles">
<li class="written">
<a href="/note1_1">
seks
</a>
<small>
только что
<span class="divide">|</span>
<a href="/note1_1">Комментарии</a>
</small>
</li>
</ul>
</div>
</div>
</div>
</div>
{/block}

View file

@ -0,0 +1,61 @@
{extends "../@layout.xml"}
{block title}
{_developers_nopage}
{/block}
{block header}
{_developers_nopage}
{/block}
{block content}
{css "css/dev.css"}
<div class="knowledgeBaseArticle" style="float: left">
<p>братишка ты что-то попутал</p>
</div>
<!-- я сделал так, чтобы можно было сделать норм 404 для девелоперов, типа рекомендуемые функции -->
<!-- или ВЫ ОШИБЛИСЬ, А МОЖЕТ ВЫ ХОТЕЛИ НАПИСАТЬ ...? ну вы поняли я надеюсь -->
<div id="dev_sidebar" class="dev_sidebar fl_r">
<div id="dev_mlist_cont">
<div id="dev_page_narrow">
<a
href="/dev/main"
class="dev_section_menu"
>Документация</a>
<a
href="/dev/native"
class="dev_section_submenu"
>Приложения и игры</a>
<a
href="/dev/standalone"
class="dev_section_submenu"
>Standalone/Mobile</a>
<a
href="/dev/sites"
class="dev_section_submenu"
>Сайты и виджеты</a>
<a
href="/dev/methods"
class="dev_section_menu"
>Список методов</a>
<a
href="/dev/datatypes"
class="dev_section_submenu"
>Типы данных</a>
<a
href="/dev/help"
class="dev_section_menu"
>Поддержка</a>
<a
href="/dev/rules"
class="dev_section_submenu"
>Правила платформы</a>
</div>
</div>
</div>
{/block}

View file

@ -0,0 +1,144 @@
{extends "../@layout.xml"}
{block title}
{_developers_title}
{/block}
{block header}
{_developers_title}
{/block}
{block content}
{css "css/dev.css"}
{script "js/al_dev.js"}
<div style="margin-left: 0px; width: 141.321px;" id="dev_steps_s">
<div class="borders">
<div class="content" style="margin-left: -2px;">
<div class="dev_step first">
<div>
<b>Developers</b><br>Where to start
</div>
</div>
<div class="dev_step">
<div>
<b>Documentation</b><br>VK API
</div>
</div>
<div class="dev_step last">
<div>
<b>Integration Examples</b><br>Consider the platform advantages
</div>
</div>
</div>
</div>
</div>
<div id="dev_steps">
<div class="dev_step first current" id="dev_step1" onclick="return dev_step(1);">
<div>
<b>Developers</b><br>Where to start
</div>
</div>
<div class="dev_step" id="dev_step2" onclick="return dev_step(2)">
<div>
<b>Documentation</b><br>VK API
</div>
</div>
<div class="dev_step last" id="dev_step3" onclick="return dev_step(3)">
<div>
<b>Integration Examples</b><br>Consider the platform advantages
</div>
</div>
</div>
<div class="dev_steps_c" id="dev_steps_c" style="height: 460px;">
<div class="content" style="margin-left: 0px;">
<div class="step" id="dev_step1_c" style="height: auto; position: static; left: auto;">
<div class="borders">
<div class="introPage">
<a href="/developers.php?id=-1_27971896&amp;s=1" class="info first apps"><span class="header">VK Flash/IFrame Applications</span>
Interactive appliation use <b>VK API</b> to deeply integrate with the site. You can use VK servers as well as any number of your own servers.
<span class="more">More »</span>
</a>
<a href="/developers.php?id=-1_37230422&amp;s=1" class="info mobile"><span class="header">Standalone/Mobile Applications</span>
These are launched in the same way as a regular program on a user's device, be it a desktop computer or a smartphone. All algorithms available to applications on the <b>VK API</b> platform work for desktop applications too.
<span class="more">More »</span>
</a>
<a href="/developers.php?id=-1_27987570&amp;s=1" class="info sites"><span class="header">Widgets and External Sites</span>
VK's lineup of <b>widgets</b> for external sites allows you to add a social aspect to your project. Also a deeper integration via <b>Open API</b> or <b>OAuth 2.0.</b> is available.
<span class="more">More »</span>
</a>
<a href="/developers.php?id=-1_12904887&s=1" class="info shops"><span class="header">Stores</span>
With the help of <b>Merchant API</b> you can connect your internet store or a VK API application to VK&#39;s payment system and gain full access to millions of potential customers.
<span class="more">More »</span>
</a>
</div>
</div>
</div>
<div class="step" id="dev_step2_c" style="position: static; left: auto; height: auto;">
<div class="borders">
<div class="introPage">
<!--4-->
<a name="Connect"></a>
<div class="wk_header">Connect</div>
<ul class="listing">
<li><span><a href="developers.php?oid=-17680044&amp;p=VK_Applications">VK Applications</a> Where to start creating Flash and IFrame applications </span></li>
<li><span><a href="developers.php?oid=-17680044&amp;p=Flash_Applications">Flash Applications</a> Interaction with API from Flash applications </span></li>
<li><span><a href="developers.php?oid=-17680044&amp;p=Using_the_Flash_Wrapper">Flash Applications with Wrapper</a> Interaction with API using the Flash wrapper </span></li>
<li><span><a href="developers.php?oid=-17680044&amp;p=IFrame_Applications">IFrame Applications</a> Interaction with API from IFrame applications </span></li>
<li><span><a href="developers.php?oid=-17680044&amp;p=Application_Interaction_with_API">Application Interaction with API</a> Calling API from an application or from an external server </span></li>
<li><span><a href="developers.php?oid=-17680044&amp;p=Authorization">Authorization</a> Authorization from Desktop and Mobile applications </span></li>
</ul>
<br>
<a name="VK API"></a>
<div class="wk_header">VK API</div>
<ul class="listing">
<li><span><a href="developers.php?oid=-17680044&amp;p=API_Method_Description">API Method Description</a> Standard API methods </span></li>
<li><span><a href="developers.php?oid=-17680044&amp;p=Advanced_API_Methods">Advanced API Methods</a> Methods for Desktop/Mobile applications </span></li>
<li><span><a href="developers.php?oid=-17680044&amp;p=Storing_Data_on_VK_Servers">Storing Data on VK Servers</a> </span></li>
<li><span><a href="developers.php?oid=-17680044&amp;p=Uploading_Files_to_the_VK_Server_Procedure">Uploading Files to the VK Server Procedure</a> </span></li>
<li><span><a href="developers.php?oid=-17680044&amp;p=Application_Localization">Application Localization</a> </span></li>
<li><span><a href="developers.php?oid=-17680044&amp;p=Ads_API">Ads API</a> <sup><b>beta</b></sup> methods for working with advertising accounts. </span></li>
</ul>
<br>
<a name="JS API"></a>
<div class="wk_header">JS API</div>
<ul class="listing">
<li><span><a href="developers.php?oid=-17680044&amp;p=IFrame_Applications">IFrame Applications</a> Calling JS API methods from IFrame applications </span></li>
<li><span><a href="developers.php?oid=-17680044&amp;p=Flash_Applications">Flash Applications</a> Calling JS API methods from Flash applications </span></li>
<li><span><a href="developers.php?oid=-17680044&amp;p=Using_the_Flash_Wrapper">Using the Flash Wrapper</a> Calling JS API methods from Flash applications using a wrapper </span></li>
</ul>
<br>
<a name="For Websites"></a>
<div class="wk_header">For Websites</div>
<ul class="listing">
<li><span><a href="developers.php?oid=-17680044&amp;p=Comments">Comments</a> Embed a universal comments block to an external website </span></li>
<li><span><a href="developers.php?oid=-17680044&amp;p=Groups">Groups</a> Embed a community block on an external website </span></li>
<li><span><a href="developers.php?oid=-17680044&amp;p=Like">Like</a> Let users "like" items on your site and share links with friends </span></li>
<li><span><a href="developers.php?oid=-17680044&amp;p=Poll">Polls</a> Poll your visitors and and let them pass the poll on to their friends </span></li>
<li><span><a href="developers.php?oid=-17680044&amp;p=Auth">Auth</a> Hassle-free authorization on your site </span></li>
<li><span><a href="developers.php?oid=-17680044&amp;p=Share">Share button</a> Let users quickly share links to your items </span></li>
<li><span><a href="developers.php?oid=-17680044&amp;p=Open_API">Open API</a> Use VK API on external websites </span></li>
</ul>
<br>
<a name="For Online Stores"></a>
<div class="wk_header">For Online Stores</div>
<ul class="listing">
<li><span><a href="developers.php?oid=-17680044&amp;p=Merchant_API">Merchant API</a> connect applications and external internet stores to the VK payment system </span></li>
</ul>
<!--ru:2438534-->
</div>
</div>
</div>
<div class="step" id="dev_step3_c" style="height: auto; position: static; left: auto;">
<div class="borders">
<div class="introPage">
<!--4--><b>VK</b> is the fastest growing site in Europe. Its audience is doubled every few months and now VK controls more than half of the traffic in Russian-speaking countries. If you would like to develop applications on the internet, you can become part of this success and gain access to an audience of more than <b>100 million</b> users. <br><br>
It is not uncommon for an <a class="wk_vk_link" href="/apps">an application</a> created within a month to be visited by millions of people daily. Every month the most popular applications bring their creators millions of dollars. If you want to become one of them, we will give you the tools for distributing and promoting applications and also will fully take on all <b>payment</b> matters, <b>traffic</b> costs and the translation of your application into <b>all languages spoken in the world</b>. <br><br>
<a href="developers.php?oid=-17680044&amp;p=Flash_Applications">VK API</a> is the most advanced platform for Flash and iFrame applications on the worldwide web. We provide a massive field for your applications (up to <b>827x4050</b> pixels), access to the left user menu, counters, on-site and <b>SMS</b> notifications, profile, photos, friends, rating and audio files of users, and much more. <br><br>
<a class="wk_vk_link" href="/apps">Applications Catalog</a> <br>
</div>
</div>
</div>
</div>
</div>
{/block}

View file

@ -67,7 +67,7 @@
}, },
success: (response) => { success: (response) => {
if (response?.reports?.length != _content) { if (response?.reports?.length != _content) {
NewNotification("Обратите внимание", "В списке появились новые жалобы. Работа ждёт :)"); // NewNotification("Обратите внимание", "В списке появились новые жалобы. Работа ждёт :)");
} }
if (response.reports.length > 0) { if (response.reports.length > 0) {

View file

@ -72,7 +72,7 @@
<span class="nobold">{_"2fa_code"}</span> <span class="nobold">{_"2fa_code"}</span>
</td> </td>
<td> <td>
<input type="text" name="email_change_code" style="width: 100%;" /> <input type="number" autocomplete="off" name="email_change_code" style="width: 100%;" />
</td> </td>
</tr> </tr>
<tr> <tr>
@ -161,7 +161,7 @@
<span class="nobold">{_"2fa_code"}</span> <span class="nobold">{_"2fa_code"}</span>
</td> </td>
<td> <td>
<input type="text" name="password_change_code" style="width: 100%;" /> <input type="number" autocomplete="off" name="password_change_code" style="width: 100%;" />
</td> </td>
</tr> </tr>
<tr> <tr>

View file

@ -0,0 +1,95 @@
<?php
declare(strict_types=1);
namespace openvk\Web\Util;
use openvk\Web\Models\Entities\User;
use openvk\Web\Models\RowModel;
use Chandler\Patterns\TSimpleSingleton;
class EventRateLimiter
{
use TSimpleSingleton;
private $config;
public function __construct()
{
$this->config = OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["eventsLimit"];
}
public function tryToLimit(?User $user, string $event_type, bool $is_update = true): bool
{
/*
Checks count of actions for last x seconds
Uses OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["eventsLimit"]
This check should be peformed only after checking other conditions cuz by default it increments counter
Returns:
true limit has exceed and the action must be restricted
false the action can be performed
Also returns "true" if this option is disabled
*/
$isEnabled = $this->config['enable'];
$isIgnoreForAdmins = $this->config['ignoreForAdmins'];
$restrictionTime = $this->config['restrictionTime'];
$eventsList = $this->config['list'];
if (!$isEnabled) {
return false;
}
if ($isIgnoreForAdmins && $user->isAdmin()) {
return false;
}
$eventsStats = $user->getEventCounters($eventsList);
$limitForThatEvent = $eventsList[$event_type];
$counters = $eventsStats["counters"];
$refresh_time = $eventsStats["refresh_time"];
$is_restrict_over = $refresh_time < (time() - $restrictionTime);
$event_counter = $counters[$event_type];
if ($refresh_time && $is_restrict_over) {
$user->resetEvents($eventsList);
return false;
}
$is_limit_exceed = $event_counter >= $limitForThatEvent;
if (!$is_limit_exceed && $is_update) {
$this->incrementEvent($counters, $event_type, $user);
}
return $is_limit_exceed;
}
public function incrementEvent(array $old_values, string $event_type, User $initiator): bool
{
/*
Updates counter for user
*/
$isEnabled = $this->config['enable'];
$eventsList = $this->config['list'];
if (!$isEnabled) {
return false;
}
$old_values[$event_type] += 1;
$initiator->stateEvents($old_values);
$initiator->save();
return true;
}
}

View file

@ -57,3 +57,4 @@ services:
- openvk\Web\Models\Repositories\Faves - openvk\Web\Models\Repositories\Faves
- openvk\Web\Presenters\MaintenancePresenter - openvk\Web\Presenters\MaintenancePresenter
- openvk\Web\Presenters\NoSpamPresenter - openvk\Web\Presenters\NoSpamPresenter
- openvk\Web\Presenters\DevelopersPresenter

View file

@ -407,8 +407,18 @@ routes:
handler: "About->robotsTxt" handler: "About->robotsTxt"
- url: "/humans.txt" - url: "/humans.txt"
handler: "About->humansTxt" handler: "About->humansTxt"
- url: "/.well-known/assetlinks.json"
handler: "About->AssetLinksJSON"
- url: "/dev" - url: "/dev"
handler: "About->dev" handler: "Developers->Main"
- url: "/dev/"
handler: "Developers->Main"
- url: "/dev/standalone"
handler: "Developers->Standalone"
- url: "/developers.php"
handler: "Developers->Standalone"
- url: "/dev/{text}"
handler: "Developers->DevelopersBaseArticle"
- url: "/iapi/getPhotosFromPost/{num}_{num}" - url: "/iapi/getPhotosFromPost/{num}_{num}"
handler: "InternalAPI->getPhotosFromPost" handler: "InternalAPI->getPhotosFromPost"
- url: "/iapi/getPostTemplate/{num}_{num}" - url: "/iapi/getPostTemplate/{num}_{num}"

416
Web/static/css/dev.css Normal file
View file

@ -0,0 +1,416 @@
.fl_r {
float: right;
}
.fl_l {
float: left;
}
.dev_parts_list {
width: 378px;
padding: 0;
}
.dev_part {
display: block;
padding: 15px 20px;
}
.dev_part:hover {
text-decoration: none;
background: #F7F7F7;
}
.dev_part_name {
display: block;
padding-bottom: 8px;
color: #4d7199;
font-weight: bold;
font-size: 1.2em;
}
.dev_part_desc {
color: #808080;
}
.dev_platform_info {
margin-top: 20px;
width: 200px;
padding: 15px 16px;
}
.dev_info_header {
display: block;
color: #4d7199;
font-weight: bold;
font-size: 1.2em;
border-bottom: 1px solid #DAE2E8;
margin-bottom: 8px;
}
.dev_api_status_good {
color: #038A07;
font-weight: bold;
}
.dev_api_status_bad {
color: #8A0307;
font-weight: bold;
}
.dev_last_news {
padding: 14px 16px 20px 16px;
width: 200px;
}
.dev_main_widget_cont {
height: 242px;
overflow: hidden;
}
.dev_head {
padding: 20px 10px 80px 40px;
background: #edf1f5;
-webkit-box-shadow: inset 0 -4px 2px -2px rgba(0,0,0,0.03);
-moz-box-shadow: inset 0 -4px 2px -2px rgba(0,0,0,0.03);
box-shadow: inset 0 -4px 2px -2px rgba(0,0,0,0.03);
margin: -10px -7px 0px -10px;
}
.dev_main_header {
color: #5c7b9b;
font-weight: bold;
font-size: 1.3em;
}
.dev_main_desc {
margin-top: 15px;
line-height: 160%;
max-width: 400px
}
.dev_main_narrow {
float: right;
width: 180px;
height: 100px;
padding: 35px 38px 0px 38px;
text-align: center;
}
.dev_sect {
display: block;
width: 168px;
height: 142px;
padding: 10px;
background: #f7f9fb;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
position: absolute;
-webkit-box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.2);
box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.2);
color: #5c7b9b;
text-align: center;
font-weight: bold;
font-size: 1.2em;
line-height: 160%;
-webkit-transition: all 200ms ease-out;
-moz-transition: all 200ms ease-out;
-o-transition: all 200ms ease-out;
transition: all 200ms ease-out;
}
.dev_sect:hover {
text-decoration: none;
background: #fafafc;
margin-top: -8px;
-webkit-box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.15);
-moz-box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.15);
box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.15);
}
.dev_main_sections {
padding-top: 20px;
padding-bottom: 25px;
height: 150px;
}
.dev_sect_games {
}
.dev_sect_standalone {
margin-left: 209px;
}
.dev_sect_widgets {
margin-left: 419px;
}
.dev_sect_icon {
width: 95px;
height: 83px;
margin: 8px 35px 10px;
background: url('/assets/packages/static/openvk/img/dev/dev_icons.png') no-repeat;
}
.dev_sect_standalone .dev_sect_icon {
background-position: 0px -82px;
}
.dev_sect_widgets .dev_sect_icon {
background-position: 0px -163px;
}
#dev_steps_s .content {
padding: 0;
}
.dev_cont {
margin-top: -70px;
}
#dev_steps {
height: 48px;
background-color: #F7F7F7;
border-color: #DAE2E8;
border-style: solid;
border-width: 1px 0;
width: 520px;
}
#dev_steps_s {
border: 1px solid #3B6798;
margin-left: 152px;
margin-top: -1px;
padding-right: 2px;
width: 141px;
position: absolute;
color: #ffffff;
text-shadow: 0 1px 1px #35587E;
}
#dev_steps_s .dev_step,
#dev_steps_s .dev_step.first {
border: none;
}
#dev_steps_s .borders {
background-color: #6D8FB3;
border-color: #7E9CBC #5C82AB #5C82AB;
border-right: 1px solid #5C82AB;
border-style: solid;
border-width: 1px;
height: 48px;
overflow: hidden;
position: relative;
width: 100%;
}
#dev_steps_s .content {
padding: 0;
}
#dev_steps .dev_step {
border-left: 1px solid #FFFFFF;
cursor: pointer;
float: left;
}
#dev_steps .dev_step.first,
#dev_steps .dev_step.first div {
border-color: #F7F7F7;
}
.dev_step div {
float: left;
height: 41px;
padding: 7px 0 0 22px;
width: 120px;
font-size: 10px;
}
#dev_steps .dev_step div {
border-left: 1px solid #EAEAEA;
padding: 7px 0 0 20px;
}
.dev_step div b {
font-size: 11px;
line-height: 18px;
}
.dev_step.last div {
width: 200px;
}
.dev_steps_c {
width: 600px;
margin-top: 10px;
overflow: hidden;
border: 1px solid #FFFFFF;
}
.content {
width: 2000px;
}
.step {
width: 587px;
margin-right: 13px;
height: 500px;
float: left;
}
.introPage .info {
display: block;
border-bottom: 1px solid #d9d9d9;
padding: 15px 0px 20px 70px;
height: 80px;
color: #333333;
}
.introPage .info:focus {
outline: none;
}
.introPage .info.first {
padding: 0px 0px 20px 70px;
}
.introPage .info:hover {
text-decoration: none;
}
.introPage .info .header {
display: block;
font-weight: bold;
margin-bottom: 3px;
color: #2B587A;
}
.introPage .info .more {
display: block;
margin-top: 6px;
color: #2B587A;
}
.introPage .info.apps {
background: url('/assets/packages/static/openvk/img/dev/dev_apps.png') 0px 6px no-repeat;
}
.introPage .info.sites {
background: url('/assets/packages/static/openvk/img/dev/dev_sites.png') 0px 21px no-repeat;
}
.introPage .info.mobile {
background: url('/assets/packages/static/openvk/img/dev/dev_mobile.png') 0px 21px no-repeat;
}
.introPage .info.shops {
background: url('/assets/packages/static/openvk/img/dev/dev_shops.png') 0px 21px no-repeat;
border-bottom: none;
}
.wk_header,
.dev_page_cont .wk_header {
border-bottom: none;
font-size: 1.2em;
}
.introPage .listing {
padding-left: 14px;
}
.listing span {
font-size: 11px;
font-weight: normal;
}
.listing span.bold {
font-weight: bold;
}
h3 {
border-bottom: 1px solid #B9C4DA;
font-size: 12px;
color: #45688E;
margin-bottom: 0px;
font-weight: bold;
padding-bottom: 2px;
}
.dev_sidebar {
margin: 5px 5px 5px 5px;
background: #F0F2F4;
width: 156px;
padding: 0px 10px 10px 10px;
}
.dev_section_menu, .dev_section_submenu {
display: block;
padding: 4px 8px 5px 8px;
overflow: hidden;
text-overflow: ellipsis;
}
.dev_section_menu {
margin-top: 10px;
font-weight: bold;
}
.dev_section_menu:hover, .dev_section_submenu:hover {
text-decoration: none;
background: #E0E5E9;
}
.dev_section_submenu {
padding-left: 20px;
}
.dev_mlist_sel {
background: #5e82a8;
color: #FFFFFF;
}
.dev_mlist_sel:hover {
background: #5e82a8;
}
.knowledgeBaseArticle {
float: left;
width: 424px;
max-width: 424px;
}
.knowledgeBaseArticle img {
max-width: 424px;
}
pre {
padding: 10px;
border-left: 9px solid #C9D8DF;
border-bottom: 2px solid #EBEFF2;
background-color: #F5F7F8;
margin: 10px 0px 0px 0px;
}
h1 {
margin: 0px;
padding: 0px;
font-size: 14px; }
h1 a { color: white; text-decoration: none; }
h1 a:hover { color: white; text-decoration: underline; }
h1 em { font-style: normal; border-bottom: solid 1px #DAE1E8; }
h2 {
font-size: 11px;
margin: 0px;
padding: 0px; }
h6 { border-bottom: 1px solid #DAE1E8;
color: #36638E;
margin:0px; padding:0px;
font-size: 14px;
padding-bottom: 5px; }
h3 {
color: #444;
font-size: 11px;
font-weight: normal;
margin: 0px;
padding: 0px; }
h4 {
color: #36638E;
font-size: 11px;
margin: 0px 0px 6px 8px;}
h4 .edit { display: inline; margin-left: 8px; }
h4 .edit a { color: lightgrey; }
h4 .edit a:hover { color: #36638E; }
h5 {
color: #36638E;
font-size: 11px;
margin: 0px 0px 6px 8px; }
h5 .edit { display: inline; margin-left: 8px; }
h5 .edit a { color: lightgrey; }
h5 .edit a:hover { color: #36638E; }
h3 {
border-bottom:1px solid #B9C4DA;
font-size: 12px;
color:#45688E;
margin-bottom: 0px;
font-weight: bold;
padding-bottom:2px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

147
Web/static/js/al_dev.js Normal file
View file

@ -0,0 +1,147 @@
var nav = null;
function init_dev_steps(step) {
this.step = step;
this.moving = false;
this.steps_glass = document.getElementById('dev_steps_s');
this.steps_glass_c = this.steps_glass.querySelector('.content');
this.steps_wrap = document.getElementById('dev_steps_c');
this.steps_content = this.steps_wrap.querySelector('.content');
this.easeInOut = function(t, b, c, d) {
t /= d/2;
if (t < 1) return c/2*t*t + b;
t--;
return -c/2 * (t*(t-2) - 1) + b;
};
this.move = function(toStep) {
var self = this;
if (toStep === this.step || this.moving) return;
this.moving = true;
var startGlass = (this.step - 1) * 140;
var startContent = (1 - this.step) * 600;
var startHeight = this.steps_wrap.offsetHeight;
var endGlass = (toStep - 1) * 140;
var endContent = (1 - toStep) * 600;
var targetNode = document.getElementById('dev_step' + toStep + '_c');
targetNode.style.position = 'absolute';
targetNode.style.left = '-5000px';
var endHeight = targetNode.querySelector('.borders').offsetHeight + 12;
targetNode.style.position = 'static';
targetNode.style.left = 'auto';
for (var i = 1; i <= 3; i++) {
var stepEl = document.getElementById('dev_step' + i + '_c');
stepEl.style.height = 'auto';
stepEl.style.position = 'static';
stepEl.style.left = 'auto';
}
var duration = 400;
var startTime = Date.now();
var startGlassWidth = (this.step === 3) ? 236 : 141;
var endGlassWidth = (toStep === 3) ? 236 : 141;
function animate() {
var elapsed = Date.now() - startTime;
var progress = Math.min(elapsed / duration, 1);
var easedProgress = self.easeInOut(progress, 0, 1, 1);
var currentGlass = startGlass + (endGlass - startGlass) * easedProgress;
var currentContent = startContent + (endContent - startContent) * easedProgress;
var currentHeight = startHeight + (endHeight - startHeight) * easedProgress;
var currentGlassWidth = startGlassWidth + (endGlassWidth - startGlassWidth) * easedProgress;
self.steps_glass.style.marginLeft = currentGlass + 'px';
self.steps_glass_c.style.marginLeft = (-currentGlass - 2) + 'px';
self.steps_content.style.marginLeft = currentContent + 'px';
self.steps_wrap.style.height = currentHeight + 'px';
self.steps_glass.style.width = currentGlassWidth + 'px';
if (progress < 1) {
requestAnimationFrame(animate);
} else {
self.step = toStep;
self.moving = false;
if (window.history && window.history.replaceState) {
window.history.replaceState(null, null, '#devstep' + toStep);
} else {
location.hash = 'devstep' + toStep;
}
}
}
requestAnimationFrame(animate);
};
this.steps_wrap.style.height = 'auto';
var initialHeight = document.getElementById('dev_step' + this.step + '_c').querySelector('.borders').offsetHeight + 12;
this.steps_wrap.style.height = initialHeight + 'px';
this.steps_glass.style.marginLeft = ((this.step - 1) * 140) + 'px';
this.steps_glass_c.style.marginLeft = (-(this.step - 1) * 140 - 2) + 'px';
this.steps_content.style.marginLeft = ((1 - this.step) * 600) + 'px';
this.steps_glass.style.width = (this.step === 3 ? 236 : 141) + 'px';
this.steps_glass.style.display = 'block';
this.steps_content.style.display = 'block';
for (var i = 1; i <= 3; i++) {
var stepElement = document.getElementById('dev_step' + i + '_c');
stepElement.style.height = 'auto';
stepElement.style.position = 'static';
stepElement.style.left = 'auto';
}
}
function dev_step(toStep) {
if (nav) {
nav.move(toStep);
}
return false;
}
function onDomReady(callback) {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', callback);
} else {
callback();
}
}
window.prevSection = false;
function slideSection(obj, directly) {
if (!directly) obj = obj.parentNode.children[1];
if (window.prevSection && window.prevSection !== obj) {
window.prevSection.style.display = 'none';
}
window.prevSection = obj;
obj.style.display = obj.style.display === 'none' ? 'block' : 'none';
}
onDomReady(function() {
var step = 1;
var hash = location.hash;
var match = hash.match(/devstep(\d)/);
if (match) {
var hashStep = parseInt(match[1]);
if (hashStep >= 1 && hashStep <= 3) {
nav = new init_dev_steps(hashStep);
dev_step(hashStep);
}
} else {
nav = new init_dev_steps(1);
}
});

View file

@ -3,4 +3,6 @@
<Client tag="vk4me" name="VK4ME" url="http://vk4me.crx.moe/" img="/assets/packages/static/openvk/img/app_icons/vk4me.png" /> <Client tag="vk4me" name="VK4ME" url="http://vk4me.crx.moe/" img="/assets/packages/static/openvk/img/app_icons/vk4me.png" />
<Client tag="openvk_legacy_android" name="OpenVK Legacy" url="https://f-droid.org/packages/uk.openvk.android.legacy/" img="/assets/packages/static/openvk/img/app_icons/openvk_legacy.png" /> <Client tag="openvk_legacy_android" name="OpenVK Legacy" url="https://f-droid.org/packages/uk.openvk.android.legacy/" img="/assets/packages/static/openvk/img/app_icons/openvk_legacy.png" />
<Client tag="openvk_refresh_android" name="OpenVK Refresh" url="https://github.com/openvk/mobile-android-refresh" img="/assets/packages/static/openvk/img/app_icons/openvk_refresh.png" /> <Client tag="openvk_refresh_android" name="OpenVK Refresh" url="https://github.com/openvk/mobile-android-refresh" img="/assets/packages/static/openvk/img/app_icons/openvk_refresh.png" />
</Clients> <Client tag="openvk_native" name="OpenVK Native" url="https://ovk.to/club9628" img="/assets/packages/static/openvk/img/app_icons/openvk_native.png" />
<Client tag="openvk_native_ios" name="OpenVK Native" url="https://ovk.to/club9628" img="/assets/packages/static/openvk/img/app_icons/openvk_native.png" />
</Clients>

View file

@ -0,0 +1 @@
datatypes fallback

1
data/developers/help.md Normal file
View file

@ -0,0 +1 @@
help fallback

68
data/developers/main.md Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
methods fallback

View file

@ -0,0 +1 @@
native fallback

1
data/developers/rules.md Normal file
View file

@ -0,0 +1 @@
rules fallback

1
data/developers/sites.md Normal file
View file

@ -0,0 +1 @@
sites fallback

View file

@ -0,0 +1,3 @@
ALTER TABLE `profiles`
ADD `events_counters` VARCHAR(299) NULL DEFAULT NULL AFTER `audio_broadcast_enabled`,
ADD `events_refresh_time` BIGINT(20) UNSIGNED NULL DEFAULT NULL AFTER `events_counters`;

View file

@ -1657,6 +1657,8 @@
"error_geolocation" = "Error while trying to pin geolocation"; "error_geolocation" = "Error while trying to pin geolocation";
"error_no_geotag" = "There is no geo-tag pinned in this post"; "error_no_geotag" = "There is no geo-tag pinned in this post";
"limit_exceed_exception" = "You're doing this action too often. Try again later.";
/* Admin actions */ /* Admin actions */
"login_as" = "Login as $1"; "login_as" = "Login as $1";

View file

@ -1462,7 +1462,7 @@
"error_access_denied_short" = "Ошибка доступа"; "error_access_denied_short" = "Ошибка доступа";
"error_access_denied" = "У вас недостаточно прав, чтобы редактировать этот ресурс"; "error_access_denied" = "У вас недостаточно прав, чтобы редактировать этот ресурс";
"success" = "Успешно"; "success" = "Успешно";
"comment_will_not_appear" = "Этот комментарий больше не будет показыватся."; "comment_will_not_appear" = "Этот комментарий больше не будет показываться.";
"error_when_gifting" = "Не удалось подарить"; "error_when_gifting" = "Не удалось подарить";
"error_user_not_exists" = "Пользователь или набор не существуют."; "error_user_not_exists" = "Пользователь или набор не существуют.";
@ -1561,6 +1561,8 @@
"error_geolocation" = "Ошибка при прикреплении геометки"; "error_geolocation" = "Ошибка при прикреплении геометки";
"error_no_geotag" = "У поста не указана гео-метка"; "error_no_geotag" = "У поста не указана гео-метка";
"limit_exceed_exception" = "Вы совершаете это действие слишком часто. Повторите позже.";
/* Admin actions */ /* Admin actions */
"login_as" = "Войти как $1"; "login_as" = "Войти как $1";

View file

@ -41,6 +41,16 @@ openvk:
maxViolations: 50 maxViolations: 50
maxViolationsAge: 120 maxViolationsAge: 120
autoban: true autoban: true
eventsLimit:
enable: true
ignoreForAdmins: true
restrictionTime: 86400
list:
groups.create: 5
groups.sub: 50
friends.outgoing_sub: 25
wall.post: 5000
gifts.send: 30
blacklists: blacklists:
limit: 100 limit: 100
applyToAdmins: true applyToAdmins: true

View file

@ -1,321 +1,378 @@
body { body {
background: url("/themepack/openvk_modern/0.0.1.0/resource/1.png") repeat-x fixed; background: url("/themepack/openvk_modern/0.0.1.0/resource/1.png") repeat-x
} fixed;
}
.page_header {
position: fixed; .page_header {
height: 42px; position: fixed;
background: #3C3C3C; height: 42px;
z-index: 199; background: #3c3c3c;
} z-index: 199;
}
.home_button {
background: #3C3C3C url("/themepack/openvk_modern/0.0.1.0/resource/2.png") no-repeat; .home_button {
background-size: 80%; background: #3c3c3c url("/themepack/openvk_modern/0.0.1.0/resource/2.png")
background-position-y: 0px; no-repeat;
background-position-x: 1px; background-size: 80%;
} background-position-y: 0px;
background-position-x: 1px;
.home_button_custom { }
background: #3C3C3C url("/themepack/openvk_modern/0.0.1.0/resource/4.png") no-repeat;
background-size: 80%; .home_button_custom {
background-position-y: 0px; background: #3c3c3c url("/themepack/openvk_modern/0.0.1.0/resource/4.png")
background-position-x: 1px; no-repeat;
text-shadow: none; background-size: 80%;
} background-position-y: 0px;
background-position-x: 1px;
.header_navigation .link, .header_navigation .header_divider_stick { width: 145px !important;
background: unset !important; text-shadow: none;
} }
.header_navigation .link a:hover { .header_navigation .link,
text-decoration: none; .header_navigation .header_divider_stick {
} background: unset !important;
}
.sidebar {
margin-top: 47px; .header_navigation .link a:hover {
position: fixed; text-decoration: none;
} }
.page_body { .header_navigation #search_box .search_box_button {
margin-top: 42px; border: solid 1px #606060;
} box-shadow: unset;
}
.toTop {
margin-top: 42px; .header_navigation #search_box .search_box_button:active {
} background-color: #606060;
box-shadow: unset;
.content_title_expanded { }
cursor: pointer;
background-image: unset !important; .sidebar {
padding: 3px 10px; margin-top: 47px;
border-top: #e6e6e6 solid 1px; position: fixed;
} background-color: #fff;
z-index: 199;
.content_title_unexpanded { }
background-image: unset !important;
padding: 3px 10px; .page_body {
border-top: #eee solid 1px; margin-top: 42px;
} }
.content_subtitle { .toTop {
border-top: #F0F0F0 solid 1px; margin-top: 42px;
border-bottom: 1px solid #F0F0F0; }
}
.content_title_expanded {
.user-alert { cursor: pointer;
border: 1px solid #f3ddbd; background-image: unset !important;
} padding: 3px 10px;
border-top: #e6e6e6 solid 1px;
.msg { }
border: 1pt solid #e6f2f3;
} .content_title_unexpanded {
background-image: unset !important;
.msg.msg_succ { padding: 3px 10px;
border-color: #ddf3d7; border-top: #eee solid 1px;
} }
.msg.msg_err { .content_subtitle {
border-color: #f5e9ec; border-top: #f0f0f0 solid 1px;
} border-bottom: 1px solid #f0f0f0;
}
.navigation .link:hover {
border-top: 1px solid #E4E4E4; .user-alert {
} border: 1px solid #f3ddbd;
}
#profile_link, .profile_link {
border-bottom: 1px solid transparent; .msg {
} border: 1pt solid #e6f2f3;
}
.completeness-gauge-gold {
border: 1px solid #f6ebbb; .msg.msg_succ {
} border-color: #ddf3d7;
}
.post-author {
border-top: #fff solid 1px; .msg.msg_err {
border-bottom: #fff solid 1px; border-color: #f5e9ec;
background-color: #fff; }
padding: 0px 5px 3px;
} .navigation .link:hover {
border-top: 1px solid #e4e4e4;
.post-author .date { }
color: gray;
} #profile_link,
.profile_link {
.page_yellowheader { border-bottom: 1px solid transparent;
background: #E2E2E2; }
border-right: solid 1px #E2E2E2;
border-left: solid 1px #E2E2E2; .completeness-gauge {
border-bottom: solid 1px #E2E2E2; width: 100%;
} border: unset;
border-top: unset;
.page_yellowheader span { }
color: #BBBBBB;
} .post-author {
border-top: #fff solid 1px;
.page_yellowheader a { border-bottom: #fff solid 1px;
color: #5C5C5C; background-color: #fff;
} padding: 0px 5px 3px;
}
.page-wrap {
border-bottom: solid 1px #fff; .post-author .date {
border-left: solid 1px #fff; color: gray;
border-right: solid 1px #fff; }
}
.page_yellowheader {
.page_wrap { background: #e2e2e2;
border-bottom: solid 1px #fff; border-right: solid 1px #e2e2e2;
border-left: solid 1px #fff; border-left: solid 1px #e2e2e2;
border-right: solid 1px #fff; border-bottom: solid 1px #e2e2e2;
} }
#wrapHI { .page_yellowheader span {
border-right: solid 1px #E2E2E2; color: #bbbbbb;
border-left: solid 1px #E2E2E2; }
}
.page_yellowheader a {
.left_small_block { color: #5c5c5c;
border-right: 1px #fff solid; }
}
.page-wrap {
.menu_divider { border-bottom: solid 1px #fff;
background: #E5E5E5; border-left: solid 1px #fff;
} border-right: solid 1px #fff;
}
.postFeedWrapper {
border-bottom: 1px solid rgb(240,240,240); .page_wrap {
} border-bottom: solid 1px #fff;
border-left: solid 1px #fff;
.container_gray { border-right: solid 1px #fff;
border-top: #EBEBEB solid 1px; }
}
#wrapHI {
.container_gray .content { border-right: solid 1px #e2e2e2;
border: #E5E5E5 solid 1px; border-left: solid 1px #e2e2e2;
} }
.accent-box { .left_small_block {
border: 1px solid white; border-right: 1px #fff solid;
} }
input[type="text"], input[type="password"], input[type~="text"], .menu_divider {
input[type~="password"], input[type="email"], input[type="phone"], background: #e5e5e5;
input[type~="email"], input[type~="phone"], input[type="date"], }
input[type~="date"], input[type="search"], input[type~="search"],
textarea, select { .postFeedWrapper {
border: 1px solid #E5E5E5; border-bottom: 1px solid rgb(240, 240, 240);
} }
input[type=checkbox] { .container_gray {
background-image: url("/themepack/openvk_modern/0.0.1.0/resource/6.png") border-top: #ebebeb solid 1px;
} }
ul { .container_gray .content {
list-style: none; border: #e5e5e5 solid 1px;
list-style-type: disc; }
}
.accent-box {
.mb_tab#active div { border: 1px solid white;
border: 2px solid #898989; }
}
input[type="text"],
.summaryBar { input[type="password"],
border-bottom: #fff solid 1px; input[type~="text"],
} input[type~="password"],
input[type="email"],
.page_footer .link:hover { input[type="phone"],
border-top: 0px; input[type~="email"],
} input[type~="phone"],
input[type="date"],
.ovk-video > .preview { input[type~="date"],
border: #fff; input[type="search"],
} input[type~="search"],
textarea,
.crp-list { select {
border-top: 1px solid #fff; border: 1px solid #e5e5e5;
width: 629px; }
}
input[type="checkbox"] {
.crp-entry:first-of-type { background-image: url("/themepack/openvk_modern/0.0.1.0/resource/6.png");
border-color: #E5E5E5; }
}
ul {
.crp-entry { list-style: none;
width: 593px; list-style-type: disc;
border-color: #E5E5E5; }
}
.like_tooltip_wrapper .like_tooltip_head {
#faqhead { background: #515151;
border: 1px solid #FBF3C3; box-shadow: unset;
} border: solid 1px #515151;
}
#faqcontent {
border: 1px solid #FAFAFA; .like_tooltip_wrapper .like_tooltip_body {
} border: 1px solid #515151;
}
.ovk-diag {
border: none; .mb_tab#active div {
border-radius: 2px; border: 2px solid #898989;
} }
.ovk-diag-cont { .summaryBar {
border-radius: 2px; border-bottom: #fff solid 1px;
} }
.ovk-diag-head { .page_footer .link:hover {
border-bottom: 1px solid #757575; border-top: 0px;
border-top-left-radius: 2px; }
border-top-right-radius: 2px;
} .ovk-video > .preview {
border: #fff;
.ovk-diag-action { }
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px; .crp-list {
} border-top: 1px solid #fff;
width: 629px;
#votesBalance { }
border-bottom: none;
} .crp-entry:first-of-type {
border-color: #e5e5e5;
.floating_sidebar,.floating_sidebar.show { }
display:none
} .crp-entry {
width: 593px;
#backdrop:before { border-color: #e5e5e5;
content:""; }
display:block;
position:fixed; #faqhead {
top:0; border: 1px solid #fbf3c3;
left:0; }
height:42px;
width:100%; #faqcontent {
background-color:#3c3c3c border: 1px solid #fafafa;
} }
.search_box_button { .ovk-diag {
box-shadow: none; border: none;
} border-radius: 2px;
}
.search_box_button:active {
box-shadow: none; .ovk-diag-cont {
} border-radius: 2px;
}
.verticalGrayTabs #used {
background: #3c3c3c !important; .ovk-diag-head {
border: 1px solid #3c3c3c; border-bottom: 1px solid #757575;
} border-top-left-radius: 2px;
border-top-right-radius: 2px;
.verticalGrayTabs #used a { }
color: white;
} .ovk-diag-action {
border-bottom-left-radius: 2px;
.search_option_name { border-bottom-right-radius: 2px;
background-color: #a4a4a4; }
border-bottom: unset;
} #votesBalance,
#news {
.verticalGrayTabsWrapper { border-bottom: unset;
border-top: unset; }
}
.floating_sidebar,
.sugglist { .floating_sidebar.show {
border-top: unset; display: none;
border-bottom: 1px solid gray; }
}
#backdrop:before {
.musicIcon { content: "";
filter: contrast(200%) !important; display: block;
} position: fixed;
top: 0;
.audioEntry .playerButton .playIcon { left: 0;
filter: contrast(2) !important; height: 42px;
} width: 100%;
background-color: #3c3c3c;
.audioEmbed .track > .selectableTrack, .bigPlayer .selectableTrack { }
border-top: #404040 1px solid !important;
} .search_box_button {
box-shadow: none;
.bigPlayer .paddingLayer .slider, .audioEmbed .track .slider { }
background: #3c3c3c !important;
} .search_box_button:active {
box-shadow: none;
.audioEntry.nowPlaying { }
background: #4b4b4b !important;
} .verticalGrayTabs #used {
background: #3c3c3c !important;
.audioEntry.nowPlaying:hover { border: 1px solid #3c3c3c;
background: #373737 !important; }
}
.verticalGrayTabs #used a {
.musicIcon.pressed { color: white;
filter: brightness(150%) !important; }
}
.search_option_name {
.musicIcon.lagged { background-color: #a4a4a4;
opacity: 50%; border-bottom: unset;
} }
.bigPlayer { .verticalGrayTabsWrapper {
position: sticky; border-top: unset;
top: 42px; border-left: unset;
} }
.sugglist {
border-top: unset;
border-bottom: 1px solid gray;
}
.musicIcon {
filter: contrast(200%) !important;
}
.audioEntry .playerButton .playIcon {
filter: contrast(2) !important;
}
.audioEmbed .track > .selectableTrack,
.bigPlayer .selectableTrack {
border-top: #404040 1px solid !important;
}
.bigPlayer .paddingLayer .slider,
.audioEmbed .track .slider {
background: #3c3c3c !important;
}
.audioEntry.nowPlaying {
background: #4b4b4b !important;
}
.audioEntry.nowPlaying:hover {
background: #373737 !important;
}
.musicIcon.pressed {
filter: brightness(150%) !important;
}
.musicIcon.lagged {
opacity: 50%;
}
.bigPlayer {
position: sticky;
top: 42px;
box-shadow: unset;
}
#audio_upload {
border: 1px solid #ccc;
}
#wallAttachmentMenu {
box-shadow: unset;
}
#backdropEditor {
border: unset;
}