diff --git a/Web/Models/Entities/User.php b/Web/Models/Entities/User.php index 3525e5f3..67a1e263 100644 --- a/Web/Models/Entities/User.php +++ b/Web/Models/Entities/User.php @@ -219,6 +219,11 @@ class User extends RowModel return $this->getRecord()->coins; } + function getRating(): int + { + return $this->getRecord()->rating; + } + function getReputation(): int { return $this->getRecord()->reputation; @@ -393,7 +398,8 @@ class User extends RowModel } return (object) [ - "total" => 100 - $incompleteness, + "total" => 100 - $incompleteness + $this->getRating(), + "percent" => min(100 - $incompleteness + $this->getRating(), 100), "unfilled" => $unfilled, ]; } diff --git a/Web/Models/Entities/Voucher.php b/Web/Models/Entities/Voucher.php new file mode 100644 index 00000000..6469dddc --- /dev/null +++ b/Web/Models/Entities/Voucher.php @@ -0,0 +1,85 @@ +getRecord()->coins; + } + + function getRating(): int + { + return $this->getRecord()->rating; + } + + function getToken(): string + { + return $this->getRecord()->token; + } + + function getFormattedToken(): string + { + $fmtTok = ""; + $token = $this->getRecord()->token; + foreach(array_chunk(str_split($token), 6) as $chunk) + $fmtTok .= implode("", $chunk) . "-"; + + return substr($fmtTok, 0, -1); + } + + function getRemainingUsages(): float + { + return (float) ($this->getRecord()->usages_left ?? INF); + } + + function getUsers(int $page = -1, ?int $perPage = NULL): \Traversable + { + $relations = $this->getRecord()->related("voucher_users.voucher"); + if($page !== -1) + $relations = $relations->page($page, $perPage ?? OPENVK_DEFAULT_PER_PAGE); + + foreach($relations as $relation) + yield (new Users)->get($relation->user); + } + + function isExpired(): bool + { + return $this->getRemainingUsages() < 1; + } + + function wasUsedBy(User $user): bool + { + $record = $this->getRecord()->related("voucher_users.voucher")->where("user", $user->getId()); + + return sizeof($record) > 0; + } + + function willUse(User $user): bool + { + if($this->wasUsedBy($user)) + return false; + + if($this->isExpired()) + return false; + + $this->setRemainingUsages($this->getRemainingUsages() - 1); + DB::i()->getContext()->table("voucher_users")->insert([ + "voucher" => $this->getId(), + "user" => $user->getId(), + ]); + + return true; + } + + function setRemainingUsages(float $usages): void + { + $this->stateChanges("usages_left", $usages === INF ? NULL : ((int) $usages)); + $this->save(); + } +} diff --git a/Web/Models/Repositories/Repository.php b/Web/Models/Repositories/Repository.php index 3d8e15e4..fe3d6afe 100644 --- a/Web/Models/Repositories/Repository.php +++ b/Web/Models/Repositories/Repository.php @@ -6,8 +6,8 @@ use Nette\Database\Table\ActiveRow; abstract class Repository { - private $context; - private $table; + protected $context; + protected $table; protected $tableName; protected $modelName; @@ -29,5 +29,18 @@ abstract class Repository return $this->toEntity($this->table->get($id)); } + function size(bool $withDeleted = false): int + { + return sizeof($this->table->where("deleted", $withDeleted)); + } + + function enumerate(int $page, ?int $perPage = NULL, bool $withDeleted = false): \Traversable + { + $perPage ??= OPENVK_DEFAULT_PER_PAGE; + + foreach($this->table->where("deleted", $withDeleted)->page($page, $perPage) as $entity) + yield $this->toEntity($entity); + } + use \Nette\SmartObject; } diff --git a/Web/Models/Repositories/Vouchers.php b/Web/Models/Repositories/Vouchers.php new file mode 100644 index 00000000..62d0a54e --- /dev/null +++ b/Web/Models/Repositories/Vouchers.php @@ -0,0 +1,19 @@ +table->where([ + "token" => $token, + "deleted" => $withDeleted, + ])->fetch(); + + return $this->toEntity($voucher); + } +} diff --git a/Web/Presenters/AdminPresenter.php b/Web/Presenters/AdminPresenter.php index 64620704..fe5dab25 100644 --- a/Web/Presenters/AdminPresenter.php +++ b/Web/Presenters/AdminPresenter.php @@ -1,17 +1,19 @@ users = $users; - $this->clubs = $clubs; + $this->users = $users; + $this->clubs = $clubs; + $this->vouchers = $vouchers; parent::__construct(); } @@ -106,6 +108,57 @@ final class AdminPresenter extends OpenVKPresenter } } + function renderVouchers(): void + { + $this->template->count = $this->vouchers->size(); + $this->template->vouchers = iterator_to_array($this->vouchers->enumerate((int) ($this->queryParam("p") ?? 1))); + } + + function renderVoucher(int $id): void + { + $voucher = NULL; + $this->template->form = (object) []; + if($id === 0) { + $this->template->form->id = 0; + $this->template->form->token = NULL; + $this->template->form->coins = 0; + $this->template->form->rating = 0; + $this->template->form->usages = -1; + $this->template->form->users = []; + } else { + $voucher = $this->vouchers->get($id); + if(!$voucher) + $this->notFound(); + + $this->template->form->id = $voucher->getId(); + $this->template->form->token = $voucher->getToken(); + $this->template->form->coins = $voucher->getCoins(); + $this->template->form->rating = $voucher->getRating(); + $this->template->form->usages = $voucher->getRemainingUsages(); + $this->template->form->users = iterator_to_array($voucher->getUsers()); + + if($this->template->form->usages === INF) + $this->template->form->usages = -1; + else + $this->template->form->usages = (int) $this->template->form->usages; + } + + if($_SERVER["REQUEST_METHOD"] !== "POST") + return; + + $voucher ??= new Voucher; + $voucher->setCoins((int) $this->postParam("coins")); + $voucher->setRating((int) $this->postParam("rating")); + $voucher->setRemainingUsages($this->postParam("usages") === '-1' ? INF : ((int) $this->postParam("usages"))); + if(!empty($tok = $this->postParam("token")) && strlen($tok) === 24) + $voucher->setToken($tok); + + $voucher->save(); + + $this->redirect("/admin/vouchers/id" . $voucher->getId(), static::REDIRECT_TEMPORARY); + exit; + } + function renderFiles(): void { diff --git a/Web/Presenters/UserPresenter.php b/Web/Presenters/UserPresenter.php index 6b768c38..12245671 100644 --- a/Web/Presenters/UserPresenter.php +++ b/Web/Presenters/UserPresenter.php @@ -7,6 +7,7 @@ use openvk\Web\Models\Repositories\Users; use openvk\Web\Models\Repositories\Albums; use openvk\Web\Models\Repositories\Videos; use openvk\Web\Models\Repositories\Notes; +use openvk\Web\Models\Repositories\Vouchers; final class UserPresenter extends OpenVKPresenter { @@ -240,7 +241,7 @@ final class UserPresenter extends OpenVKPresenter if(!$user->setShortCode(empty($this->postParam("sc")) ? NULL : $this->postParam("sc"))) $this->flashFail("err", tr("error"), tr("error_shorturl_incorrect")); - }elseif($_GET['act'] === "privacy") { + } else if($_GET['act'] === "privacy") { $settings = [ "page.read", "page.info.read", @@ -256,7 +257,22 @@ final class UserPresenter extends OpenVKPresenter $input = $this->postParam(str_replace(".", "_", $setting)); $user->setPrivacySetting($setting, min(3, abs($input ?? $user->getPrivacySetting($setting)))); } - }elseif($_GET['act'] === "interface") { + } else if($_GET['act'] === "finance.top-up") { + $token = $this->postParam("key0") . $this->postParam("key1") . $this->postParam("key2") . $this->postParam("key3"); + $voucher = (new Vouchers)->getByToken($token); + if(!$voucher) + $this->flashFail("err", tr("invalid_voucher"), tr("voucher_bad")); + + $perm = $voucher->willUse($user); + if(!$perm) + $this->flashFail("err", tr("invalid_voucher"), tr("voucher_bad")); + + $user->setCoins($user->getCoins() + $voucher->getCoins()); + $user->setRating($user->getRating() + $voucher->getRating()); + $user->save(); + + $this->flashFail("succ", tr("voucher_good"), tr("voucher_redeemed")); + } else if($_GET['act'] === "interface") { if (isset(Themepacks::i()[$this->postParam("style")]) || $this->postParam("style") === Themepacks::DEFAULT_THEME_ID) $user->setStyle($this->postParam("style")); @@ -271,7 +287,7 @@ final class UserPresenter extends OpenVKPresenter if(in_array($this->postParam("nsfw"), [0, 1, 2])) $user->setNsfwTolerance((int) $this->postParam("nsfw")); - }elseif($_GET['act'] === "lMenu") { + } else if($_GET['act'] === "lMenu") { $settings = [ "menu_bildoj" => "photos", "menu_filmetoj" => "videos", @@ -300,7 +316,7 @@ final class UserPresenter extends OpenVKPresenter ); } $this->template->mode = in_array($this->queryParam("act"), [ - "main", "privacy", "finance", "interface" + "main", "privacy", "finance", "finance.top-up", "interface" ]) ? $this->queryParam("act") : "main"; $this->template->user = $user; diff --git a/Web/Presenters/templates/Admin/@layout.xml b/Web/Presenters/templates/Admin/@layout.xml index 9854e2f9..2fb177fe 100644 --- a/Web/Presenters/templates/Admin/@layout.xml +++ b/Web/Presenters/templates/Admin/@layout.xml @@ -69,9 +69,14 @@ Группы + +