Skip to content

Commit

Permalink
appify custom navigation links
Browse files Browse the repository at this point in the history
  • Loading branch information
Spine committed Apr 7, 2024
1 parent 34d5e6d commit 88d1a19
Show file tree
Hide file tree
Showing 13 changed files with 302 additions and 80 deletions.
25 changes: 0 additions & 25 deletions app/Manager/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -1629,31 +1629,6 @@ public function updateLastAccess(): int {
return $affected;
}

public function userNavList(\Gazelle\User $user): array {
$navList = $user->navigationList();
$list = [];
foreach ($this->userNavFullList() as $n) {
if ($n['mandatory'] || in_array($n['id'], $navList) || (!$navList && $n['initial'])) {
$list[] = $n;
}
}
return $list;
}

public function userNavFullList(): array {
$list = self::$cache->get_value("nav_items");
if (!$list) {
$QueryID = self::$db->get_query_id();
self::$db->prepared_query("
SELECT id, tag, title, target, tests, test_user, mandatory, initial
FROM nav_items");
$list = self::$db->to_array("id", MYSQLI_ASSOC, false);
self::$cache->cache_value("nav_items", $list, 0);
self::$db->set_query_id($QueryID);
}
return $list;
}

public function checkPassword(string $password): string {
return $password === ''
|| (bool)self::$db->scalar("
Expand Down
78 changes: 78 additions & 0 deletions app/Manager/UserNavigation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

namespace Gazelle\Manager;

class UserNavigation extends \Gazelle\BaseManager {
final public const LIST_KEY = 'unav_list';
final public const ID_KEY = 'zz_unav_%d';

/**
* Create a forum control rule
*/
public function create(
string $tag,
string $title,
string $target,
string $tests,
bool $testUser,
bool $mandatory,
bool $initial,
): \Gazelle\UserNavigation {
self::$db->prepared_query("
INSERT INTO nav_items
(tag, title, target, tests, test_user, mandatory, initial)
VALUES (?, ?, ?, ?, ?, ?, ?)
", $tag, $title, $target, $tests, $testUser, $mandatory, $initial
);
$id = self::$db->inserted_id();
self::$cache->delete_value(self::LIST_KEY);
return new \Gazelle\UserNavigation($id);
}

public function findById(int $controlId): ?\Gazelle\UserNavigation {
$key = sprintf(self::ID_KEY, $controlId);
$id = self::$cache->get_value($key);
if ($id === false) {
$id = self::$db->scalar("
SELECT ID FROM forums_nav WHERE ID = ?
", $controlId
);
if (!is_null($id)) {
self::$cache->cache_value($key, $id, 7200);
}
}
return $id ? new \Gazelle\UserNavigation($id) : null;
}

public function userControlList(\Gazelle\User $user): array {
$navList = $user->navigationList();
$list = [];
foreach ($this->fullList() as $n) {
if (($n['mandatory'] || in_array($n['id'], $navList)) || (!count($navList) && $n['initial'])) {
$list[] = $n;
}
}
return $list;
}

public function fullList(): array {
$list = self::$cache->get_value(self::LIST_KEY);
if (!$list) {
self::$db->prepared_query("
SELECT
id,
tag,
title,
target,
tests,
test_user,
mandatory,
initial
FROM nav_items
");
$list = self::$db->to_array("id", MYSQLI_ASSOC, false);
self::$cache->cache_value(self::LIST_KEY, $list, 0);
}
return $list;
}
}
10 changes: 7 additions & 3 deletions app/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ public function info(): array {
um.Enabled,
um.Invites,
um.IRCKey,
um.nav_list,
um.Paranoia,
um.PassHash,
um.PermissionID,
Expand Down Expand Up @@ -171,6 +172,7 @@ public function info(): array {
}

$this->info['CommentHash'] = sha1($this->info['AdminComment']);
$this->info['nav_list'] = json_decode($this->info['nav_list'], true);
$this->info['NavItems'] = empty($this->info['NavItems']) ? [] : explode(',', $this->info['NavItems']);
$this->info['ParanoiaRaw'] = $this->info['Paranoia'];
$this->info['Paranoia'] = $this->info['Paranoia'] ? unserialize($this->info['Paranoia']) : [];
Expand Down Expand Up @@ -198,7 +200,7 @@ public function info(): array {
}

/**
* Get the custom navigation configuration.
* Get the custom user link navigation configuration.
*/
public function navigationList(): array {
return $this->info()['NavItems'];
Expand Down Expand Up @@ -1047,11 +1049,13 @@ public function modify(): bool {

$userInfo = [];
if ($this->field('nav_list') !== null) {
$userInfo['NavItems = ?'] = implode(',', $this->clearField('nav_list'));
$userInfo['NavItems = ?'] = implode(',', $this->field('nav_list'));
$this->setField('nav_list', json_encode($this->clearField('nav_list')));
}
if ($this->field('option_list') !== null) {
$userInfo['SiteOptions = ?'] = serialize($this->clearField('option_list'));
$userInfo['SiteOptions = ?'] = serialize($this->clearField('option_list')); // remove field
}

foreach (['AdminComment', 'BanDate', 'BanReason', 'PermittedForums', 'RestrictedForums', 'RatioWatchDownload'] as $field) {
if ($this->field($field) !== null) {
$userInfo["$field = ?"] = $this->clearField($field);
Expand Down
99 changes: 99 additions & 0 deletions app/UserNavigation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php

namespace Gazelle;

class UserNavigation extends BaseObject {
final public const tableName = 'nav_items';
final public const CACHE_KEY = 'unav_%d';

public function flush(): static {
self::$cache->delete_value(sprintf(self::CACHE_KEY, $this->id));
self::$cache->delete_value(Manager\UserNavigation::LIST_KEY);
unset($this->info);
return $this;
}
public function link(): string {
return "<a href=\"{$this->location()}\">User Link Editor</a>";
}
public function location(): string {
return "tools.php?action=navigation";
}

public function info(): array {
if (isset($this->info)) {
return $this->info;
}
$key = sprintf(self::CACHE_KEY, $this->id);
$info = self::$cache->get_value($key);
if ($info === false) {
$info = self::$db->rowAssoc("
SELECT tag,
title,
target,
tests,
test_user,
mandatory,
initial
FROM nav_items
WHERE id = ?
", $this->id
);
self::$cache->cache_value($key, $info, 86400);
}
$this->info = $info;
return $this->info;
}

public function isTestUser(): bool {
return $this->info()['test_user'];
}

public function isMandatory(): bool {
return $this->info()['mandatory'];
}

public function isInitial(): bool {
return $this->info()['initial'];
}

public function tag(): string {
return $this->info()['tag'];
}

public function target(): string {
return $this->info()['target'];
}

public function tests(): string {
return $this->info()['tests'];
}

public function title(): string {
return $this->info()['title'];
}

public function modify(): bool {
$success = parent::modify();
if ($success) {
self::$cache->delete_value(Manager\UserNavigation::LIST_KEY);
}
return $success;
}

public function remove(): int {
$id = $this->id;
$this->flush();
self::$db->prepared_query("
DELETE FROM nav_items WHERE id = ?
", $this->id
);
$affected = self::$db->affected_rows();
if ($affected) {
self::$cache->delete_multi([
sprintf(Manager\UserNavigation::ID_KEY, $id),
Manager\UserNavigation::LIST_KEY,
]);
}
return $affected;
}
}
24 changes: 24 additions & 0 deletions bin/migrate-user-nav-items
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env php
<?php

require_once(__DIR__ . '/../lib/bootstrap.php');

$db = Gazelle\DB::DB();

$db->prepared_query("
SELECT UserID, NavItems FROM users_info WHERE NavItems != '';
");

$manager = new Gazelle\Manager\User;

$n = 0;
foreach ($db->to_array(false, MYSQLI_NUM, false) as [$id, $items]) {
$user = $manager->findById($id);
if (is_null($user)) {
continue;
}
$user->setField('nav_list', array_map('intval', explode(',', $items)))->modify();
++$n;
}

echo "migrated $n users\n";
2 changes: 1 addition & 1 deletion classes/view.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public static function header(string $pageTitle, array $option = []): string {

$PageID = [$Document, $_REQUEST['action'] ?? false, $_REQUEST['type'] ?? false];
$navLinks = [];
foreach ((new Gazelle\Manager\User)->userNavList($Viewer) as $n) {
foreach ((new Gazelle\Manager\UserNavigation)->userControlList($Viewer) as $n) {
[$ID, $Key, $Title, $Target, $Tests, $TestUser, $Mandatory] = array_values($n);
if (str_contains($Tests, ':')) {
$testList = [];
Expand Down
12 changes: 12 additions & 0 deletions misc/phinx/migrations/20240129000000_user_navigation_json.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);

use Phinx\Migration\AbstractMigration;

final class UserNavigationJson extends AbstractMigration {
public function change(): void {
$this->table('users_main')
->addColumn('nav_list', 'json', ['null' => true])
->save();
}
}
80 changes: 39 additions & 41 deletions sections/tools/managers/navigation_alter.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,59 +8,57 @@

authorize();

$P = array_map('trim', $_POST);
$db = Gazelle\DB::DB();
$manager = new Gazelle\Manager\UserNavigation;

if ($_POST['submit'] == 'Delete') {
if (!is_number($_POST['id']) || $_POST['id'] == '') {
error(0);
$id = (int)($_POST['id'] ?? 0);
$control = $manager->findById($id);
if (is_null($control)) {
error(404);
}

$db->prepared_query("DELETE FROM nav_items WHERE id = ?", $P['id']);
$control->remove();
} else {
$Val = new Gazelle\Util\Validator;
$Val->setFields([
['tag', true, 'string', 'The key must be set, and has a max length of 20 characters', ['maxlength' => 20]],
['title', true, 'string', 'The title must be set, and has a max length of 50 characters', ['maxlength' => 50]],
['target', true, 'string', 'The target must be set, and has a max length of 200 characters', ['maxlength' => 200]],
['tests', false, 'string', 'The tests are optional, and have a max length of 200 characters', ['maxlength' => 200]],
['testuser', true, 'checkbox', ''],
$validator = new Gazelle\Util\Validator;
$validator->setFields([
['tag', true, 'string', 'The key must be set, and has a max length of 20 characters', ['maxlength' => 20]],
['title', true, 'string', 'The title must be set, and has a max length of 50 characters', ['maxlength' => 50]],
['target', true, 'string', 'The target must be set, and has a max length of 200 characters', ['maxlength' => 200]],
['tests', false, 'string', 'The tests are optional, and have a max length of 200 characters', ['maxlength' => 200]],
['testuser', true, 'checkbox', ''],
['mandatory', true, 'checkbox', ''],
['default', true, 'checkbox', ''],
['default', true, 'checkbox', ''],
]);
if (!$Val->validate($_POST)) {
error($Val->errorMessage());
if (!$validator->validate($_POST)) {
error($validator->errorMessage());
}

if ($_POST['submit'] == 'Create') {
$db->prepared_query("
INSERT INTO nav_items (tag, title, target, tests, test_user, mandatory, initial)
VALUES (?, ?, ?, ?, ?, ?, ?)",
$P['tag'], $P['title'], $P['target'], $P['tests'],
$P['testuser'] == 'on' ? 1 : 0, $P['mandatory'] == 'on' ? 1 : 0,
$P['default'] == 'on' ? 1 : 0
$control = $manager->create(
trim($_POST['tag']),
trim($_POST['title']),
trim($_POST['target']),
trim($_POST['tests']),
$_POST['testuser'] == 'on',
$_POST['mandatory'] == 'on',
$_POST['default'] == 'on',
);
} elseif ($_POST['submit'] == 'Edit') {
if (!is_number($_POST['id']) || $_POST['id'] == '') {
error(0);
$id = (int)($_POST['id'] ?? 0);
$control = $manager->findById($id);
if (is_null($control)) {
error(404);
}

$db->prepared_query("
UPDATE nav_items
SET tag = ?,
title = ?,
target = ?,
tests = ?,
test_user = ?,
mandatory = ?,
initial = ?
WHERE id = ?",
$P['tag'], $P['title'], $P['target'], $P['tests'],
$P['testuser'] == 'on' ? 1 : 0, $P['mandatory'] == 'on' ? 1 : 0,
$P['default'] == 'on' ? 1 : 0, $P['id']
);
$control->setField('tag', trim($_POST['tag']))
->setField('target', trim($_POST['target']))
->setField('tests', trim($_POST['tests']))
->setField('title', trim($_POST['title']))
->setField('test_user', $_POST['testuser'] == 'on')
->setField('mandatory', $_POST['mandatory'] == 'on')
->setField('initial', $_POST['default'] == 'on')
->modify();
} else {
error(0);
}
}

$Cache->delete_value('nav_items');
header('Location: tools.php?action=navigation');
header("Location: {$control->location()}");
Loading

0 comments on commit 88d1a19

Please sign in to comment.