diff --git a/CODE_OF_CONFLICT.md b/CODE_OF_CONFLICT.md
new file mode 100644
index 00000000..e45656d3
--- /dev/null
+++ b/CODE_OF_CONFLICT.md
@@ -0,0 +1,11 @@
+The OpenVK development effort is a very personal process compared to "traditional" ways of developing software.
+Your code and ideas behind it will be carefully reviewed, often resulting in critique and criticism.
+The review will almost always require improvements to the code before it can be included in the repo.
+Know that this happens because everyone involved wants to see the best possible solution for the overall success of OpenVK.
+
+If however, anyone feels personally abused, threatened, or otherwise uncomfortable due to this process, that is not acceptable.
+If so, please contact the community manager @WerySkok and report the issue.
+
+As a reviewer of code, please strive to keep things civil and focused on the technical issues involved. We are all humans,
+and frustrations can be high on both sides of the process.
+Try to keep in mind the immortal words of Bill and Ted, "Be excellent to each other."
diff --git a/CODE_STYLE.md b/CODE_STYLE.md
new file mode 100644
index 00000000..85f5ab74
--- /dev/null
+++ b/CODE_STYLE.md
@@ -0,0 +1,277 @@
+# Names
+## Namespace Names
+Namespaces should be written in PascalCase.
+
+## File Names
+Code directories should have their name written in PascalCase. Code files should contain only one class and have the name of that class.
+In case of multiple class definitions in one file, it's name should be the same as the "primary" class name.
+Non-code directories, non-class and non-code files should be named in lisp-case.
+
+## Variable Names
+Variable names should be written in camelCase. This also applies to function arguments, class instance names and methods.
+
+## Constant Names
+Constants are written in SCREAMING_SNAKE_CASE, but should be declared case-insensetive.
+
+## Class Names
+Classes in OpenVK should belong to `openvk\` namespace and be in the corresponding directory (according to PSR-4). Names of classes should be written in PascalCase.
+
+## Function Names
+camelCase and snake_case are allowed, but first one is the recommended way. This rule does not apply to class methods, which are written in camelCase only.
+
+---
+
+# Coding Rules
+## File header
+All OpenVK files must start with `where("meow", true);
+$photo = $photos->fetch();
+$arr = [
+ "a" => 10,
+ "bb" => true,
+];
+
+# NOT OK
+$photos = (new Photos)->where("meow", true);
+$photo = $photos->fetch();
+$arr = [
+ "a" => 10,
+ "bb" => true,
+];
+```
+
+## Tab/Space
++ **Do not use tabs**. Use spaces, as tabs are defined differently for different editors and printers.
++ Put one space after a comma and semicolons: `exp(1, 2)` `for($i = 1; $i < 100; $i++)`
++ Put one space around assignment operators: `$a = 1`
++ Always put a space around conditional operators: `$a = ($a > $b) ? $a : $b`
++ Do not put spaces between unary operators and their operands, primary operators and keywords:
+```php
+# OK
+-$a;
+$a++;
+$b[1] = $a;
+fun($b);
+if($a) { ... }
+
+# NOT OK
+- $a;
+$a ++;
+$b [1] = $a;
+fun ($b);
+if ($a) { ... }
+```
+
+## Blank Lines
++ Use blank lines to create paragraphs in the code or comments to make the code more understandable
++ Use blank lines before `return` statement if it isn't the only statement in the block
++ Use blank lines after shorthand if/else/etc
+```php
+# OK
+if($a)
+ return $x;
+
+doSomething();
+
+return "yay";
+
+# NOT OK
+if($a) return $x; # return must be on separate line
+doSomething(); # doSomething must be separated by an extra blank line after short if/else
+return "yay"; # do use blank lines before return statement
+```
+
+
+## Method/Function Arguments
++ When all arguments for a function do not fit on one line, try to line up the first argument in each line:
+
+
++ If the argument lists are still too long to fit on the line, you may line up the arguments with the method name instead.
+
+## Maximum characters per line
+Lines should be no more than 80 characters long.
+
+## Usage of curly braces
++ Curly braces should be on separate line for class, method, and function definitions.
++ In loops, if/else, try/catch, switch constructions the opening brace should be on the same line as the operator.
++ Braces must be ommited if the block contains only one statement **AND** the related blocks are also single statemented.
++ Nested single-statement+operator blocks must not be surrounded by braces.
+```php
+# OK
+class A
+{
+ function doSomethingFunny(): int
+ {
+ return 2;
+ }
+}
+
+if(true) {
+ doSomething();
+ doSomethingElse();
+} else {
+ doSomethingFunny();
+}
+
+if($a)
+ return false;
+else
+ doSomething();
+
+foreach($b as $c => $d)
+ if($c == $d)
+ unset($b[$c]);
+
+# NOT OK
+class A {
+ function doSomethingFunny(): int {
+ return 2;
+ }
+}
+
+if(true) {
+ doSomething();
+ doSomethingElse();
+} else
+ doSomethingFunny(); # why?
+
+if($a) {
+ return false;
+} else {
+ doSomething();
+}
+
+foreach($b as $c => $d) {
+ if($c == $d)
+ unset($b[$c]);
+}
+
+# lmao
+if($a) { doSomething(); } else doSomethingElse();
+```
+
+## if/else, try/catch
++ Operators must not be indented with space from their operands but must have 1-space margin from braces:
+```php
+# OK
+if($a) {
+ doSomething();
+ doSomethingElse();
+} else if($b) {
+ try {
+ nukeSaintPetersburg('😈');
+ } finally {
+ return PEACE;
+ }
+}
+
+# NOT OK
+if ($a) { # do not add space between control flow operator IF and it's operand
+ doSomething();
+ doSomethingElse();
+}elseif($b){ # do add margin from braces; also ELSE and IF should be separate here
+ try{
+ nukeSaintPetersburg('😈');
+ }finally{
+ return PEACE;
+ }
+}
+```
+
+## Switches
++ `break` must be on same indentation level as the code of le case (not the case definiton itself)
++ If there is no need to `break` a comment `# NOTICE falling through` must be places instead
+```php
+# OK
+switch($a) {
+ case 1:
+ echo $a;
+ break;
+
+ case 2:
+ echo $a++;
+ # NOTICE falling through
+
+ default:
+ echo "c";
+}
+
+# NOT OK
+switch($a) {
+ case 1:
+ echo $a;
+ break;
+
+ case 2:
+ echo $a++;
+
+ default:
+ echo "c";
+}
+```
diff --git a/Email/change-email.eml.latte b/Email/change-email.eml.latte
new file mode 100644
index 00000000..6cff8c11
--- /dev/null
+++ b/Email/change-email.eml.latte
@@ -0,0 +1,204 @@
+
+
+
+
+
+ Подтверждение изменения Email
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Подтверждение изменения Email
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Здравствуйте, {$name}! Вы вероятно изменили свой адрес электронной почты в OpenVK. Чтобы изменение вступило в силу, необходимо подтвердить ваш новый Email.
+
+ Обратите внимание на то, что эту ссылку нельзя:
+
+
+
+
Передавать другим людям (даже друзьям, питомцам, соседам, любимым девушкам)
+
Использовать, если прошло более двух дней с её генерации
+
+
+
+
+
+
+ Ещё раз обратите внимание на то, что данную ссылку или письмо ни в коем случае нельзя передавать другим людям! Даже если они представляются службой поддержки.
+ Это письмо предназначено исключительно для одноразового, непосредственного использования владельцем аккаунта.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ С уважением, овк-тян.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Вы получили это письмо так как кто-то или вы изменили адрес электронной почты. Это не рассылка и от неё нельзя отписаться. Если вы всё равно хотите перестать получать подобные письма, деактивируйте ваш аккаунт.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/VKAPI/Handlers/Account.php b/VKAPI/Handlers/Account.php
index 7e707cf3..d1722b29 100644
--- a/VKAPI/Handlers/Account.php
+++ b/VKAPI/Handlers/Account.php
@@ -13,9 +13,9 @@ final class Account extends VKAPIRequestHandler
"last_name" => $this->getUser()->getLastName(),
"home_town" => $this->getUser()->getHometown(),
"status" => $this->getUser()->getStatus(),
- "bdate" => "1.1.1970", // TODO
- "bdate_visibility" => 0, // TODO
- "phone" => "+420 ** *** 228", // TODO
+ "bdate" => "1.1.1970", # TODO
+ "bdate_visibility" => 0, # TODO
+ "phone" => "+420 ** *** 228", # TODO
"relation" => $this->getUser()->getMaritalStatus(),
"sex" => $this->getUser()->isFemale() ? 1 : 2
];
@@ -25,12 +25,12 @@ final class Account extends VKAPIRequestHandler
{
$this->requireUser();
- // Цiй метод є заглушка
+ # Цiй метод є заглушка
return (object) [
"2fa_required" => 0,
- "country" => "CZ", // TODO
- "eu_user" => false, // TODO
+ "country" => "CZ", # TODO
+ "eu_user" => false, # TODO
"https_required" => 1,
"intro" => 0,
"community_comments" => false,
@@ -55,7 +55,7 @@ final class Account extends VKAPIRequestHandler
{
$this->requireUser();
- // Цiй метод є заглушка
+ # Цiй метод є заглушка
return 1;
}
@@ -73,6 +73,6 @@ final class Account extends VKAPIRequestHandler
"messages" => $this->getUser()->getUnreadMessagesCount()
];
- // TODO: Filter
+ # TODO: Filter
}
}
diff --git a/VKAPI/Handlers/Friends.php b/VKAPI/Handlers/Friends.php
index 2e917f2d..760cef21 100644
--- a/VKAPI/Handlers/Friends.php
+++ b/VKAPI/Handlers/Friends.php
@@ -25,7 +25,7 @@ final class Friends extends VKAPIRequestHandler
$usersApi = new Users($this->getUser());
if (!is_null($fields)) {
- $response = $usersApi->get(implode(',', $friends), $fields, 0, $count); // FIXME
+ $response = $usersApi->get(implode(',', $friends), $fields, 0, $count); # FIXME
}
return (object) [
diff --git a/VKAPI/Handlers/Groups.php b/VKAPI/Handlers/Groups.php
index eb5d3230..ff34d562 100644
--- a/VKAPI/Handlers/Groups.php
+++ b/VKAPI/Handlers/Groups.php
@@ -48,7 +48,7 @@ final class Groups extends VKAPIRequestHandler
"name" => "DELETED",
"deactivated" => "deleted"
];
- }else if($clbs[$i] == null){
+ }else if($clbs[$i] == NULL){
}else{
$rClubs[$i] = (object)[
@@ -95,10 +95,10 @@ final class Groups extends VKAPIRequestHandler
$clubs = new ClubsRepo;
- if ($group_ids == null && $group_id != null)
+ if ($group_ids == NULL && $group_id != NULL)
$group_ids = $group_id;
- if ($group_ids == null && $group_id == null)
+ if ($group_ids == NULL && $group_id == NULL)
$this->fail(100, "One of the parameters specified was missing or invalid: group_ids is undefined");
$clbs = explode(',', $group_ids);
@@ -123,7 +123,7 @@ final class Groups extends VKAPIRequestHandler
"type" => "group",
"description" => "This group was deleted or it doesn't exist"
];
- }else if($clbs[$i] == null){
+ }else if($clbs[$i] == NULL){
}else{
$response[$i] = (object)[
diff --git a/VKAPI/Handlers/Likes.php b/VKAPI/Handlers/Likes.php
index b002075b..a9f184b6 100644
--- a/VKAPI/Handlers/Likes.php
+++ b/VKAPI/Handlers/Likes.php
@@ -63,7 +63,7 @@ final class Likes extends VKAPIRequestHandler
return (object)[
"liked" => (int) $post->hasLikeFrom($user),
- "copied" => 0 // TODO: handle this
+ "copied" => 0 # TODO: handle this
];
break;
default:
diff --git a/VKAPI/Handlers/Users.php b/VKAPI/Handlers/Users.php
index 3664903f..8aa25b69 100644
--- a/VKAPI/Handlers/Users.php
+++ b/VKAPI/Handlers/Users.php
@@ -7,9 +7,9 @@ final class Users extends VKAPIRequestHandler
{
function get(string $user_ids = "0", string $fields = "", int $offset = 0, int $count = 100, User $authuser = null /* костыль(( */): array
{
- // $this->requireUser();
+ # $this->requireUser();
- if($authuser == null) $authuser = $this->getUser();
+ if($authuser == NULL) $authuser = $this->getUser();
$users = new UsersRepo;
if($user_ids == "0")
@@ -34,7 +34,7 @@ final class Users extends VKAPIRequestHandler
"last_name" => "",
"deactivated" => "deleted"
];
- }else if($usrs[$i] == null){
+ }else if($usrs[$i] == NULL){
}else{
$response[$i] = (object)[
@@ -73,21 +73,21 @@ final class Users extends VKAPIRequestHandler
case 'photo_200':
$response[$i]->photo_50 = $usr->getAvatarURL("normal");
break;
- case 'photo_200_orig': // вообще не ебу к чему эта строка ну пусть будет кек
+ case 'photo_200_orig': # вообще не ебу к чему эта строка ну пусть будет кек
$response[$i]->photo_50 = $usr->getAvatarURL("normal");
break;
case 'photo_400_orig':
$response[$i]->photo_50 = $usr->getAvatarURL("normal");
break;
- // Она хочет быть выебанной видя матан
- // Покайфу когда ты Виет а вокруг лишь дискриминант
+ # Она хочет быть выебанной видя матан
+ # Покайфу когда ты Виет а вокруг лишь дискриминант
case 'status':
- if($usr->getStatus() != null)
+ if($usr->getStatus() != NULL)
$response[$i]->status = $usr->getStatus();
break;
case 'screen_name':
- if($usr->getShortCode() != null)
+ if($usr->getShortCode() != NULL)
$response[$i]->screen_name = $usr->getShortCode();
break;
case 'friend_status':
diff --git a/VKAPI/Handlers/Wall.php b/VKAPI/Handlers/Wall.php
index 7635342b..50677435 100644
--- a/VKAPI/Handlers/Wall.php
+++ b/VKAPI/Handlers/Wall.php
@@ -23,56 +23,21 @@ final class Wall extends VKAPIRequestHandler
foreach ($posts->getPostsFromUsersWall((int)$owner_id, 1, $count, $offset) as $post) {
$from_id = get_class($post->getOwner()) == "openvk\Web\Models\Entities\Club" ? $post->getOwner()->getId() * (-1) : $post->getOwner()->getId();
- $attachments;
- foreach($post->getChildren() as $attachment)
- {
- if($attachment instanceof \openvk\Web\Models\Entities\Photo)
- {
+ $attachments = [];
+ foreach($post->getChildren() as $attachment) {
+ if($attachment instanceof \openvk\Web\Models\Entities\Photo) {
+ if($attachment->isDeleted())
+ continue;
+
$attachments[] = [
"type" => "photo",
"photo" => [
- "album_id" => $attachment->getAlbum() ? $attachment->getAlbum()->getId() : null,
- "date" => $attachment->getPublicationTime()->timestamp(),
- "id" => $attachment->getVirtualId(),
+ "album_id" => $attachment->getAlbum() ? $attachment->getAlbum()->getId() : NULL,
+ "date" => $attachment->getPublicationTime()->timestamp(),
+ "id" => $attachment->getVirtualId(),
"owner_id" => $attachment->getOwner()->getId(),
- "sizes" => array(
- [
- "height" => 2560,
- "url" => $attachment->getURLBySizeId("normal"),
- "type" => "m",
- "width" => 2560,
- ],
- [
- "height" => 130,
- "url" => $attachment->getURLBySizeId("tiny"),
- "type" => "o",
- "width" => 130,
- ],
- [
- "height" => 604,
- "url" => $attachment->getURLBySizeId("normal"),
- "type" => "p",
- "width" => 604,
- ],
- [
- "height" => 807,
- "url" => $attachment->getURLBySizeId("large"),
- "type" => "q",
- "width" => 807,
- ],
- [
- "height" => 1280,
- "url" => $attachment->getURLBySizeId("larger"),
- "type" => "r",
- "width" => 1280,
- ],
- [
- "height" => 75, // Для временного компросима оставляю статическое число. Если каждый раз обращаться к файлу за количеством пикселов, то наступает пuпuська полная с производительностью, так что пока так
- "url" => $attachment->getURLBySizeId("miniscule"),
- "type" => "s",
- "width" => 75,
- ]),
- "text" => "",
+ "sizes" => array_values($attachment->getVkApiSizes()),
+ "text" => "",
"has_tags" => false
]
];
@@ -86,10 +51,10 @@ final class Wall extends VKAPIRequestHandler
"date" => $post->getPublicationTime()->timestamp(),
"post_type" => "post",
"text" => $post->getText(),
- "can_edit" => 0, // TODO
+ "can_edit" => 0, # TODO
"can_delete" => $post->canBeDeletedBy($this->getUser()),
"can_pin" => $post->canBePinnedBy($this->getUser()),
- "can_archive" => false, // TODO MAYBE
+ "can_archive" => false, # TODO MAYBE
"is_archived" => false,
"is_pinned" => $post->isPinned(),
"attachments" => $attachments,
@@ -115,7 +80,7 @@ final class Wall extends VKAPIRequestHandler
else
$groups[] = $from_id * -1;
- $attachments = null; // free attachments so it will not clone everythingg
+ $attachments = NULL; # free attachments so it will not clone everythingg
}
if($extended == 1)
@@ -170,9 +135,9 @@ final class Wall extends VKAPIRequestHandler
];
}
- function getById(string $posts, int $extended = 0, string $fields = "", User $user = null)
+ function getById(string $posts, int $extended = 0, string $fields = "", User $user = NULL)
{
- if($user == null) $user = $this->getUser(); // костыли костыли крылышки
+ if($user == NULL) $user = $this->getUser(); # костыли костыли крылышки
$items = [];
$profiles = [];
@@ -195,7 +160,7 @@ final class Wall extends VKAPIRequestHandler
$attachments[] = [
"type" => "photo",
"photo" => [
- "album_id" => $attachment->getAlbum() ? $attachment->getAlbum()->getId() : null,
+ "album_id" => $attachment->getAlbum() ? $attachment->getAlbum()->getId() : NULL,
"date" => $attachment->getPublicationTime()->timestamp(),
"id" => $attachment->getVirtualId(),
"owner_id" => $attachment->getOwner()->getId(),
@@ -231,7 +196,7 @@ final class Wall extends VKAPIRequestHandler
"width" => 1280,
],
[
- "height" => 75, // Для временного компросима оставляю статическое число. Если каждый раз обращаться к файлу за количеством пикселов, то наступает пuпuська полная с производительностью, так что пока так
+ "height" => 75, # Для временного компросима оставляю статическое число. Если каждый раз обращаться к файлу за количеством пикселов, то наступает пuпuська полная с производительностью, так что пока так
"url" => $attachment->getURLBySizeId("miniscule"),
"type" => "s",
"width" => 75,
@@ -250,10 +215,10 @@ final class Wall extends VKAPIRequestHandler
"date" => $post->getPublicationTime()->timestamp(),
"post_type" => "post",
"text" => $post->getText(),
- "can_edit" => 0, // TODO
+ "can_edit" => 0, # TODO
"can_delete" => $post->canBeDeletedBy($user),
"can_pin" => $post->canBePinnedBy($user),
- "can_archive" => false, // TODO MAYBE
+ "can_archive" => false, # TODO MAYBE
"is_archived" => false,
"is_pinned" => $post->isPinned(),
"post_source" => (object)["type" => "vk"],
@@ -279,7 +244,7 @@ final class Wall extends VKAPIRequestHandler
else
$groups[] = $from_id * -1;
- $attachments = null; // free attachments so it will not clone everythingg
+ $attachments = NULL; # free attachments so it will not clone everythingg
}
}
@@ -370,12 +335,12 @@ final class Wall extends VKAPIRequestHandler
if($signed == 1)
$flags |= 0b01000000;
- // TODO: Compatible implementation of this
+ # TODO: Compatible implementation of this
try {
- $photo = null;
- $video = null;
+ $photo = NULL;
+ $video = NULL;
if($_FILES["photo"]["error"] === UPLOAD_ERR_OK) {
- $album = null;
+ $album = NULL;
if(!$anon && $owner_id > 0 && $owner_id === $this->getUser()->getId())
$album = (new AlbumsRepo)->getUserWallAlbum($wallOwner);
diff --git a/Web/Models/Entities/Club.php b/Web/Models/Entities/Club.php
index f306a8e6..ffe81ab4 100644
--- a/Web/Models/Entities/Club.php
+++ b/Web/Models/Entities/Club.php
@@ -99,6 +99,14 @@ class Club extends RowModel
{
return $this->getRecord()->about;
}
+
+ function getDescriptionHtml(): ?string
+ {
+ if(!is_null($this->getDescription()))
+ return nl2br(htmlspecialchars($this->getDescription(), ENT_DISALLOWED | ENT_XHTML));
+ else
+ return NULL;
+ }
function getShortCode(): ?string
{
@@ -302,8 +310,8 @@ class Club extends RowModel
{
$manager = (new Managers)->getByUserAndClub($user->getId(), $this->getId());
- if ($ignoreHidden && $manager !== null && $manager->isHidden())
- return null;
+ if ($ignoreHidden && $manager !== NULL && $manager->isHidden())
+ return NULL;
return $manager;
}
diff --git a/Web/Models/Entities/Correspondence.php b/Web/Models/Entities/Correspondence.php
index 031b905e..e4b7d96e 100644
--- a/Web/Models/Entities/Correspondence.php
+++ b/Web/Models/Entities/Correspondence.php
@@ -131,7 +131,7 @@ class Correspondence
*/
function getPreviewMessage(): ?Message
{
- $messages = $this->getMessages(1, null, 1);
+ $messages = $this->getMessages(1, NULL, 1);
return $messages[0] ?? NULL;
}
diff --git a/Web/Models/Entities/EmailChangeVerification.php b/Web/Models/Entities/EmailChangeVerification.php
new file mode 100644
index 00000000..e9b92db1
--- /dev/null
+++ b/Web/Models/Entities/EmailChangeVerification.php
@@ -0,0 +1,15 @@
+getRecord()->new_email;
+ }
+}
diff --git a/Web/Models/Entities/Message.php b/Web/Models/Entities/Message.php
index f4fcc9af..de840606 100644
--- a/Web/Models/Entities/Message.php
+++ b/Web/Models/Entities/Message.php
@@ -126,7 +126,7 @@ class Message extends RowModel
],
"timing" => [
"sent" => (string) $this->getSendTime()->format("%e %B %G" . tr("time_at_sp") . "%X"),
- "edited" => is_null($this->getEditTime()) ? null : (string) $this->getEditTime(),
+ "edited" => is_null($this->getEditTime()) ? NULL : (string) $this->getEditTime(),
],
"text" => $this->getText(),
"read" => !$this->isUnread(),
diff --git a/Web/Models/Entities/Photo.php b/Web/Models/Entities/Photo.php
index 0f223679..71d5efcb 100644
--- a/Web/Models/Entities/Photo.php
+++ b/Web/Models/Entities/Photo.php
@@ -165,8 +165,11 @@ class Photo extends Media
foreach($manifest->Size as $size)
$mappings[(string) $size["id"]] = (string) $size["vkId"];
- foreach($sizes as $id => $meta)
- $res[$mappings[$id] ?? $id] = $meta;
+ foreach($sizes as $id => $meta) {
+ $type = $mappings[$id] ?? $id;
+ $meta->type = $type;
+ $res[$type] = $meta;
+ }
return $res;
}
diff --git a/Web/Models/Entities/Postable.php b/Web/Models/Entities/Postable.php
index c3585dc3..b4f8b4c6 100644
--- a/Web/Models/Entities/Postable.php
+++ b/Web/Models/Entities/Postable.php
@@ -87,7 +87,7 @@ abstract class Postable extends Attachable
]));
}
- // TODO add pagination
+ # TODO add pagination
function getLikers(): \Traversable
{
$sel = DB::i()->getContext()->table("likes")->where([
diff --git a/Web/Models/Entities/User.php b/Web/Models/Entities/User.php
index 68ad6a77..7c432561 100644
--- a/Web/Models/Entities/User.php
+++ b/Web/Models/Entities/User.php
@@ -85,6 +85,11 @@ class User extends RowModel
{
return (bool) $this->getRecord()->microblog;
}
+
+ function getMainPage(): int
+ {
+ return $this->getRecord()->main_page;
+ }
function getChandlerGUID(): string
{
@@ -877,6 +882,17 @@ class User extends RowModel
return true;
}
+ function changeEmail(string $email): void
+ {
+ DatabaseConnection::i()->getContext()->table("ChandlerUsers")
+ ->where("id", $this->getChandlerUser()->getId())->update([
+ "login" => $email
+ ]);
+
+ $this->stateChanges("email", $email);
+ $this->save();
+ }
+
function adminNotify(string $message): bool
{
$admId = OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["adminAccount"];
@@ -928,7 +944,7 @@ class User extends RowModel
return $this->getRecord()->website;
}
- // ты устрица
+ # ты устрица
function isActivated(): bool
{
return (bool) $this->getRecord()->activated;
diff --git a/Web/Models/Entities/Video.php b/Web/Models/Entities/Video.php
index 7bf1b010..b45072b9 100644
--- a/Web/Models/Entities/Video.php
+++ b/Web/Models/Entities/Video.php
@@ -63,7 +63,7 @@ class Video extends Media
if(!file_exists($this->getFileName())) {
if((time() - $this->getRecord()->last_checked) > 3600) {
- // TODO notify that video processor is probably dead
+ # TODO notify that video processor is probably dead
}
return false;
diff --git a/Web/Models/Repositories/EmailChangeVerifications.php b/Web/Models/Repositories/EmailChangeVerifications.php
new file mode 100644
index 00000000..0e8c668b
--- /dev/null
+++ b/Web/Models/Repositories/EmailChangeVerifications.php
@@ -0,0 +1,33 @@
+context = DatabaseConnection::i()->getContext();
+ $this->verifications = $this->context->table("email_change_verifications");
+ }
+
+ function toEmailChangeVerification(?ActiveRow $ar): ?EmailChangeVerification
+ {
+ return is_null($ar) ? NULL : new EmailChangeVerification($ar);
+ }
+
+ function getByToken(string $token): ?EmailChangeVerification
+ {
+ return $this->toEmailChangeVerification($this->verifications->where("key", $token)->fetch());
+ }
+
+ function getLatestByUser(User $user): ?EmailChangeVerification
+ {
+ return $this->toEmailChangeVerification($this->verifications->where("profile", $user->getId())->order("timestamp DESC")->fetch());
+ }
+}
diff --git a/Web/Models/Repositories/Notes.php b/Web/Models/Repositories/Notes.php
index a41c6914..7d95f975 100644
--- a/Web/Models/Repositories/Notes.php
+++ b/Web/Models/Repositories/Notes.php
@@ -39,7 +39,7 @@ class Notes
if(!is_null($note))
return new Note($note);
else
- return null;
+ return NULL;
}
function getUserNotesCount(User $user): int
diff --git a/Web/Models/Repositories/Posts.php b/Web/Models/Repositories/Posts.php
index 94ce6482..6dde27c8 100644
--- a/Web/Models/Repositories/Posts.php
+++ b/Web/Models/Repositories/Posts.php
@@ -96,7 +96,7 @@ class Posts
if(!is_null($post))
return new Post($post);
else
- return null;
+ return NULL;
}
diff --git a/Web/Models/Repositories/TicketComments.php b/Web/Models/Repositories/TicketComments.php
index 4d3c5316..9277218f 100644
--- a/Web/Models/Repositories/TicketComments.php
+++ b/Web/Models/Repositories/TicketComments.php
@@ -1,8 +1,5 @@
comments->where(['ticket_id' => $ticket_id, 'deleted' => 0]) as $comment) yield new TicketComment($comment);
}
-
- // private function toTicket(?ActiveRow $ar): ?Ticket
- // {
- // return is_null($ar) ? NULL : new Ticket($ar);
- // }
-
- // function getTicketsByuId(int $user_id): \Traversable
- // {
- // foreach($this->tickets->where(['user_id' => $user_id, 'deleted' => 0]) as $ticket) yield new Ticket($ticket);
- // }
-
- // function getRequestById(int $req_id): ?Ticket
- // {
- // $requests = $this->tickets->where(['id' => $req_id])->fetch();
- // if(!is_null($requests))
-
- // return new Req($requests);
- // else
- // return null;
-
- // }
-
- // function get(int $id): ?Ticket
- // {
- // return $this->toTicket($this->tickets->get($id));
- // }
function get(int $id): ?TicketComment
{
diff --git a/Web/Models/Repositories/Tickets.php b/Web/Models/Repositories/Tickets.php
index 7f00e62b..1e84ebd8 100644
--- a/Web/Models/Repositories/Tickets.php
+++ b/Web/Models/Repositories/Tickets.php
@@ -50,7 +50,7 @@ class Tickets
if(!is_null($requests))
return new Req($requests);
else
- return null;
+ return NULL;
}
diff --git a/Web/Models/Repositories/Topics.php b/Web/Models/Repositories/Topics.php
index 23b854d4..1c176310 100644
--- a/Web/Models/Repositories/Topics.php
+++ b/Web/Models/Repositories/Topics.php
@@ -35,7 +35,7 @@ class Topics
{
$perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE;
- // Get pinned topics first
+ # Get pinned topics first
$query = "SELECT `id` FROM `topics` WHERE `pinned` = 1 AND `group` = ? AND `deleted` = 0 UNION SELECT `id` FROM `topics` WHERE `pinned` = 0 AND `group` = ? AND `deleted` = 0";
$query .= " LIMIT " . $perPage . " OFFSET " . ($page - 1) * $perPage;
diff --git a/Web/Presenters/AboutPresenter.php b/Web/Presenters/AboutPresenter.php
index cf4572b8..3caa007d 100644
--- a/Web/Presenters/AboutPresenter.php
+++ b/Web/Presenters/AboutPresenter.php
@@ -14,7 +14,12 @@ final class AboutPresenter extends OpenVKPresenter
{
if(!is_null($this->user)) {
header("HTTP/1.1 302 Found");
- header("Location: /id" . $this->user->id);
+
+ if($this->user->identity->getMainPage())
+ header("Location: /feed");
+ else
+ header("Location: /id" . $this->user->id);
+
exit;
}
@@ -85,7 +90,7 @@ final class AboutPresenter extends OpenVKPresenter
if(is_null($lg))
$this->throwError(404, "Not found", "Language is not found");
header("Content-Type: application/javascript");
- echo "window.lang = " . json_encode($localizer->export($lang)) . ";"; // привет хардкод :DDD
+ echo "window.lang = " . json_encode($localizer->export($lang)) . ";"; # привет хардкод :DDD
exit;
}
@@ -120,7 +125,7 @@ final class AboutPresenter extends OpenVKPresenter
function renderHumansTxt(): void
{
- // :D
+ # :D
header("HTTP/1.1 302 Found");
header("Location: https://github.com/openvk/openvk#readme");
diff --git a/Web/Presenters/AdminPresenter.php b/Web/Presenters/AdminPresenter.php
index 39845ae4..f35e1081 100644
--- a/Web/Presenters/AdminPresenter.php
+++ b/Web/Presenters/AdminPresenter.php
@@ -23,7 +23,7 @@ final class AdminPresenter extends OpenVKPresenter
private function warnIfNoCommerce(): void
{
if(!OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"])
- $this->flash("warn", "Коммерция отключена системным администратором", "Настройки ваучеров и подарков будут сохранены, но не будут оказывать никакого влияния.");
+ $this->flash("warn", tr("admin_commerce_disabled"), tr("admin_commerce_disabled_desc"));
}
private function searchResults(object $repo, &$count)
@@ -70,14 +70,14 @@ final class AdminPresenter extends OpenVKPresenter
$user->setLast_Name($this->postParam("last_name"));
$user->setPseudo($this->postParam("nickname"));
$user->setStatus($this->postParam("status"));
- $user->setVerified(empty($this->postParam("verify") ? 0 : 1));
- if($user->onlineStatus() != $this->postParam("online")) $user->setOnline(intval($this->postParam("online")));
if(!$user->setShortCode(empty($this->postParam("shortcode")) ? NULL : $this->postParam("shortcode")))
$this->flash("err", tr("error"), tr("error_shorturl_incorrect"));
+ $user->changeEmail($this->postParam("email"));
+ if($user->onlineStatus() != $this->postParam("online")) $user->setOnline(intval($this->postParam("online")));
+ $user->setVerified(empty($this->postParam("verify") ? 0 : 1));
+
$user->save();
break;
-
-
}
}
diff --git a/Web/Presenters/CommentPresenter.php b/Web/Presenters/CommentPresenter.php
index 41f0bc79..81783a2f 100644
--- a/Web/Presenters/CommentPresenter.php
+++ b/Web/Presenters/CommentPresenter.php
@@ -60,7 +60,7 @@ final class CommentPresenter extends OpenVKPresenter
}
}
- // TODO move to trait
+ # TODO move to trait
try {
$photo = NULL;
$video = NULL;
diff --git a/Web/Presenters/GroupPresenter.php b/Web/Presenters/GroupPresenter.php
index 65d3640f..c239ff22 100644
--- a/Web/Presenters/GroupPresenter.php
+++ b/Web/Presenters/GroupPresenter.php
@@ -89,7 +89,7 @@ final class GroupPresenter extends OpenVKPresenter
$this->template->club = $this->clubs->get($id);
$this->template->onlyShowManagers = $this->queryParam("onlyAdmins") == "1";
if($this->template->onlyShowManagers) {
- $this->template->followers = null;
+ $this->template->followers = NULL;
$this->template->managers = $this->template->club->getManagers((int) ($this->queryParam("p") ?? 1), !$this->template->club->canBeModifiedBy($this->user->identity));
if($this->template->club->canBeModifiedBy($this->user->identity) || !$this->template->club->isOwnerHidden()) {
@@ -99,7 +99,7 @@ final class GroupPresenter extends OpenVKPresenter
$this->template->count = $this->template->club->getManagersCount();
} else {
$this->template->followers = $this->template->club->getFollowers((int) ($this->queryParam("p") ?? 1));
- $this->template->managers = null;
+ $this->template->managers = NULL;
$this->template->count = $this->template->club->getFollowersCount();
}
@@ -116,7 +116,7 @@ final class GroupPresenter extends OpenVKPresenter
$user = is_null($this->queryParam("user")) ? $this->postParam("user") : $this->queryParam("user");
$comment = $this->postParam("comment");
$removeComment = $this->postParam("removeComment") === "1";
- $hidden = ["0" => false, "1" => true][$this->queryParam("hidden")] ?? null;
+ $hidden = ["0" => false, "1" => true][$this->queryParam("hidden")] ?? NULL;
//$index = $this->queryParam("index");
if(!$user)
$this->badRequest();
diff --git a/Web/Presenters/InternalAPIPresenter.php b/Web/Presenters/InternalAPIPresenter.php
index f0551b7f..60c0b550 100644
--- a/Web/Presenters/InternalAPIPresenter.php
+++ b/Web/Presenters/InternalAPIPresenter.php
@@ -80,11 +80,11 @@ final class InternalAPIPresenter extends OpenVKPresenter
if ($postTZ != $sessionOffset || $sessionOffset == null) {
Session::i()->set("_timezoneOffset", $postTZ ? $postTZ : 3 * MINUTE );
$this->returnJson([
- "success" => 1 // If it's new value
+ "success" => 1 # If it's new value
]);
} else {
$this->returnJson([
- "success" => 2 // If it's the same value (if for some reason server will call this func)
+ "success" => 2 # If it's the same value (if for some reason server will call this func)
]);
}
} else {
diff --git a/Web/Presenters/MessengerPresenter.php b/Web/Presenters/MessengerPresenter.php
index fdcb934e..968c1936 100644
--- a/Web/Presenters/MessengerPresenter.php
+++ b/Web/Presenters/MessengerPresenter.php
@@ -106,7 +106,7 @@ final class MessengerPresenter extends OpenVKPresenter
$messages = [];
$correspondence = new Correspondence($this->user->identity, $correspondent);
- foreach($correspondence->getMessages(1, $lastMsg === 0 ? null : $lastMsg) as $message)
+ foreach($correspondence->getMessages(1, $lastMsg === 0 ? NULL : $lastMsg) as $message)
$messages[] = $message->simplify();
header("Content-Type: application/json");
diff --git a/Web/Presenters/OpenVKPresenter.php b/Web/Presenters/OpenVKPresenter.php
index f4192cce..d9202a7d 100755
--- a/Web/Presenters/OpenVKPresenter.php
+++ b/Web/Presenters/OpenVKPresenter.php
@@ -118,7 +118,7 @@ abstract class OpenVKPresenter extends SimplePresenter
return ($action === "register" || $action === "login");
}
- return (bool) $this->user->raw->can($action)->model($model)->whichBelongsTo($context === -1 ? null : $context);
+ return (bool) $this->user->raw->can($action)->model($model)->whichBelongsTo($context === -1 ? NULL : $context);
}
protected function assertPermission(string $model, string $action, int $context, bool $throw = false): void
@@ -252,7 +252,7 @@ abstract class OpenVKPresenter extends SimplePresenter
exit;
}
- // ето для емейл уже надо (и по хорошему надо бы избавится от повторяющегося кода мда)
+ # ето для емейл уже надо (и по хорошему надо бы избавится от повторяющегося кода мда)
if(!$this->user->identity->isActivated() && !$this->activationTolerant) {
header("HTTP/1.1 403 Forbidden");
$this->getTemplatingEngine()->render(__DIR__ . "/templates/@email.xml", [
@@ -290,7 +290,7 @@ abstract class OpenVKPresenter extends SimplePresenter
$whichbrowser = new WhichBrowser\Parser(getallheaders());
$mobiletheme = OPENVK_ROOT_CONF["openvk"]["preferences"]["defaultMobileTheme"];
- if($mobiletheme && $whichbrowser->isType('mobile') && Session::i()->get("_tempTheme") == null)
+ if($mobiletheme && $whichbrowser->isType('mobile') && Session::i()->get("_tempTheme") == NULL)
$this->setSessionTheme($mobiletheme);
$theme = NULL;
@@ -301,7 +301,7 @@ abstract class OpenVKPresenter extends SimplePresenter
$theme = Themepacks::i()[Session::i()->get("_sessionTheme", "ovk")];
} else if($this->requestParam("themePreview")) {
$theme = Themepacks::i()[$this->requestParam("themePreview")];
- } else if($this->user->identity !== null && $this->user->identity->getTheme()) {
+ } else if($this->user->identity !== NULL && $this->user->identity->getTheme()) {
$theme = $this->user->identity->getTheme();
}
diff --git a/Web/Presenters/SearchPresenter.php b/Web/Presenters/SearchPresenter.php
index 0fc4611f..a5eaedc7 100644
--- a/Web/Presenters/SearchPresenter.php
+++ b/Web/Presenters/SearchPresenter.php
@@ -29,7 +29,7 @@ final class SearchPresenter extends OpenVKPresenter
if($query != "")
$this->assertUserLoggedIn();
- // https://youtu.be/pSAWM5YuXx8
+ # https://youtu.be/pSAWM5YuXx8
$repos = [ "groups" => "clubs", "users" => "users" ];
$repo = $repos[$type] or $this->throwError(400, "Bad Request", "Invalid search entity $type.");
diff --git a/Web/Presenters/SupportPresenter.php b/Web/Presenters/SupportPresenter.php
index 9c2bcc8e..b8d197d6 100644
--- a/Web/Presenters/SupportPresenter.php
+++ b/Web/Presenters/SupportPresenter.php
@@ -28,6 +28,41 @@ final class SupportPresenter extends OpenVKPresenter
$this->assertUserLoggedIn();
$this->template->mode = in_array($this->queryParam("act"), ["faq", "new", "list"]) ? $this->queryParam("act") : "faq";
+ if($this->template->mode === "faq") {
+ $lang = Session::i()->get("lang", "ru");
+ $base = OPENVK_ROOT . "/data/knowledgebase/faq";
+ if(file_exists("$base.$lang.md"))
+ $file = "$base.$lang.md";
+ else if(file_exists("$base.md"))
+ $file = "$base.md";
+ else
+ $file = NULL;
+
+ if(is_null($file)) {
+ $this->template->faq = [];
+ } else {
+ $lines = file($file);
+ $faq = [];
+ $index = 0;
+
+ foreach($lines as $line) {
+ if(strpos($line, "# ") === 0)
+ ++$index;
+
+ $faq[$index][] = $line;
+ }
+
+ $this->template->faq = array_map(function($section) {
+ $title = substr($section[0], 2);
+ array_shift($section);
+ return [
+ $title,
+ (new Parsedown())->text(implode("\n", $section))
+ ];
+ }, $faq);
+ }
+ }
+
$this->template->count = $this->tickets->getTicketsCountByUserId($this->user->id);
if($this->template->mode === "list") {
$this->template->page = (int) ($this->queryParam("p") ?? 1);
@@ -79,12 +114,13 @@ final class SupportPresenter extends OpenVKPresenter
$act = $this->queryParam("act") ?? "open";
switch($act) {
default:
+ # NOTICE falling through
case "open":
$state = 0;
- break;
+ break;
case "answered":
$state = 1;
- break;
+ break;
case "closed":
$state = 2;
}
diff --git a/Web/Presenters/TopicsPresenter.php b/Web/Presenters/TopicsPresenter.php
index f29c7979..16dd1795 100644
--- a/Web/Presenters/TopicsPresenter.php
+++ b/Web/Presenters/TopicsPresenter.php
@@ -91,7 +91,7 @@ final class TopicsPresenter extends OpenVKPresenter
$topic->setFlags($flags);
$topic->save();
- // TODO move to trait
+ # TODO move to trait
try {
$photo = NULL;
$video = NULL;
diff --git a/Web/Presenters/UserPresenter.php b/Web/Presenters/UserPresenter.php
index b9e8b714..c6a41616 100644
--- a/Web/Presenters/UserPresenter.php
+++ b/Web/Presenters/UserPresenter.php
@@ -9,12 +9,15 @@ use openvk\Web\Models\Repositories\Albums;
use openvk\Web\Models\Repositories\Videos;
use openvk\Web\Models\Repositories\Notes;
use openvk\Web\Models\Repositories\Vouchers;
+use openvk\Web\Models\Repositories\EmailChangeVerifications;
use openvk\Web\Models\Exceptions\InvalidUserNameException;
use openvk\Web\Util\Validator;
use openvk\Web\Models\Entities\Notifications\{CoinsTransferNotification, RatingUpNotification};
+use openvk\Web\Models\Entities\EmailChangeVerification;
use Chandler\Security\Authenticator;
use lfkeitel\phptotp\{Base32, Totp};
use chillerlan\QRCode\{QRCode, QROptions};
+use Nette\Database\UniqueConstraintViolationException;
final class UserPresenter extends OpenVKPresenter
{
@@ -132,7 +135,7 @@ final class UserPresenter extends OpenVKPresenter
if(!$id)
$this->notFound();
-
+
$user = $this->users->get($id);
if($_SERVER["REQUEST_METHOD"] === "POST") {
$this->willExecuteWriteAction($_GET['act'] === "status");
@@ -300,7 +303,7 @@ final class UserPresenter extends OpenVKPresenter
if(!$id)
$this->notFound();
-
+
if(in_array($this->queryParam("act"), ["finance", "finance.top-up"]) && !OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"])
$this->flashFail("err", tr("error"), tr("feature_disabled"));
@@ -312,7 +315,7 @@ final class UserPresenter extends OpenVKPresenter
if($this->postParam("old_pass") && $this->postParam("new_pass") && $this->postParam("repeat_pass")) {
if($this->postParam("new_pass") === $this->postParam("repeat_pass")) {
if($this->user->identity->is2faEnabled()) {
- $code = $this->postParam("code");
+ $code = $this->postParam("password_change_code");
if(!($code === (new Totp)->GenerateToken(Base32::decode($this->user->identity->get2faSecret())) || $this->user->identity->use2faBackupCode((int) $code)))
$this->flashFail("err", tr("error"), tr("incorrect_2fa_code"));
}
@@ -323,6 +326,46 @@ final class UserPresenter extends OpenVKPresenter
$this->flashFail("err", tr("error"), tr("error_new_password"));
}
}
+
+ if($this->postParam("new_email")) {
+ if(!Validator::i()->emailValid($this->postParam("new_email")))
+ $this->flashFail("err", tr("invalid_email_address"), tr("invalid_email_address_comment"));
+
+ if(!Authenticator::verifyHash($this->postParam("email_change_pass"), $user->getChandlerUser()->getRaw()->passwordHash))
+ $this->flashFail("err", tr("error"), tr("incorrect_password"));
+
+ if($user->is2faEnabled()) {
+ $code = $this->postParam("email_change_code");
+ if(!($code === (new Totp)->GenerateToken(Base32::decode($user->get2faSecret())) || $user->use2faBackupCode((int) $code)))
+ $this->flashFail("err", tr("error"), tr("incorrect_2fa_code"));
+ }
+
+ if($this->postParam("new_email") !== $user->getEmail()) {
+ if (OPENVK_ROOT_CONF['openvk']['preferences']['security']['requireEmail']) {
+ $request = (new EmailChangeVerifications)->getLatestByUser($user);
+ if(!is_null($request) && $request->isNew())
+ $this->flashFail("err", tr("forbidden"), tr("email_rate_limit_error"));
+
+ $verification = new EmailChangeVerification;
+ $verification->setProfile($user->getId());
+ $verification->setNew_Email($this->postParam("new_email"));
+ $verification->save();
+
+ $params = [
+ "key" => $verification->getKey(),
+ "name" => $user->getCanonicalName(),
+ ];
+ $this->sendmail($this->postParam("new_email"), "change-email", $params); #Vulnerability possible
+ $this->flashFail("succ", tr("information_-1"), tr("email_change_confirm_message"));
+ }
+
+ try {
+ $user->changeEmail($this->postParam("new_email"));
+ } catch(UniqueConstraintViolationException $ex) {
+ $this->flashFail("err", tr("error"), tr("user_already_exists"));
+ }
+ }
+ }
if(!$user->setShortCode(empty($this->postParam("sc")) ? NULL : $this->postParam("sc")))
$this->flashFail("err", tr("error"), tr("error_shorturl_incorrect"));
@@ -376,6 +419,9 @@ final class UserPresenter extends OpenVKPresenter
if(in_array($this->postParam("nsfw"), [0, 1, 2]))
$user->setNsfwTolerance((int) $this->postParam("nsfw"));
+
+ if(in_array($this->postParam("main_page"), [0, 1]))
+ $user->setMain_Page((int) $this->postParam("main_page"));
} else if($_GET['act'] === "lMenu") {
$settings = [
"menu_bildoj" => "photos",
@@ -400,11 +446,7 @@ final class UserPresenter extends OpenVKPresenter
throw $ex;
}
- $this->flash(
- "succ",
- "Изменения сохранены",
- "Новые данные появятся на вашей странице."
- );
+ $this->flash("succ", tr("changes_saved"), tr("changes_saved_comment"));
}
$this->template->mode = in_array($this->queryParam("act"), [
"main", "privacy", "finance", "finance.top-up", "interface"
@@ -456,7 +498,7 @@ final class UserPresenter extends OpenVKPresenter
$this->template->secret = $secret;
}
- // Why are these crutch? For some reason, the QR code is not displayed if you just pass the render output to the view
+ # Why are these crutch? For some reason, the QR code is not displayed if you just pass the render output to the view
$issuer = OPENVK_ROOT_CONF["openvk"]["appearance"]["name"];
$email = $this->user->identity->getEmail();
@@ -502,6 +544,9 @@ final class UserPresenter extends OpenVKPresenter
$this->assertUserLoggedIn();
$this->willExecuteWriteAction();
+ if(!OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"])
+ $this->flashFail("err", tr("error"), tr("feature_disabled"));
+
$receiverAddress = $this->postParam("receiver");
$value = (int) $this->postParam("value");
$message = $this->postParam("message");
@@ -517,7 +562,7 @@ final class UserPresenter extends OpenVKPresenter
$receiver = $this->users->getByAddress($receiverAddress);
if(!$receiver)
- $this->flashFail("err", tr("failed_to_tranfer_points"), tr("receiver_not_found"));
+ $this->flashFail("err", tr("failed_to_tranfer_points"), tr("receiver_not_found"));
if($this->user->identity->getCoins() < $value)
$this->flashFail("err", tr("failed_to_tranfer_points"), tr("you_dont_have_enough_points"));
@@ -574,4 +619,24 @@ final class UserPresenter extends OpenVKPresenter
$this->flashFail("succ", tr("information_-1"), tr("rating_increase_successful", $receiver->getURL(), htmlentities($receiver->getCanonicalName()), $value));
}
+
+ function renderEmailChangeFinish(): void
+ {
+ $request = (new EmailChangeVerifications)->getByToken(str_replace(" ", "+", $this->queryParam("key")));
+ if(!$request || !$request->isStillValid()) {
+ $this->flash("err", tr("token_manipulation_error"), tr("token_manipulation_error_comment"));
+ $this->redirect("/settings");
+ } else {
+ $request->delete(false);
+
+ try {
+ $request->getUser()->changeEmail($request->getNewEmail());
+ } catch(UniqueConstraintViolationException $ex) {
+ $this->flashFail("err", tr("error"), tr("user_already_exists"));
+ }
+
+ $this->flash("succ", tr("changes_saved"), tr("changes_saved_comment"));
+ $this->redirect("/settings");
+ }
+ }
}
diff --git a/Web/Presenters/WallPresenter.php b/Web/Presenters/WallPresenter.php
index 9da0311c..5bdac066 100644
--- a/Web/Presenters/WallPresenter.php
+++ b/Web/Presenters/WallPresenter.php
@@ -414,7 +414,7 @@ final class WallPresenter extends OpenVKPresenter
$post->unpin();
}
- // TODO localize message based on language and ?act=(un)pin
+ # TODO localize message based on language and ?act=(un)pin
$this->flashFail("succ", tr("information_-1"), tr("changes_saved_comment"));
}
}
diff --git a/Web/Presenters/templates/@layout.xml b/Web/Presenters/templates/@layout.xml
index bd8de7a0..6ef482cc 100644
--- a/Web/Presenters/templates/@layout.xml
+++ b/Web/Presenters/templates/@layout.xml
@@ -17,7 +17,7 @@
{script "js/l10n.js"}
{script "js/openvk.cls.js"}
- {if $isTimezoned == null}
+ {if $isTimezoned == NULL}
{script "js/timezone.js"}
{/if}
@@ -26,7 +26,7 @@
{css "css/nsfw-posts.css"}
{/if}
- {if $theme !== null}
+ {if $theme !== NULL}
{if $theme->inheritDefault()}
{css "css/style.css"}
{css "css/dialog.css"}
@@ -103,7 +103,7 @@