From 0a8f1ecf564124eacf67bd4e780325e1de36d017 Mon Sep 17 00:00:00 2001 From: ghost Date: Tue, 12 Sep 2023 17:07:53 +0300 Subject: [PATCH] implement API, init data distribution features #1 --- .gitignore | 14 +- README.md | 10 +- database/yggtracker.mwb | Bin 30110 -> 30132 bytes example/environment/crontab | 3 + src/config/app.php.example | 26 +- src/config/nodes.json | 7 + src/crontab/export/feed.php | 458 ++++++++++++++++++++++++++++++++++++ src/crontab/export/push.php | 3 + src/library/database.php | 107 ++++++++- src/public/action.php | 14 +- src/public/api/index.html | 0 src/public/download.php | 4 + src/public/edit.php | 4 + src/public/faq.php | 4 + src/public/index.php | 4 + src/public/magnet.php | 12 +- src/public/welcome.php | 4 + 17 files changed, 647 insertions(+), 27 deletions(-) create mode 100644 src/config/nodes.json create mode 100644 src/crontab/export/feed.php create mode 100644 src/crontab/export/push.php create mode 100644 src/public/api/index.html diff --git a/.gitignore b/.gitignore index 20ae7f6..23d0c29 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,19 @@ -/.vscode/ -/vendor/ +/.vscode + +/vendor /database/yggtracker.mwb.bak + /src/config/app.php +/src/public/api/manifest.json +/src/public/api/users.json +/src/public/api/magnets.json +/src/public/api/downloads.json +/src/public/api/comments.json +/src/public/api/stars.json +/src/public/api/views.json + /composer.lock *test* \ No newline at end of file diff --git a/README.md b/README.md index aace4da..341ca75 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ sphinxsearch * Deploy the database using [MySQL Workbench](https://www.mysql.com/products/workbench) project presented in the `/database` folder * Install [Sphinx Search Server](https://sphinxsearch.com) * Configuration examples presented at `/config` folder +* Make sure `/src/api` folder writable * Set up the `/src/crontab` by following [example](https://github.com/YGGverse/YGGtracker/blob/main/example/environment/crontab) #### Contribute @@ -100,7 +101,14 @@ git checkout -b my-pr-branch-name + [x] Sitemap + [x] RSS + [x] Moderation - + [ ] Federative API + + [x] API + + [x] Manifest + + [x] Users + + [x] Magnets + + [x] Downloads + + [x] Comments + + [x] Stars + + [x] Views #### Donate to contributors diff --git a/database/yggtracker.mwb b/database/yggtracker.mwb index d38ddfe24ef9ad7cfc1bb2916c0f2d503f697461..e860e06a863e00934f88ba39abe8b1b97bdb43d7 100644 GIT binary patch delta 12511 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 delta 12484 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

