diff --git a/Web/Models/Entities/Link.php b/Web/Models/Entities/Link.php new file mode 100644 index 00000000..97e3ac8a --- /dev/null +++ b/Web/Models/Entities/Link.php @@ -0,0 +1,117 @@ +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; +} diff --git a/Web/Models/Repositories/Links.php b/Web/Models/Repositories/Links.php new file mode 100644 index 00000000..eed992cd --- /dev/null +++ b/Web/Models/Repositories/Links.php @@ -0,0 +1,38 @@ +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)); + } +} diff --git a/Web/Presenters/GroupPresenter.php b/Web/Presenters/GroupPresenter.php index 00d74c2e..93fc464c 100644 --- a/Web/Presenters/GroupPresenter.php +++ b/Web/Presenters/GroupPresenter.php @@ -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; } diff --git a/Web/Presenters/LinksPresenter.php b/Web/Presenters/LinksPresenter.php new file mode 100644 index 00000000..0de0730b --- /dev/null +++ b/Web/Presenters/LinksPresenter.php @@ -0,0 +1,137 @@ +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")); + } +} diff --git a/Web/Presenters/UserPresenter.php b/Web/Presenters/UserPresenter.php index 3acabc84..aa742cfc 100644 --- a/Web/Presenters/UserPresenter.php +++ b/Web/Presenters/UserPresenter.php @@ -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; } diff --git a/Web/Presenters/templates/Group/View.xml b/Web/Presenters/templates/Group/View.xml index dc223af1..6b6d167c 100644 --- a/Web/Presenters/templates/Group/View.xml +++ b/Web/Presenters/templates/Group/View.xml @@ -191,6 +191,32 @@ +