mirror of
https://github.com/openvk/openvk
synced 2024-12-22 16:42:32 +03:00
Users, Groups: Add a block with links
This is similar to the links that are in the original VK, but unlike the original, links can be not only the group but also the page (Linktree moment).
This commit is contained in:
parent
7d72cd182b
commit
1b71a3ad25
12 changed files with 488 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}
|
||||
|
|
|
@ -24,6 +24,7 @@ services:
|
|||
- openvk\Web\Presenters\ThemepacksPresenter
|
||||
- openvk\Web\Presenters\VKAPIPresenter
|
||||
- openvk\Web\Presenters\BannedLinkPresenter
|
||||
- openvk\Web\Presenters\LinksPresenter
|
||||
- openvk\Web\Models\Repositories\Users
|
||||
- openvk\Web\Models\Repositories\Posts
|
||||
- openvk\Web\Models\Repositories\Photos
|
||||
|
@ -42,6 +43,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
|
||||
|
|
|
@ -207,6 +207,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;
|
Loading…
Reference in a new issue