diff --git a/README.md b/README.md index 56211045..43f8e958 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ ln -s /path/to/chandler/extensions/available/commitcaptcha /path/to/chandler/ext ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions/enabled/ ``` -4. Import `install/init-static-db.sql` to **same database** you installed Chandler to +4. Import `install/init-static-db.sql` to **same database** you installed Chandler to and import all sqls from `install/sqls` to **same database** 5. Import `install/init-event-db.sql` to **separate database** 6. Copy `openvk-example.yml` to `openvk.yml` and change options 7. Run `composer install` in OpenVK directory @@ -75,11 +75,12 @@ You may reach out to us via: * [Bug-tracker](https://github.com/openvk/openvk/projects/1) * [Ticketing system](https://openvk.su/support?act=new) -* Telegram chat: Go to [our channel](https://t.me/openvkch) and open discussion in our channel menu. +* Telegram chat: Go to [our channel](https://t.me/openvkenglish) and open discussion in our channel menu. * [Reddit](https://www.reddit.com/r/openvk/) * [Discussions](https://github.com/openvk/openvk/discussions) +* Matrix chat: #openvk:matrix.org -**Attention**: bug tracker and telegram chat are public places. And ticketing system is being served by volunteers. If you need to report something, that shouldn't be immediately disclosed to general public (for instance, vulnerability report), please use contact us directly at this email: **openvk [at] tutanota [dot] com** +**Attention**: bug tracker, telegram and matrix chat are public places. And ticketing system is being served by volunteers. If you need to report something, that shouldn't be immediately disclosed to general public (for instance, vulnerability report), please use contact us directly at this email: **openvk [at] tutanota [dot] com** Get it on Codeberg diff --git a/README_RU.md b/README_RU.md index 2ed13a7b..65b0dbd6 100644 --- a/README_RU.md +++ b/README_RU.md @@ -48,7 +48,7 @@ ln -s /path/to/chandler/extensions/available/commitcaptcha /path/to/chandler/ext ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions/enabled/ ``` -4. Импортируйте `install/init-static-db.sql` в **ту же базу данных**, в которую вы установили Chandler +4. Импортируйте `install/init-static-db.sql` в **ту же базу данных**, в которую вы установили Chandler, и импортируйте все SQL файлы из папки `install/sqls` в **ту же базу данных** 5. Импортируйте `install/init-event-db.sql` в **отдельную базу данных** 6. Скопируйте `openvk-example.yml` в `openvk.yml` и измените параметры 7. Запустите `composer install` в директории OpenVK @@ -78,8 +78,9 @@ ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions * Telegram-чат: Перейдите на [наш канал](https://t.me/openvkch) и откройте обсуждение в меню нашего канала. * [Reddit](https://www.reddit.com/r/openvk/) * [Обсуждения](https://github.com/openvk/openvk/discussions) +* Чат в Matrix: #ovk:matrix.org -**Внимание**: баг-трекер и телеграм-чат являются публичными местами, и жалобы в OVK обслуживается волонтерами. Если вам нужно сообщить о чем-то, что не должно быть раскрыто широкой публике (например, сообщение об уязвимости), пожалуйста, свяжитесь с нами напрямую по этому адресу: **openvk [at] tutanota [dot] com**. +**Внимание**: баг-трекер, телеграм- и matrix-чат являются публичными местами, и жалобы в OVK обслуживается волонтерами. Если вам нужно сообщить о чем-то, что не должно быть раскрыто широкой публике (например, сообщение об уязвимости), пожалуйста, свяжитесь с нами напрямую по этому адресу: **openvk [at] tutanota [dot] com**. Get it on Codeberg diff --git a/VKAPI/README.md b/VKAPI/README.md new file mode 100644 index 00000000..b3c85918 --- /dev/null +++ b/VKAPI/README.md @@ -0,0 +1,57 @@ +# VK API Compatability layer for OpenVK + +This directory contains VK api handlers, structures and relared +exceptions. It is still a work-in-progress functionality. +**Note**: requests to api are routed through +openvk.Web.Presenters.VKAPIPresenter, this dir contains only handlers. + +## Implementing API methods + +VK API methods have names like this: `example.test`. To implement a +method like this you will need to create a class `Example` in the +Handlers subdirectory. This class **must** extend VKAPIHandler and be +final. +Next step is to create test method. It **must** have a type hint that is +not void. Everything else is fine, the return value of method will be +authomatically converted to JSON and sent back to client. + +### Parameters + +Method arguments are parameters. To declare a parameter just create an +argument with the same name. You should also provide correct type hints +for them. Type conversion is done automatically if possible. If not +possible error №1 will be returned. +If parameter is not passed by client then router will pass default value +to argument. If there is no default value but argument accepts NULL then +NULL will be passed. If NULL is not acceptable, default value is +undefined and parameter is not passed, API will return missing parameter +error to client. + +### Returning errors + +To return an error, call fail method like this: `$this->fail(5, +"error")` (first argument is error code and second is error message). +You can also throw the exception manually: `throw new +APIErrorException("error", 5)` (class: +openvk.VKAPI.Exceptions.APIErrorException). +If you throw any exception that does not inherit APIErrorException then +API will return error №1 (unknown error) to client. + +### Refering to user + +To get user use `getUser` method: `$this->getUser()`. Keep in mind it +will return NULL if user is undefined (no access\_token passed or it is +invalid/expired or roaming authentification failed). +If you need to check whether user is defined use `userAuthorized`. This +method returns true if user is present and false if not. +If your method can’t work without user context call `requireUser` and it +will automatically return unauthorized error. + +### Working with data + +You can use OpenVK models for that. However, **do not** return them +(either you will leak data or JSON conversion will fail). It is better +to create a response object and return it. It is also a good idea to +define a structure in Structures subdirectory. + +Have a lot of fun diff --git a/VKAPI/README.textile b/VKAPI/README.textile deleted file mode 100644 index f212658d..00000000 --- a/VKAPI/README.textile +++ /dev/null @@ -1,31 +0,0 @@ -h1. VK API Compatability layer for OpenVK - -This directory contains VK api handlers, structures and relared exceptions. It is still a work-in-progress functionality. -**Note**: requests to api are routed through openvk.Web.Presenters.VKAPIPresenter, this dir contains only handlers. - -h2. Implementing API methods - -VK API methods have names like this: @example.test@. To implement a method like this you will need to create a class @Example@ in the Handlers subdirectory. This class **must** extend VKAPIHandler and be final. -Next step is to create %test% method. It **must** have a type hint that is not %void%. Everything else is fine, the return value of method will be authomatically converted to JSON and sent back to client. - -h3. Parameters - -Method arguments are parameters. To declare a parameter just create an argument with the same name. You should also provide correct type hints for them. Type conversion is done automatically if possible. If not possible error №1 will be returned. -If parameter is not passed by client then router will pass default value to argument. If there is no default value but argument accepts NULL then NULL will be passed. If NULL is not acceptable, default value is undefined and parameter is not passed, API will return missing parameter error to client. - -h3. Returning errors - -To return an error, call %fail% method like this: @$this->fail(5, "error")@ (first argument is error code and second is error message). You can also throw the exception manually: @throw new APIErrorException("error", 5)@ (class: openvk.VKAPI.Exceptions.APIErrorException). -If you throw any exception that does not inherit APIErrorException then API will return error №1 (unknown error) to client. - -h3. Refering to user - -To get user use @getUser@ method: @$this->getUser()@. Keep in mind it will return NULL if user is undefined (no access_token passed or it is invalid/expired or roaming authentification failed). -If you need to check whether user is defined use @userAuthorized@. This method returns true if user is present and false if not. -If your method can't work without user context call @requireUser@ and it will automatically return unauthorized error. - -h3. Working with data - -You can use OpenVK models for that. However, **do not** return them (either you will leak data or JSON conversion will fail). It is better to create a response object and return it. It is also a good idea to define a structure in Structures subdirectory. - -Have a lot of fun ^^ diff --git a/Web/Models/Entities/Club.php b/Web/Models/Entities/Club.php index 2230b1a0..0c91b11e 100644 --- a/Web/Models/Entities/Club.php +++ b/Web/Models/Entities/Club.php @@ -40,7 +40,7 @@ class Club extends RowModel function getAvatarUrl(): string { - $serverUrl = ovk_scheme(true) . $_SERVER["SERVER_NAME"]; + $serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"]; $avPhoto = $this->getAvatarPhoto(); return is_null($avPhoto) ? "$serverUrl/assets/packages/static/openvk/img/camera_200.png" : $avPhoto->getURL(); @@ -64,16 +64,6 @@ class Club extends RowModel else return "/club" . $this->getId(); } - /* - function getAvatarUrl(): string - { - $avAlbum = (new Albums)->getUserAvatarAlbum($this); - $avCount = $avAlbum->getPhotosCount(); - $avPhotos = $avAlbum->getPhotos($avCount, 1); - $avPhoto = iterator_to_array($avPhotos)[0] ?? NULL; - - return is_null($avPhoto) ? "/assets/packages/static/openvk/img/camera_200.png" : $avPhoto->getURL(); - } */ function getName(): string { diff --git a/Web/Models/Entities/Notifications/CoinsTransferNotification.php b/Web/Models/Entities/Notifications/CoinsTransferNotification.php index d515631a..2cfc5230 100644 --- a/Web/Models/Entities/Notifications/CoinsTransferNotification.php +++ b/Web/Models/Entities/Notifications/CoinsTransferNotification.php @@ -1,6 +1,6 @@ $2", $text); - $text = preg_replace("%(#([\p{L}_-]++[0-9]*[\p{L}_-]*))%Xu", "$1", $text); + $text = preg_replace("%([\n\r\s]|^)(#([\p{L}_-]++[0-9]*[\p{L}_-]*))%Xu", "$1$2", $text); $text = $this->formatEmojis($text); } diff --git a/Web/Models/Entities/User.php b/Web/Models/Entities/User.php index 84e48041..64b689e4 100644 --- a/Web/Models/Entities/User.php +++ b/Web/Models/Entities/User.php @@ -5,6 +5,7 @@ use openvk\Web\Util\DateTime; use openvk\Web\Models\RowModel; use openvk\Web\Models\Entities\{Photo, Message, Correspondence, Gift}; use openvk\Web\Models\Repositories\{Users, Clubs, Albums, Gifts, Notifications}; +use openvk\Web\Models\Exceptions\InvalidUserNameException; use Nette\Database\Table\ActiveRow; use Chandler\Database\DatabaseConnection; use Chandler\Security\User as ChandlerUser; @@ -103,7 +104,7 @@ class User extends RowModel function getAvatarUrl(): string { - $serverUrl = ovk_scheme(true) . $_SERVER["SERVER_NAME"]; + $serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"]; if($this->getRecord()->deleted) return "$serverUrl/assets/packages/static/openvk/img/camera_200.png"; @@ -739,6 +740,27 @@ class User extends RowModel return true; } + function setFirst_Name(string $firstName): void + { + $firstName = mb_convert_case($firstName, MB_CASE_TITLE); + if(!preg_match('%^[\p{Lu}\p{Lo}]\p{Mn}?(?:[\p{L&}\p{Lo}]\p{Mn}?){1,16}$%u', $firstName)) + throw new InvalidUserNameException; + + $this->stateChanges("first_name", $firstName); + } + + function setLast_Name(string $lastName): void + { + if(!empty($lastName)) + { + $lastName = mb_convert_case($lastName, MB_CASE_TITLE); + if(!preg_match('%^[\p{Lu}\p{Lo}]\p{Mn}?([\p{L&}\p{Lo}]\p{Mn}?){1,16}(\-\g<1>+)?$%u', $lastName)) + throw new InvalidUserNameException; + } + + $this->stateChanges("last_name", $lastName); + } + function setNsfwTolerance(int $tolerance): void { $this->stateChanges("nsfw_tolerance", $tolerance); diff --git a/Web/Models/Exceptions/InvalidUserNameException.php b/Web/Models/Exceptions/InvalidUserNameException.php new file mode 100644 index 00000000..12a595a6 --- /dev/null +++ b/Web/Models/Exceptions/InvalidUserNameException.php @@ -0,0 +1,7 @@ +users->where("CONCAT_WS(' ', first_name, last_name) LIKE ?", $query)->where("deleted", 0); + $result = $this->users->where("CONCAT_WS(' ', first_name, last_name, pseudo) LIKE ?", $query)->where("deleted", 0); return new Util\EntityStream("User", $result); } diff --git a/Web/Models/VideoDrivers/YouTubeVideoDriver.php b/Web/Models/VideoDrivers/YouTubeVideoDriver.php index 1c5b3160..f517d7fd 100644 --- a/Web/Models/VideoDrivers/YouTubeVideoDriver.php +++ b/Web/Models/VideoDrivers/YouTubeVideoDriver.php @@ -5,7 +5,7 @@ final class YouTubeVideoDriver extends VideoDriver { function getThumbnailURL(): string { - return "https://img.youtube.com/vi/$this->id/mq3.jpg"; + return "https://img.youtube.com/vi/$this->id/mqdefault.jpg"; } function getURL(): string diff --git a/Web/Presenters/UserPresenter.php b/Web/Presenters/UserPresenter.php index b05f7843..c3765d82 100644 --- a/Web/Presenters/UserPresenter.php +++ b/Web/Presenters/UserPresenter.php @@ -9,8 +9,9 @@ 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\Exceptions\InvalidUserNameException; use openvk\Web\Util\Validator; -use openvk\Web\Models\Entities\Notifications\CoinsTransferNotification; +use openvk\Web\Models\Entities\Notifications\{CoinsTransferNotification, RatingUpNotification}; use Chandler\Security\Authenticator; use lfkeitel\phptotp\{Base32, Totp}; use chillerlan\QRCode\{QRCode, QROptions}; @@ -137,10 +138,18 @@ final class UserPresenter extends OpenVKPresenter $this->willExecuteWriteAction($_GET['act'] === "status"); if($_GET['act'] === "main" || $_GET['act'] == NULL) { - $user->setFirst_Name(empty($this->postParam("first_name")) ? $user->getFirstName() : $this->postParam("first_name")); - $user->setLast_Name(empty($this->postParam("last_name")) ? "" : $this->postParam("last_name")); + try { + $user->setFirst_Name(empty($this->postParam("first_name")) ? $user->getFirstName() : $this->postParam("first_name")); + $user->setLast_Name(empty($this->postParam("last_name")) ? "" : $this->postParam("last_name")); + } catch(InvalidUserNameException $ex) { + $this->flashFail("err", tr("error"), tr("invalid_real_name")); + } + $user->setPseudo(empty($this->postParam("pseudo")) ? NULL : $this->postParam("pseudo")); $user->setStatus(empty($this->postParam("status")) ? NULL : $this->postParam("status")); + $user->setHometown(empty($this->postParam("hometown")) ? NULL : $this->postParam("hometown")); + + if (strtotime($this->postParam("birthday")) < time()) $user->setBirthday(strtotime($this->postParam("birthday"))); @@ -179,7 +188,7 @@ final class UserPresenter extends OpenVKPresenter $user->setCity(empty($this->postParam("city")) ? NULL : $this->postParam("city")); $user->setAddress(empty($this->postParam("address")) ? NULL : $this->postParam("address")); - + $website = $this->postParam("website") ?? ""; if(empty($website)) $user->setWebsite(NULL); @@ -510,4 +519,44 @@ final class UserPresenter extends OpenVKPresenter $this->flashFail("succ", tr("information_-1"), tr("points_transfer_successful", tr("points_amount", $value), $receiver->getURL(), htmlentities($receiver->getCanonicalName()))); } + + function renderIncreaseRating(): void + { + $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"); + + if(!$receiverAddress || !$value) + $this->flashFail("err", tr("failed_to_increase_rating"), tr("not_all_information_has_been_entered")); + + if($value < 0) + $this->flashFail("err", tr("failed_to_increase_rating"), tr("negative_rating_value")); + + if(iconv_strlen($message) > 255) + $this->flashFail("err", tr("failed_to_increase_rating"), tr("message_is_too_long")); + + $receiver = $this->users->getByAddress($receiverAddress); + if(!$receiver) + $this->flashFail("err", tr("failed_to_increase_rating"), tr("receiver_not_found")); + + if($this->user->identity->getCoins() < $value) + $this->flashFail("err", tr("failed_to_increase_rating"), tr("you_dont_have_enough_points")); + + $this->user->identity->setCoins($this->user->identity->getCoins() - $value); + $this->user->identity->save(); + + $receiver->setRating($receiver->getRating() + $value); + $receiver->save(); + + if($this->user->id !== $receiver->getId()) + (new RatingUpNotification($receiver, $this->user->identity, $value, $message))->emit(); + + $this->flashFail("succ", tr("information_-1"), tr("rating_increase_successful", $receiver->getURL(), htmlentities($receiver->getCanonicalName()), $value)); + } } diff --git a/Web/Presenters/WallPresenter.php b/Web/Presenters/WallPresenter.php index 574832e9..3fb4ba63 100644 --- a/Web/Presenters/WallPresenter.php +++ b/Web/Presenters/WallPresenter.php @@ -5,6 +5,9 @@ use openvk\Web\Models\Entities\Notifications\{RepostNotification, WallPostNotifi use openvk\Web\Models\Repositories\{Posts, Users, Clubs, Albums}; use Chandler\Database\DatabaseConnection; use Nette\InvalidStateException as ISE; +use Bhaktaraz\RSSGenerator\Item; +use Bhaktaraz\RSSGenerator\Feed; +use Bhaktaraz\RSSGenerator\Channel; final class WallPresenter extends OpenVKPresenter { @@ -52,14 +55,14 @@ final class WallPresenter extends OpenVKPresenter $canPost = $owner->getPrivacyPermission("wall.write", $this->user->identity); else $this->flashFail("err", tr("error"), "Ошибка доступа"); - } else if($user < 0) { + } else if($user < 0) { if($owner->canBeModifiedBy($this->user->identity)) $canPost = true; else $canPost = $owner->canPost(); } else { $canPost = false; - } + } if ($embedded == true) $this->template->_template = "components/wall.xml"; $this->template->oObj = $owner; @@ -82,6 +85,49 @@ final class WallPresenter extends OpenVKPresenter { $this->renderWall($user, true); } + + function renderRSS(int $user): void + { + if(false) + exit("Ошибка доступа: " . (string) random_int(0, 255)); + + $owner = ($user < 0 ? (new Clubs) : (new Users))->get(abs($user)); + if(is_null($this->user)) { + $canPost = false; + } else if($user > 0) { + if(!$owner->isBanned()) + $canPost = $owner->getPrivacyPermission("wall.write", $this->user->identity); + else + $this->flashFail("err", tr("error"), "Ошибка доступа"); + } else if($user < 0) { + if($owner->canBeModifiedBy($this->user->identity)) + $canPost = true; + else + $canPost = $owner->canPost(); + } else { + $canPost = false; + } + + $posts = iterator_to_array($this->posts->getPostsFromUsersWall($user)); + + $feed = new Feed(); + + $channel = new Channel(); + $channel->title(OPENVK_ROOT_CONF['openvk']['appearance']['name'])->url(ovk_scheme(true) . $_SERVER["SERVER_NAME"])->appendTo($feed); + + foreach($posts as $post) { + $item = new Item(); + $item + ->title($post->getOwner()->getCanonicalName()) + ->description($post->getText()) + ->url(ovk_scheme(true).$_SERVER["SERVER_NAME"]."/wall{$post->getPrettyId()}") + ->pubDate($post->getPublicationTime()->timestamp()) + ->appendTo($channel); + } + + header("Content-Type: application/rss+xml"); + exit($feed); + } function renderFeed(): void { diff --git a/Web/Presenters/templates/@layout.xml b/Web/Presenters/templates/@layout.xml index 9d723096..e6697c9d 100644 --- a/Web/Presenters/templates/@layout.xml +++ b/Web/Presenters/templates/@layout.xml @@ -262,7 +262,7 @@ {_footer_privacy}

OpenVK {php echo OPENVK_VERSION} | PHP: {phpversion()} | DB: {$dbVersion}

-

+

{php echo OPENVK_ROOT_CONF["openvk"]["appearance"]["motd"]}

diff --git a/Web/Presenters/templates/@listView.xml b/Web/Presenters/templates/@listView.xml index 35c9a2f0..e6d02350 100644 --- a/Web/Presenters/templates/@listView.xml +++ b/Web/Presenters/templates/@listView.xml @@ -2,57 +2,69 @@ {block wrap}
-
+
{include tabs}
- -
- {var data = is_array($iterator) ? $iterator : iterator_to_array($iterator)} - - {if sizeof($data) > 0} -
- - - - - - - - -
- - {include preview, x => $dat} - - - - - {include name, x => $dat} - - -
- - {include description, x => $dat} -
-
- -
- {include "components/paginator.xml", conf => (object) [ - "page" => $page, - "count" => $count, - "amount" => sizeof($data), - "perPage" => $perPage ?? OPENVK_DEFAULT_PER_PAGE, - "atBottom" => true, - ]} -
- {else} - {ifset customErrorMessage} - {include customErrorMessage} + + {ifset size} + {include size, x => $dat} + {/ifset} + + {ifset specpage} + {include specpage, x => $dat} + {else} +
+ {var data = is_array($iterator) ? $iterator : iterator_to_array($iterator)} + + {if sizeof($data) > 0} +
+ + + + + + + + +
+ + {include preview, x => $dat} + + + {ifset infoTable} + {include infoTable, x => $dat} + {else} + + + {include name, x => $dat} + + +
+ {include description, x => $dat} + {/ifset} +
+
+ {include "components/paginator.xml", conf => (object) [ + "page" => $page, + "count" => $count, + "amount" => sizeof($data), + "perPage" => $perPage ?? OPENVK_DEFAULT_PER_PAGE, + "atBottom" => true, + ]} {else} - {include "components/nothing.xml"} - {/ifset} - {/if} -
+ {ifset customErrorMessage} + {include customErrorMessage} + {else} + {include "components/nothing.xml"} + {/ifset} + {/if} +
+ {/ifset} + + {ifset bottom} + {include bottom} + {/ifset}
-{/block} +{/block} \ No newline at end of file diff --git a/Web/Presenters/templates/Notes/List.xml b/Web/Presenters/templates/Notes/List.xml index 44d831f5..2fb31c0c 100644 --- a/Web/Presenters/templates/Notes/List.xml +++ b/Web/Presenters/templates/Notes/List.xml @@ -5,12 +5,25 @@ {block title}{_notes}{/block} {block header} - {$owner->getCanonicalName()} - » - {_notes} - -
- {_create_note} + {if isset($thisUser) && $thisUser->getId() == $owner->getId()} + {_my_notes} + {else} + + {$owner->getCanonicalName()} + » + {_notes} + {/if} +{/block} + +{block size} +
+
+ {tr("notes_list", $count)} + +  |  + {_create_note} + +
{/block} @@ -20,18 +33,66 @@ {* BEGIN ELEMENTS DESCRIPTION *} -{block link|strip|stripHtml} - /note{$x->getPrettyId()} -{/block} +{block specpage} +
-{block preview} -
{_note}
-{/block} + {var data = is_array($iterator) ? $iterator : iterator_to_array($iterator)} + {if sizeof($data) > 0} -{block name} - {$x->getName()} -{/block} +
+
+ + + +
+ +
+ + {else} + {if isset($thisUser) && $thisUser->getId() == $owner->getId()} + +

{_welcome}

{_notes_start_screen}
+ + {else} + {ifset customErrorMessage} + {include ../customErrorMessage} + {else} + {include ../components/nothing.xml} + {/ifset} + {/if} + {/if} +
{/block} diff --git a/Web/Presenters/templates/User/Edit.xml b/Web/Presenters/templates/User/Edit.xml index 21ecf591..96d12f75 100644 --- a/Web/Presenters/templates/User/Edit.xml +++ b/Web/Presenters/templates/User/Edit.xml @@ -80,6 +80,14 @@ + + + {_"hometown"}: + + + + + {_"relationship"}: diff --git a/Web/Presenters/templates/User/Groups.xml b/Web/Presenters/templates/User/Groups.xml index 18860fd8..f1926d1e 100644 --- a/Web/Presenters/templates/User/Groups.xml +++ b/Web/Presenters/templates/User/Groups.xml @@ -2,37 +2,45 @@ {var iterator = $user->getClubs($page, $admin)} {var count = $user->getClubCount($admin)} -{block title}{_"groups"}{/block} +{block title} + {_groups} +{/block} {block header} - {$user->getCanonicalName()} » {_"groups"} - -
- - - - {_"create_group"} - - - -
+ {if !is_null($thisUser) && $user->getId() === $thisUser->getId()} + {_my_groups} + {else} + {$user->getCanonicalName()} » {_groups} + {/if} {/block} {* BEGIN ELEMENTS DESCRIPTION *} {block tabs} - {if !is_null($thisUser) && $user->getId() === $thisUser->getId()} - - - {/if} + {if !is_null($thisUser) && $user->getId() === $thisUser->getId()} + + + {/if} +{/block} + +{block size} +
+
+ {if !is_null($thisUser) && $user->getId() === $thisUser->getId()} + {tr("groups_list", $thisUser->getClubCount())} + {else} + {tr("groups", $user->getClubCount())} + {/if} +
+
{/block} {block link|strip|stripHtml} @@ -43,8 +51,21 @@ Фотография группы {/block} -{block name} - {$x->getName()} +{block name}{/block} + +{block infoTable} + + + + + + + + + + + +
{_name}: {$x->getName()}
{_size}:{tr("participants", $x->getFollowersCount())}
{/block} {block description} @@ -53,13 +74,47 @@ {block actions} {var clubPinned = $thisUser->isClubPinned($x)} - {if $x->canBeModifiedBy($thisUser ?? NULL) && ($clubPinned || $thisUser->getPinnedClubCount() <= 10)} - - {if $clubPinned} - {_remove_from_left_menu} - {else} - {_add_to_left_menu} - {/if} + {if $x->canBeModifiedBy($thisUser ?? NULL)} + + {_check_community} + {if ($clubPinned || $thisUser->getPinnedClubCount() <= 10)} + + {if $clubPinned} + {_remove_from_left_menu} + {else} + {_add_to_left_menu} + {/if} + + {/if} +
+ + + + +
+ {/if} +{/block} + +{block bottom} + {if !is_null($thisUser) && $user->getId() === $thisUser->getId()} +
+
+

{_open_new_group}

+ {_open_group_desc} +
+ +
+
+
+

{_search_group}

+ {_search_group_desc} +
+ + + +
+
+
{/if} {/block} diff --git a/Web/Presenters/templates/User/View.xml b/Web/Presenters/templates/User/View.xml index 6a7dfb9e..f421925c 100644 --- a/Web/Presenters/templates/User/View.xml +++ b/Web/Presenters/templates/User/View.xml @@ -72,6 +72,9 @@ + {else} {if $thisUser->getChandlerUser()->can("substitute")->model('openvk\Web\Models\Entities\User')->whichBelongsTo(0)} diff --git a/Web/Presenters/templates/Videos/List.xml b/Web/Presenters/templates/Videos/List.xml index 0f04b482..be9e3cad 100644 --- a/Web/Presenters/templates/Videos/List.xml +++ b/Web/Presenters/templates/Videos/List.xml @@ -8,9 +8,17 @@ {block header} {$user->getCanonicalName()} » {_"videos"} - -
- {_"upload_video"} +{/block} + +{block size} +
+
+ {tr("videos", $count)} + +  |  + {_upload_video} + +
{/block} diff --git a/Web/Presenters/templates/components/comment.xml b/Web/Presenters/templates/components/comment.xml index 774fc4d9..5671ac54 100644 --- a/Web/Presenters/templates/components/comment.xml +++ b/Web/Presenters/templates/components/comment.xml @@ -7,10 +7,12 @@ - + + +