4=AF7Vu<{k9&A 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 diff --git a/example/environment/crontab b/example/environment/crontab index 378d952..42201b6 100644 --- a/example/environment/crontab +++ b/example/environment/crontab @@ -4,4 +4,7 @@ * * * * * indexer magnet --rotate * * * * * /usr/bin/php /YGGtracker/src/crontab/scrape.php +* 5 * * * /usr/bin/php /YGGtracker/src/crontab/import/feed.php +* 0 * * * /usr/bin/php /YGGtracker/src/crontab/export/feed.php +* * * * * /usr/bin/php /YGGtracker/src/crontab/export/push.php 0 0 * * * /usr/bin/php /YGGtracker/src/crontab/sitemap.php \ No newline at end of file diff --git a/src/config/app.php.example b/src/config/app.php.example index abcf5f5..ac96c7c 100644 --- a/src/config/app.php.example +++ b/src/config/app.php.example @@ -104,10 +104,10 @@ define('MAGNET_STOP_WORDS_SIMILAR', ); // Comment -define('COMMENT_DEFAULT_APPROVED', false); -define('COMMENT_DEFAULT_PUBLIC', false); -define('COMMENT_MIN_LENGTH', 1); -define('COMMENT_MAX_LENGTH', 1000); +define('MAGNET_COMMENT_DEFAULT_APPROVED', false); +define('MAGNET_COMMENT_DEFAULT_PUBLIC', false); +define('MAGNET_COMMENT_MIN_LENGTH', 1); +define('MAGNET_COMMENT_MAX_LENGTH', 1000); // Yggdrasil define('YGGDRASIL_HOST_REGEX', '/^0{0,1}[2-3][a-f0-9]{0,2}:/'); // thanks to @ygguser (https://github.com/YGGverse/YGGo/issues/1#issuecomment-1498182228 ) @@ -116,6 +116,18 @@ define('YGGDRASIL_HOST_REGEX', '/^0{0,1}[2-3][a-f0-9]{0,2}:/'); // thanks to @yg define('CRAWLER_SCRAPE_QUEUE_LIMIT', 1); define('CRAWLER_SCRAPE_TIME_OFFLINE_TIMEOUT', 60*60*24); -// Rules -define('RULE_SUBJECT', 'Common'); -define('RULE_LANGUAGES', 'All'); \ No newline at end of file +// Node +define('NODE_RULE_SUBJECT', 'Common'); +define('NODE_RULE_LANGUAGES', 'All'); + +// API +define('API_VERSION', 1); + +define('API_ENABLED', true); + +define('API_FEED_USERS_ENABLED', true); +define('API_FEED_MAGNETS_ENABLED', true); +define('API_FEED_DOWNLOADS_ENABLED', true); +define('API_FEED_COMMENTS_ENABLED', true); +define('API_FEED_STARS_ENABLED', true); +define('API_FEED_VIEWS_ENABLED', true); \ No newline at end of file diff --git a/src/config/nodes.json b/src/config/nodes.json new file mode 100644 index 0000000..700e9f2 --- /dev/null +++ b/src/config/nodes.json @@ -0,0 +1,7 @@ +[ + { + "description":"YGGtracker instance for main branch tests", + "url":"http://[201:23b4:991a:634d:8359:4521:5576:15b7]/yggtracker", + "manifest":"http://[201:23b4:991a:634d:8359:4521:5576:15b7]/yggtracker/api/manifest.json" + } +] \ No newline at end of file diff --git a/src/crontab/export/feed.php b/src/crontab/export/feed.php new file mode 100644 index 0000000..b2bf0d0 --- /dev/null +++ b/src/crontab/export/feed.php @@ -0,0 +1,458 @@ + [ + 'ISO8601' => date('c'), + 'total' => microtime(true), + ], +]; + +// Define public registry +$public = [ + 'user' => [], + 'magnet' => [], +]; + +// Begin export +try +{ + // Connect DB + $db = new Database(DB_HOST, DB_PORT, DB_NAME, DB_USERNAME, DB_PASSWORD); + + // Init API folder if not exists + @mkdir(__DIR__ . '/../public/api'); + + // Delete cached feeds + @unlink(__DIR__ . '/../public/api/manifest.json'); + + @unlink(__DIR__ . '/../public/api/users.json'); + @unlink(__DIR__ . '/../public/api/magnets.json'); + @unlink(__DIR__ . '/../public/api/comments.json'); + @unlink(__DIR__ . '/../public/api/downloads.json'); + @unlink(__DIR__ . '/../public/api/stars.json'); + @unlink(__DIR__ . '/../public/api/views.json'); + + if (API_ENABLED) + { + // Manifest + $manifest = + [ + 'version' => API_VERSION, + + 'settings' => + [ + 'YGGDRASIL_HOST_REGEX' => YGGDRASIL_HOST_REGEX, + + 'NODE_RULE_SUBJECT' => NODE_RULE_SUBJECT, + 'NODE_RULE_LANGUAGES' => NODE_RULE_LANGUAGES, + + 'USER_DEFAULT_APPROVED' => USER_DEFAULT_APPROVED, + 'USER_AUTO_APPROVE_ON_MAGNET_APPROVE' => USER_AUTO_APPROVE_ON_MAGNET_APPROVE, + 'USER_AUTO_APPROVE_ON_COMMENT_APPROVE' => USER_AUTO_APPROVE_ON_COMMENT_APPROVE, + 'USER_DEFAULT_IDENTICON' => USER_DEFAULT_IDENTICON, + 'USER_IDENTICON_FIELD' => USER_IDENTICON_FIELD, + + 'MAGNET_DEFAULT_APPROVED' => MAGNET_DEFAULT_APPROVED, + 'MAGNET_DEFAULT_PUBLIC' => MAGNET_DEFAULT_PUBLIC, + 'MAGNET_DEFAULT_COMMENTS' => MAGNET_DEFAULT_COMMENTS, + 'MAGNET_DEFAULT_SENSITIVE' => MAGNET_DEFAULT_SENSITIVE, + + 'MAGNET_EDITOR_LOCK_TIMEOUT' => MAGNET_EDITOR_LOCK_TIMEOUT, + + 'MAGNET_TITLE_MIN_LENGTH' => MAGNET_TITLE_MIN_LENGTH, + 'MAGNET_TITLE_MAX_LENGTH' => MAGNET_TITLE_MAX_LENGTH, + + 'MAGNET_PREVIEW_MIN_LENGTH' => MAGNET_PREVIEW_MIN_LENGTH, + 'MAGNET_PREVIEW_MAX_LENGTH' => MAGNET_PREVIEW_MAX_LENGTH, + + 'MAGNET_DESCRIPTION_MIN_LENGTH' => MAGNET_DESCRIPTION_MIN_LENGTH, + 'MAGNET_DESCRIPTION_MAX_LENGTH' => MAGNET_DESCRIPTION_MAX_LENGTH, + + 'MAGNET_COMMENT_DEFAULT_APPROVED' => MAGNET_COMMENT_DEFAULT_APPROVED, + 'MAGNET_COMMENT_DEFAULT_PUBLIC' => MAGNET_COMMENT_DEFAULT_PUBLIC, + 'MAGNET_COMMENT_DEFAULT_PUBLIC' => MAGNET_COMMENT_DEFAULT_PUBLIC, + 'MAGNET_COMMENT_MIN_LENGTH' => MAGNET_COMMENT_MIN_LENGTH, + 'MAGNET_COMMENT_MAX_LENGTH' => MAGNET_COMMENT_MAX_LENGTH, + + 'MAGNET_STOP_WORDS_SIMILAR' => MAGNET_STOP_WORDS_SIMILAR, + ], + + 'users' => API_FEED_USERS_ENABLED ? sprintf('%s/api/users.json', WEBSITE_URL) : false, + 'magnets' => API_FEED_MAGNETS_ENABLED ? sprintf('%s/api/magnets.json', WEBSITE_URL) : false, + 'downloads' => API_FEED_DOWNLOADS_ENABLED ? sprintf('%s/api/downloads.json', WEBSITE_URL) : false, + 'comments' => API_FEED_COMMENTS_ENABLED ? sprintf('%s/api/comments.json', WEBSITE_URL) : false, + 'stars' => API_FEED_STARS_ENABLED ? sprintf('%s/api/stars.json', WEBSITE_URL) : false, + 'views' => API_FEED_VIEWS_ENABLED ? sprintf('%s/api/views.json', WEBSITE_URL) : false, + + 'totals' => + [ + 'magnets' => + [ + 'total' => $db->getMagnetsTotal(), + 'distributed' => $db->getMagnetsTotalByUsersPublic(true), + 'local' => $db->getMagnetsTotalByUsersPublic(false), + ], + 'downloads' => + [ + 'total' => $db->getMagnetDownloadsTotal(), + 'distributed' => $db->findMagnetDownloadsTotalByUsersPublic(true), + 'local' => $db->findMagnetDownloadsTotalByUsersPublic(false), + ], + 'comments' => + [ + 'total' => $db->getMagnetCommentsTotal(), + 'distributed' => $db->findMagnetCommentsTotalByUsersPublic(true), + 'local' => $db->findMagnetCommentsTotalByUsersPublic(false), + ], + 'stars' => + [ + 'total' => $db->getMagnetStarsTotal(), + 'distributed' => $db->findMagnetStarsTotalByUsersPublic(true), + 'local' => $db->findMagnetStarsTotalByUsersPublic(false), + ], + 'views' => + [ + 'total' => $db->getMagnetViewsTotal(), + 'distributed' => $db->findMagnetViewsTotalByUsersPublic(true), + 'local' => $db->findMagnetViewsTotalByUsersPublic(false), + ], + ], + + 'trackers' => json_decode(file_get_contents(__DIR__ . '/../../config/trackers.json')), + 'nodes' => json_decode(file_get_contents(__DIR__ . '/../../config/nodes.json')), + ]; + + /// Dump manifest manifest + if ($handle = fopen(__DIR__ . '/../../public/api/manifest.json', 'w+')) + { + fwrite($handle, json_encode($manifest)); + fclose($handle); + } + + // Users + if (API_FEED_USERS_ENABLED) + { + $users = []; + + foreach ($db->getUsers() as $user) + { + // Dump public data only + if ($user->public === '1') + { + $users[] = (object) + [ + 'userId' => $user->userId, + 'address' => $user->address, + 'timeAdded' => $user->timeAdded, + 'timeUpdated' => $user->timeUpdated, + 'approved' => (bool) $user->approved, + 'magnets' => $db->findMagnetsTotalByUserId($user->userId), + 'downloads' => $db->findMagnetDownloadsTotalByUserId($user->userId), + 'comments' => $db->findMagnetCommentsTotalByUserId($user->userId), + 'stars' => $db->findMagnetStarsTotalByUserId($user->userId), + 'views' => $db->findMagnetViewsTotalByUserId($user->userId), + ]; + } + + // Cache public status + $public['user'][$user->userId] = $user->public; + } + + /// Dump users feed + if ($handle = fopen(__DIR__ . '/../../public/api/users.json', 'w+')) + { + fwrite($handle, json_encode($users)); + fclose($handle); + } + } + + // Magnets + if (API_FEED_MAGNETS_ENABLED) + { + $magnets = []; + + foreach ($db->getMagnets($user->userId) as $magnet) + { + // Dump public data only + if ($magnet->public === '1') + { + // Info Hash + $xt = []; + foreach ($db->findMagnetToInfoHashByMagnetId($magnet->magnetId) as $result) + { + if ($infoHash = $db->getInfoHash($result->infoHashId)) + { + $xt[$infoHash->version] = $infoHash->value; + } + } + + // Keyword Topic + $kt = []; + + foreach ($db->findKeywordTopicByMagnetId($magnet->magnetId) as $result) + { + $kt[] = $db->getKeywordTopic($result->keywordTopicId)->value; + } + + // Address Tracker + $tr = []; + foreach ($db->findAddressTrackerByMagnetId($magnet->magnetId) as $result) + { + $addressTracker = $db->getAddressTracker($result->addressTrackerId); + + $scheme = $db->getScheme($addressTracker->schemeId); + $host = $db->getHost($addressTracker->hostId); + $port = $db->getPort($addressTracker->portId); + $uri = $db->getUri($addressTracker->uriId); + + // Yggdrasil host only + if (!preg_match(YGGDRASIL_HOST_REGEX, str_replace(['[',']'], false, $host->value))) + { + continue; + } + + $tr[] = $port->value ? sprintf('%s://%s:%s%s', $scheme->value, + $host->value, + $port->value, + $uri->value) : sprintf('%s://%s%s', $scheme->value, + $host->value, + $uri->value); + } + + // Acceptable Source + $as = []; + + foreach ($db->findAcceptableSourceByMagnetId($magnet->magnetId) as $result) + { + $acceptableSource = $db->getAcceptableSource($result->acceptableSourceId); + + $scheme = $db->getScheme($acceptableSource->schemeId); + $host = $db->getHost($acceptableSource->hostId); + $port = $db->getPort($acceptableSource->portId); + $uri = $db->getUri($acceptableSource->uriId); + + // Yggdrasil host only + if (!preg_match(YGGDRASIL_HOST_REGEX, str_replace(['[',']'], false, $host->value))) + { + continue; + } + + $as[] = $port->value ? sprintf('%s://%s:%s%s', $scheme->value, + $host->value, + $port->value, + $uri->value) : sprintf('%s://%s%s', $scheme->value, + $host->value, + $uri->value); + } + + // Exact Source + $xs = []; + + foreach ($db->findExactSourceByMagnetId($magnet->magnetId) as $result) + { + $eXactSource = $db->getExactSource($result->eXactSourceId); + + $scheme = $db->getScheme($eXactSource->schemeId); + $host = $db->getHost($eXactSource->hostId); + $port = $db->getPort($eXactSource->portId); + $uri = $db->getUri($eXactSource->uriId); + + // Yggdrasil host only + if (!preg_match(YGGDRASIL_HOST_REGEX, str_replace(['[',']'], false, $host->value))) + { + continue; + } + + $xs[] = $port->value ? sprintf('%s://%s:%s%s', $scheme->value, + $host->value, + $port->value, + $uri->value) : sprintf('%s://%s%s', $scheme->value, + $host->value, + $uri->value); + } + + $magnets[] = (object) + [ + 'magnetId' => $magnet->magnetId, + 'userId' => $magnet->userId, + 'title' => $magnet->title, + 'preview' => $magnet->preview, + 'description' => $magnet->description, + 'comments' => $magnet->comments, + 'sensitive' => $magnet->sensitive, + 'approved' => $magnet->approved, + 'timeAdded' => $magnet->timeAdded, + 'timeUpdated' => $magnet->timeUpdated, + 'dn' => $magnet->dn, + 'xl' => $magnet->xl, + 'xt' => $xt, + 'kt' => $kt, + 'tr' => $tr, + 'as' => $as, + 'xs' => $xs, + ]; + } + + // Cache public status + $public['magnet'][$magnet->magnetId] = $magnet->public; + } + + /// Dump magnets feed + if ($handle = fopen(__DIR__ . '/../../public/api/magnets.json', 'w+')) + { + fwrite($handle, json_encode($magnets)); + fclose($handle); + } + } + + // Downloads + if (API_FEED_DOWNLOADS_ENABLED) + { + $downloads = []; + + foreach ($db->getMagnetDownloads() as $download) + { + // Dump public data only + if (isset($public['magnet'][$download->magnetId]) && $public['magnet'][$download->magnetId] === '1' && + isset($public['user'][$download->userId]) && $public['user'][$download->userId] === '1') + { + $downloads[] = (object) + [ + 'magnetDownloadId' => $download->magnetDownloadId, + 'userId' => $download->userId, + 'magnetId' => $download->magnetId, + 'timeAdded' => $download->timeAdded, + ]; + } + } + + /// Dump downloads feed + if ($handle = fopen(__DIR__ . '/../../public/api/downloads.json', 'w+')) + { + fwrite($handle, json_encode($downloads)); + fclose($handle); + } + } + + // Comments + if (API_FEED_COMMENTS_ENABLED) + { + $comments = []; + + foreach ($db->getMagnetComments() as $comment) + { + // Dump public data only + if (isset($public['magnet'][$comment->magnetId]) && $public['magnet'][$comment->magnetId] === '1' && + isset($public['user'][$comment->userId]) && $public['user'][$comment->userId] === '1') + { + $comments[] = (object) + [ + 'magnetCommentId' => $comment->magnetCommentId, + 'userId' => $comment->userId, + 'magnetId' => $comment->magnetId, + 'timeAdded' => $comment->timeAdded, + ]; + } + } + + /// Dump comments feed + if ($handle = fopen(__DIR__ . '/../../public/api/comments.json', 'w+')) + { + fwrite($handle, json_encode($comments)); + fclose($handle); + } + } + + // Stars + if (API_FEED_STARS_ENABLED) + { + $stars = []; + + foreach ($db->getMagnetStars() as $star) + { + // Dump public data only + if (isset($public['magnet'][$star->magnetId]) && $public['magnet'][$star->magnetId] === '1' && + isset($public['user'][$star->userId]) && $public['user'][$star->userId] === '1') + { + $stars[] = (object) + [ + 'magnetStarId' => $star->magnetStarId, + 'userId' => $star->userId, + 'magnetId' => $star->magnetId, + 'value' => $star->value, + 'timeAdded' => $star->timeAdded, + ]; + } + } + + /// Dump stars feed + if ($handle = fopen(__DIR__ . '/../../public/api/stars.json', 'w+')) + { + fwrite($handle, json_encode($stars)); + fclose($handle); + } + } + // Views + if (API_FEED_VIEWS_ENABLED) + { + $views = []; + + foreach ($db->getMagnetViews() as $view) + { + // Dump public data only + if (isset($public['magnet'][$view->magnetId]) && $public['magnet'][$view->magnetId] === '1' && + isset($public['user'][$view->userId]) && $public['user'][$view->userId] === '1') + { + $views[] = (object) + [ + 'magnetViewId' => $view->magnetViewId, + 'userId' => $view->userId, + 'magnetId' => $view->magnetId, + 'timeAdded' => $view->timeAdded, + ]; + } + } + + /// Dump views feed + if ($handle = fopen(__DIR__ . '/../../public/api/views.json', 'w+')) + { + fwrite($handle, json_encode($views)); + fclose($handle); + } + } + } + +} catch (EXception $e) { + + var_dump($e); +} + +// Debug output +$debug['time']['total'] = microtime(true) - $debug['time']['total']; + +print_r( + array_merge($debug, [ + 'db' => [ + 'total' => [ + 'select' => $db->getDebug()->query->select->total, + 'insert' => $db->getDebug()->query->insert->total, + 'update' => $db->getDebug()->query->update->total, + 'delete' => $db->getDebug()->query->delete->total, + ] + ] + ]) +); \ No newline at end of file diff --git a/src/crontab/export/push.php b/src/crontab/export/push.php new file mode 100644 index 0000000..a46a2f5 --- /dev/null +++ b/src/crontab/export/push.php @@ -0,0 +1,3 @@ +fetch()->result; } + public function getMagnetComments() { + + $this->_debug->query->select->total++; + + $query = $this->_db->query('SELECT * FROM `magnetComment`'); + + return $query->fetchAll(); + } + public function findMagnetCommentsTotalByMagnetId(int $magnetId) : int { $this->_debug->query->select->total++; @@ -1288,18 +1297,18 @@ class Database { return $query->fetch()->result; } - public function findMagnetCommentsTotal(int $userId, int $magnetId) : int { + public function findMagnetCommentsTotal(int $magnetId, int $userId) : int { $this->_debug->query->select->total++; - $query = $this->_db->prepare('SELECT COUNT(*) AS `result` FROM `magnetComment` WHERE `userId` = ? AND `magnetId` = ?'); + $query = $this->_db->prepare('SELECT COUNT(*) AS `result` FROM `magnetComment` WHERE `magnetId` = ? AND `userId` = ?'); - $query->execute([$userId, $magnetId]); + $query->execute([$magnetId, $userId]); return $query->fetch()->result; } - public function getMagnetCommentsTotalByUsersPublic(bool $public) : int { + public function findMagnetCommentsTotalByUsersPublic(bool $public) : int { $this->_debug->query->select->total++; @@ -1312,7 +1321,7 @@ class Database { return $query->fetch()->result; } - public function getMagnetComments(int $magnetId, mixed $magnetCommentIdParent = null) { + public function findMagnetComments(int $magnetId, mixed $magnetCommentIdParent = null) { $this->_debug->query->select->total++; @@ -1355,6 +1364,24 @@ class Database { return $this->_db->lastInsertId(); } + public function getMagnetStars() { + + $this->_debug->query->select->total++; + + $query = $this->_db->query('SELECT * FROM `magnetStar`'); + + return $query->fetchAll(); + } + + public function getMagnetStarsTotal() : int { + + $this->_debug->query->select->total++; + + $query = $this->_db->query('SELECT COUNT(*) AS `result` FROM `magnetStar`'); + + return $query->fetch()->result; + } + public function findMagnetStarsTotalByMagnetId(int $magnetId) : int { $this->_debug->query->select->total++; @@ -1388,6 +1415,19 @@ class Database { return $query->rowCount() ? (bool) $query->fetch()->value : false; } + public function findMagnetStarsTotalByUsersPublic(bool $public) : int { + + $this->_debug->query->select->total++; + + $query = $this->_db->prepare('SELECT COUNT(*) AS `result` FROM `magnetStar` + JOIN `user` ON (`user`.`userId` = `magnetStar`.`userId`) + WHERE `user`.`public` = ?'); + + $query->execute([(int) $public]); + + return $query->fetch()->result; + } + // Magnet download public function addMagnetDownload(int $magnetId, int $userId, int $timeAdded) : int { @@ -1400,6 +1440,24 @@ class Database { return $this->_db->lastInsertId(); } + public function getMagnetDownloads() { + + $this->_debug->query->select->total++; + + $query = $this->_db->query('SELECT * FROM `magnetDownload`'); + + return $query->fetchAll(); + } + + public function getMagnetDownloadsTotal() : int { + + $this->_debug->query->select->total++; + + $query = $this->_db->query('SELECT COUNT(*) AS `result` FROM `magnetDownload`'); + + return $query->fetch()->result; + } + public function findMagnetDownloadsTotal(int $magnetId, int $userId) : int { $this->_debug->query->select->total++; @@ -1433,6 +1491,19 @@ class Database { return $query->fetch()->result; } + public function findMagnetDownloadsTotalByUsersPublic(bool $public) : int { + + $this->_debug->query->select->total++; + + $query = $this->_db->prepare('SELECT COUNT(*) AS `result` FROM `magnetDownload` + JOIN `user` ON (`user`.`userId` = `magnetDownload`.`userId`) + WHERE `user`.`public` = ?'); + + $query->execute([(int) $public]); + + return $query->fetch()->result; + } + // Magnet view public function addMagnetView(int $magnetId, int $userId, int $timeAdded) : int { @@ -1445,13 +1516,20 @@ class Database { return $this->_db->lastInsertId(); } - public function getMagnetViewsTotal(int $magnetId) : int { + public function getMagnetViews() { $this->_debug->query->select->total++; - $query = $this->_db->prepare('SELECT COUNT(*) AS `result` FROM `magnetView` WHERE `magnetId` = ?'); + $query = $this->_db->query('SELECT * FROM `magnetView`'); - $query->execute([$magnetId]); + return $query->fetchAll(); + } + + public function getMagnetViewsTotal() : int { + + $this->_debug->query->select->total++; + + $query = $this->_db->query('SELECT COUNT(*) AS `result` FROM `magnetView`'); return $query->fetch()->result; } @@ -1466,4 +1544,17 @@ class Database { return $query->fetch()->result; } + + public function findMagnetViewsTotalByUsersPublic(bool $public) : int { + + $this->_debug->query->select->total++; + + $query = $this->_db->prepare('SELECT COUNT(*) AS `result` FROM `magnetView` + JOIN `user` ON (`user`.`userId` = `magnetView`.`userId`) + WHERE `user`.`public` = ?'); + + $query->execute([(int) $public]); + + return $query->fetch()->result; + } } \ No newline at end of file diff --git a/src/public/action.php b/src/public/action.php index 818eb22..416b381 100644 --- a/src/public/action.php +++ b/src/public/action.php @@ -318,11 +318,11 @@ switch (isset($_GET['target']) ? urldecode($_GET['target']) : false) // Validate comment value else if (empty($_POST['comment']) || - mb_strlen($_POST['comment']) < COMMENT_MIN_LENGTH || - mb_strlen($_POST['comment']) > COMMENT_MAX_LENGTH) + mb_strlen($_POST['comment']) < MAGNET_COMMENT_MIN_LENGTH || + mb_strlen($_POST['comment']) > MAGNET_COMMENT_MAX_LENGTH) { $response->success = false; - $response->message = sprintf(_('Valid comment value required, %s-%s chars allowed'), COMMENT_MIN_LENGTH, COMMENT_MAX_LENGTH); + $response->message = sprintf(_('Valid comment value required, %s-%s chars allowed'), MAGNET_COMMENT_MIN_LENGTH, MAGNET_COMMENT_MAX_LENGTH); } // Request valid @@ -332,8 +332,8 @@ switch (isset($_GET['target']) ? urldecode($_GET['target']) : false) $user->userId, null, // @TODO implement threads trim($_POST['comment']), - $user->approved || in_array($user->address, MODERATOR_IP_LIST) ? true : COMMENT_DEFAULT_APPROVED, - COMMENT_DEFAULT_PUBLIC, + $user->approved || in_array($user->address, MODERATOR_IP_LIST) ? true : MAGNET_COMMENT_DEFAULT_APPROVED, + MAGNET_COMMENT_DEFAULT_PUBLIC, time())) { // Redirect to referrer page @@ -709,6 +709,10 @@ switch (isset($_GET['target']) ? urldecode($_GET['target']) : false) | + + | + + | diff --git a/src/public/api/index.html b/src/public/api/index.html new file mode 100644 index 0000000..e69de29 diff --git a/src/public/download.php b/src/public/download.php index 3cd7599..0742d93 100644 --- a/src/public/download.php +++ b/src/public/download.php @@ -311,6 +311,10 @@ else | + + | + + | diff --git a/src/public/edit.php b/src/public/edit.php index 34adbd7..5ba9305 100644 --- a/src/public/edit.php +++ b/src/public/edit.php @@ -864,6 +864,10 @@ else { | + + | + + | diff --git a/src/public/faq.php b/src/public/faq.php index 551b672..54cb6f7 100644 --- a/src/public/faq.php +++ b/src/public/faq.php @@ -228,6 +228,10 @@ else if (is_null($user->public)) | + + | + + | diff --git a/src/public/index.php b/src/public/index.php index 52be2da..6b34508 100644 --- a/src/public/index.php +++ b/src/public/index.php @@ -426,6 +426,10 @@ echo '' . PHP_EOL ?> | + + | + + | diff --git a/src/public/magnet.php b/src/public/magnet.php index e5c680a..45e0968 100644 --- a/src/public/magnet.php +++ b/src/public/magnet.php @@ -155,7 +155,7 @@ echo '' . PHP_EOL ?> magnet->magnetId) ?> <?php echo sprintf(_('%s - Comments - %s'), htmlentities($response->magnet->title), WEBSITE_NAME) ?> - getMagnetComments($response->magnet->magnetId) as $magnetComment) { ?> + findMagnetComments($response->magnet->magnetId) as $magnetComment) { ?> user->address == $db->getUser($magnetComment->userId)->address || in_array($response->user->address, MODERATOR_IP_LIST)) { ?> <?php echo sprintf('%s - comment #%s', htmlspecialchars($magnet->title, ENT_QUOTES, 'UTF-8'), $magnetComment->magnetCommentId) ?> @@ -388,7 +388,7 @@ echo '' . PHP_EOL ?>

- getMagnetComments($response->magnet->magnetId) as $magnetComment) { ?> + findMagnetComments($response->magnet->magnetId) as $magnetComment) { ?>
user->address == $db->getUser($magnetComment->userId)->address || @@ -463,8 +463,8 @@ echo '' . PHP_EOL ?> name="comment" value="" placeholder="" - minlength="" - maxlength=""> + minlength="" + maxlength="">
@@ -503,6 +503,10 @@ echo '' . PHP_EOL ?> | + + | + + |
diff --git a/src/public/welcome.php b/src/public/welcome.php index 984aed5..2159f87 100644 --- a/src/public/welcome.php +++ b/src/public/welcome.php @@ -146,6 +146,10 @@ else if (isset($_POST['public'])) | + + | + + |