mirror of
https://github.com/openvk/openvk
synced 2025-07-07 16:29:50 +03:00
Merge 363f259217
into a6ab5d38bb
This commit is contained in:
commit
36689d1acb
14 changed files with 540 additions and 2 deletions
117
Web/Models/Entities/Link.php
Normal file
117
Web/Models/Entities/Link.php
Normal file
|
@ -0,0 +1,117 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Entities;
|
||||
use Nette\Utils\Image;
|
||||
use Nette\Utils\UnknownImageFileException;
|
||||
use openvk\Web\Models\Repositories\Users;
|
||||
use openvk\Web\Models\Repositories\Clubs;
|
||||
use openvk\Web\Models\RowModel;
|
||||
|
||||
class Link extends RowModel
|
||||
{
|
||||
protected $tableName = "links";
|
||||
|
||||
private function getIconsDir(): string
|
||||
{
|
||||
$uploadSettings = OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"];
|
||||
if($uploadSettings["mode"] === "server" && $uploadSettings["server"]["kind"] === "cdn")
|
||||
return $uploadSettings["server"]["directory"];
|
||||
else
|
||||
return OPENVK_ROOT . "/storage/";
|
||||
}
|
||||
|
||||
function getId(): int
|
||||
{
|
||||
return $this->getRecord()->id;
|
||||
}
|
||||
|
||||
function getOwner(): RowModel
|
||||
{
|
||||
$ownerId = (int) $this->getRecord()->owner;
|
||||
|
||||
if($ownerId > 0)
|
||||
return (new Users)->get($ownerId);
|
||||
else
|
||||
return (new Clubs)->get($ownerId * -1);
|
||||
}
|
||||
|
||||
function getTitle(): string
|
||||
{
|
||||
return $this->getRecord()->title;
|
||||
}
|
||||
|
||||
function getDescription(): ?string
|
||||
{
|
||||
return $this->getRecord()->description;
|
||||
}
|
||||
|
||||
function getDescriptionOrDomain(): string
|
||||
{
|
||||
$description = $this->getDescription();
|
||||
|
||||
if(is_null($description))
|
||||
return $this->getDomain();
|
||||
else
|
||||
return $description;
|
||||
}
|
||||
|
||||
function getUrl(): string
|
||||
{
|
||||
return $this->getRecord()->url;
|
||||
}
|
||||
|
||||
function getIconUrl(): string
|
||||
{
|
||||
$serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
|
||||
if(is_null($this->getRecord()->icon_hash))
|
||||
return "$serverUrl/assets/packages/static/openvk/img/camera_200.png";
|
||||
|
||||
$hash = $this->getRecord()->icon_hash;
|
||||
switch(OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["mode"]) {
|
||||
default:
|
||||
case "default":
|
||||
case "basic":
|
||||
return "$serverUrl/blob_" . substr($hash, 0, 2) . "/$hash" . "_link_icon.png";
|
||||
case "accelerated":
|
||||
return "$serverUrl/openvk-datastore/$hash" . "_link_icon.png";
|
||||
case "server":
|
||||
$settings = (object) OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["server"];
|
||||
return (
|
||||
$settings->protocol ?? ovk_scheme() .
|
||||
"://" . $settings->host .
|
||||
$settings->path .
|
||||
substr($hash, 0, 2) . "/$hash" . "_link_icon.png"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function setIcon(array $file): int
|
||||
{
|
||||
if($file["error"] !== UPLOAD_ERR_OK)
|
||||
return -1;
|
||||
|
||||
try {
|
||||
$image = Image::fromFile($file["tmp_name"]);
|
||||
} catch (UnknownImageFileException $e) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
$hash = hash_file("adler32", $file["tmp_name"]);
|
||||
if(!is_dir($this->getIconsDir() . substr($hash, 0, 2)))
|
||||
if(!mkdir($this->getIconsDir() . substr($hash, 0, 2)))
|
||||
return -3;
|
||||
|
||||
$image->resize(140, 140, Image::STRETCH);
|
||||
$image->save($this->getIconsDir() . substr($hash, 0, 2) . "/$hash" . "_link_icon.png");
|
||||
|
||||
$this->stateChanges("icon_hash", $hash);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function getDomain(): string
|
||||
{
|
||||
return parse_url($this->getUrl(), PHP_URL_HOST);
|
||||
}
|
||||
|
||||
use Traits\TOwnable;
|
||||
}
|
38
Web/Models/Repositories/Links.php
Normal file
38
Web/Models/Repositories/Links.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Repositories;
|
||||
use openvk\Web\Models\Entities\Link;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
|
||||
class Links
|
||||
{
|
||||
private $context;
|
||||
private $links;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->context = DatabaseConnection::i()->getContext();
|
||||
$this->links = $this->context->table("links");
|
||||
}
|
||||
|
||||
function get(int $id): ?Link
|
||||
{
|
||||
$link = $this->links->get($id);
|
||||
if(!$link) return NULL;
|
||||
|
||||
return new Link($link);
|
||||
}
|
||||
|
||||
function getByOwnerId(int $ownerId, int $page = 1, ?int $perPage = NULL): \Traversable
|
||||
{
|
||||
$perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE;
|
||||
$links = $this->links->where("owner", $ownerId)->page($page, $perPage);
|
||||
|
||||
foreach($links as $link)
|
||||
yield new Link($link);
|
||||
}
|
||||
|
||||
function getCountByOwnerId(int $id): int
|
||||
{
|
||||
return sizeof($this->links->where("owner", $id));
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\{Club, Photo};
|
||||
use openvk\Web\Models\Entities\Notifications\ClubModeratorNotification;
|
||||
use openvk\Web\Models\Repositories\{Clubs, Users, Albums, Managers, Topics};
|
||||
use openvk\Web\Models\Repositories\{Clubs, Users, Albums, Managers, Topics, Links};
|
||||
use Chandler\Security\Authenticator;
|
||||
|
||||
final class GroupPresenter extends OpenVKPresenter
|
||||
|
@ -27,6 +27,8 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$this->template->albumsCount = (new Albums)->getClubAlbumsCount($club);
|
||||
$this->template->topics = (new Topics)->getLastTopics($club, 3);
|
||||
$this->template->topicsCount = (new Topics)->getClubTopicsCount($club);
|
||||
$this->template->links = (new Links)->getByOwnerId($club->getId() * -1, 1, 5);
|
||||
$this->template->linksCount = (new Links)->getCountByOwnerId($club->getId() * -1);
|
||||
|
||||
$this->template->club = $club;
|
||||
}
|
||||
|
|
137
Web/Presenters/LinksPresenter.php
Normal file
137
Web/Presenters/LinksPresenter.php
Normal file
|
@ -0,0 +1,137 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\Link;
|
||||
use openvk\Web\Models\Repositories\{Links, Clubs, Users};
|
||||
|
||||
final class LinksPresenter extends OpenVKPresenter
|
||||
{
|
||||
private $links;
|
||||
|
||||
function __construct(Links $links)
|
||||
{
|
||||
$this->links = $links;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
function renderList(int $ownerId): void
|
||||
{
|
||||
$owner = ($ownerId < 0 ? (new Clubs) : (new Users))->get(abs($ownerId));
|
||||
if(!$owner)
|
||||
$this->notFound();
|
||||
|
||||
$this->template->owner = $owner;
|
||||
$this->template->ownerId = $ownerId;
|
||||
$page = (int) ($this->queryParam("p") ?? 1);
|
||||
|
||||
$this->template->links = $this->links->getByOwnerId($ownerId, $page);
|
||||
$this->template->count = $this->links->getCountByOwnerId($ownerId);
|
||||
|
||||
$this->template->paginatorConf = (object) [
|
||||
"count" => $this->template->count,
|
||||
"page" => $page,
|
||||
"amount" => NULL,
|
||||
"perPage" => OPENVK_DEFAULT_PER_PAGE,
|
||||
];
|
||||
}
|
||||
|
||||
function renderCreate(int $ownerId): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
|
||||
$owner = ($ownerId < 0 ? (new Clubs) : (new Users))->get(abs($ownerId));
|
||||
if(!$owner)
|
||||
$this->notFound();
|
||||
|
||||
if($ownerId < 0 ? !$owner->canBeModifiedBy($this->user->identity) : $owner->getId() !== $this->user->id)
|
||||
$this->notFound();
|
||||
|
||||
$this->template->_template = "Links/Edit.xml";
|
||||
$this->template->create = true;
|
||||
$this->template->owner = $owner;
|
||||
$this->template->ownerId = $ownerId;
|
||||
}
|
||||
|
||||
function renderEdit(int $ownerId, int $id): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
|
||||
$owner = ($ownerId < 0 ? (new Clubs) : (new Users))->get(abs($ownerId));
|
||||
if(!$owner)
|
||||
$this->notFound();
|
||||
|
||||
$link = $this->links->get($id);
|
||||
if(!$link && $id !== 0) // If the link ID is 0, consider the request as link creation
|
||||
$this->notFound();
|
||||
|
||||
if($ownerId < 0 ? !$owner->canBeModifiedBy($this->user->identity) : $owner->getId() !== $this->user->id)
|
||||
$this->notFound();
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$create = $id === 0;
|
||||
$title = $this->postParam("title");
|
||||
$description = $this->postParam("description");
|
||||
$url = $this->postParam("url");
|
||||
$url = (!parse_url($url, PHP_URL_SCHEME) ? "https://" : "") . $url;
|
||||
|
||||
if(!$title || !$url)
|
||||
$this->flashFail("err", tr($create ? "failed_to_create_link" : "failed_to_change_link"), tr("not_all_data_entered"));
|
||||
|
||||
if(!filter_var($url, FILTER_VALIDATE_URL))
|
||||
$this->flashFail("err", tr($create ? "failed_to_create_link" : "failed_to_change_link"), tr("wrong_address"));
|
||||
|
||||
if($create)
|
||||
$link = new Link;
|
||||
|
||||
$link->setOwner($ownerId);
|
||||
$link->setTitle(ovk_proc_strtr($title, 127));
|
||||
$link->setDescription($description === "" ? NULL : ovk_proc_strtr($description, 127));
|
||||
$link->setUrl($url);
|
||||
|
||||
if(isset($_FILES["icon"]) && $_FILES["icon"]["size"] > 0) {
|
||||
if(($res = $link->setIcon($_FILES["icon"])) !== 0)
|
||||
$this->flashFail("err", tr("unable_to_upload_icon"), tr("unable_to_upload_icon_desc", $res));
|
||||
}
|
||||
|
||||
$link->save();
|
||||
|
||||
$this->flash("succ", tr("information_-1"), tr($create ? "link_created" : "link_changed"));
|
||||
$this->redirect("/links" . $ownerId);
|
||||
}
|
||||
|
||||
if($id === 0) // But there is a separate handler for displaying page with the fields to create, so here we do not skip
|
||||
$this->notFound();
|
||||
|
||||
$this->template->linkId = $link->getId();
|
||||
$this->template->title = $link->getTitle();
|
||||
$this->template->description = $link->getDescription();
|
||||
$this->template->url = $link->getUrl();
|
||||
$this->template->create = false;
|
||||
$this->template->owner = $owner;
|
||||
$this->template->ownerId = $ownerId;
|
||||
$this->template->link = $link;
|
||||
}
|
||||
|
||||
function renderDelete(int $ownerId, int $id): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
|
||||
$owner = ($ownerId < 0 ? (new Clubs) : (new Users))->get(abs($ownerId));
|
||||
if(!$owner)
|
||||
$this->notFound();
|
||||
|
||||
$link = $this->links->get($id);
|
||||
if(!$link)
|
||||
$this->notFound();
|
||||
|
||||
if(!$link->canBeModifiedBy($this->user->identity))
|
||||
$this->notFound();
|
||||
|
||||
$this->willExecuteWriteAction();
|
||||
$link->delete(false);
|
||||
|
||||
$this->flashFail("succ", tr("information_-1"), tr("link_deleted"));
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ use openvk\Web\Util\Sms;
|
|||
use openvk\Web\Themes\Themepacks;
|
||||
use openvk\Web\Models\Entities\{Photo, Post, EmailChangeVerification};
|
||||
use openvk\Web\Models\Entities\Notifications\{CoinsTransferNotification, RatingUpNotification};
|
||||
use openvk\Web\Models\Repositories\{Users, Clubs, Albums, Videos, Notes, Vouchers, EmailChangeVerifications};
|
||||
use openvk\Web\Models\Repositories\{Users, Clubs, Albums, Videos, Notes, Vouchers, EmailChangeVerifications, Links};
|
||||
use openvk\Web\Models\Exceptions\InvalidUserNameException;
|
||||
use openvk\Web\Util\Validator;
|
||||
use Chandler\Security\Authenticator;
|
||||
|
@ -43,6 +43,8 @@ final class UserPresenter extends OpenVKPresenter
|
|||
$this->template->videosCount = (new Videos)->getUserVideosCount($user);
|
||||
$this->template->notes = (new Notes)->getUserNotes($user, 1, 4);
|
||||
$this->template->notesCount = (new Notes)->getUserNotesCount($user);
|
||||
$this->template->links = (new Links)->getByOwnerId($user->getId(), 1, 5);
|
||||
$this->template->linksCount = (new Links)->getCountByOwnerId($user->getId());
|
||||
|
||||
$this->template->user = $user;
|
||||
}
|
||||
|
|
|
@ -191,6 +191,32 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div n:if="$linksCount > 0 || ($thisUser && $club->canBeModifiedBy($thisUser))">
|
||||
<div class="content_title_expanded" onclick="hidePanel(this, {$linksCount});">
|
||||
{_links}
|
||||
</div>
|
||||
<div>
|
||||
<div class="content_subtitle">
|
||||
{tr("links_count", $linksCount)}
|
||||
<div style="float: right;">
|
||||
<a href="/links-{$club->getId()}">{_all_title}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="avatar-list">
|
||||
<div class="avatar-list-item" n:foreach="$links as $link">
|
||||
<div class="avatar">
|
||||
<a href="/away.php?to={$link->getUrl()}">
|
||||
<img height="32" class="ava" src="{$link->getIconUrl()}" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="info">
|
||||
<a href="/away.php?to={$link->getUrl()}" class="title">{$link->getTitle()}</a>
|
||||
<div class="subtitle">{$link->getDescriptionOrDomain()}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div n:if="$albumsCount > 0 || ($thisUser && $club->canBeModifiedBy($thisUser))">
|
||||
<div class="content_title_expanded" onclick="hidePanel(this, {$albumsCount});">
|
||||
{_albums}
|
||||
|
|
71
Web/Presenters/templates/Links/Edit.xml
Normal file
71
Web/Presenters/templates/Links/Edit.xml
Normal file
|
@ -0,0 +1,71 @@
|
|||
{extends "../@layout.xml"}
|
||||
{block title}
|
||||
{if $create}
|
||||
{_new_link}
|
||||
{else}
|
||||
{_edit_link} "{$title}"
|
||||
{/if}
|
||||
{/block}
|
||||
|
||||
{block header}
|
||||
<a href="{$owner->getURL()}">{$owner->getCanonicalName()}</a>
|
||||
»
|
||||
<a href="/links{$ownerId}">{_links}</a>
|
||||
»
|
||||
{if $create}{_new_link}{else}{_edit_link}{/if}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
<div class="container_gray">
|
||||
<h4>{if $create}{_new_link}{else}{_edit_link}{/if}</h4>
|
||||
<form method="POST" action="/links{$ownerId}/edit{$linkId ?? 0}" enctype="multipart/form-data">
|
||||
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="120" valign="top">
|
||||
<span class="nobold">{_title}:</span>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="title" value="{$title ?? ''}" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="120" valign="top">
|
||||
<span class="nobold">{_address}:</span>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="url" value="{$url ?? ''}" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="120" valign="top">
|
||||
<span class="nobold">{_description}:</span>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="description" value="{$description ?? ''}" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="120" valign="top">
|
||||
<span class="nobold">{_icon}: </span>
|
||||
</td>
|
||||
<td>
|
||||
<input type="file" name="icon" accept="image/*" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="120" valign="top">
|
||||
|
||||
</td>
|
||||
<td>
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
<input type="submit" value="{_save}" class="button" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
</form>
|
||||
</div>
|
||||
{/block}
|
42
Web/Presenters/templates/Links/List.xml
Normal file
42
Web/Presenters/templates/Links/List.xml
Normal file
|
@ -0,0 +1,42 @@
|
|||
{extends "../@listView.xml"}
|
||||
{var $iterator = iterator_to_array($links)}
|
||||
{var $page = $paginatorConf->page}
|
||||
|
||||
{block title}{_links} {$owner->getCanonicalName()}{/block}
|
||||
|
||||
{block header}
|
||||
<a href="{$owner->getURL()}">{$owner->getCanonicalName()}</a> » {_links}
|
||||
|
||||
<div n:if="!is_null($thisUser) && ($ownerId > 0 ? $ownerId === $thisUser->getId() : $owner->canBeModifiedBy($thisUser))" style="float: right;">
|
||||
<a href="/links{$ownerId}/create">{_create_link}</a>
|
||||
</div>
|
||||
{/block}
|
||||
|
||||
{* BEGIN ELEMENTS DESCRIPTION *}
|
||||
|
||||
{block link|strip|stripHtml}
|
||||
/away.php?to={$x->getUrl()}
|
||||
{/block}
|
||||
|
||||
{block preview}
|
||||
<img src="{$x->getIconUrl()}" alt="{$x->getTitle()}" width=75 />
|
||||
{/block}
|
||||
|
||||
{block name}
|
||||
{$x->getTitle()}
|
||||
{/block}
|
||||
|
||||
{block description}
|
||||
{$x->getDescriptionOrDomain()}
|
||||
{/block}
|
||||
|
||||
{block actions}
|
||||
{if !is_null($thisUser) && $x->canBeModifiedBy($thisUser)}
|
||||
<a class="profile_link" href="/links{$ownerId}/edit{$x->getId()}">
|
||||
{_edit}
|
||||
</a>
|
||||
<a class="profile_link" href="/links{$ownerId}/delete{$x->getId()}">
|
||||
{_delete}
|
||||
</a>
|
||||
{/if}
|
||||
{/block}
|
|
@ -229,6 +229,32 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div n:if="$linksCount > 0 || $thisUser != NULL && $user->getId() === $thisUser->getId()">
|
||||
<div class="content_title_expanded" onclick="hidePanel(this, {$linksCount});">
|
||||
{_links}
|
||||
</div>
|
||||
<div>
|
||||
<div class="content_subtitle">
|
||||
{tr("links_count", $linksCount)}
|
||||
<div style="float: right;">
|
||||
<a href="/links{$user->getId()}">{_all_title}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="avatar-list">
|
||||
<div class="avatar-list-item" n:foreach="$links as $link">
|
||||
<div class="avatar">
|
||||
<a href="/away.php?to={$link->getUrl()}">
|
||||
<img height="32" class="ava" src="{$link->getIconUrl()}" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="info">
|
||||
<a href="/away.php?to={$link->getUrl()}" class="title">{$link->getTitle()}</a>
|
||||
<div class="subtitle">{$link->getDescriptionOrDomain()}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div n:if="$albumsCount > 0 && $user->getPrivacyPermission('photos.read', $thisUser ?? NULL)">
|
||||
<div class="content_title_expanded" onclick="hidePanel(this, {$albumsCount});">
|
||||
{_albums}
|
||||
|
|
|
@ -25,6 +25,7 @@ services:
|
|||
- openvk\Web\Presenters\VKAPIPresenter
|
||||
- openvk\Web\Presenters\PollPresenter
|
||||
- openvk\Web\Presenters\BannedLinkPresenter
|
||||
- openvk\Web\Presenters\LinksPresenter
|
||||
- openvk\Web\Models\Repositories\Users
|
||||
- openvk\Web\Models\Repositories\Posts
|
||||
- openvk\Web\Models\Repositories\Polls
|
||||
|
@ -44,6 +45,7 @@ services:
|
|||
- openvk\Web\Models\Repositories\Gifts
|
||||
- openvk\Web\Models\Repositories\Topics
|
||||
- openvk\Web\Models\Repositories\Applications
|
||||
- openvk\Web\Models\Repositories\Links
|
||||
- openvk\Web\Models\Repositories\ContentSearchRepository
|
||||
- openvk\Web\Models\Repositories\Aliases
|
||||
- openvk\Web\Models\Repositories\BannedLinks
|
||||
|
|
|
@ -211,6 +211,14 @@ routes:
|
|||
handler: "Topics->edit"
|
||||
- url: "/topic{num}_{num}/delete"
|
||||
handler: "Topics->delete"
|
||||
- url: "/links{num}"
|
||||
handler: "Links->list"
|
||||
- url: "/links{num}/create"
|
||||
handler: "Links->create"
|
||||
- url: "/links{num}/edit{num}"
|
||||
handler: "Links->edit"
|
||||
- url: "/links{num}/delete{num}"
|
||||
handler: "Links->delete"
|
||||
- url: "/audios{num}"
|
||||
handler: "Audios->app"
|
||||
- url: "/audios{num}.json"
|
||||
|
|
15
install/sqls/00034-links.sql
Normal file
15
install/sqls/00034-links.sql
Normal file
|
@ -0,0 +1,15 @@
|
|||
CREATE TABLE IF NOT EXISTS `links` (
|
||||
`id` bigint(20) unsigned NOT NULL,
|
||||
`owner` bigint(20) NOT NULL,
|
||||
`title` varchar(128) COLLATE utf8mb4_unicode_520_ci NOT NULL,
|
||||
`description` varchar(128) COLLATE utf8mb4_unicode_520_ci,
|
||||
`url` varchar(128) COLLATE utf8mb4_unicode_520_ci NOT NULL,
|
||||
`icon_hash` char(128) COLLATE utf8mb4_unicode_520_ci
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
|
||||
|
||||
ALTER TABLE `links`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD KEY `owner` (`owner`);
|
||||
|
||||
ALTER TABLE `links`
|
||||
MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;
|
|
@ -790,6 +790,31 @@
|
|||
"app_err_note" = "Failed to attach a news note";
|
||||
"app_err_note_desc" = "Make sure the link is correct and the note belongs to you.";
|
||||
|
||||
/* Links */
|
||||
|
||||
"links" = "Links";
|
||||
"create_link" = "Create link";
|
||||
"new_link" = "New link";
|
||||
"edit_link" = "Edit link";
|
||||
"icon" = "Icon";
|
||||
|
||||
"failed_to_create_link" = "Failed to create link";
|
||||
"failed_to_change_link" = "Failed to change link";
|
||||
|
||||
"unable_to_upload_icon" = "Unable to upload icon";
|
||||
"unable_to_upload_icon_desc" = "Icon too big or wrong: general error #$res.";
|
||||
|
||||
"not_all_data_entered" = "Not all data has been entered.";
|
||||
"wrong_address" = "Wrong address :(";
|
||||
|
||||
"link_created" = "Link created.";
|
||||
"link_changed" = "Link changed";
|
||||
"link_deleted" = "Link deleted.";
|
||||
|
||||
"links_count_zero" = "No links";
|
||||
"links_count_one" = "One link";
|
||||
"links_count_other" = "$1 links";
|
||||
|
||||
/* Support */
|
||||
|
||||
"support_opened" = "Opened";
|
||||
|
|
|
@ -725,6 +725,33 @@
|
|||
"app_err_note" = "Не удалось прикрепить новостную заметку";
|
||||
"app_err_note_desc" = "Убедитесь, что ссылка правильная и заметка принадлежит Вам.";
|
||||
|
||||
/* Links */
|
||||
|
||||
"links" = "Ссылки";
|
||||
"create_link" = "Создать ссылку";
|
||||
"new_link" = "Новая ссылка";
|
||||
"edit_link" = "Редактировать ссылку";
|
||||
"icon" = "Иконка";
|
||||
|
||||
"failed_to_create_link" = "Не удалось создать ссылку";
|
||||
"failed_to_change_link" = "Не удалось изменить ссылку";
|
||||
|
||||
"failed_to_upload_icon" = "Не удалось загрузить иконку";
|
||||
"failed_to_upload_icon_desc" = "Иконка слишком большая или кривая: ошибка общего характера №$res.";
|
||||
|
||||
"not_all_data_entered" = "Не все данные введены.";
|
||||
"wrong_address" = "Неправильный адрес :(";
|
||||
|
||||
"link_created" = "Ссылка создана.";
|
||||
"link_changed" = "Ссылка изменена";
|
||||
"link_deleted" = "Ссылка удалена.";
|
||||
|
||||
"links_count_zero" = "Нет ссылок";
|
||||
"links_count_one" = "Одна ссылка";
|
||||
"links_count_few" = "$1 ссылки";
|
||||
"links_count_many" = "$1 ссылок";
|
||||
"links_count_other" = "$1 ссылок";
|
||||
|
||||
/* Support */
|
||||
|
||||
"support_opened" = "Открытые";
|
||||
|
|
Loading…
Reference in a new issue